@tiledesk/tiledesk-whatsapp-connector 0.1.53 → 0.1.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -11,6 +11,7 @@ const { v4: uuidv4 } = require('uuid');
11
11
  const cors = require('cors');
12
12
  var winston = require('./winston');
13
13
  const url = require('url');
14
+ const mongoose = require('mongoose');
14
15
 
15
16
  //const ext = require('./routes/ext')
16
17
  //const extRoute = ext.router;
@@ -27,6 +28,7 @@ const { TiledeskAppsClient } = require('./tiledesk/TiledeskAppsClient');
27
28
  const { MessageHandler } = require('./tiledesk/MessageHandler');
28
29
  const { TiledeskBotTester } = require('./tiledesk/TiledeskBotTester');
29
30
  const { TemplateManager } = require('./tiledesk/TemplateManager');
31
+ const { WhatsappLogger } = require('./tiledesk/WhatsappLogger');
30
32
 
31
33
  // mongo
32
34
  const { KVBaseMongo } = require('./tiledesk/KVBaseMongo');
@@ -58,6 +60,9 @@ let REDIS_HOST = null;
58
60
  let REDIS_PORT = null;
59
61
  let REDIS_PASSWORD = null;
60
62
  let BASE_FILE_URL = null;
63
+ let AMQP_MANAGER_URL = null;
64
+ let JOB_TOPIC_EXCHANGE = null;
65
+
61
66
 
62
67
  // Handlebars register helpers
63
68
  handlebars.registerHelper('isEqual', (a, b) => {
@@ -779,13 +784,13 @@ router.post('/tiledesk', async (req, res) => {
779
784
  twClient.sendMessage(phone_number_id, whatsappJsonMessage).then((response) => {
780
785
  winston.verbose("(wab) Message sent to WhatsApp! " + response.status + " " + response.statusText);
781
786
  }).catch((err) => {
782
- res.status(400).send({ success: false, error: "il template non esiste" });
783
787
  winston.error("(wab) error send message: ", err);
788
+ res.status(400).send({ success: false, error: "Template not existing" });
784
789
  })
785
790
 
786
791
  } else {
787
- res.status(400).send({ success: false, error: "il template non esiste" });
788
792
  winston.error("(wab) Whatsapp Json Message is undefined!")
793
+ res.status(400).send({ success: false, error: "An error occurred during message translation" });
789
794
  }
790
795
 
791
796
  } else {
@@ -1038,6 +1043,29 @@ router.post("/webhook/:project_id", async (req, res) => {
1038
1043
 
1039
1044
  }
1040
1045
  }
1046
+
1047
+ if (req.body.entry &&
1048
+ req.body.entry[0].changes &&
1049
+ req.body.entry[0].changes[0] &&
1050
+ req.body.entry[0].changes[0].value.statuses &&
1051
+ req.body.entry[0].changes[0].value.statuses[0]
1052
+ ) {
1053
+ let whatsappStatusMessage = req.body.entry[0].changes[0].value.statuses[0];
1054
+ winston.verbose("(wab) whatsappStatusMessage: ", whatsappStatusMessage);
1055
+
1056
+ let message_id = whatsappStatusMessage.id;
1057
+ let status = whatsappStatusMessage.status;
1058
+ let error = null;
1059
+ if (whatsappStatusMessage.errors) {
1060
+ error = whatsappStatusMessage.errors[0].title;
1061
+ }
1062
+ winston.verbose("(wab) update status in " + status+ " for message_id " + message_id);
1063
+
1064
+ const wl = new WhatsappLogger();
1065
+ wl.updateMessageStatus(message_id, status, error);
1066
+
1067
+ }
1068
+
1041
1069
  res.sendStatus(200);
1042
1070
 
1043
1071
  } else {
@@ -1324,6 +1352,26 @@ async function startApp(settings, callback) {
1324
1352
  winston.info("(wab) Missing redis parameters --> Test it out on WhatsApp disabled");
1325
1353
  }
1326
1354
 
1355
+ if (!settings.AMQP_MANAGER_URL) {
1356
+ winston.error("(wab) AMQP_MANAGER_URL is mandatory (?). Exit...");
1357
+ } else {
1358
+ AMQP_MANAGER_URL = settings.AMQP_MANAGER_URL;
1359
+ winston.info("(wab) AMQP_MANAGER_URL is present");
1360
+ }
1361
+
1362
+ if (!settings.JOB_TOPIC_EXCHANGE) {
1363
+ winston.warning("(wab) JOB_TOPIC_EXCHANGE should be present. Using default value");
1364
+ JOB_TOPIC_EXCHANGE = "tiledesk-whatsapp";
1365
+ } else {
1366
+ JOB_TOPIC_EXCHANGE = settings.JOB_TOPIC_EXCHANGE;
1367
+ winston.info("(wab) JOB_TOPIC_EXCHANGE is present");
1368
+ }
1369
+
1370
+ mongoose.connect(process.env.MONGODB_URL)
1371
+ .then(() => { winston.info("Mongoose DB Connected") })
1372
+ .catch((err) => { winston.error("(Mongoose) Unable to connect with MongoDB ", err)
1373
+ })
1374
+
1327
1375
  db.connect(settings.MONGODB_URL, () => {
1328
1376
  winston.info("(wab) KVBaseMongo successfully connected.");
1329
1377
 
@@ -1336,7 +1384,9 @@ async function startApp(settings, callback) {
1336
1384
  DB: db,
1337
1385
  API_URL: API_URL,
1338
1386
  GRAPH_URL: GRAPH_URL,
1339
- BASE_FILE_URL: BASE_FILE_URL
1387
+ BASE_FILE_URL: BASE_FILE_URL,
1388
+ AMQP_MANAGER_URL: AMQP_MANAGER_URL,
1389
+ JOB_TOPIC_EXCHANGE: JOB_TOPIC_EXCHANGE,
1340
1390
  }, (err) => {
1341
1391
  if (!err) {
1342
1392
  winston.info("(wab) API route successfully started.")
@@ -0,0 +1,36 @@
1
+ const mongoose = require('mongoose');
2
+
3
+ const MessageLogSchema = mongoose.Schema({
4
+ json_message: {
5
+ type: Object,
6
+ required: true
7
+ },
8
+ transaction_id: {
9
+ type: String,
10
+ required: true
11
+ },
12
+ message_id: {
13
+ type: String,
14
+ required: false
15
+ },
16
+ status: {
17
+ type: String,
18
+ required: true
19
+ },
20
+ status_code: {
21
+ type: Number,
22
+ required: true
23
+ },
24
+ error: {
25
+ type: String
26
+ },
27
+ timestamp: {
28
+ type: Date,
29
+ default: Date.now
30
+ }
31
+ })
32
+
33
+
34
+ const MessageLog = mongoose.model("MessageLog", MessageLogSchema);
35
+
36
+ module.exports = { MessageLog };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-whatsapp-connector",
3
- "version": "0.1.53",
3
+ "version": "0.1.55",
4
4
  "description": "Tiledesk Whatsapp connector",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -19,6 +19,7 @@
19
19
  "express": "^4.18.1",
20
20
  "form-data": "^4.0.0",
21
21
  "handlebars": "^4.7.7",
22
+ "jobs-worker-queued": "^0.0.5",
22
23
  "jsonwebtoken": "^8.5.1",
23
24
  "mongoose": "^6.5.0",
24
25
  "mongodb": "^3.5.5",
package/routes/api.js CHANGED
@@ -6,11 +6,13 @@ const router = express.Router({ mergeParams: true });
6
6
  const { TemplateManager } = require('../tiledesk/TemplateManager');
7
7
  const { TiledeskWhatsappTranslator } = require('../tiledesk/TiledeskWhatsappTranslator');
8
8
  const { TiledeskWhatsapp } = require('../tiledesk/TiledeskWhatsapp');
9
+ const { Scheduler } = require('../tiledesk/Scheduler');
9
10
 
10
11
  let db = null;
11
12
  let API_URL = null;
12
13
  let GRAPH_URL = null;
13
14
  let BASE_FILE_URL = null;
15
+ let AMQP_MANAGER_URL = null;
14
16
 
15
17
  router.get('/', async (req, res) => {
16
18
  res.status(200).send({ message: "API route works"})
@@ -125,14 +127,19 @@ router.get("/templates/:project_id", async (req, res) => {
125
127
 
126
128
  router.post('/tiledesk/broadcast', async (req, res) => {
127
129
  winston.verbose("(wab) Action received from Tiledesk (Broadcast)");
128
- winston.debug("Body (broadcast): ",JSON.stringify(req.body, null, 2));
130
+ winston.debug("Body (broadcast): ", JSON.stringify(req.body, null, 2));
129
131
 
130
132
  let body = req.body;
131
133
 
132
134
  let project_id = body.id_project;
133
135
  let receiver_list = body.receiver_list;
134
- let phone_number_id = body. phone_number_id;
136
+ let phone_number_id = body.phone_number_id;
135
137
  let template = body.template;
138
+ let transaction_id = body.transaction_id;
139
+
140
+ if (!transaction_id) {
141
+ transaction_id = "tiledesk-broadcast-" + Date.now();
142
+ }
136
143
 
137
144
  let CONTENT_KEY = "whatsapp-" + project_id;
138
145
  let settings = await db.get(CONTENT_KEY);
@@ -145,51 +152,22 @@ router.post('/tiledesk/broadcast', async (req, res) => {
145
152
  return res.status(400).send({ success: false, error: "Missing parameter 'WhatsApp Business Account ID'. Please update your app."})
146
153
  }
147
154
 
148
- const tm = new TemplateManager({ token: settings.wab_token, business_account_id: settings.business_account_id, GRAPH_URL: GRAPH_URL })
149
- const tlr = new TiledeskWhatsappTranslator();
150
- const twClient = new TiledeskWhatsapp({ token: settings.wab_token, GRAPH_URL: GRAPH_URL, API_URL: API_URL, BASE_FILE_URL: BASE_FILE_URL });
151
-
152
- let response = await tm.getTemplates();
153
- let templates = response.data;
155
+ let scheduler = new Scheduler({ AMQP_MANAGER_URL: AMQP_MANAGER_URL, JOB_TOPIC_EXCHANGE: JOB_TOPIC_EXCHANGE });
156
+ let data_To_scheduler = {
157
+ project_id: project_id,
158
+ receiver_list: receiver_list,
159
+ phone_number_id: phone_number_id,
160
+ transaction_id: transaction_id,
161
+ template: template,
162
+ settings: settings
163
+ };
154
164
 
155
- let selected_template = templates.find(t => t.name === template.name);
156
- let params_object = await tm.generateParamsObject(selected_template);
165
+ winston.debug('(wab) data_To_scheduler: ', data_To_scheduler);
157
166
 
158
- // Send messages
159
- let messages_sent = 0;
160
- let errors = [];
161
- let error_count = 0;
167
+ let schedulerResult = await scheduler.goSchedule(data_To_scheduler);
168
+ winston.verbose('(wab) schedulerResult: ', schedulerResult);
162
169
 
163
- if (receiver_list) {
164
- let i = 0;
165
- async function execute(receiver) {
166
- let whatsappJsonMessage = await tlr.createTemplateMessage(selected_template, receiver, params_object);
167
- winston.debug("(wab) whatsappJsonMessage: ", JSON.stringify(whatsappJsonMessage, null, 2));
168
-
169
- await twClient.sendMessage(phone_number_id, whatsappJsonMessage).then((response) => {
170
- winston.verbose("(wab) Message sent to WhatsApp! " + response.status + " " + response.statusText);
171
- messages_sent += 1;
172
- i += 1;
173
- if (i < receiver_list.length) {
174
- execute(receiver_list[i])
175
- } else {
176
- winston.debug("(wab) End of list")
177
- return res.status(200).send({ success: true, message: "Broadcast terminated", messages_sent: messages_sent, errors: errors });
178
- }
179
- }).catch((err) => {
180
- winston.error("(wab) error send message: " + err.response.data.error.message);
181
- errors.push({ receiver: receiver.phone_number, error: err.response.data.error.message });
182
- i += 1;
183
- if (i < receiver_list.length) {
184
- execute(receiver_list[i])
185
- } else {
186
- winston.debug("(wab) End of list")
187
- return res.status(200).send({ success: true, message: "Broadcast terminated", messages_sent: messages_sent, errors: errors });
188
- }
189
- })
190
- }
191
- execute(receiver_list[0]);
192
- }
170
+ res.status(200).send({ success: true, message: "Job started. Send messages in queue." })
193
171
 
194
172
  })
195
173
 
@@ -230,7 +208,19 @@ async function startRoute(settings, callback) {
230
208
  winston.info("(wab api) BASE_FILE_URL: " + BASE_FILE_URL);
231
209
  }
232
210
 
233
-
211
+ if (!settings.AMQP_MANAGER_URL) {
212
+ winston.error("(wab api) AMQP_MANAGER_URL is mandatory (?). Exit...");
213
+ } else {
214
+ AMQP_MANAGER_URL = settings.AMQP_MANAGER_URL;
215
+ winston.info("(wab api) AMQP_MANAGER_URL is present");
216
+ }
217
+
218
+ if (!settings.JOB_TOPIC_EXCHANGE) {
219
+ winston.error("(wab api) JOB_TOPIC_EXCHANGE is mandatory (?). Exit...");
220
+ } else {
221
+ JOB_TOPIC_EXCHANGE = settings.JOB_TOPIC_EXCHANGE;
222
+ winston.info("(wab api) JOB_TOPIC_EXCHANGE is present");
223
+ }
234
224
 
235
225
  }
236
226
 
@@ -216,35 +216,36 @@
216
216
 
217
217
  <div style="width: 100%; display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 15px;">
218
218
  <div>
219
+ <p>Added Messages Logs for WhatsApp broadcast</p>
220
+ <p>Added Queue Manager for Whatsapp broadcast</p>
219
221
  <p>Bug fixes and performance improvements</p>
220
222
  </div>
221
- <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.53</b></p>
223
+ <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.55</b></p>
222
224
  </div>
223
-
225
+
224
226
  <div style="width: 100%; display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 15px;">
225
227
  <div>
226
228
  <p>Bug fixes and performance improvements</p>
227
229
  </div>
228
- <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.52</b></p>
230
+ <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.53</b></p>
229
231
  </div>
230
232
 
231
233
  <div style="width: 100%; display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 15px;">
232
234
  <div>
233
- <p style="margin-bottom: 0px">Bug fixes</p>
234
- <!--<p>Bug fixes and performance improvements</p>-->
235
+ <p>Bug fixes and performance improvements</p>
235
236
  </div>
236
- <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.50</b></p>
237
+ <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.52</b></p>
237
238
  </div>
238
239
 
239
240
  <div style="width: 100%; display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 15px;">
240
241
  <div>
241
- <p style="margin-bottom: 0px">Added support for WhatsApp voice messages</p>
242
- <p style="margin-bottom: 0px">Bug fixes and minor improvements</p>
242
+ <p style="margin-bottom: 0px">Bug fixes</p>
243
243
  <!--<p>Bug fixes and performance improvements</p>-->
244
244
  </div>
245
- <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.49</b></p>
245
+ <p style="font-size: 12px; color: #676767; margin-bottom: 0px;">Version <b>0.1.50</b></p>
246
246
  </div>
247
247
 
248
+
248
249
  <hr style="margin-top: 50px; margin-bottom: 20px">
249
250
 
250
251
  <script>
@@ -0,0 +1,48 @@
1
+ var JobManager = require("jobs-worker-queued");
2
+ var winston = require('../winston');
3
+ var jobManager;
4
+
5
+ class Scheduler {
6
+
7
+ constructor(config) {
8
+ if (!config) {
9
+ throw new Error('config is mandatory');
10
+ }
11
+
12
+ if (!config.AMQP_MANAGER_URL) {
13
+ throw new Error('config.AMQP_MANAGER_URL is mandatory');
14
+ }
15
+
16
+ if (!config.JOB_TOPIC_EXCHANGE) {
17
+ throw new Error('config.JOB_TOPIC_EXCHANGE is mandatory');
18
+ }
19
+
20
+ this.AMQP_MANAGER_URL = config.AMQP_MANAGER_URL;
21
+ this.JOB_TOPIC_EXCHANGE = config.JOB_TOPIC_EXCHANGE;
22
+
23
+ this.log = false;
24
+ if (config.log) {
25
+ this.log = config.log;
26
+ }
27
+
28
+ jobManager = new JobManager(this.AMQP_MANAGER_URL, {
29
+ debug: false,
30
+ topic: this.JOB_TOPIC_EXCHANGE,
31
+ exchange: this.JOB_TOPIC_EXCHANGE
32
+ });
33
+ }
34
+
35
+ async goSchedule(mydata) {
36
+ mydata.date = new Date();
37
+ try {
38
+ winston.debug("(wab) Data arrived to scheduler: ", mydata);
39
+ jobManager.publish(mydata);
40
+ return { success: true, msg: 'scheduling success!' };
41
+ } catch (err) {
42
+ winston.error('(wab) scheduling error ', err);
43
+ return { success: false, msg: 'Error in scheduling' };
44
+ }
45
+ }
46
+ }
47
+
48
+ module.exports = { Scheduler };
@@ -0,0 +1,48 @@
1
+ const winston = require("../winston");
2
+ const { MessageLog } = require("../models/WhatsappLog");
3
+
4
+ class WhatsappLogger {
5
+
6
+ constructor() {}
7
+
8
+ async updateMessageStatus(message_id, status, error) {
9
+ let status_code = this.getCodeFromStatus(status);
10
+ winston.debug("(wab) getCodeFromStatus result " + status_code);
11
+
12
+ MessageLog.findOneAndUpdate({ message_id: message_id }, { $set: {status: status, status_code: status_code, error: error }}, { new: true }).then((messageLog) => {
13
+ winston.verbose("(wab) status of message_id " + message_id + " updated to " + status);
14
+ winston.debug("(wab) messageLog updated ", messageLog);
15
+ }).catch((err) => {
16
+ winston.error("(wab) findOneAndUpdate error: ", err);
17
+ })
18
+
19
+ }
20
+
21
+ getCodeFromStatus(status) {
22
+ let code = null;
23
+ switch (status) {
24
+ case "rejected":
25
+ code = -1;
26
+ break;
27
+ case "accepted":
28
+ code = 0;
29
+ break;
30
+ case "sent":
31
+ code = 1;
32
+ break;
33
+ case "delivered":
34
+ code = 2;
35
+ break;
36
+ case "read":
37
+ code = 3;
38
+ break;
39
+ default:
40
+ code = -2;
41
+ break;
42
+ }
43
+ return code;
44
+ }
45
+
46
+ }
47
+
48
+ module.exports = { WhatsappLogger };