From 939c0d4e5e2543890545c3a183d9c3373058d5ab Mon Sep 17 00:00:00 2001 From: Moe Date: Sun, 8 Jul 2018 23:32:52 -0700 Subject: [PATCH] Event on Trigger gets Discord notifications and video clip attachments - Traditional Recording must be enabled to get a video clip and JPEG API must be enabled to get a JPEG snapshot (for now) - Email also gets the attachments --- camera.js | 186 +++++++++++++++++++++++++------ languages/en_CA.json | 2 + web/pages/blocks/monitoredit.ejs | 39 ++++--- 3 files changed, 177 insertions(+), 50 deletions(-) diff --git a/camera.js b/camera.js index 97ee0899..f4e55c2c 100644 --- a/camera.js +++ b/camera.js @@ -219,23 +219,6 @@ if(databaseOptions.client === 'sqlite3' && databaseOptions.connection.filename = databaseOptions.connection.filename = __dirname+"/shinobi.sqlite" } s.databaseEngine = knex(databaseOptions) -s.sqlDate = function(value){ - var dateQueryFunction = '' - if(databaseOptions.client === 'sqlite3'){ - value = value.toLowerCase() - if (value.slice(-1) !== 's') { - value = value+'s' - } - dateQueryFunction = "datetime('now', '-"+value+"')" - }else{ - value = value.toUpperCase() - if (value.slice(-1) === 'S') { - value = value.slice(0, -1); - } - dateQueryFunction = "DATE_SUB(NOW(), INTERVAL "+value+")" - } - return dateQueryFunction -} s.mergeQueryValues = function(query,values){ if(!values){values=[]} var valuesNotFunction = true; @@ -295,7 +278,42 @@ s.sqlQuery = function(query,values,onMoveOn){ } }) } - +//discord bot +s.sendDiscordAlert = function(){} +if(config.discordBot && config.discordBot.token && config.discordBot.alertChannel){ + try{ + const Discord = require("discord.js") + const discordBot = new Discord.Client() + + if(config.discordBot.sendAlert===undefined)config.discordBot.sendAlert = true + + discordBot.on('ready', () => { + console.log(`Discord Bot Logged in as ${discordBot.user.tag}!`); + s.sendDiscordAlert = function(data,files){ + if(config.discordBot.sendAlert === false)return false; + if(!data)data = {}; + var sendBody = Object.assign({ + color: 3447003, + title: 'Alert from Shinobi', + description: "", + fields: [], + timestamp: new Date(), + footer: { + icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png", + text: "Shinobi Systems" + } + },data) + discordBot.channels.get(config.discordBot.alertChannel).send({ + embed: sendBody, + files: files + }) + } + }) + discordBot.login(config.discordBot.token) + }catch(err){ + console.log('Could not start Discord bot, please run "npm install discord.js" inside the Shinobi folder.') + } +} //kill any ffmpeg running s.ffmpegKill=function(){ var cmd='' @@ -454,6 +472,21 @@ s.getFunctionParamNames = function(func) { result = []; return result; } +s.getDetectorStreams = function(monitor){ + var pathDir = s.dir.streams+monitor.ke+'/'+monitor.id+'/' + var streamDirItems = fs.readdirSync(pathDir) + var items = [] + streamDirItems.forEach(function(filename){ + if(filename.indexOf('detectorStream') > -1 && filename.indexOf('.m3u8') === -1){ + try{ + items.push(pathDir+filename) + }catch(err){ + console.log(err) + } + } + }) + return items +} s.createPamDiffRegionArray = function(regions,globalSensitivity,fullFrame){ var pamDiffCompliantArray = [], arrayForOtherStuff = [], @@ -3200,8 +3233,71 @@ s.camera=function(x,e,cn,tx){ }).end(); } + var screenshotName = 'Motion_'+(d.mon.name.replace(/[^\w\s]/gi,''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime() + var screenshotBuffer = null + var detectorStreamBuffers = null + + //discord bot + if(d.mon.details.detector_discordbot === '1' && !s.group[d.ke].mon[d.id].detector_discordbot){ + var detector_discordbot_timeout + if(!d.mon.details.detector_discordbot_timeout||d.mon.details.detector_discordbot_timeout===''){ + detector_discordbot_timeout = 1000*60*10; + }else{ + detector_discordbot_timeout = parseFloat(d.mon.details.detector_discordbot_timeout)*1000*60; + } + //lock mailer so you don't get emailed on EVERY trigger event. + s.group[d.ke].mon[d.id].detector_discordbot=setTimeout(function(){ + //unlock so you can mail again. + clearTimeout(s.group[d.ke].mon[d.id].detector_discordbot); + delete(s.group[d.ke].mon[d.id].detector_discordbot); + },detector_discordbot_timeout); + var files = [] + var sendAlert = function(){ + s.sendDiscordAlert({ + author: { + name: s.group[d.ke].mon_conf[d.id].name, + icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png" + }, + title: lang.Event+' - '+screenshotName, + description: lang.EventText1+' '+s.timeObject(new Date).format(), + fields: [], + timestamp: new Date(), + footer: { + icon_url: "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png", + text: "Shinobi Systems" + } + },files) + } + if(!detectorStreamBuffers){ + detectorStreamBuffers = s.getDetectorStreams(d) + } + detectorStreamBuffers.slice(detectorStreamBuffers.length - 2,detectorStreamBuffers.length).forEach(function(filepath,n){ + files.push({ + attachment: filepath, + name: 'Video Clip '+n+'.ts' + }) + }) + if(screenshotBuffer){ + sendAlert() + }else if(d.mon.details.snap === '1'){ + fs.readFile(s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg',function(err, frame){ + if(err){ + s.systemLog(lang.EventText2+' '+d.ke+' '+d.id,err) + }else{ + screenshotBuffer = frame + files.push({ + attachment: screenshotBuffer, + name: screenshotName+'.jpg' + }) + } + sendAlert() + }) + }else{ + sendAlert() + } + } //mailer - if(config.mail&&!s.group[d.ke].mon[d.id].detector_mail&&d.mon.details.detector_mail==='1'){ + if(config.mail && !s.group[d.ke].mon[d.id].detector_mail && d.mon.details.detector_mail === '1'){ s.sqlQuery('SELECT mail FROM Users WHERE ke=? AND details NOT LIKE ?',[d.ke,'%"sub"%'],function(err,r){ r=r[0]; var detector_mail_timeout @@ -3216,35 +3312,53 @@ s.camera=function(x,e,cn,tx){ clearTimeout(s.group[d.ke].mon[d.id].detector_mail); delete(s.group[d.ke].mon[d.id].detector_mail); },detector_mail_timeout); - d.frame_filename='Motion_'+(d.mon.name.replace(/[^\w\s]/gi, ''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime()+'.jpg'; - fs.readFile(s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg',function(err, frame){ + var files = [] + var sendMail = function(){ d.mailOptions = { from: '"ShinobiCCTV" ', // sender address to: r.mail, // list of receivers - subject: lang.Event+' - '+d.frame_filename, // Subject line + subject: lang.Event+' - '+screenshotName, // Subject line html: ''+lang.EventText1+' '+s.timeObject(new Date).format()+'.', - }; - if(err){ - s.systemLog(lang.EventText2+' '+d.ke+' '+d.id,err) - }else{ - d.mailOptions.attachments=[ - { - filename: d.frame_filename, - content: frame - } - ] - d.mailOptions.html=''+lang.EventText3+'' + attachments: files } - Object.keys(d.details).forEach(function(v,n){ + Object.keys(d.details).forEach(function(v,n){ d.mailOptions.html+='
'+v+' : '+d.details[v]+'
' }) nodemailer.sendMail(d.mailOptions, (error, info) => { if (error) { s.systemLog(lang.MailError,error) - return ; + return false; } - }); + }) + } + if(!detectorStreamBuffers){ + detectorStreamBuffers = s.getDetectorStreams(d) + } + detectorStreamBuffers.slice(detectorStreamBuffers.length - 2,detectorStreamBuffers.length).forEach(function(filepath,n){ + files.push({ + attachment: filepath, + name: 'Video Clip '+n+'.ts' + }) }) + if(screenshotBuffer){ + sendMail() + }else if(d.mon.details.snap === '1'){ + fs.readFile(s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg',function(err, frame){ + if(err){ + s.systemLog(lang.EventText2+' '+d.ke+' '+d.id,err) + }else{ + screenshotBuffer = frame + files.push({ + filename: screenshotName+'.jpg', + content: frame + }) + d.mailOptions.html=''+lang.EventText3+'' + } + sendMail() + }) + }else{ + sendMail() + } }); } if(d.mon.details.detector_command_enable==='1'&&!s.group[d.ke].mon[d.id].detector_command){ diff --git a/languages/en_CA.json b/languages/en_CA.json index 295d9b5a..75293d7e 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -377,7 +377,9 @@ "Allow Next Trigger": "Allow Next Trigger in Milliseconds", "Save Events to SQL": "Save Events to SQL", "Email on Trigger": "Email on Trigger Emails go to the main account holder's login address.", + "Discord Alert on Trigger": "Discord Alert on Trigger", "Allow Next Email": "Allow Next Email in Minutes", + "Allow Next Discord Alert": "Allow Next Discord Alert in Minutes", "How to Record": "How to Record", "Trigger Record": "Trigger Record", "Recording Timeout": "Recording Timeout in Minutes", diff --git a/web/pages/blocks/monitoredit.ejs b/web/pages/blocks/monitoredit.ejs index 5a9ac683..8dcf2e2c 100644 --- a/web/pages/blocks/monitoredit.ejs +++ b/web/pages/blocks/monitoredit.ejs @@ -953,20 +953,31 @@
-
-
- -
-
- -
+
+ +
+
+ +
+
+ +
+
+