EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
meshsms.js
Go to the documentation of this file.
1/**
2* @description MeshCentral SMS gateway 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// For Twilio, add this in config.json
19"sms": {
20 "provider": "twilio",
21 "sid": "ACxxxxxxxxx",
22 "auth": "xxxxxxx",
23 "from": "+15555555555"
24},
25
26// For Plivo, add this in config.json
27"sms": {
28 "provider": "plivo",
29 "id": "xxxxxxx",
30 "token": "xxxxxxx",
31 "from": "15555555555"
32}
33
34// For Telnyx, add this in config.json
35"sms": {
36 "provider": "telnyx",
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
48// Construct a SMS server object
49module.exports.CreateMeshSMS = function (parent) {
50 var obj = {};
51 obj.parent = parent;
52 obj.provider = null;
53
54 // SMS gateway provider setup
55 switch (parent.config.sms.provider) {
56 case 'twilio': {
57 // Validate Twilio configuration values
58 if (typeof parent.config.sms.sid != 'string') { console.log('Invalid or missing SMS gateway provider sid.'); return null; }
59 if (typeof parent.config.sms.auth != 'string') { console.log('Invalid or missing SMS gateway provider auth.'); return null; }
60 if (typeof parent.config.sms.from != 'string') { console.log('Invalid or missing SMS gateway provider from.'); return null; }
61
62 // Setup Twilio
63 var Twilio = require('twilio');
64 obj.provider = new Twilio(parent.config.sms.sid, parent.config.sms.auth);
65 break;
66 }
67 case 'plivo': {
68 // Validate Plivo configuration values
69 if (typeof parent.config.sms.id != 'string') { console.log('Invalid or missing SMS gateway provider id.'); return null; }
70 if (typeof parent.config.sms.token != 'string') { console.log('Invalid or missing SMS gateway provider token.'); return null; }
71 if (typeof parent.config.sms.from != 'string') { console.log('Invalid or missing SMS gateway provider from.'); return null; }
72
73 // Setup Plivo
74 var plivo = require('plivo');
75 obj.provider = new plivo.Client(parent.config.sms.id, parent.config.sms.token);
76 break;
77 }
78 case 'telnyx': {
79 // Validate Telnyx configuration values
80 if (typeof parent.config.sms.apikey != 'string') { console.log('Invalid or missing SMS gateway provider apikey.'); return null; }
81 if (typeof parent.config.sms.from != 'string') { console.log('Invalid or missing SMS gateway provider from.'); return null; }
82
83 // Setup Telnyx
84 obj.provider = require('telnyx')(parent.config.sms.apikey);
85 break;
86 }
87 case 'url': {
88 // Validate URL configuration values
89 if (parent.config.sms.url != 'console') {
90 if (typeof parent.config.sms.url != 'string') { console.log('Invalid or missing SMS gateway URL value.'); return null; }
91 if (!parent.config.sms.url.toLowerCase().startsWith('http://') && !parent.config.sms.url.toLowerCase().startsWith('https://')) { console.log('Invalid or missing SMS gateway, URL must start with http:// or https://.'); return null; }
92 if (parent.config.sms.url.indexOf('{{message}}') == -1) { console.log('Invalid or missing SMS gateway, URL must include {{message}}.'); return null; }
93 if (parent.config.sms.url.indexOf('{{phone}}') == -1) { console.log('Invalid or missing SMS gateway, URL must include {{phone}}.'); return null; }
94 }
95 break;
96 }
97 default: {
98 // Unknown SMS gateway provider
99 console.log('Unknown SMS gateway provider: ' + parent.config.sms.provider);
100 return null;
101 }
102 }
103
104 // Send an SMS message
105 obj.sendSMS = function (to, msg, func) {
106 parent.debug('email', 'Sending SMS to: ' + to + ': ' + msg);
107 if (parent.config.sms.provider == 'twilio') { // Twilio
108 obj.provider.messages.create({
109 from: parent.config.sms.from,
110 to: to,
111 body: msg
112 }, function (err, result) {
113 if (err != null) { parent.debug('email', 'SMS error: ' + err.message); } else { parent.debug('email', 'SMS result: ' + JSON.stringify(result)); }
114 if (func != null) { func((err == null) && (result.status == 'queued'), err ? err.message : null, result); }
115 });
116 } else if (parent.config.sms.provider == 'plivo') { // Plivo
117 if (to.split('-').join('').split(' ').join('').split('+').join('').length == 10) { to = '1' + to; } // If we only have 10 digits, add a 1 in front.
118 obj.provider.messages.create(
119 parent.config.sms.from,
120 to,
121 msg
122 ).then(function (result) {
123 parent.debug('email', 'SMS result: ' + JSON.stringify(result));
124 if (func != null) { func((result != null) && (result.messageUuid != null), null, result); }
125 }
126 ).catch(function (err) {
127 var msg = null;
128 if ((err != null) && err.message) { msg = JSON.parse(err.message).error; }
129 parent.debug('email', 'SMS error: ' + msg);
130 if (func != null) { func(false, msg, null); }
131 }
132 );
133 } else if (parent.config.sms.provider == 'telnyx') { // Telnyx
134 obj.provider.messages.create({
135 from: parent.config.sms.from,
136 to: to,
137 text: msg
138 }, function (err, result) {
139 if (err != null) { parent.debug('email', 'SMS error: ' + err.type); } else { parent.debug('email', 'SMS result: ' + JSON.stringify(result)); }
140 if (func != null) { func((err == null), err ? err.type : null, result); }
141 });
142 } else if (parent.config.sms.provider == 'url') { // URL
143 if (parent.config.sms.url == 'console') {
144 // This is for debugging, just display the SMS to the console
145 console.log('SMS (' + to + '): ' + msg);
146 if (func != null) { func(true, null, null); }
147 } else {
148 var sms = parent.config.sms.url.split('{{phone}}').join(encodeURIComponent(to)).split('{{message}}').join(encodeURIComponent(msg));
149 parent.debug('email', 'SMS URL: ' + sms);
150 sms = require('url').parse(sms);
151 if (sms.protocol == 'https:') {
152 // HTTPS GET request
153 const options = { hostname: sms.hostname, port: sms.port ? sms.port : 443, path: sms.path, method: 'GET', rejectUnauthorized: false };
154 const request = require('https').request(options, function (res) { parent.debug('email', 'SMS result: ' + res.statusCode); if (func != null) { func(res.statusCode == 200, (res.statusCode == 200) ? null : res.statusCode, null); } res.on('data', function (d) { }); });
155 request.on('error', function (err) { parent.debug('email', 'SMS error: ' + err); if (func != null) { func(false, err, null); } });
156 request.end();
157 } else {
158 // HTTP GET request
159 const options = { hostname: sms.hostname, port: sms.port ? sms.port : 80, path: sms.path, method: 'GET' };
160 const request = require('http').request(options, function (res) { parent.debug('email', 'SMS result: ' + res.statusCode); if (func != null) { func(res.statusCode == 200, (res.statusCode == 200) ? null : res.statusCode, null); } res.on('data', function (d) { }); });
161 request.on('error', function (err) { parent.debug('email', 'SMS error: ' + err); if (func != null) { func(false, err, null); } });
162 request.end();
163 }
164 }
165 }
166 }
167
168 // Get the correct SMS template
169 function getTemplate(templateNumber, domain, lang) {
170 parent.debug('email', 'Getting SMS template #' + templateNumber + ', lang: ' + lang);
171 if (Array.isArray(lang)) { lang = lang[0]; } // TODO: For now, we only use the first language given.
172 if (lang != null) { lang = lang.split('-')[0]; } // Take the first part of the language, "xx-xx"
173
174 var r = {}, emailsPath = null;
175 if ((domain != null) && (domain.webemailspath != null)) { emailsPath = domain.webemailspath; }
176 else if (obj.parent.webEmailsOverridePath != null) { emailsPath = obj.parent.webEmailsOverridePath; }
177 else if (obj.parent.webEmailsPath != null) { emailsPath = obj.parent.webEmailsPath; }
178 if ((emailsPath == null) || (obj.parent.fs.existsSync(emailsPath) == false)) { return null }
179
180 // Get the non-english email if needed
181 var txtfile = null;
182 if ((lang != null) && (lang != 'en')) {
183 var translationsPath = obj.parent.path.join(emailsPath, 'translations');
184 var translationsPathTxt = obj.parent.path.join(emailsPath, 'translations', 'sms-messages_' + lang + '.txt');
185 if (obj.parent.fs.existsSync(translationsPath) && obj.parent.fs.existsSync(translationsPathTxt)) {
186 txtfile = obj.parent.fs.readFileSync(translationsPathTxt).toString();
187 }
188 }
189
190 // Get the english email
191 if (txtfile == null) {
192 var pathTxt = obj.parent.path.join(emailsPath, 'sms-messages.txt');
193 if (obj.parent.fs.existsSync(pathTxt)) {
194 txtfile = obj.parent.fs.readFileSync(pathTxt).toString();
195 }
196 }
197
198 // If no english sms and a non-english language is requested, try to get the default translated sms
199 if (txtfile == null && (lang != null) && (lang != 'en')) {
200 var translationsPath = obj.parent.path.join(obj.parent.webEmailsPath, 'translations');
201 var translationsPathTxt = obj.parent.path.join(obj.parent.webEmailsPath, 'translations', 'sms-messages_' + lang + '.txt');
202 if (obj.parent.fs.existsSync(translationsPath) && obj.parent.fs.existsSync(translationsPathTxt)) {
203 txtfile = obj.parent.fs.readFileSync(translationsPathTxt).toString();
204 }
205 }
206
207 // If no default translated sms, try to get the default english sms
208 if (txtfile == null) {
209 var pathTxt = obj.parent.path.join(obj.parent.webEmailsPath, 'sms-messages.txt');
210 if (obj.parent.fs.existsSync(pathTxt)) {
211 txtfile = obj.parent.fs.readFileSync(pathTxt).toString();
212 }
213 }
214
215 // No email templates
216 if (txtfile == null) { return null; }
217
218 // Decode the TXT file
219 var lines = txtfile.split('\r\n').join('\n').split('\n')
220 if (lines.length <= templateNumber) return null;
221
222 return lines[templateNumber];
223 }
224
225 // Send phone number verification SMS
226 obj.sendPhoneCheck = function (domain, phoneNumber, verificationCode, language, func) {
227 parent.debug('email', "Sending verification SMS to " + phoneNumber);
228
229 var sms = getTemplate(0, domain, language);
230 if (sms == null) { parent.debug('email', "Error: Failed to get SMS template"); return; } // No SMS template found
231
232 // Setup the template
233 sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
234 sms = sms.split('[[1]]').join(verificationCode);
235
236 // Send the SMS
237 obj.sendSMS(phoneNumber, sms, func);
238 };
239
240 // Send phone number verification SMS
241 obj.sendToken = function (domain, phoneNumber, verificationCode, language, func) {
242 parent.debug('email', "Sending login token SMS to " + phoneNumber);
243
244 var sms = getTemplate(1, domain, language);
245 if (sms == null) { parent.debug('email', "Error: Failed to get SMS template"); return; } // No SMS template found
246
247 // Setup the template
248 sms = sms.split('[[0]]').join(domain.title ? domain.title : 'MeshCentral');
249 sms = sms.split('[[1]]').join(verificationCode);
250
251 // Send the SMS
252 obj.sendSMS(phoneNumber, sms, func);
253 };
254
255 return obj;
256};