1
0
Fork 0
mirror of https://github.com/Ylianst/MeshCentral.git synced 2025-02-12 11:01:52 +00:00

Added XMPP support (#4679)

This commit is contained in:
Ylian Saint-Hilaire 2022-10-28 12:34:29 -07:00
parent 8f84fa3a5b
commit cbc26b3a43
7 changed files with 90 additions and 4 deletions

View file

@ -948,6 +948,7 @@ module.exports.CreateAmtManager = function (parent) {
// Perform a power action: 2 = Power up, 5 = Power cycle, 8 = Power down, 10 = Reset, 11 = Power on to BIOS, 12 = Reset to BIOS, 13 = Power on to BIOS with SOL, 14 = Reset to BIOS with SOL // Perform a power action: 2 = Power up, 5 = Power cycle, 8 = Power down, 10 = Reset, 11 = Power on to BIOS, 12 = Reset to BIOS, 13 = Power on to BIOS with SOL, 14 = Reset to BIOS with SOL
function performPowerAction(nodeid, action) { function performPowerAction(nodeid, action) {
console.log('performPowerAction', nodeid, action);
var devices = obj.amtDevices[nodeid]; var devices = obj.amtDevices[nodeid];
if (devices == null) return; if (devices == null) return;
for (var i in devices) { for (var i in devices) {

View file

@ -80,6 +80,26 @@ Discord integration requires that MeshCentral be run on NodeJS v17 or higher. On
Once users will need to join the same Discord server as the bot, the optional "serverurl" can be used to give the users a URL link to join the server, this can be a server invitation link or some other URL with instructions. Once users will need to join the same Discord server as the bot, the optional "serverurl" can be used to give the users a URL link to join the server, this can be a server invitation link or some other URL with instructions.
## XMPP Setup
For XMPP integration, you need to provide MeshCentral with a XMPP server, username and password so that MeshCentral can login and send notifications to users. You can get a XMPP account to any number of servers or start up your own XMPP server.
```json
{
"messaging": {
"xmpp": {
service: "xmppserver.com",
credentials: {
username: 'username',
password: 'password'
}
}
}
}
```
An easy way to get setup with XMPP is to create a free account with [chatterboxtown.us](https://chatterboxtown.us/) and then, setup MeshCentral with the service value set to "chatterboxtown.us" along with the username and password of you account. This can be done in minutes. Once setup, users will be able to setup and verify XMLL accounts and use this for notifications and 2FA verification.
## User Setup ## User Setup
Once a messaging system is setup with MeshCentral, users will be able to register their handle and verify that they own that account by typing in a 6 digit code. Once a messaging system is setup with MeshCentral, users will be able to register their handle and verify that they own that account by typing in a 6 digit code.

View file

@ -4030,6 +4030,7 @@ function mainStart() {
if (config.messaging != null) { if (config.messaging != null) {
if (config.messaging.telegram != null) { modules.push('telegram'); modules.push('input'); } if (config.messaging.telegram != null) { modules.push('telegram'); modules.push('input'); }
if (config.messaging.discord != null) { if (nodeVersion >= 17) { modules.push('discord.js@14.6.0'); } else { delete config.messaging.discord; addServerWarning('This NodeJS version does not support Discord.js.', 26); } } if (config.messaging.discord != null) { if (nodeVersion >= 17) { modules.push('discord.js@14.6.0'); } else { delete config.messaging.discord; addServerWarning('This NodeJS version does not support Discord.js.', 26); } }
if (config.messaging.xmpp != null) { modules.push('@xmpp/client'); }
} }
// Setup web based push notifications // Setup web based push notifications

View file

@ -40,16 +40,31 @@
"token": "xxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxx" "token": "xxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxx"
} }
} }
// For XMPP login, add this in config.json
"messaging": {
"xmpp": {
service: "xmppserver.com",
//domain: "xmppserver.com",
//resource: "example",
credentials: {
username: 'username',
password: 'password'
}
}
}
*/ */
// Construct a messaging server object // Construct a messaging server object
module.exports.CreateServer = function (parent) { module.exports.CreateServer = function (parent) {
var obj = {}; var obj = {};
obj.parent = parent; obj.parent = parent;
obj.providers = 0; // 1 = Telegram, 2 = Signal, 4 = Discord obj.providers = 0; // 1 = Telegram, 2 = Signal, 4 = Discord, 8 = XMPP
obj.telegramClient = null; obj.telegramClient = null;
obj.discordClient = null; obj.discordClient = null;
obj.discordUrl = null; obj.discordUrl = null;
obj.xmppClient = null;
var xmppXml = null;
// Telegram client setup // Telegram client setup
if (parent.config.messaging.telegram) { if (parent.config.messaging.telegram) {
@ -137,6 +152,30 @@ module.exports.CreateServer = function (parent) {
} }
} }
// XMPP client setup
if (parent.config.messaging.xmpp) {
// Validate Discord configuration values
var xmppOK = true;
if (typeof parent.config.messaging.xmpp.service != 'string') { console.log('Invalid or missing XMPP service.'); xmppOK = false; }
if (xmppOK) {
// Setup XMPP
const { client, xml } = require('@xmpp/client');
const xmpp = client(parent.config.messaging.xmpp);
xmpp.on('error', function (err) { parent.debug('email', 'XMPP error: ' + err); console.error('XMPP error', err); });
xmpp.on('offline', function () { parent.debug('email', 'XMPP client is offline.'); console.log('XMPP offline'); });
//xmpp.on('stanza', async function (stanza) { if (stanza.is("message")) { await xmpp.send(xml('presence', { type: 'unavailable' })); await xmpp.stop(); } });
xmpp.on('online', async function (address) {
// await xmpp.send(xml("presence")); const message = xml("message", { type: "chat", to: "username@server.com" }, xml("body", {}, "hello world")); await xmpp.send(message);
xmppXml = xml;
obj.xmppClient = xmpp;
obj.providers += 8; // Enable XMPP messaging
console.log("MeshCentral XMPP client is connected.");
});
xmpp.start().catch(console.error);
}
}
// Send a direct message to a specific userid // Send a direct message to a specific userid
async function discordSendMsg(userId, message) { async function discordSendMsg(userId, message) {
const user = await obj.discordClient.users.fetch(userId).catch(function () { return null; }); const user = await obj.discordClient.users.fetch(userId).catch(function () { return null; });
@ -158,6 +197,13 @@ module.exports.CreateServer = function (parent) {
}); });
} }
// Send an XMPP message
async function sendXmppMessage(to, msg, func) {
const message = xmppXml('message', { type: 'chat', to: to.substring(5) }, xmppXml('body', {}, msg));
await obj.xmppClient.send(message);
if (func != null) { func(true); }
}
// Send an user message // Send an user message
obj.sendMessage = function(to, msg, func) { obj.sendMessage = function(to, msg, func) {
if ((to.startsWith('telegram:')) && (obj.telegramClient != null)) { // Telegram if ((to.startsWith('telegram:')) && (obj.telegramClient != null)) { // Telegram
@ -168,7 +214,13 @@ module.exports.CreateServer = function (parent) {
} }
sendTelegramMessage(to, msg, func); sendTelegramMessage(to, msg, func);
} else if ((to.startsWith('discord:')) && (obj.discordClient != null)) { // Discord } else if ((to.startsWith('discord:')) && (obj.discordClient != null)) { // Discord
discordFindUserByTag(to.substring(8), function (userid) { discordSendMsg(userid, msg); if (func != null) { func(true); } }); discordFindUserByTag(to.substring(8), function (userid) {
parent.debug('email', 'Sending Discord message to: ' + to.substring(9) + ', ' + userid + ': ' + msg);
discordSendMsg(userid, msg); if (func != null) { func(true); }
});
} else if ((to.startsWith('xmpp:')) && (obj.xmppClient != null)) { // XMPP
parent.debug('email', 'Sending XMPP message to: ' + to.substring(5) + ': ' + msg);
sendXmppMessage(to, msg, func);
} else { } else {
// No providers found // No providers found
func(false, "No messaging providers found for this message."); func(false, "No messaging providers found for this message.");

View file

@ -6695,6 +6695,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
var handle = null; var handle = null;
if ((command.service == 1) && ((parent.parent.msgserver.providers & 1) != 0)) { handle = 'telegram:@' + command.handle; } if ((command.service == 1) && ((parent.parent.msgserver.providers & 1) != 0)) { handle = 'telegram:@' + command.handle; }
if ((command.service == 4) && ((parent.parent.msgserver.providers & 4) != 0)) { handle = 'discord:' + command.handle; } if ((command.service == 4) && ((parent.parent.msgserver.providers & 4) != 0)) { handle = 'discord:' + command.handle; }
if ((command.service == 8) && ((parent.parent.msgserver.providers & 8) != 0)) { handle = 'xmpp:' + command.handle; }
if (handle == null) return; if (handle == null) return;
// Send a verification message // Send a verification message
@ -6837,6 +6838,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((parent.parent.msgserver.providers & 1) != 0) { r.push("Usage: MSG \"telegram:@UserHandle\" \"Message\"."); } if ((parent.parent.msgserver.providers & 1) != 0) { r.push("Usage: MSG \"telegram:@UserHandle\" \"Message\"."); }
if ((parent.parent.msgserver.providers & 2) != 0) { r.push("Usage: MSG \"signal:UserHandle\" \"Message\"."); } if ((parent.parent.msgserver.providers & 2) != 0) { r.push("Usage: MSG \"signal:UserHandle\" \"Message\"."); }
if ((parent.parent.msgserver.providers & 4) != 0) { r.push("Usage: MSG \"discord:Username#0000\" \"Message\"."); } if ((parent.parent.msgserver.providers & 4) != 0) { r.push("Usage: MSG \"discord:Username#0000\" \"Message\"."); }
if ((parent.parent.msgserver.providers & 8) != 0) { r.push("Usage: MSG \"xmpp:username@server.com\" \"Message\"."); }
cmdData.result = r.join('\r\n'); cmdData.result = r.join('\r\n');
} else { } else {
parent.parent.msgserver.sendMessage(cmdData.cmdargs['_'][0], cmdData.cmdargs['_'][1], function (status, msg) { parent.parent.msgserver.sendMessage(cmdData.cmdargs['_'][0], cmdData.cmdargs['_'][1], function (status, msg) {

View file

@ -30,7 +30,7 @@ module.exports.CreateMpsServer = function (parent, db, args, certificates) {
const net = require('net'); const net = require('net');
const tls = require('tls'); const tls = require('tls');
const MAX_IDLE = 90000; // 90 seconds max idle time, higher than the typical KEEP-ALIVE periode of 60 seconds const MAX_IDLE = 90000; // 90 seconds max idle time, higher than the typical KEEP-ALIVE periode of 60 seconds
const KEEPALIVE_INTERVAL = 30; // 30 seconds is typical keepalive interval for AMT CIRA connection const KEEPALIVE_INTERVAL = 30; // 30 seconds is typical keepalive interval for AMT CIRA connection
// This MPS server is also a tiny HTTPS server. HTTP responses are here. // This MPS server is also a tiny HTTPS server. HTTP responses are here.
obj.httpResponses = { obj.httpResponses = {

View file

@ -11985,6 +11985,7 @@
if ((serverinfo.userMsgProviders & 1) != 0) { y += '<option value=1>' + "Telegram" + '</option>'; } if ((serverinfo.userMsgProviders & 1) != 0) { y += '<option value=1>' + "Telegram" + '</option>'; }
if ((serverinfo.userMsgProviders & 2) != 0) { y += '<option value=2>' + "Signal Messenger" + '</option>'; } if ((serverinfo.userMsgProviders & 2) != 0) { y += '<option value=2>' + "Signal Messenger" + '</option>'; }
if ((serverinfo.userMsgProviders & 4) != 0) { y += '<option value=4>' + "Discord" + '</option>'; } if ((serverinfo.userMsgProviders & 4) != 0) { y += '<option value=4>' + "Discord" + '</option>'; }
if ((serverinfo.userMsgProviders & 8) != 0) { y += '<option value=8>' + "XMPP" + '</option>'; }
y += '</select>'; y += '</select>';
x += '<table><tr><td>' + "Service" + '<td>' + y; x += '<table><tr><td>' + "Service" + '<td>' + y;
x += '<tr><td>' + "Handle" + '<td><input maxlength=64 style=width:160px;margin-left:8px id=d2handleinput onKeyUp=account_manageMessagingValidate() onkeypress="if (event.key==\'Enter\') account_manageMessagingValidate(1)">'; x += '<tr><td>' + "Handle" + '<td><input maxlength=64 style=width:160px;margin-left:8px id=d2handleinput onKeyUp=account_manageMessagingValidate() onkeypress="if (event.key==\'Enter\') account_manageMessagingValidate(1)">';
@ -11998,7 +11999,9 @@
function account_manageMessagingValidate(x) { function account_manageMessagingValidate(x) {
if (serverinfo.discordUrl) { QV('d2discordurl', Q('d2serviceselect').value == 4); } if (serverinfo.discordUrl) { QV('d2discordurl', Q('d2serviceselect').value == 4); }
if (Q('d2serviceselect').value == 4) { Q('d2handleinput')['placeholder'] = "Username:0000"; } else { Q('d2handleinput')['placeholder'] = "Username"; } if (Q('d2serviceselect').value == 4) { Q('d2handleinput')['placeholder'] = "Username:0000"; }
else if (Q('d2serviceselect').value == 8) { Q('d2handleinput')['placeholder'] = "username@server.com"; }
else { Q('d2handleinput')['placeholder'] = "Username"; }
var ok = (Q('d2handleinput').value.length > 0); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } var ok = (Q('d2handleinput').value.length > 0); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); }
} }
function account_manageMessagingAdd() { if (Q('d2handleinput').value.length == 0) return; QE('d2handleinput', false); meshserver.send({ action: 'verifyMessaging', service: Q('d2serviceselect').value, handle: Q('d2handleinput').value }); } function account_manageMessagingAdd() { if (Q('d2handleinput').value.length == 0) return; QE('d2handleinput', false); meshserver.send({ action: 'verifyMessaging', service: Q('d2serviceselect').value, handle: Q('d2handleinput').value }); }
@ -15860,6 +15863,7 @@
if ((serverinfo.userMsgProviders & 1) != 0) { y += '<option value=1>' + "Telegram" + '</option>'; } if ((serverinfo.userMsgProviders & 1) != 0) { y += '<option value=1>' + "Telegram" + '</option>'; }
if ((serverinfo.userMsgProviders & 2) != 0) { y += '<option value=2>' + "Signal Messenger" + '</option>'; } if ((serverinfo.userMsgProviders & 2) != 0) { y += '<option value=2>' + "Signal Messenger" + '</option>'; }
if ((serverinfo.userMsgProviders & 4) != 0) { y += '<option value=4>' + "Discord" + '</option>'; } if ((serverinfo.userMsgProviders & 4) != 0) { y += '<option value=4>' + "Discord" + '</option>'; }
if ((serverinfo.userMsgProviders & 8) != 0) { y += '<option value=8>' + "XMPP" + '</option>'; }
y += '</select>'; y += '</select>';
x += '<table style=margin-top:12px><tr><td>' + "Service" + '<td>' + y; x += '<table style=margin-top:12px><tr><td>' + "Service" + '<td>' + y;
x += '<tr><td>' + "Handle" + '<td><input maxlength=64 style=width:160px;margin-left:8px id=d2handleinput onKeyUp=p30editMessagingValidate() onkeypress="if (event.key==\'Enter\') p30editMessagingValidate(1)">'; x += '<tr><td>' + "Handle" + '<td><input maxlength=64 style=width:160px;margin-left:8px id=d2handleinput onKeyUp=p30editMessagingValidate() onkeypress="if (event.key==\'Enter\') p30editMessagingValidate(1)">';
@ -15870,6 +15874,7 @@
if (userinfo.msghandle) { if (userinfo.msghandle) {
if (userinfo.msghandle.startsWith('telegram:') && ((serverinfo.userMsgProviders & 1) != 0)) { Q('d2serviceselect').value = 1; Q('d2handleinput').value = userinfo.msghandle.substring(10); } if (userinfo.msghandle.startsWith('telegram:') && ((serverinfo.userMsgProviders & 1) != 0)) { Q('d2serviceselect').value = 1; Q('d2handleinput').value = userinfo.msghandle.substring(10); }
if (userinfo.msghandle.startsWith('discord:') && ((serverinfo.userMsgProviders & 4) != 0)) { Q('d2serviceselect').value = 4; Q('d2handleinput').value = userinfo.msghandle.substring(8); } if (userinfo.msghandle.startsWith('discord:') && ((serverinfo.userMsgProviders & 4) != 0)) { Q('d2serviceselect').value = 4; Q('d2handleinput').value = userinfo.msghandle.substring(8); }
if (userinfo.msghandle.startsWith('xmpp:') && ((serverinfo.userMsgProviders & 8) != 0)) { Q('d2serviceselect').value = 4; Q('d2handleinput').value = userinfo.msghandle.substring(5); }
} }
p30editMessagingValidate(); p30editMessagingValidate();
} }
@ -15877,6 +15882,10 @@
function p30editMessagingValidate(x) { function p30editMessagingValidate(x) {
QE('d2handleinput', Q('d2serviceselect').value != 0); QE('d2handleinput', Q('d2serviceselect').value != 0);
if (serverinfo.discordUrl) { QV('d2discordurl', Q('d2serviceselect').value == 4); } if (serverinfo.discordUrl) { QV('d2discordurl', Q('d2serviceselect').value == 4); }
if (Q('d2serviceselect').value == 0) { Q('d2handleinput')['placeholder'] = ''; }
else if (Q('d2serviceselect').value == 4) { Q('d2handleinput')['placeholder'] = "Username:0000"; }
else if (Q('d2serviceselect').value == 8) { Q('d2handleinput')['placeholder'] = "username@server.com"; }
else { Q('d2handleinput')['placeholder'] = "Username"; }
if (x == 1) { dialogclose(1); } if (x == 1) { dialogclose(1); }
} }
@ -15886,6 +15895,7 @@
if ((Q('d2handleinput').value == '') || (Q('d2serviceselect').value == 0)) { handle = ''; } if ((Q('d2handleinput').value == '') || (Q('d2serviceselect').value == 0)) { handle = ''; }
else if (Q('d2serviceselect').value == 1) { handle = 'telegram:@' + Q('d2handleinput').value; } else if (Q('d2serviceselect').value == 1) { handle = 'telegram:@' + Q('d2handleinput').value; }
else if (Q('d2serviceselect').value == 4) { handle = 'discord:' + Q('d2handleinput').value; } else if (Q('d2serviceselect').value == 4) { handle = 'discord:' + Q('d2handleinput').value; }
else if (Q('d2serviceselect').value == 8) { handle = 'xmpp:' + Q('d2handleinput').value; }
if (handle != null) { meshserver.send({ action: 'edituser', id: currentUser._id, msghandle: handle }); } if (handle != null) { meshserver.send({ action: 'edituser', id: currentUser._id, msghandle: handle }); }
} }