From 3f235022145c60230f983831db3dbf3fca9e78ec Mon Sep 17 00:00:00 2001 From: Moe Date: Tue, 7 Dec 2021 21:56:41 -0800 Subject: [PATCH] start rebuilding email notifications to be configured from user account (not active yet) --- languages/en_CA.json | 2 + libs/notifications/emailByUser.js | 412 ++++++++++++++++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100644 libs/notifications/emailByUser.js diff --git a/languages/en_CA.json b/languages/en_CA.json index 5b414d1c..864066b5 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -734,6 +734,7 @@ "NotifyErrorText": "Sending Notification caused an Error", "Check the Channel ID": "Check the Channel ID", "Check the Recipient ID": "Check the Recipient ID", + "AppNotEnabledText": "App Not Enabled, Enable it in your Account Settings.", "DiscordNotEnabledText": "Discord Bot Not Enabled, Enable it in your Account Settings.", "Account Settings": "Account Settings", "How to Record": "How to Record", @@ -1254,6 +1255,7 @@ "Close All Monitors": "Close All Monitors", "Daily Events": "Daily Events", "Send Notification": "Send Notification", + "Send to": "Send to", "setMaxStorageAmountText": "You should set your Max Storage Amount in your Account Settings located on the left. Find the option under the Profile section. Default is 10 GB.", "Save Events": "Save Events", "Original Choice": "Original Choice", diff --git a/libs/notifications/emailByUser.js b/libs/notifications/emailByUser.js new file mode 100644 index 00000000..2eeff646 --- /dev/null +++ b/libs/notifications/emailByUser.js @@ -0,0 +1,412 @@ +var fs = require('fs'); +const { + template, + checkEmail, +} = require("./emailUtils.js") +module.exports = function (s, config, lang, getSnapshot) { + const { getEventBasedRecordingUponCompletion } = require('../events/utils.js')(s, config, lang); + const nodeMailer = require('nodemailer'); + try { + const sendMessage = async function (sendBody, files, groupKey) { + const transporter = s.group[groupKey].emailClient; + if (!transporter) { + s.userLog( + { ke: groupKey, mid: '$USER' }, + { + type: lang.NotifyErrorText, + msg: { + msg: lang.AppNotEnabledText, + app: lang.Email + }, + } + ); + return; + } + try { + const appOptions = s.group[groupKey].emailClientOptions.transport; + const sendTo = appOptions.sendTo; + const sendData = { + from: `"shinobi.video" <${transportOptions.auth.user}>`, + to: sendTo, + subject: sendBody.subject, + html: sendBody.html, + }; + if (files.length > 0) { + // sadly pushover allows only ONE single attachment + msg.file = { + name: files[0].name, + data: files[0].attachment, + }; + } + transporter.sendMail(sendData, function (err, result) { + if (err) { + throw err; + } + s.userLog(result); + s.debugLog(result); + }); + } catch (err) { + s.userLog( + { ke: groupKey, mid: '$USER' }, + { type: lang.NotifyErrorText, msg: err } + ); + } + }; + + const loadAppForUser = function (user) { + const userDetails = s.parseJSON(user.details); + const optionsHost = userDetails.emailClient_host + const optionsUser = userDetails.emailClient_user + const optionsSendTo = userDetails.emailClient_sendTo || '' + if ( + !s.group[user.ke].emailClient && + userDetails.emailClient === '1' && + optionsHost && + optionsUser && + optionsSendTo + ){ + const optionsPass = userDetails.emailClient_pass || '' + const optionsSecure = userDetails.emailClient_secure === '1' ? true : false + const optionsPort = isNaN(userDetails.emailClient_port) ? (optionsSecure ? 465 : 587) : parseInt(userDetails.emailClient_port) + const clientOptions = { + host: optionsHost, + port: optionsPort, + secure: optionsSecure, + auth: { + user: optionsUser, + pass: optionsPass + } + } + s.group[user.ke].emailClientOptions = { + transport: clientOptions, + sendTo: optionsSendTo, + } + s.group[user.ke].emailClient = nodeMailer.createTransport(clientOptions) + } + }; + + const unloadAppForUser = function (user) { + if ( + s.group[user.ke].emailClient && + s.group[user.ke].emailClient.close + ) { + s.group[user.ke].emailClient.close(); + } + delete s.group[user.ke].emailClient; + delete s.group[user.ke].emailClientOptions; + }; + + const onTwoFactorAuthCodeNotificationForApp = function (r) { + // r = user + if (r.details.factor_emailClient === '1') { + sendMessage({ + subject: r.lang['2-Factor Authentication'], + html: template.createFramework({ + title: r.lang['2-Factor Authentication'], + subtitle: r.lang['Enter this code to proceed'], + body: ''+s.factorAuth[r.ke][r.uid].key+'

'+r.lang.FactorAuthText1, + }), + },[],r.ke); + } + }; + + const onEventTriggerForApp = async (d, filter) => { + const monitorConfig = s.group[d.ke].rawMonitorConfigurations[d.id]; + // d = event object + if ( + s.group[d.ke].emailClient && + (filter.emailClient || monitorConfig.details.notify_emailClient === '1') && + !s.group[d.ke].activeMonitors[d.id].detector_emailClient + ) { + var detector_emailClient_timeout; + if ( + !monitorConfig.details.detector_emailClient_timeout || + monitorConfig.details.detector_emailClient_timeout === '' + ) { + detector_emailClient_timeout = 1000 * 60 * 10; + } else { + detector_emailClient_timeout = + parseFloat( + monitorConfig.details.detector_emailClient_timeout + ) * + 1000 * + 60; + } + s.group[d.ke].activeMonitors[d.id].detector_emailClient = setTimeout(function () { + s.group[d.ke].activeMonitors[d.id].detector_emailClient = null; + }, detector_emailClient_timeout); + + // lock passed + const sendMail = function(files){ + const infoRows = [] + Object.keys(d.details).forEach(function(key){ + var value = d.details[key] + var text = value + if(value instanceof Object){ + text = JSON.stringify(value,null,3) + } + infoRows.push(template.createRow({ + title: key, + text: text + })) + }) + sendMessage({ + subject: lang.Event+' - '+d.screenshotName, + html: template.createFramework({ + title: lang.EventText1 + ' ' + d.currentTimestamp, + subtitle: lang.Event, + body: infoRows.join(''), + }), + },files || [],r.ke) + } + if(monitorConfig.details.detector_mail_send_video === '1'){ + let videoPath = null + let videoName = null + const eventBasedRecording = await getEventBasedRecordingUponCompletion({ + ke: d.ke, + mid: d.mid + }) + if(eventBasedRecording.filePath){ + videoPath = eventBasedRecording.filePath + videoName = eventBasedRecording.filename + }else{ + const siftedVideoFileFromRam = await s.mergeDetectorBufferChunks(d) + videoPath = siftedVideoFileFromRam.filePath + videoName = siftedVideoFileFromRam.filename + } + if(videoPath){ + fs.readFile(mergedFilepath,function(err,buffer){ + if(buffer){ + sendMail([ + { + filename: videoName, + content: buffer + } + ]) + } + }) + } + } + await getSnapshot(d,monitorConfig) + sendMail([ + { + filename: d.screenshotName + '.jpg', + content: d.screenshotBuffer + } + ]) + } + }; + + const onEventTriggerBeforeFilterForApp = function (d, filter) { + filter.emailClient = false; + }; + + const onDetectorNoTriggerTimeoutForApp = function (e) { + //e = monitor object + var currentTime = new Date(); + if (e.details.detector_notrigger_emailClient === '1') { + var html = + '*' + + lang.NoMotionEmailText2 + + ' ' + + (e.details.detector_notrigger_timeout || 10) + + ' ' + + lang.minutes + + '.*\n'; + html += + '**' + lang['Monitor Name'] + '** : ' + e.name + '\n'; + html += '**' + lang['Monitor ID'] + '** : ' + e.id + '\n'; + html += currentTime; + sendMessage({ + subject: lang['"No Motion" Detector'], + html: template.createFramework({ + title: lang['"No Motion" Detector'], + subtitle: 'Shinobi Event', + body: html, + }), + },[],e.ke); + } + }; + + const onMonitorUnexpectedExitForApp = (monitorConfig) => { + if ( + monitorConfig.details.notify_emailClient === '1' && + monitorConfig.details.notify_onUnexpectedExit === '1' + ){ + const ffmpegCommand = s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].ffmpeg + const subject = lang['Process Unexpected Exit'] + ' : ' + monitorConfig.name + const currentTime = new Date(); + sendMessage({ + subject: subject, + html: template.createFramework({ + title: subject, + subtitle: lang['Process Crashed for Monitor'], + body: ffmpegCommand, + footerText: currentTime + }), + },[],monitorConfig.ke); + } + }; + + s.loadGroupAppExtender(loadAppForUser); + s.unloadGroupAppExtender(unloadAppForUser); + s.onTwoFactorAuthCodeNotification(onTwoFactorAuthCodeNotificationForApp); + s.onEventTrigger(onEventTriggerForApp); + s.onEventTriggerBeforeFilter(onEventTriggerBeforeFilterForApp); + s.onDetectorNoTriggerTimeout(onDetectorNoTriggerTimeoutForApp); + s.onMonitorUnexpectedExit(onMonitorUnexpectedExitForApp); + s.definitions['Monitor Settings'].blocks[ + 'Notifications' + ].info[0].info.push({ + name: 'detail=notify_emailClient', + field: lang.Email, + description: '', + default: '0', + example: '', + selector: 'h_det_emailClient', + fieldType: 'select', + possible: [ + { + name: lang.No, + value: '0', + }, + { + name: lang.Yes, + value: '1', + }, + ], + }); + s.definitions['Monitor Settings'].blocks['Notifications'].info.push( + { + evaluation: "$user.details.use_emailClient !== '0'", + isFormGroupGroup: true, + name: lang.Email, + color: 'blue', + 'section-class': 'h_det_emailClient_input h_det_emailClient_1', + info: [ + { + name: 'detail=detector_emailClient_timeout', + field: `${lang['Allow Next Alert']} (${lang['on Event']})`, + default: '10', + }, + ], + } + ); + s.definitions['Account Settings'].blocks[ + '2-Factor Authentication' + ].info.push({ + name: 'detail=factor_emailClient', + field: lang.Email, + default: '1', + example: '', + fieldType: 'select', + possible: [ + { + name: lang.No, + value: '0', + }, + { + name: lang.Yes, + value: '1', + }, + ], + }); + s.definitions['Account Settings'].blocks['Email'] = { + evaluation: "$user.details.use_emailClient !== '0'", + field: lang.Email, + color: 'blue', + info: [ + { + name: 'detail=emailClient', + selector: 'u_emailClient', + field: lang.Enabled, + default: '0', + example: '', + fieldType: 'select', + possible: [ + { + name: lang.No, + value: '0', + }, + { + name: lang.Yes, + value: '1', + }, + ], + }, + { + hidden: true, + field: lang.Host, + name: 'detail=emailClient_host', + example: 'smtp.gmail.com', + 'form-group-class': 'u_emailClient_input u_emailClient_1', + }, + { + hidden: true, + field: lang.Port, + name: 'detail=emailClient_port', + example: '587', + 'form-group-class': 'u_emailClient_input u_emailClient_1', + }, + { + name: 'detail=emailClient_secure', + field: lang.Secure, + default: '0', + example: '', + fieldType: 'select', + possible: [ + { + name: lang.No, + value: '0', + }, + { + name: lang.Yes, + value: '1', + }, + ], + }, + { + hidden: true, + field: lang.Email, + name: 'detail=emailClient_user', + example: 'test@gmail.com', + 'form-group-class': 'u_emailClient_input u_emailClient_1', + }, + { + hidden: true, + field: lang.Password, + name: 'detail=emailClient_pass', + 'form-group-class': 'u_emailClient_input u_emailClient_1', + }, + { + hidden: true, + field: lang['Send to'], + name: 'detail=emailClient_sendTo', + 'form-group-class': 'u_emailClient_input u_emailClient_1', + }, + ], + }; + s.definitions["Event Filters"].blocks["Action for Selected"].info.push({ + "name": "actions=emailClient", + "field": lang['Email'], + "fieldType": "select", + "form-group-class": "actions-row", + "default": "", + "example": "1", + "possible": [ + { + "name": lang['Original Choice'], + "value": "", + "selected": true + }, + { + "name": lang.Yes, + "value": "1", + } + ] + }) + } catch (err) { + console.log(err); + console.log('Could not engage Email notifications.'); + } +};