First pass at adding Telegram support (#4650)

Ylian Saint-Hilaire committed Oct 22, 2022 at 07:23 UTC 7e3dce0ef7c02cb213e5842992d7eb081e50f101
8 files changed +394 -25
meshcentral-config-schema.json
+41 -10
@@ -1299,13 +1299,16 @@
1299 "required": [ "host", "port", "from", "tls" ]
1300 },
1301 "sms": {
1302 - "title" : "SMS provider",
1302 + "title": "SMS provider",
1303 "description": "Connects MeshCentral to a SMS text messaging provider, allows MeshCentral to send SMS messages for 2FA or user notification.",
1304 "oneOf": [
1305 {
1306 - "type": "object",
1306 + "type": "object",
1307 "properties": {
1308 - "provider": { "type": "string", "enum": [ "twilio" ] },
1308 + "provider": {
1309 + "type": "string",
1310 + "enum": [ "twilio" ]
1311 + },
1312 "sid": { "type": "string" },
1313 "auth": { "type": "string" },
1314 "from": { "type": "string" }
@@ -1313,9 +1316,12 @@
1316 "required": [ "provider", "sid", "auth", "from" ]
1317 },
1318 {
1316 - "type": "object",
1319 + "type": "object",
1320 "properties": {
1318 - "provider": { "type": "string", "enum": [ "plivo" ] },
1321 + "provider": {
1322 + "type": "string",
1323 + "enum": [ "plivo" ]
1324 + },
1325 "id": { "type": "string" },
1326 "token": { "type": "string" },
1327 "from": { "type": "string" }
@@ -1323,23 +1329,48 @@
1329 "required": [ "provider", "id", "token", "from" ]
1330 },
1331 {
1326 - "type": "object",
1332 + "type": "object",
1333 "properties": {
1328 - "provider": { "type": "string", "enum": [ "telnyx" ] },
1334 + "provider": {
1335 + "type": "string",
1336 + "enum": [ "telnyx" ]
1337 + },
1338 "apikey": { "type": "string" },
1339 "from": { "type": "string" }
1340 },
1341 "required": [ "provider", "apikey", "from" ]
1342 },
1343 {
1335 - "type": "object",
1344 + "type": "object",
1345 "properties": {
1337 - "provider": { "type": "string", "enum": [ "url" ] },
1338 - "url": { "type": "string", "description": "A http or https URL with {{phone}} and {{message}} in the string. These will be replaced with the URL encoded target phone number and message." }
1346 + "provider": {
1347 + "type": "string",
1348 + "enum": [ "url" ]
1349 + },
1350 + "url": {
1351 + "type": "string",
1352 + "description": "A http or https URL with {{phone}} and {{message}} in the string. These will be replaced with the URL encoded target phone number and message."
1353 + }
1354 },
1355 "required": [ "url" ]
1356 }
1357 ]
1358 + },
1359 + "messaging": {
1360 + "title" : "Messaging server",
1361 + "description": "This section allow MeshCentral to send messages over user messaging networks like Telegram",
1362 + "type": "object",
1363 + "properties": {
1364 + "telegram": {
1365 + "type": "object",
1366 + "description": "Configure Telegram messaging system",
1367 + "properties": {
1368 + "apiid": { "type": "number" },
1369 + "apihash": { "type": "string" },
1370 + "session": { "type": "string" }
1371 + }
1372 + }
1373 + }
1374 }
1375 },
1376 "required": [ "settings", "domains" ]
meshcentral.js
+26 -13
@@ -22,19 +22,20 @@ if (process.argv[2] == '--launch') { try { require('appmetrics-dash').monitor({
22 function CreateMeshCentralServer(config, args) {
23 const obj = {};
24 obj.db = null;
25 - obj.webserver = null;
26 - obj.redirserver = null;
27 - obj.mpsserver = null;
28 - obj.mqttbroker = null;
29 - obj.swarmserver = null;
30 - obj.smsserver = null;
25 + obj.webserver = null; // HTTPS main web server, typically on port 443
26 + obj.redirserver = null; // HTTP relay web server, typically on port 80
27 + obj.mpsserver = null; // Intel AMT CIRA server, typically on port 4433
28 + obj.mqttbroker = null; // MQTT server, not is not often used
29 + obj.swarmserver = null; // Swarm server, this is used only to update older MeshCentral v1 agents
30 + obj.smsserver = null; // SMS server, used to send user SMS messages
31 + obj.msgserver = null; // Messaging server, used to sent used messages
32 obj.amtEventHandler = null;
33 obj.pluginHandler = null;
34 obj.amtScanner = null;
34 - obj.amtManager = null;
35 + obj.amtManager = null; // Intel AMT manager, used to oversee all Intel AMT devices, activate them and sync policies
36 obj.meshScanner = null;
37 obj.taskManager = null;
37 - obj.letsencrypt = null;
38 + obj.letsencrypt = null; // Let's encrypt server, used to get and renew TLS certificates
39 obj.eventsDispatch = {};
40 obj.fs = require('fs');
41 obj.path = require('path');
@@ -758,7 +759,7 @@ function CreateMeshCentralServer(config, args) {
759 }
760
761 // Check top level configuration for any unrecognized values
761 - if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domaindefaults', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms', 'sendgrid', 'sendmail', 'firebase', 'firebaserelay', '$schema'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".', 3, [ i ]); } } }
762 + if (config) { for (var i in config) { if ((typeof i == 'string') && (i.length > 0) && (i[0] != '_') && (['settings', 'domaindefaults', 'domains', 'configfiles', 'smtp', 'letsencrypt', 'peers', 'sms', 'messaging', 'sendgrid', 'sendmail', 'firebase', 'firebaserelay', '$schema'].indexOf(i) == -1)) { addServerWarning('Unrecognized configuration option \"' + i + '\".', 3, [ i ]); } } }
763
764 // Read IP lists from files if applicable
765 config.settings.userallowedip = obj.args.userallowedip = readIpListFromFile(obj.args.userallowedip);
@@ -858,7 +859,7 @@ function CreateMeshCentralServer(config, args) {
859 if (err != null) { console.log("Database error: " + err); process.exit(); return; }
860 if ((docs == null) || (docs.length == 0)) { console.log("Unknown userid, usage: --resetaccount [userid] --domain (domain) --pass [password]."); process.exit(); return; }
861 const user = docs[0]; if ((user.siteadmin) && (user.siteadmin != 0xFFFFFFFF) && (user.siteadmin & 32) != 0) { user.siteadmin -= 32; } // Unlock the account.
861 - delete user.phone; delete user.otpekey; delete user.otpsecret; delete user.otpkeys; delete user.otphkeys; delete user.otpdev; delete user.otpsms; // Disable 2FA
862 + delete user.phone; delete user.otpekey; delete user.otpsecret; delete user.otpkeys; delete user.otphkeys; delete user.otpdev; delete user.otpsms; delete user.otpmsg; // Disable 2FA
863 if (obj.args.hashpass) {
864 // Reset an account using a pre-hashed password. Use --hashpassword to pre-hash a password.
865 var hashpasssplit = obj.args.hashpass.split(',');
@@ -1777,6 +1778,11 @@ function CreateMeshCentralServer(config, args) {
1778 if ((obj.smsserver != null) && (obj.args.lanonly == true)) { addServerWarning("SMS gateway has limited use in LAN mode.", 19); }
1779 }
1780
1781 + // Setup user messaging
1782 + if (config.messaging != null) {
1783 + obj.msgserver = require('./meshmessaging.js').CreateServer(obj);
1784 + }
1785 +
1786 // Setup web based push notifications
1787 if ((typeof config.settings.webpush == 'object') && (typeof config.settings.webpush.email == 'string')) {
1788 obj.webpush = require('web-push');
@@ -4008,9 +4014,16 @@ function mainStart() {
4014 if (config.settings.desktopmultiplex === true) { modules.push('image-size'); }
4015
4016 // SMS support
4011 - if ((config.sms != null) && (config.sms.provider == 'twilio')) { modules.push('twilio'); }
4012 - if ((config.sms != null) && (config.sms.provider == 'plivo')) { modules.push('plivo'); }
4013 - if ((config.sms != null) && (config.sms.provider == 'telnyx')) { modules.push('telnyx'); }
4017 + if (config.sms != null) {
4018 + if (config.sms.provider == 'twilio') { modules.push('twilio'); }
4019 + if (config.sms.provider == 'plivo') { modules.push('plivo'); }
4020 + if (config.sms.provider == 'telnyx') { modules.push('telnyx'); }
4021 + }
4022 +
4023 + // Messaging support
4024 + if (config.messaging != null) {
4025 + if (config.messaging.telegram != null) { modules.push('telegram'); modules.push('input'); }
4026 + }
4027
4028 // Setup web based push notifications
4029 if ((typeof config.settings.webpush == 'object') && (typeof config.settings.webpush.email == 'string')) { modules.push('web-push'); }
meshmessaging.js new
+151
@@ -0,0 +1,151 @@
1 +/**
2 +* @description MeshCentral user messaging communication module
3 +* @author Ylian Saint-Hilaire
4 +* @copyright Intel Corporation 2022
5 +* @license Apache-2.0
6 +* @version v0.0.1
7 +*/
8 +
9 +/*xjslint node: true */
10 +/*xjslint plusplus: true */
11 +/*xjslint maxlen: 256 */
12 +/*jshint node: true */
13 +/*jshint strict: false */
14 +/*jshint esversion: 6 */
15 +"use strict";
16 +
17 +/*
18 +// For Telegram, add this in config.json
19 +"messaging": {
20 + "telegram": {
21 + "apiid": 00000000,
22 + "apihash": "00000000000000000000000",
23 + "session": "aaaaaaaaaaaaaaaaaaaaaaa"
24 + }
25 +}
26 +*/
27 +
28 +// Construct a SMS server object
29 +module.exports.CreateServer = function (parent) {
30 + var obj = {};
31 + obj.parent = parent;
32 + obj.providers = 0; // 1 = Telegram, 2 = Signal
33 + obj.telegramClient = null;
34 +
35 + // Messaging client setup
36 + if (parent.config.messaging.telegram) {
37 + // Validate Telegram configuration values
38 + var telegramOK = true;
39 + if (typeof parent.config.messaging.telegram.apiid != 'number') { console.log('Invalid or missing Telegram apiid.'); telegramOK = false; }
40 + if (typeof parent.config.messaging.telegram.apihash != 'string') { console.log('Invalid or missing Telegram apihash.'); telegramOK = false; }
41 + if (typeof parent.config.messaging.telegram.session != 'string') { console.log('Invalid or missing Telegram session.'); telegramOK = false; }
42 +
43 + if (telegramOK) {
44 + // Setup Telegram
45 + async function setupTelegram() {
46 + const { TelegramClient } = require('telegram');
47 + const { StringSession } = require('telegram/sessions');
48 + const input = require('input');
49 + const stringSession = new StringSession(parent.config.messaging.telegram.session);
50 + const client = new TelegramClient(stringSession, parent.config.messaging.telegram.apiid, parent.config.messaging.telegram.apihash, { connectionRetries: 5 });
51 + await client.start({
52 + phoneNumber: async function () { await input.text("Please enter your number: "); },
53 + password: async function () { await input.text("Please enter your password: "); },
54 + phoneCode: async function () { await input.text("Please enter the code you received: "); },
55 + onError: function (err) { console.log('Telegram error', err); },
56 + });
57 + obj.telegramClient = client;
58 + obj.providers += 1; // Enable Telegram messaging
59 + console.log("MeshCentral Telegram client is connected.");
60 + }
61 + setupTelegram();
62 + }
63 + }
64 +
65 + // Send an user message
66 + obj.sendMessage = function(to, msg, func) {
67 + // Telegram
68 + if ((to.startsWith('telegram:')) && (obj.telegramClient != null)) {
69 + async function sendTelegramMessage(to, msg, func) {
70 + if (obj.telegramClient == null) return;
71 + parent.debug('email', 'Sending Telegram message to: ' + to.substring(9) + ': ' + msg);
72 + try { await obj.telegramClient.sendMessage(to.substring(9), { message: msg }); func(true); } catch (ex) { func(false, ex); }
73 + }
74 + sendTelegramMessage(to, msg, func);
75 + } else {
76 + // No providers found
77 + func(false, "No messaging providers found for this message.");
78 + }
79 + }
80 +
81 + // Get the correct SMS template
82 + function getTemplate(templateNumber, domain, lang) {
83 + parent.debug('email', 'Getting SMS template #' + templateNumber + ', lang: ' + lang);
84 + if (Array.isArray(lang)) { lang = lang[0]; } // TODO: For now, we only use the first language given.
85 +
86 + var r = {}, emailsPath = null;
87 + if ((domain != null) && (domain.webemailspath != null)) { emailsPath = domain.webemailspath; }
88 + else if (obj.parent.webEmailsOverridePath != null) { emailsPath = obj.parent.webEmailsOverridePath; }
89 + else if (obj.parent.webEmailsPath != null) { emailsPath = obj.parent.webEmailsPath; }
90 + if ((emailsPath == null) || (obj.parent.fs.existsSync(emailsPath) == false)) { return null }
91 +
92 + // Get the non-english email if needed
93 + var txtfile = null;
94 + if ((lang != null) && (lang != 'en')) {
95 + var translationsPath = obj.parent.path.join(emailsPath, 'translations');
96 + var translationsPathTxt = obj.parent.path.join(emailsPath, 'translations', 'sms-messages_' + lang + '.txt');
97 + if (obj.parent.fs.existsSync(translationsPath) && obj.parent.fs.existsSync(translationsPathTxt)) {
98 + txtfile = obj.parent.fs.readFileSync(translationsPathTxt).toString();
99 + }
100 + }
101 +
102 + // Get the english email
103 + if (txtfile == null) {
104 + var pathTxt = obj.parent.path.join(emailsPath, 'sms-messages.txt');
105 + if (obj.parent.fs.existsSync(pathTxt)) {
106 + txtfile = obj.parent.fs.readFileSync(pathTxt).toString();
107 + }
108 + }
109 +
110 + // No email templates
111 + if (txtfile == null) { return null; }
112 +
113 + // Decode the TXT file
114 + var lines = txtfile.split('\r\n').join('\n').split('\n')
115 + if (lines.length <= templateNumber) return null;
116 +
117 + return lines[templateNumber];
118 + }
119 +
120 + // Send phone number verification SMS
121 + obj.sendPhoneCheck = function (domain, to, verificationCode, language, func) {
122 + parent.debug('email', "Sending verification message to " + to);
123 +
124 + var sms = getTemplate(0, domain, language);
125 + if (sms == null) { parent.debug('email', "Error: Failed to get SMS template"); return; } // No SMS template found
126 +
127 + // Setup the template
128 + sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
129 + sms = sms.split('[[1]]').join(verificationCode);
130 +
131 + // Send the SMS
132 + obj.sendSMS(to, sms, func);
133 + };
134 +
135 + // Send phone number verification SMS
136 + obj.sendToken = function (domain, to, verificationCode, language, func) {
137 + parent.debug('email', "Sending login token message to " + to);
138 +
139 + var sms = getTemplate(1, domain, language);
140 + if (sms == null) { parent.debug('email', "Error: Failed to get SMS template"); return; } // No SMS template found
141 +
142 + // Setup the template
143 + sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
144 + sms = sms.split('[[1]]').join(verificationCode);
145 +
146 + // Send the SMS
147 + obj.sendSMS(to, sms, func);
148 + };
149 +
150 + return obj;
151 +};
meshsms.js
+7 -1
@@ -37,9 +37,15 @@
37 "apikey": "xxxxxxx",
38 "from": "15555555555"
39 }
40 +
41 +// For URL, add this in config.json
42 +"sms": {
43 + "provider": "url",
44 + "url": "https://sample.com/?phone={{phone}}&msg={{message}}"
45 +}
46 */
47
42 -// Construct a MeshAgent object, called upon connection
48 +// Construct a SMS server object
49 module.exports.CreateMeshSMS = function (parent) {
50 var obj = {};
51 obj.parent = parent;
meshuser.js
+24 -1
@@ -575,6 +575,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
575 if (domain.passwordrequirements.lock2factor == true) { serverinfo.lock2factor = true; } // Indicate 2FA change are not allowed
576 if (typeof domain.passwordrequirements.maxfidokeys == 'number') { serverinfo.maxfidokeys = domain.passwordrequirements.maxfidokeys; }
577 }
578 + if (parent.parent.msgserver != null) { serverinfo.userMsgProviders = parent.parent.msgserver.providers; }
579
580 // Build the mobile agent URL, this is used to connect mobile devices
581 var agentServerName = parent.getWebServerName(domain, req);
@@ -5315,7 +5316,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
5316 'serverupdate': [serverUserCommandServerUpdate, "Updates server to latest version. Optional version argument to install specific version. Example: serverupdate 0.8.49"],
5317 'setmaxtasks': [serverUserCommandSetMaxTasks, ""],
5318 'showpaths': [serverUserCommandShowPaths, ""],
5318 - 'sms': [serverUserCommandSMS, ""],
5319 + 'sms': [serverUserCommandSMS, "Send a SMS message to a specified phone number"],
5320 + 'msg': [serverUserCommandMsg, "Send a user message to a user handle"],
5321 'swarmstats': [serverUserCommandSwarmStats, ""],
5322 'tasklimiter': [serverUserCommandTaskLimiter, "Returns the internal status of the tasklimiter. This is a system used to smooth out work done by the server. It's used by, for example, agent updates so that not all agents are updated at the same time."],
5323 'trafficdelta': [serverUserCommandTrafficDelta, ""],
@@ -6727,6 +6729,27 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6729 }
6730 }
6731
6732 + function serverUserCommandMsg(cmdData) {
6733 + if ((parent.parent.msgserver == null) || (parent.parent.msgserver.providers == 0)) {
6734 + cmdData.result = "No messaging providers configured.";
6735 + } else {
6736 + if (cmdData.cmdargs['_'].length != 2) {
6737 + var r = [];
6738 + if ((parent.parent.msgserver.providers & 1) != 0) { r.push("Usage: MSG \"telegram:@UserHandle\" \"Message\"."); }
6739 + if ((parent.parent.msgserver.providers & 2) != 0) { r.push("Usage: MSG \"signal:@UserHandle\" \"Message\"."); }
6740 + cmdData.result = r.join('\r\n');
6741 + } else {
6742 + parent.parent.msgserver.sendMessage(cmdData.cmdargs['_'][0], cmdData.cmdargs['_'][1], function (status, msg) {
6743 + if (typeof msg == 'string') {
6744 + try { ws.send(JSON.stringify({ action: 'serverconsole', value: status ? ('Success: ' + msg) : ('Failed: ' + msg), tag: cmdData.command.tag })); } catch (ex) { }
6745 + } else {
6746 + try { ws.send(JSON.stringify({ action: 'serverconsole', value: status ? 'Success' : 'Failed', tag: cmdData.command.tag })); } catch (ex) { }
6747 + }
6748 + });
6749 + }
6750 + }
6751 + }
6752 +
6753 function serverUserCommandEmail(cmdData) {
6754 if (domain.mailserver == null) {
6755 cmdData.result = "No email service enabled.";
sample-config-advanced.json
+7
@@ -614,5 +614,12 @@
614 "____sms": {
615 "provider": "url",
616 "url": "http://example.com/sms.ashx?phone={{phone}}&message={{message}}"
617 + },
618 + "_messaging": {
619 + "telegram": {
620 + "apiid": 0,
621 + "apihash": "hexBalue",
622 + "session": "base64Value"
623 + }
624 }
625 }
telegram.js new
+136
@@ -0,0 +1,136 @@
1 +/**
2 +* @description MeshCentral Telegram communication module
3 +* @author Ylian Saint-Hilaire
4 +* @copyright Intel Corporation 2018-2022
5 +* @license Apache-2.0
6 +* @version v0.0.1
7 +*/
8 +
9 +/*xjslint node: true */
10 +/*xjslint plusplus: true */
11 +/*xjslint maxlen: 256 */
12 +/*jshint node: true */
13 +/*jshint strict: false */
14 +/*jshint esversion: 6 */
15 +"use strict";
16 +
17 +/*
18 + "telegram": {
19 + "apiid": 0000000,
20 + "apihash": "hexvalue",
21 + "session": "base64value"
22 + }
23 +*/
24 +
25 +// Construct a Telegram server object
26 +module.exports.CreateServer = function (parent) {
27 + var obj = {};
28 + obj.parent = parent;
29 +
30 + // Check that we have the correct values
31 + if (typeof parent.config.telegram != 'object') return null;
32 + if (typeof parent.config.telegram.apiid != 'number') return null;
33 + if (typeof parent.config.telegram.apihash != 'string') return null;
34 + if (typeof parent.config.telegram.session != 'string') return null;
35 +
36 + // Connect to the telegram server
37 + async function connect() {
38 + const { TelegramClient } = require('telegram');
39 + const { StringSession } = require('telegram/sessions');
40 + const input = require('input');
41 +
42 + const stringSession = new StringSession(parent.config.telegram.session);
43 + const client = new TelegramClient(stringSession, parent.config.telegram.apiid, parent.config.telegram.apihash, { connectionRetries: 5 });
44 + await client.start({
45 + phoneNumber: async function () { await input.text("Please enter your number: "); },
46 + password: async function () { await input.text("Please enter your password: "); },
47 + phoneCode: async function () { await input.text("Please enter the code you received: "); },
48 + onError: function (err) { console.log('Telegram error', err); },
49 + });
50 + console.log("MeshCentral Telegram session is connected.");
51 + obj.client = client;
52 + //console.log(client.session.save()); // Save this string to avoid logging in again
53 + }
54 +
55 + // Send an Telegram message
56 + obj.sendMessage = async function (to, msg, func) {
57 + if (obj.client == null) return;
58 + parent.debug('email', 'Sending Telegram to: ' + to + ': ' + msg);
59 + await client.sendMessage(to, { message: msg });
60 + func(true);
61 + }
62 +
63 + // Get the correct SMS template
64 + function getTemplate(templateNumber, domain, lang) {
65 + parent.debug('email', 'Getting SMS template #' + templateNumber + ', lang: ' + lang);
66 + if (Array.isArray(lang)) { lang = lang[0]; } // TODO: For now, we only use the first language given.
67 +
68 + var r = {}, emailsPath = null;
69 + if ((domain != null) && (domain.webemailspath != null)) { emailsPath = domain.webemailspath; }
70 + else if (obj.parent.webEmailsOverridePath != null) { emailsPath = obj.parent.webEmailsOverridePath; }
71 + else if (obj.parent.webEmailsPath != null) { emailsPath = obj.parent.webEmailsPath; }
72 + if ((emailsPath == null) || (obj.parent.fs.existsSync(emailsPath) == false)) { return null }
73 +
74 + // Get the non-english email if needed
75 + var txtfile = null;
76 + if ((lang != null) && (lang != 'en')) {
77 + var translationsPath = obj.parent.path.join(emailsPath, 'translations');
78 + var translationsPathTxt = obj.parent.path.join(emailsPath, 'translations', 'sms-messages_' + lang + '.txt');
79 + if (obj.parent.fs.existsSync(translationsPath) && obj.parent.fs.existsSync(translationsPathTxt)) {
80 + txtfile = obj.parent.fs.readFileSync(translationsPathTxt).toString();
81 + }
82 + }
83 +
84 + // Get the english email
85 + if (txtfile == null) {
86 + var pathTxt = obj.parent.path.join(emailsPath, 'sms-messages.txt');
87 + if (obj.parent.fs.existsSync(pathTxt)) {
88 + txtfile = obj.parent.fs.readFileSync(pathTxt).toString();
89 + }
90 + }
91 +
92 + // No email templates
93 + if (txtfile == null) { return null; }
94 +
95 + // Decode the TXT file
96 + var lines = txtfile.split('\r\n').join('\n').split('\n')
97 + if (lines.length <= templateNumber) return null;
98 +
99 + return lines[templateNumber];
100 + }
101 +
102 + // Send telegram user verification message
103 + obj.sendPhoneCheck = function (domain, to, verificationCode, language, func) {
104 + parent.debug('email', "Sending verification Telegram to " + to);
105 +
106 + var sms = getTemplate(0, domain, language);
107 + if (sms == null) { parent.debug('email', "Error: Failed to get SMS template"); return; } // No SMS template found
108 +
109 + // Setup the template
110 + sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
111 + sms = sms.split('[[1]]').join(verificationCode);
112 +
113 + // Send the SMS
114 + obj.sendMessage(to, sms, func);
115 + };
116 +
117 + // Send login token verification message
118 + obj.sendToken = function (domain, to, verificationCode, language, func) {
119 + parent.debug('email', "Sending login token Telegram to " + to);
120 +
121 + var sms = getTemplate(1, domain, language);
122 + if (sms == null) { parent.debug('email', "Error: Failed to get SMS template"); return; } // No SMS template found
123 +
124 + // Setup the template
125 + sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
126 + sms = sms.split('[[1]]').join(verificationCode);
127 +
128 + // Send the SMS
129 + obj.sendMessage(to, sms, func);
130 + };
131 +
132 + // Connect the Telegram session
133 + connect();
134 +
135 + return obj;
136 +};
webserver.js
+2
@@ -3162,6 +3162,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates, doneF
3162 if (domain.allowsavingdevicecredentials == false) { features2 += 0x00400000; } // Do not allow device credentials to be saved on the server
3163 if ((typeof domain.files == 'object') && (domain.files.sftpconnect === false)) { features2 += 0x00800000; } // Remove the "SFTP Connect" button in the "Files" tab when the device is agent managed
3164 if ((typeof domain.terminal == 'object') && (domain.terminal.sshconnect === false)) { features2 += 0x01000000; } // Remove the "SSH Connect" button in the "Terminal" tab when the device is agent managed
3165 + if ((parent.msgserver != null) && (parent.msgserver.providers != 0)) { features2 += 0x02000000; } // User messaging server is enabled
3166 + if ((parent.msgserver != null) && (parent.msgserver.providers != 0) && ((typeof domain.passwordrequirements != 'object') || (domain.passwordrequirements.msg2factor != false))) { features2 += 0x04000000; } // User messaging 2FA is allowed
3167 return { features: features, features2: features2 };
3168 }
3169