diff --git a/libs/notification.js b/libs/notification.js index 296f462a..a8ad9c48 100644 --- a/libs/notification.js +++ b/libs/notification.js @@ -24,4 +24,5 @@ module.exports = function(s,config,lang){ require('./notifications/pushover.js')(s,config,lang,getSnapshot) require('./notifications/webhook.js')(s,config,lang,getSnapshot) require('./notifications/mqtt.js')(s,config,lang,getSnapshot) + require('./notifications/matrix.js')(s,config,lang,getSnapshot) } diff --git a/libs/notifications/matrix.js b/libs/notifications/matrix.js new file mode 100644 index 00000000..02be6574 --- /dev/null +++ b/libs/notifications/matrix.js @@ -0,0 +1,387 @@ +cat Shinobi/libs/notifications/matrix.js +const fs = require("fs") +const fetch = require("node-fetch") +module.exports = function(s,config,lang,getSnapshot){ + const sdk = require("matrix-js-sdk") + const { + getEventBasedRecordingUponCompletion, + } = require('../events/utils.js')(s,config,lang) + //matrix bot + if(config.matrixBot === true){ + try{ + function sendFile(file,groupKey){ + const client = s.group[groupKey].matrixBot + const roomId = s.group[groupKey].matrixBotRoom + const { + buffer, + name, + type, + info, + opttype, + } = file; + client.uploadContent(buffer, { + name: name, + type: opttype, + }).then(function(url) { + const content = { + msgtype: type || "m.file", + body: name, + info: info, + url: url.content_uri + }; + client.sendMessage(roomId, content); + }).catch(err => { + s.userLog({ + ke: groupKey, + mid: '$USER' + },{ + type: 'matrix.org Error', + msg: err + }); + }) + } + const sendMessage = function(data,files,groupKey){ + if(!data)data = {}; + const client = s.group[groupKey].matrixBot + const roomId = s.group[groupKey].matrixBotRoom + if(!client){ + s.userLog({ke:groupKey,mid:'$USER'},{type:'matrix.org Error'}) + return + } + if(client.sendMessage){ + if(data.text){ + const sendBody = { + "body": data.text, + "msgtype": "m.text" + } + client.sendMessage(roomId, sendBody); + } + files.forEach((file) => { + sendFile(file,groupKey) + }); + }else{ + s.userLog({ + ke: groupKey, + mid: '$USER' + },{ + type: 'matrix.org Error', + msg: '!sendMessage' + }); + } + } + const onEventTriggerBeforeFilterForMatrixBot = function(d,filter){ + filter.matrixBot = false + } + const onEventTriggerForMatrixBot = async (d,filter) => { + const monitorConfig = s.group[d.ke].rawMonitorConfigurations[d.id] + // d = event object + const isEnabled = filter.matrixBot || monitorConfig.details.detector_matrixbot === '1' || monitorConfig.details.notify_matrix === '1' + if(s.group[d.ke].matrixBot && isEnabled && !s.group[d.ke].activeMonitors[d.id].detector_matrixbot){ + var detector_matrixbot_timeout + if(!monitorConfig.details.detector_matrixbot_timeout||monitorConfig.details.detector_matrixbot_timeout===''){ + detector_matrixbot_timeout = 1000 * 60 * 10; + }else{ + detector_matrixbot_timeout = parseFloat(monitorConfig.details.detector_matrixbot_timeout) * 1000 * 60; + } + s.group[d.ke].activeMonitors[d.id].detector_matrixbot = setTimeout(function(){ + clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_matrixbot); + s.group[d.ke].activeMonitors[d.id].detector_matrixbot = null + },detector_matrixbot_timeout) + await getSnapshot(d,monitorConfig) + if(d.screenshotBuffer){ + const imageEventText = `${lang.Event} ${d.screenshotName} ${d.currentTimestamp}` + sendMessage({ + text: imageEventText, + },[ + { + buffer: d.screenshotBuffer, + name: d.screenshotName+'.jpg', + type: 'm.image', + opttype: 'image/jpeg', + info: { + mimetype: 'image/jpeg', + }, + } + ],d.ke) + } + if(monitorConfig.details.detector_matrixbot_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){ + sendMessage({},[ + { + buffer: await fs.promises.readFile(videoPath), + name: videoName, + type: 'm.video', + opttype: 'video/mp4', + info: { + mimetype: 'video/mp4', + }, + } + ],d.ke) + } + } + } + } + const onTwoFactorAuthCodeNotificationForMatrixBot = function(user){ + // r = user + if(r.details.factor_matrixbot === '1'){ + const eventText = `${user.lang['2-Factor Authentication']} : ${user.lang['Enter this code to proceed']} **${factorAuthKey}** ${user.lang.FactorAuthText1}` + sendMessage({ + text: eventText, + },[],d.ke) + } + } + const loadMatrixBotForUser = function(user){ + const userDetails = s.parseJSON(user.details); + //matrixbot + if(!s.group[user.ke].matrixBot && + config.matrixBot === true && + userDetails.matrixbot === '1' && + userDetails.matrixbot_baseUrl && + userDetails.matrixbot_accessToken && + userDetails.matrixbot_userId && + userDetails.matrixbot_roomId + ){ + s.debugLog(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`) + s.debugLog(`Matrix Connecting... ${userDetails.matrixbot_baseUrl}`) + const client = sdk.createClient({ + baseUrl: userDetails.matrixbot_baseUrl, + accessToken: userDetails.matrixbot_accessToken, + userId: userDetails.matrixbot_userId + }); + s.group[user.ke].matrixBot = client + s.group[user.ke].matrixBotRoom = userDetails.matrixbot_roomId + client.startClient(); + client.once('sync', function(state, prevState, res) { + s.userLog({ + ke: user.ke, + mid: '$USER' + },{ + type: 'matrix.org', + msg: lang.Ready + }) + }); + } + } + const unloadMatrixBotForUser = function(user){ + if(s.group[user.ke].matrixBot && s.group[user.ke].matrixBot.stopClient){ + s.group[user.ke].matrixBot.stopClient() + delete(s.group[user.ke].matrixBot) + } + } + const onDetectorNoTriggerTimeoutForMatrixBot = function(e){ + //e = monitor object + var currentTime = new Date() + if(e.details.detector_notrigger_matrixbot === '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 + const eventText = `${lang['"No Motion" Detector']} : ${html}` + sendMessage({ + text: eventText, + },[],e.ke) + } + } + const onMonitorUnexpectedExitForMatrixBot = (monitorConfig) => { + const isEnabled = monitorConfig.details.detector_matrixbot === '1' || monitorConfig.details.notify_matrix === '1' + if(isEnabled && monitorConfig.details.notify_onUnexpectedExit === '1'){ + const ffmpegCommand = s.group[monitorConfig.ke].activeMonitors[monitorConfig.mid].ffmpeg + const description = lang['Process Crashed for Monitor'] + '\n' + ffmpegCommand + const currentTime = new Date() + const eventText = `${lang['Process Unexpected Exit']} : ${monitorConfig.name} : ${description}` + sendMessage({ + text: eventText, + },[],monitorConfig.ke) + } + } + s.loadGroupAppExtender(loadMatrixBotForUser) + s.unloadGroupAppExtender(unloadMatrixBotForUser) + s.onTwoFactorAuthCodeNotification(onTwoFactorAuthCodeNotificationForMatrixBot) + s.onEventTrigger(onEventTriggerForMatrixBot) + s.onEventTriggerBeforeFilter(onEventTriggerBeforeFilterForMatrixBot) + s.onDetectorNoTriggerTimeout(onDetectorNoTriggerTimeoutForMatrixBot) + s.onMonitorUnexpectedExit(onMonitorUnexpectedExitForMatrixBot) + s.definitions["Monitor Settings"].blocks["Notifications"].info[0].info.push( + { + "name": "detail=notify_matrix", + "field": "Matrix.org", + "description": "", + "default": "0", + "example": "", + "selector": "h_det_matrixbot", + "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_matrixbot !== '0'", + isFormGroupGroup: true, + "name": "Matrix.org", + "color": "purple", + "section-class": "h_det_matrixbot_input h_det_matrixbot_1", + "info": [ + { + "name": "detail=detector_matrixbot_send_video", + "field": lang["Attach Video Clip"] + ` (${lang['on Event']})`, + "description": "", + "default": "0", + "example": "", + "fieldType": "select", + "possible": [ + { + "name": lang.No, + "value": "0" + }, + { + "name": lang.Yes, + "value": "1" + } + ] + }, + { + "name": "detail=detector_matrixbot_timeout", + "field": lang['Allow Next Alert'] + ` (${lang['on Event']})`, + "description": "", + "default": "10", + "example": "", + "possible": "" + }, + { + "name": "detail=detector_notrigger_matrixbot", + "field": lang['No Trigger'], + "description": lang.noTriggerText, + "default": "0", + "example": "", + "fieldType": "select", + "possible": [ + { + "name": lang.No, + "value": "0" + }, + { + "name": lang.Yes, + "value": "1" + } + ] + }, + ] + }) + s.definitions["Account Settings"].blocks["2-Factor Authentication"].info.push({ + "name": "detail=factor_matrixbot", + "field": 'Matrix.org', + "default": "1", + "example": "", + "fieldType": "select", + "possible": [ + { + "name": lang.No, + "value": "0" + }, + { + "name": lang.Yes, + "value": "1" + } + ] + }) + s.definitions["Account Settings"].blocks["Matrix.org"] = { + "evaluation": "$user.details.use_matrixbot !== '0'", + "name": "Matrix.org", + "color": "purple", + "info": [ + { + "name": "detail=matrixbot", + "selector":"u_matrixbot_bot", + "field": lang.Enabled, + "default": "0", + "example": "", + "fieldType": "select", + "possible": [ + { + "name": lang.No, + "value": "0" + }, + { + "name": lang.Yes, + "value": "1" + } + ] + }, + { + hidden: true, + "name": "detail=matrixbot_baseUrl", + "placeholder": "", + "field": lang["Host"], + "form-group-class":"u_matrixbot_bot_input u_matrixbot_bot_1", + }, + { + hidden: true, + "name": "detail=matrixbot_userId", + "placeholder": "xxxxxxxxxxxxxxxxxx", + "field": lang["User ID"], + "form-group-class":"u_matrixbot_bot_input u_matrixbot_bot_1", + }, + { + hidden: true, + "name": "detail=matrixbot_roomId", + "placeholder": "xxxxxxxxxxxxxxxxxx", + "field": lang["Room ID"], + "form-group-class":"u_matrixbot_bot_input u_matrixbot_bot_1", + }, + { + hidden: true, + "name": "detail=matrixbot_accessToken", + "fieldType": "password", + "placeholder": "XXXXXXXXXXXXXXXXXXXXXXXX", + "field": lang.Token, + "form-group-class":"u_matrixbot_bot_input u_matrixbot_bot_1", + }, + ] + } + s.definitions["Event Filters"].blocks["Action for Selected"].info.push({ + "name": "actions=matrixBot", + "field": 'Matrix.org', + "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 start Matrix bot, please run "npm install matrix-js-sdk" inside the Shinobi folder.') + } + } +} \ No newline at end of file