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.');
+ }
+};