update chain framework

action-chain
Moe 2023-09-18 21:16:55 -07:00
parent 21a6d2a355
commit a5d36f0e44
9 changed files with 244 additions and 41 deletions

View File

@ -33,6 +33,8 @@ require('./libs/ffmpeg.js')(s,config,lang, async () => {
require('./libs/auth.js')(s,config,lang)
//express web server with ejs
const app = require('./libs/webServer.js')(s,config,lang,io)
//chain framework
require('./libs/chains.js')(s,config,lang,app,io)
//data port
require('./libs/dataPort.js')(s,config,lang,app,io)
//page layout load
@ -97,6 +99,4 @@ require('./libs/ffmpeg.js')(s,config,lang, async () => {
require('./libs/cron.js')(s,config,lang)
//video browser functions
require('./libs/videoBrowser.js')(s,config,lang,app,io)
//chain framework
require('./libs/chains.js')(s,config,lang,app,io)
})

View File

@ -1,5 +1,6 @@
module.exports = function(s,config,lang,app,io){
s.loadedChains = {}
s.recentSnapshots = {}
s.loadedChainActions = {}
const {
loadChains,

View File

@ -1,10 +1,13 @@
const moment = require('moment')
module.exports = function(s,config,lang){
const {
findMonitorsAssociatedToTags,
createEventBasedRecording,
} = require('../events/utils.js')(s,config,lang)
const {
addExtenderAction,
getMonitorIdFromData,
doMonitorActionForItem,
} = require('./utils.js')(s,config)
// static actions
addExtenderAction('forceRecord',(groupKey,item,data) => {
@ -12,26 +15,12 @@ module.exports = function(s,config,lang){
const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId]
const monitorDetails = monitorConfig.details
const secondBefore = (parseInt(monitorDetails.detector_buffer_seconds_before) || 5) + 1
createEventBasedRecording(monitor,moment(eventTime).subtract(secondBefore,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
createEventBasedRecording(monitorConfig,moment(eventTime).subtract(secondBefore,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
}
function recurseMonitorIds(monitorIds){
for (let i = 0; i < monitorIds.length; i++) {
const monitorId = item[i]
beginRecording(monitorId)
}
}
if(item.allMonitors){
const monitorIds = Object.keys(s.group[groupKey].rawMonitorConfigurations)
recurseMonitorIds(monitorIds)
}else{
if(item.monitorIds)recurseMonitorIds(item.monitorIds);
// for (let i = 0; i < item.monitorTags.length; i++) {
// const monitorIds = someHowGetMonitorIdsFromTag(item[i])
// recurseMonitorIds(monitorIds)
// }
}
})
doMonitorActionForItem(groupKey,item,data,beginRecording)
});
addExtenderAction('createLog',(groupKey,item,data) => {
const monitorId = item.monitorId || item.monitorIdFromData ? getMonitorIdFromData(data) : '$USER';
s.userLog({
ke: groupKey,
mid: monitorId,
@ -39,5 +28,5 @@ module.exports = function(s,config,lang){
type: item.title,
text: item.text
});
})
});
}

View File

@ -1,5 +1,8 @@
module.exports = function(s,config){
let loadedChains = s.loadedChains
const {
getMonitorIdFromData,
} = require('./utils.js')(s,config)
var loadedChains = s.loadedChains
async function loadChains(){
const selectResponse = await s.knexQueryPromise({
action: "select",
@ -10,17 +13,43 @@ module.exports = function(s,config){
foundChains.forEach(loadChain);
}
function loadChain(item){
// "item" should always be the first item in a chain
const name = item.name
const groupKey = item.ke
const extenderThatStartsThis = item.ignitor
item.conditions = JSON.parse(item.conditions)
item.next = JSON.parse(item.next)
if(!loadedChains[extenderThatStartsThis])loadedChains[extenderThatStartsThis] = {}
if(!loadedChains[extenderThatStartsThis][groupKey])loadedChains[extenderThatStartsThis][groupKey] = {}
loadedChains[extenderThatStartsThis][groupKey] = item
loadedChains[extenderThatStartsThis][groupKey][name] = item
}
function unloadChain(item){
const name = item.name
const groupKey = item.ke
const extenderThatStartsThis = item.ignitor
delete(loadedChains[extenderThatStartsThis][groupKey][name])
}
function saveChain(item){
await loadChain(item)
return s.knexQueryPromise({
action: "insert",
table: "Chains",
insert: Object.assign({},item,{
conditions: JSON.stringify(item.conditions),
next: JSON.stringify(item.next),
})
})
}
function deleteChain(item){
await unloadChain(item)
return s.knexQueryPromise({
action: "delete",
table: "Chains",
where: {
ke: item.ke,
name: item.name,
ignitor: item.ignitor,
}
})
}
function evaluateCondition(condition,toCheck){
var param = toCheck[condition.p1]
@ -68,7 +97,7 @@ module.exports = function(s,config){
}
if(hasOpenBracket)++numberOfOpenAndCloseBrackets;
if(hasCloseBracket)++numberOfOpenAndCloseBrackets;
if(matrices)conditionChain[place].matrixCount = matrices.length
// if(matrices)conditionChain[place].matrixCount = matrices.length
switch(condition.p1){
case'tag':
case'x':
@ -102,9 +131,14 @@ module.exports = function(s,config){
}
}
break;
default:
conditionChain[place].ok = evaluateCondition(condition,d.details)
case'mid':
var requiredMonitorId = getMonitorIdFromData(data);
var monitorId = condition.p3;
conditionChain[place].ok = monitorId === requiredMonitorId;
break;
// default:
// conditionChain[place].ok = evaluateCondition(condition,d.details)
// break;
}
}
const allowBrackets = numberOfOpenAndCloseBrackets === 0 || isEven(numberOfOpenAndCloseBrackets);
@ -143,8 +177,8 @@ module.exports = function(s,config){
if(theChain){
for (const groupKey in theChain) {
const items = theChain[groupKey]
for (let i = 0; i < items.length; i++) {
const item = items[i];
for (const name in items) {
const item = items[name];
executeChainItem(groupKey,item,data);
}
}
@ -154,10 +188,13 @@ module.exports = function(s,config){
return {
loadChains,
loadChain,
unloadChain,
saveChain,
deleteChain,
evaluateCondition,
checkChainItemConditions,
executeChainItem,
addChainControllerToExtender,
getMonitorIdFromData,
}
}

View File

@ -1,8 +1,76 @@
module.exports = function(s,config){
var recentSnapshots = s.recentSnapshots
function addExtenderAction(name,theAction){
s.loadedChainActions[name] = theAction
}
function getMonitorIdFromData(data){
let monitorId = null
data.forEach((obj) => {
if(obj.mid || obj.id)monitorId = monitorId || obj.mid || obj.id;
})
return monitorId;
}
async function getRecentSnapshot(monitorConfig){
const monitorId = monitorConfig.mid
const groupKey = monitorConfig.ke
if(recentSnapshots[`${groupKey}${monitorId}`]){
return recentSnapshots[`${groupKey}${monitorId}`]
}
const { screenShot, isStaticFile } = await s.getRawSnapshotFromMonitor(monitorConfig,{
secondsInward: monitorConfig.details.snap_seconds_inward
});
recentSnapshots[`${groupKey}${monitorId}`] = screenShot;
setTimeout(() => {
delete(recentSnapshots[`${groupKey}${monitorId}`]);
}, 20 * 1000)
return screenShot
}
async function getRecentRecording(monitorConfig){
const monitorId = monitorConfig.mid
const groupKey = monitorConfig.ke
let videoPath = null
let videoName = null
const eventBasedRecording = await getEventBasedRecordingUponCompletion({
ke: groupKey,
mid: monitorId
})
if(eventBasedRecording.filePath){
videoPath = eventBasedRecording.filePath
videoName = eventBasedRecording.filename
}else{
const siftedVideoFileFromRam = await s.mergeDetectorBufferChunks(monitorConfig)
videoPath = siftedVideoFileFromRam.filePath
videoName = siftedVideoFileFromRam.filename
}
return {
path: videoPath,
name: videoName
}
}
function recurseMonitorIds(monitorIds,someFunction){
for (let i = 0; i < monitorIds.length; i++) {
const monitorId = item[i]
someFunction(monitorId)
}
}
function doMonitorActionForItem(groupKey,item,data,someFunction){
if(item.monitorIdFromData){
const monitorId = getMonitorIdFromData(data)
if(monitorId)someFunction(monitorId)
}else if(item.allMonitors){
const monitorIds = Object.keys(s.group[groupKey].rawMonitorConfigurations)
recurseMonitorIds(monitorIds,someFunction)
}else{
if(item.monitorIds)recurseMonitorIds(item.monitorIds,someFunction);
const monitorTags = item.monitorTags || []
const monitorIdsFromTags = findMonitorsAssociatedToTags(groupKey,monitorTags)
recurseMonitorIds(monitorIdsFromTags,someFunction)
}
}
return {
addExtenderAction,
getMonitorIdFromData,
getRecentSnapshot,
doMonitorActionForItem,
}
}

View File

@ -834,13 +834,16 @@ module.exports = (s,config,lang) => {
return `${icon} ${tag}`;
}
function getObjectTagsFromMatrices(d){
if(d.details.reason === 'motion'){
const eventDetails = d.details
if(!eventDetails){
return []
}else if(eventDetails.reason === 'motion'){
return [getTagWithIcon(lang.Motion)]
}else if(d.details.matrices){
const matrices = d.details.matrices
}else if(eventDetails.matrices){
const matrices = eventDetails.matrices
return [...new Set(matrices.map(matrix => getTagWithIcon(matrix.tag)))];
}
return [getTagWithIcon(d.details.reason)]
return [getTagWithIcon(eventDetails.reason)]
}
function getObjectTagNotifyText(d){
const monitorId = d.mid || d.id
@ -870,5 +873,6 @@ module.exports = (s,config,lang) => {
triggerEvent: triggerEvent,
addEventDetailsToString: addEventDetailsToString,
getEventBasedRecordingUponCompletion: getEventBasedRecordingUponCompletion,
findMonitorsAssociatedToTags,
}
}

View File

@ -302,13 +302,14 @@ module.exports = function(s,config,lang){
}
s.mergeDetectorBufferChunks = function(monitor,callback){
return new Promise((resolve,reject) => {
var pathDir = s.dir.streams+monitor.ke+'/'+monitor.id+'/'
const monitorId = monitor.mid || monitor.id;
var pathDir = s.dir.streams+monitor.ke+'/'+monitorId+'/'
var mergedFile = s.formattedTime()+'.mp4'
var mergedFilepath = pathDir+mergedFile
fs.readdir(pathDir,function(err,streamDirItems){
var items = []
var copiedItems = []
var videoLength = s.group[monitor.ke].rawMonitorConfigurations[monitor.id].details.detector_send_video_length
var videoLength = s.group[monitor.ke].rawMonitorConfigurations[monitorId].details.detector_send_video_length
if(!videoLength || videoLength === '')videoLength = '10'
if(videoLength.length === 1)videoLength = '0' + videoLength
var createMerged = function(copiedItems){

View File

@ -5,6 +5,13 @@ module.exports = function(s,config,lang,getSnapshot){
getObjectTagNotifyText,
getEventBasedRecordingUponCompletion,
} = require('../events/utils.js')(s,config,lang)
const {
addExtenderAction,
doMonitorActionForItem,
} = require('./utils.js')(s,config)
const {
getRecentSnapshot,
} = require('../chains/utils.js')(s,config)
//discord bot
if(config.discordBot === true){
try{
@ -224,6 +231,93 @@ module.exports = function(s,config,lang,getSnapshot){
},[],monitorConfig.ke)
}
}
const loadedChainAction = async (groupKey,item,data) => {
const currentTime = new Date()
const timeoutUntilAllowAgain = item.timeoutUntilAllowAgain
function replaceParamsInString(monitorConfig){
const name = monitorConfig.name
const objectTags = data[0] && data[0].details ? data[0].details.matrices.map(item => item.tag) : []
const newString = item.text
.replace(/${MONITOR_NAME}/g, name)
.replace(/${OBJECT_TAGS}/g, objectTags.join(', '))
return newString;
}
const notifyText = replaceParamsInString(monitorConfig,data)
async function sendText(){
sendMessage({
author: {
name: "",
icon_url: config.iconURL
},
title: notifyText,
description: notifyText+' '+currentTime,
fields: [],
timestamp: currentTime,
footer: {
icon_url: config.iconURL,
text: "Shinobi Systems"
}
},[], groupKey)
}
async function sendSnapshot(monitorId){
const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId]
const monitorName = monitorConfig.name
const snapshotBuffer = await getRecentSnapshot(monitorConfig)
// const notifyText = getObjectTagNotifyText(data[0])
sendMessage({
author: {
name: monitorName,
icon_url: config.iconURL
},
title: notifyText,
description: notifyText+' '+currentTime,
fields: [],
timestamp: currentTime,
footer: {
icon_url: config.iconURL,
text: "Shinobi Systems"
}
},[
{
attachment: snapshotBuffer,
name: notifyText + '.jpg'
}
], groupKey)
}
async function sendVideo(monitorId){
const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId]
const monitorName = monitorConfig.name
const video = await getRecentRecording(monitorConfig)
// const notifyText = getObjectTagNotifyText(data[0])
if(videoPath){
sendMessage({
author: {
name: monitorName,
icon_url: config.iconURL
},
title: `${notifyText}`,
description: notifyText+' '+currentTime,
fields: [],
timestamp: currentTime,
footer: {
icon_url: config.iconURL,
text: "Shinobi Systems"
}
},[
{
attachment: videoPath,
name: notifyText + '.mp4'
}
],d.ke)
}
}
async function sendMedia(monitorId){
if(item.sendSnapshot)await sendSnapshot(monitorId);
if(item.sendVideo)await sendVideo(monitorId);
}
await sendText()
doMonitorActionForItem(groupKey,item,data,sendMedia)
}
s.loadGroupAppExtender(loadDiscordBotForUser)
s.unloadGroupAppExtender(unloadDiscordBotForUser)
s.onTwoFactorAuthCodeNotification(onTwoFactorAuthCodeNotificationForDiscord)
@ -231,6 +325,7 @@ module.exports = function(s,config,lang,getSnapshot){
s.onEventTriggerBeforeFilter(onEventTriggerBeforeFilterForDiscord)
s.onDetectorNoTriggerTimeout(onDetectorNoTriggerTimeoutForDiscord)
s.onMonitorUnexpectedExit(onMonitorUnexpectedExitForDiscord)
s.definitions["Monitor Settings"].blocks["Notifications"].info[0].info.push(
{
"name": "detail=notify_discord",
@ -389,6 +484,9 @@ module.exports = function(s,config,lang,getSnapshot){
}
]
})
s.loadedChainActions['notifyDiscord'] = (groupKey,item,data) => {
}
}catch(err){
console.log(err)
console.log('Could not start Discord bot, please run "npm install discord.js" inside the Shinobi folder.')

View File

@ -1,22 +1,25 @@
// example chain
module.exports = {
name: 'Sample Chain',
ke: 'groupKey',
mid: 'monitorId',
ignitor: 'onEventTrigger',
conditions: [
{p1: 'time', p2: '>', p3: '05:00:00', p4: '&&'},
{p1: 'time', p2: '<', p3: '13:00:00', p4: '&&'}
{p1: 'time', p2: '<', p3: '13:00:00', p4: '&&'},
{p1: 'mid', p2: '===', p3: 'monitorId', p4: '&&'},
],
next: [
{
action: 'forceRecord',
allMonitors: false,
monitorIdFromData: true,
monitorIds: [],
monitorTags: [],
next: [
{
action: 'createLog',
monitorId: '$USER', //actual monitor id or $USER for user level log
monitorIdFromData: true,
// monitorId: '$USER', //actual monitor id or $USER for user level log
title: "Text Log on Recording After Event",
text: "Recording has Started"
},
@ -28,13 +31,14 @@ module.exports = {
timeoutUntilAllowAgain: 1000 * 60 * 10, // 10 minutes
sendSnapshot: true,
sendVideo: true,
sendForTriggeredMonitorOnly: true,
monitorIdFromData: true,
monitorIds: [],
monitorTags: [],
next: [
{
action: 'createLog',
monitorId: '$USER', //actual monitor id or $USER for user level log
monitorIdFromData: true,
// monitorId: '$USER', //actual monitor id or $USER for user level log
title: "Discord Note",
text: 'Person detected in Back-1'
},
@ -45,6 +49,7 @@ module.exports = {
sendSnapshot: true,
sendVideo: true,
allMonitors: false,
monitorIdFromData: true,
monitorIds: [],
monitorTags: [],
},