Added device state notifications to messaging applications.

This commit is contained in:
Ylian Saint-Hilaire 2022-11-01 22:39:59 -07:00
parent 1e45b69ca7
commit 1a6e78efed
3 changed files with 263 additions and 16 deletions

View File

@ -2302,7 +2302,7 @@ function CreateMeshCentralServer(config, args) {
const domainId = meshSplit[1];
if (obj.config.domains[domainId] == null) return;
const mailserver = obj.config.domains[domainId].mailserver;
if (mailserver == null) return;
if ((mailserver == null) && (obj.msgserver == null)) return;
// Get the device group for this device
const mesh = obj.webserver.meshes[meshid];
@ -2335,7 +2335,8 @@ function CreateMeshCentralServer(config, args) {
if (user.notify[nodeid] != null) { notify |= user.notify[nodeid]; }
}
if ((notify & 48) != 0) {
// Email notifications
if ((mailserver != null) && ((notify & 48) != 0)) {
if (stateSet == true) {
if ((notify & 16) != 0) {
mailserver.notifyDeviceConnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo);
@ -2351,6 +2352,24 @@ function CreateMeshCentralServer(config, args) {
}
}
}
// Messaging notifications
if ((obj.msgserver != null) && ((notify & 384) != 0)) {
if (stateSet == true) {
if ((notify & 128) != 0) {
obj.msgserver.notifyDeviceConnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo);
} else {
obj.msgserver.cancelNotifyDeviceDisconnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo);
}
}
else if (stateSet == false) {
if ((notify & 256) != 0) {
obj.msgserver.notifyDeviceDisconnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo);
} else {
obj.msgserver.cancelNotifyDeviceConnect(user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo);
}
}
}
}
}
}

View File

@ -122,6 +122,7 @@ module.exports.CreateServer = function (parent) {
obj.callMeBotClient = null;
obj.pushoverClient = null;
obj.zulipClient = null;
const sortCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' })
// Telegram client setup
if (parent.config.messaging.telegram) {
@ -347,7 +348,7 @@ module.exports.CreateServer = function (parent) {
if (func != null) { func(true); }
} else {
// No providers found
func(false, "No messaging providers found for this message.");
if (func != null) { func(false, "No messaging providers found for this message."); }
}
}
@ -436,6 +437,210 @@ module.exports.CreateServer = function (parent) {
obj.sendMessage(to, sms, domain, func);
};
// Send device state change notification
obj.sendDeviceNotify = function (domain, username, to, connections, disconnections, lang) {
if (to == null) return;
parent.debug('email', "Sending device state change message to " + to);
var sms = [];
if (connections.length > 0) { sms.push('Connections: ' + connections.join(', ')); } // TODO: Translate 'Connections: '
if (disconnections.length > 0) { sms.push('Disconnections: ' + disconnections.join(', ')); } // TODO: Translate 'Disconnections: '
if (sms.length == 0) return;
sms = sms.join(' - ');
if (sms.length > 1000) { sms = sms.substring(0, 997) + '...'; } // Limit messages to 1000 characters
// Send the message
obj.sendMessage(to, sms, domain, null);
};
//
// Device connetion and disconnection notifications
//
obj.deviceNotifications = {}; // UserId --> { timer, nodes: nodeid --> connectType }
// A device connected and a user needs to be notified about it.
obj.notifyDeviceConnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) {
const mesh = parent.webserver.meshes[meshid];
if (mesh == null) return;
// Add the user and start a timer
if (obj.deviceNotifications[user._id] == null) {
obj.deviceNotifications[user._id] = { nodes: {} };
obj.deviceNotifications[user._id].timer = setTimeout(function () { sendDeviceNotifications(user._id); }, 1 * 60 * 1000); // 1 minute before message is sent
}
// Add the device
if (obj.deviceNotifications[user._id].nodes[nodeid] == null) {
obj.deviceNotifications[user._id].nodes[nodeid] = { c: connectType }; // This device connection need to be added
} else {
const info = obj.deviceNotifications[user._id].nodes[nodeid];
if ((info.d != null) && ((info.d & connectType) != 0)) {
info.d -= connectType; // This device disconnect cancels out a device connection
if (((info.c == null) || (info.c == 0)) && ((info.d == null) || (info.d == 0))) {
// This device no longer needs a notification
delete obj.deviceNotifications[user._id].nodes[nodeid];
if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) {
// This user no longer needs a notification
clearTimeout(obj.deviceNotifications[user._id].timer);
delete obj.deviceNotifications[user._id];
}
return;
}
} else {
if (info.c != null) {
info.c |= connectType; // This device disconnect needs to be added
} else {
info.c = connectType; // This device disconnect needs to be added
}
}
}
// Set the device group name
if ((extraInfo != null) && (extraInfo.name != null)) { obj.deviceNotifications[user._id].nodes[nodeid].nn = extraInfo.name; }
obj.deviceNotifications[user._id].nodes[nodeid].mn = mesh.name;
}
// Cancel a device disconnect notification
obj.cancelNotifyDeviceDisconnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) {
const mesh = parent.webserver.meshes[meshid];
if (mesh == null) return;
if ((obj.deviceNotifications[user._id] != null) && (obj.deviceNotifications[user._id].nodes[nodeid] != null)) {
const info = obj.deviceNotifications[user._id].nodes[nodeid];
if ((info.d != null) && ((info.d & connectType) != 0)) {
info.d -= connectType; // This device disconnect cancels out a device connection
if (((info.c == null) || (info.c == 0)) && ((info.d == null) || (info.d == 0))) {
// This device no longer needs a notification
delete obj.deviceNotifications[user._id].nodes[nodeid];
if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) {
// This user no longer needs a notification
clearTimeout(obj.deviceNotifications[user._id].timer);
delete obj.deviceNotifications[user._id];
}
}
}
}
}
// A device disconnected and a user needs to be notified about it.
obj.notifyDeviceDisconnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) {
const mesh = parent.webserver.meshes[meshid];
if (mesh == null) return;
// Add the user and start a timer
if (obj.deviceNotifications[user._id] == null) {
obj.deviceNotifications[user._id] = { nodes: {} };
obj.deviceNotifications[user._id].timer = setTimeout(function () { sendDeviceNotifications(user._id); }, 1 * 60 * 1000); // 1 minute before message is sent
}
// Add the device
if (obj.deviceNotifications[user._id].nodes[nodeid] == null) {
obj.deviceNotifications[user._id].nodes[nodeid] = { d: connectType }; // This device disconnect need to be added
} else {
const info = obj.deviceNotifications[user._id].nodes[nodeid];
if ((info.c != null) && ((info.c & connectType) != 0)) {
info.c -= connectType; // This device disconnect cancels out a device connection
if (((info.d == null) || (info.d == 0)) && ((info.c == null) || (info.c == 0))) {
// This device no longer needs a notification
delete obj.deviceNotifications[user._id].nodes[nodeid];
if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) {
// This user no longer needs a notification
clearTimeout(obj.deviceNotifications[user._id].timer);
delete obj.deviceNotifications[user._id];
}
return;
}
} else {
if (info.d != null) {
info.d |= connectType; // This device disconnect needs to be added
} else {
info.d = connectType; // This device disconnect needs to be added
}
}
}
// Set the device group name
if ((extraInfo != null) && (extraInfo.name != null)) { obj.deviceNotifications[user._id].nodes[nodeid].nn = extraInfo.name; }
obj.deviceNotifications[user._id].nodes[nodeid].mn = mesh.name;
}
// Cancel a device connect notification
obj.cancelNotifyDeviceConnect = function (user, meshid, nodeid, connectTime, connectType, powerState, serverid, extraInfo) {
const mesh = parent.webserver.meshes[meshid];
if (mesh == null) return;
if ((obj.deviceNotifications[user._id] != null) && (obj.deviceNotifications[user._id].nodes[nodeid] != null)) {
const info = obj.deviceNotifications[user._id].nodes[nodeid];
if ((info.c != null) && ((info.c & connectType) != 0)) {
info.c -= connectType; // This device disconnect cancels out a device connection
if (((info.d == null) || (info.d == 0)) && ((info.c == null) || (info.c == 0))) {
// This device no longer needs a notification
delete obj.deviceNotifications[user._id].nodes[nodeid];
if (Object.keys(obj.deviceNotifications[user._id].nodes).length == 0) {
// This user no longer needs a notification
clearTimeout(obj.deviceNotifications[user._id].timer);
delete obj.deviceNotifications[user._id];
}
}
}
}
}
// Send a notification about device connections and disconnections to a user
function sendDeviceNotifications(userid) {
if (obj.deviceNotifications[userid] == null) return;
clearTimeout(obj.deviceNotifications[userid].timer);
var connections = [];
var disconnections = [];
for (var nodeid in obj.deviceNotifications[userid].nodes) {
var info = obj.deviceNotifications[userid].nodes[nodeid];
if ((info.c != null) && (info.c > 0) && (info.nn != null) && (info.mn != null)) {
/*
var c = [];
if (info.c & 1) { c.push("Agent"); }
if (info.c & 2) { c.push("CIRA"); }
if (info.c & 4) { c.push("AMT"); }
if (info.c & 8) { c.push("AMT-Relay"); }
if (info.c & 16) { c.push("MQTT"); }
connections.push(info.mn + ', ' + info.nn + ': ' + c.join(', '));
*/
if (info.c & 1) { connections.push(info.nn); }
}
if ((info.d != null) && (info.d > 0) && (info.nn != null) && (info.mn != null)) {
/*
var d = [];
if (info.d & 1) { d.push("Agent"); }
if (info.d & 2) { d.push("CIRA"); }
if (info.d & 4) { d.push("AMT"); }
if (info.d & 8) { d.push("AMT-Relay"); }
if (info.d & 16) { d.push("MQTT"); }
disconnections.push(info.mn + ', ' + info.nn + ': ' + d.join(', '));
*/
if (info.d & 1) { disconnections.push(info.nn); }
}
}
// Sort the notifications
connections.sort(sortCollator.compare);
disconnections.sort(sortCollator.compare);
// Get the user and domain
const user = parent.webserver.users[userid];
if ((user == null) || (user.email == null) || (user.emailVerified !== true)) return;
const domain = obj.parent.config.domains[user.domain];
if (domain == null) return;
// Send the message
obj.sendDeviceNotify(domain, user.name, user.msghandle, connections, disconnections, user.llang);
// Clean up
delete obj.deviceNotifications[userid];
}
return obj;
};

View File

@ -1717,12 +1717,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
{
if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1024) != 0)) return; // If this account is settings locked, return here.
// 2 = WebPage device connections
// 4 = WebPage device disconnections
// 8 = WebPage device desktop and serial events
// 16 = Email device connections
// 32 = Email device disconnections
// 64 = Email device help request
// 2 = WebPage device connections
// 4 = WebPage device disconnections
// 8 = WebPage device desktop and serial events
// 16 = Email device connections
// 32 = Email device disconnections
// 64 = Email device help request
// 128 = Messaging device connections
// 256 = Messaging device disconnections
// 512 = Messaging device help request
var err = null;
try {
@ -1767,12 +1770,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
{
if ((user.siteadmin != 0xFFFFFFFF) && ((user.siteadmin & 1024) != 0)) return; // If this account is settings locked, return here.
// 2 = WebPage device connections
// 4 = WebPage device disconnections
// 8 = WebPage device desktop and serial events
// 16 = Email device connections
// 32 = Email device disconnections
// 64 = Email device help request
// 2 = WebPage device connections
// 4 = WebPage device disconnections
// 8 = WebPage device desktop and serial events
// 16 = Email device connections
// 32 = Email device disconnections
// 64 = Email device help request
// 128 = Messaging device connections
// 256 = Messaging device disconnections
// 512 = Messaging device help request
var err = null;
try {
@ -5312,6 +5318,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
'dupagents': [serverUserCommandDupAgents, ""],
'email': [serverUserCommandEmail, ""],
'emailnotifications': [serverUserCommandEmailNotifications, ""],
'msgnotifications': [serverUserCommandMessageNotifications, ""],
'firebase': [serverUserCommandFirebase, ""],
'heapdump': [serverUserCommandHeapDump, ""],
'heapdump2': [serverUserCommandHeapDump2, ""],
@ -6892,7 +6899,23 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
x += ' ' + info.mn + ', ' + info.nn + ', c:' + (info.c ? info.c : 0) + ', d:' + (info.d ? info.d : 0) + '\r\n';
}
}
cmdData.result = ((x == '')?'None':x);
cmdData.result = ((x == '') ? 'None' : x);
}
}
function serverUserCommandMessageNotifications(cmdData) {
if (parent.parent.msgserver == null) {
cmdData.result = "No messaging service enabled.";
} else {
var x = '';
for (var userid in parent.parent.msgserver.deviceNotifications) {
x += userid + '\r\n';
for (var nodeid in parent.parent.msgserver.deviceNotifications[userid].nodes) {
const info = parent.parent.msgserver.deviceNotifications[userid].nodes[nodeid];
x += ' ' + info.mn + ', ' + info.nn + ', c:' + (info.c ? info.c : 0) + ', d:' + (info.d ? info.d : 0) + '\r\n';
}
}
cmdData.result = ((x == '') ? 'None' : x);
}
}