@tiledesk/tiledesk-whatsapp-connector 0.1.84 → 0.1.85

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
@@ -12,20 +12,16 @@ const cors = require("cors");
12
12
  var winston = require("./winston");
13
13
  const url = require("url");
14
14
  const mongoose = require("mongoose");
15
-
16
- //const ext = require('./routes/ext')
17
- //const extRoute = ext.router;
18
15
  const api = require("./routes/api");
19
16
  const apiRoute = api.router;
17
+ const axios = require('axios').default;
18
+ const { parsePhoneNumberFromString } = require('libphonenumber-js');
19
+
20
20
 
21
21
  // tiledesk clients
22
22
  const { TiledeskClient } = require("@tiledesk/tiledesk-client");
23
- const {
24
- TiledeskWhatsappTranslator,
25
- } = require("./tiledesk/TiledeskWhatsappTranslator");
26
- const {
27
- TiledeskSubscriptionClient,
28
- } = require("./tiledesk/TiledeskSubscriptionClient");
23
+ const { TiledeskWhatsappTranslator } = require("./tiledesk/TiledeskWhatsappTranslator");
24
+ const { TiledeskSubscriptionClient } = require("./tiledesk/TiledeskSubscriptionClient");
29
25
  const { TiledeskWhatsapp } = require("./tiledesk/TiledeskWhatsapp");
30
26
  const { TiledeskChannel } = require("./tiledesk/TiledeskChannel");
31
27
  const { TiledeskAppsClient } = require("./tiledesk/TiledeskAppsClient");
@@ -33,15 +29,18 @@ const { MessageHandler } = require("./tiledesk/MessageHandler");
33
29
  const { TiledeskBotTester } = require("./tiledesk/TiledeskBotTester");
34
30
  const { TemplateManager } = require("./tiledesk/TemplateManager");
35
31
  const { WhatsappLogger } = require("./tiledesk/WhatsappLogger");
36
- const translator = require('./utils/Translator')
32
+ const Utils = require("./tiledesk/Utils");
33
+ const whatsappService = require("./tiledesk/WhatsappService");
37
34
 
38
35
  // mongo
39
36
  const { KVBaseMongo } = require("./tiledesk/KVBaseMongo");
40
37
  const kvbase_collection = "kvstore";
41
38
  const db = new KVBaseMongo(kvbase_collection);
39
+ const utils = new Utils({db: db});
42
40
 
43
41
  // redis
44
42
  var redis = require("redis");
43
+ const { MFA_MISMATCH } = require("./meta_errors");
45
44
  var redis_client;
46
45
 
47
46
  router.use(bodyParser.json());
@@ -62,6 +61,8 @@ let BASE_FILE_URL = null;
62
61
  let AMQP_MANAGER_URL = null;
63
62
  let JOB_TOPIC_EXCHANGE = null;
64
63
  let BRAND_NAME = null;
64
+ let FB_APP_ID = null;
65
+ let CONFIGURATION_ID = null;
65
66
 
66
67
  // Handlebars register helpers
67
68
  handlebars.registerHelper("isEqual", (a, b) => {
@@ -76,200 +77,239 @@ handlebars.registerHelper("json", (a) => {
76
77
  return JSON.stringify(a);
77
78
  });
78
79
 
80
+ handlebars.registerHelper('formatPhone', function (rawNumber) {
81
+ try {
82
+ const phone = parsePhoneNumberFromString("+" + rawNumber);
83
+ return phone.formatInternational(); // oppure .formatNational()
84
+ } catch(err) {
85
+ return rawNumber;
86
+ }
87
+ });
88
+
79
89
  router.get("/", async (req, res) => {
80
90
  res.send("Welcome to Tiledesk-WhatsApp Business connector!");
81
91
  });
82
92
 
83
- router.get("/detail", async (req, res) => {
84
- winston.verbose("(wab) /detail");
93
+
94
+ // ***************************************
95
+ // ***** MANAGEMENT SECTION - START ******
96
+ // ***************************************
97
+ router.get("/configure", async (req, res) => {
98
+ winston.verbose("(wab) /configure");
85
99
 
86
100
  let project_id = req.query.project_id;
87
101
  let token = req.query.token;
88
- let app_id = req.query.app_id;
89
102
 
90
- const tdChannel = new TiledeskChannel({
91
- settings: { project_id: project_id, token: token },
92
- API_URL: API_URL,
93
- });
94
- let isAvailable = await tdChannel.getProjectDetail();
95
- winston.debug("(wab) app is available: ", isAvailable);
96
- if (!project_id || !token || !app_id) {
97
- return res.status(500).send("<p>Ops! An error occured.</p><p>Missing query params! project_id, token and app_id are required.</p>");
103
+ const popup = req.query.view;
104
+ const popup_view = popup === "popup"
105
+
106
+ const type = req.query.type || 'oauth';
107
+ let isOAuth = type === 'oauth';
108
+
109
+ if (!project_id || !token) {
110
+ let error_message = "Query params project_id and token are required.";
111
+ return readHTMLFile("/error.html", (err, html) => {
112
+ var template = handlebars.compile(html);
113
+
114
+ var replacements = {
115
+ app_version: pjson.version,
116
+ error_message: error_message,
117
+ };
118
+ var html = template(replacements);
119
+ res.send(html);
120
+ });
98
121
  }
99
122
 
100
- const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL });
101
- let installation = await appClient.getInstallations(project_id, app_id);
123
+ let settings = await utils.getSettingsByProjectId(project_id);
124
+ if (!settings) {
125
+ settings = await utils.getSettings(project_id, null);
126
+ }
102
127
 
103
- let installed = false;
104
- if (installation) {
105
- installed = true;
128
+ let departments = await getDepartments(project_id, token);
129
+
130
+ let proxy_url = BASE_URL + "/webhook/" + project_id;
131
+
132
+ if (settings) {
133
+
134
+ if (settings.source !== 'oauth') {
135
+ isOAuth = false;
136
+ }
137
+
138
+ readHTMLFile("/configure.html", (err, html) => {
139
+ let template = handlebars.compile(html);
140
+ let replacements = {
141
+ app_version: pjson.version,
142
+ project_id: project_id,
143
+ token: token,
144
+ proxy_url: proxy_url,
145
+ wab_token: settings.wab_token,
146
+ verify_token: settings.verify_token,
147
+ business_account_id: settings.business_account_id,
148
+ phone_number: settings.phone_number,
149
+ phone_number_id: settings.phone_number_id,
150
+ waba_id: settings.waba_id,
151
+ verified_name: settings.verified_name,
152
+ subscription_id: settings.subscriptionId,
153
+ department_id: settings.department_id,
154
+ departments: departments,
155
+ brand_name: BRAND_NAME,
156
+ isOAuth: isOAuth,
157
+ fb_app_id: FB_APP_ID,
158
+ configuration_id: CONFIGURATION_ID
159
+ }
160
+ let renderedHtml = template(replacements);
161
+ res.send(renderedHtml);
162
+ })
163
+ return;
106
164
  }
107
165
 
108
- readHTMLFile("/detail.html", (err, html) => {
109
- var template = handlebars.compile(html);
110
- var replacements = {
166
+ readHTMLFile("/configure.html", (err, html) => {
167
+ let template = handlebars.compile(html);
168
+ let replacements = {
111
169
  app_version: pjson.version,
112
170
  project_id: project_id,
113
171
  token: token,
114
- app_id: app_id,
115
- installed: installed,
116
- isAvailable: isAvailable,
172
+ proxy_url: proxy_url,
173
+ departments: departments,
174
+ popup_view: popup_view,
175
+ brand_name: BRAND_NAME,
176
+ isOAuth: isOAuth,
177
+ fb_app_id: FB_APP_ID,
178
+ configuration_id: CONFIGURATION_ID
117
179
  };
118
- var html = template(replacements);
119
- res.send(html);
120
- });
121
- });
122
-
123
- router.post("/install", async (req, res) => {
124
- winston.verbose("(wab) /install");
125
-
126
- let project_id = req.body.project_id;
127
- let app_id = req.body.app_id;
128
- let token = req.body.token;
180
+ let renderedHtml = template(replacements);
181
+ res.send(renderedHtml);
182
+ })
183
+ return;
184
+ })
129
185
 
130
- winston.debug("(wab) Install app " + app_id + " for project id " + project_id);
131
- let installation_info = {
132
- project_id: project_id,
133
- app_id: app_id,
134
- createdAt: Date.now(),
135
- };
186
+ router.get('/onboarding/callback', async (req, res) => {
187
+ const { code, waba_id, phone_number_id, business_id } = req.query;
188
+ const state = JSON.parse(req.query.state)
189
+ const project_id = state.project_id;
190
+ const token = state.token;
136
191
 
137
- const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL });
138
- appClient
139
- .install(installation_info)
140
- .then((installation) => {
141
- winston.debug("(wab) installation response: ", installation);
192
+ try {
142
193
 
143
- res.redirect(
144
- url.format({
145
- pathname: BASE_URL + "/detail",
146
- query: {
147
- project_id: project_id,
148
- app_id: app_id,
149
- token: token,
150
- },
151
- })
152
- );
194
+ const tdClient = new TiledeskSubscriptionClient({
195
+ API_URL: API_URL,
196
+ project_id: project_id,
197
+ token: token
153
198
  })
154
- .catch((err) => {
155
- winston.error("(wab) installation error: " + err.data);
156
- res.send("An error occurred during the installation");
157
- });
158
- });
159
199
 
160
- router.post("/uninstall", async (req, res) => {
161
- winston.verbose("(wab) /uninstall");
162
- let project_id = req.body.project_id;
163
- let app_id = req.body.app_id;
164
- let token = req.body.token;
200
+ const subscription_info = {
201
+ target: BASE_URL + "/tiledesk",
202
+ event: "message.create.request.channel.whatsapp",
203
+ }
204
+ const subscription = await tdClient.subscribe(subscription_info);
165
205
 
166
- const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL });
167
- appClient.uninstall(project_id, app_id).then((response) => {
168
- winston.debug("(wab) uninstallation response: ", response);
169
- res.redirect(
170
- url.format({
171
- pathname: BASE_URL + "/detail",
172
- query: {
173
- project_id: project_id,
174
- app_id: app_id,
175
- token: token,
176
- },
177
- })
178
- );
179
- }).catch((err) => {
180
- winston.error("(wab) uninstallation error: " + err.data);
181
- res.send("An error occurred during the uninstallation");
182
- });
183
- });
206
+ const data = await whatsappService.handleOnboarding(code, business_id, waba_id, phone_number_id)
184
207
 
185
- router.get("/configure", async (req, res) => {
186
- winston.verbose("(wab) /configure");
208
+ let settings = {
209
+ source: 'oauth',
210
+ project_id: project_id,
211
+ token: token,
212
+ subscriptionId: subscription._id,
213
+ secret: subscription.secret,
214
+ wab_token: data.access_token,
215
+ business_account_id: data.business_account_id,
216
+ waba_id: data.waba_id,
217
+ phone_number_id: data.phone_number_id,
218
+ phone_number: data.phone_number,
219
+ verified_name: data.verified_name
220
+ }
187
221
 
188
- let project_id = req.query.project_id;
189
- let token = req.query.token;
190
- let popup_view = false;
222
+ let CONTENT_KEY = "whatsapp-" + waba_id;
223
+ await db.set(CONTENT_KEY, settings);
191
224
 
192
- if (req.query.view && req.query.view === "popup") {
193
- popup_view = true;
194
- }
225
+ try {
226
+ let register_result = await whatsappService.registerNumber(data.access_token, data.phone_number_id)
227
+ winston.verbose("(wab) /onboarding/callback register_result: ", register_result)
228
+
229
+ let connection_result = await whatsappService.connectNumber(data.access_token, data.waba_id)
230
+ winston.verbose("(wab) /onboarding/callback connection_result: ", connection_result)
231
+ } catch (err) {
232
+ if (err.code === MFA_MISMATCH.CODE) {
233
+ res.status(202).send({
234
+ success: false,
235
+ code: MFA_MISMATCH.CODE,
236
+ message: "PIN Mismatch",
237
+ settings: settings,
238
+ })
239
+ return;
240
+ } else {
241
+ winston.error("(wab) onboarding finalizing error: ", err.response?.data || err.message);
242
+ res.status(500).send({ success: false, message: "Error during onboarding", settings: settings })
243
+ return;
244
+ }
245
+ }
195
246
 
196
- if (!project_id || !token) {
197
- let error_message = "Query params project_id and token are required.";
198
- readHTMLFile("/error.html", (err, html) => {
199
- var template = handlebars.compile(html);
247
+ res.status(200).send(settings)
200
248
 
201
- var replacements = {
202
- app_version: pjson.version,
203
- error_message: error_message,
204
- };
205
- var html = template(replacements);
206
- return res.send(html);
207
- });
208
- } else {
209
- let proxy_url = BASE_URL + "/webhook/" + project_id;
249
+ } catch (err) {
250
+ winston.error("(wab) onboarding error: ", err.response?.data || err.message);
251
+ res.status(500).json({ error: "Onboarding callback error: " + err });
252
+ return;
253
+ }
254
+ })
210
255
 
211
- let CONTENT_KEY = "whatsapp-" + project_id;
256
+ router.get('/onboarding/register', async (req, res) => {
257
+ const { pin, waba_id, wab_token, phone_number_id } = req.query;
212
258
 
213
- let settings = await db.get(CONTENT_KEY);
214
- winston.debug("(wab) settings: ", settings);
215
-
216
- // get departments
217
- const tdChannel = new TiledeskChannel({
218
- settings: { project_id: project_id, token: token },
219
- API_URL: API_URL,
220
- });
259
+ winston.verbose("(wab) pin: ", pin, "wab_token: ", wab_token, "phone_number_id: ", phone_number_id);
221
260
 
222
- let departments = await tdChannel.getDepartments().catch((err) => {
223
- winston.error("(wab) Error getting deparments");
224
- return []
261
+ if (!wab_token || !phone_number_id || !pin) {
262
+ res.status(400).send({
263
+ success: false,
264
+ message: "Missing required parameters"
225
265
  })
266
+ return;
267
+ }
226
268
 
227
- if (settings) {
228
-
229
- settings.token = token;
230
- await db.set(CONTENT_KEY, settings);
269
+ try {
270
+ let register_result = await whatsappService.registerNumber(wab_token, phone_number_id, pin);
271
+ winston.verbose("(wab) /onboarding/register register_result: ", register_result)
231
272
 
232
- readHTMLFile("/configure.html", (err, html) => {
233
- var template = handlebars.compile(html);
234
- var replacements = {
235
- app_version: pjson.version,
236
- project_id: project_id,
237
- token: token,
238
- proxy_url: proxy_url,
239
- wab_token: settings.wab_token,
240
- verify_token: settings.verify_token,
241
- business_account_id: settings.business_account_id,
242
- phone_number: settings.phone_number,
243
- phone_number_id: settings.phone_number_id,
244
- verified_name: settings.verified_name,
245
- subscription_id: settings.subscriptionId,
246
- department_id: settings.department_id,
247
- departments: departments,
248
- brand_name: BRAND_NAME
249
- };
250
- var html = template(replacements);
251
- res.send(html);
252
- });
253
- } else {
254
- readHTMLFile("/configure.html", (err, html) => {
255
- if (err) { winston.error("(wab) error read html file: " + err); }
273
+ let connection_result = await whatsappService.connectNumber(data.access_token, data.waba_id)
274
+ winston.verbose("(wab) /onboarding/callback connection_result: ", connection_result)
256
275
 
257
- var template = handlebars.compile(html);
258
- var replacements = {
259
- app_version: pjson.version,
260
- project_id: project_id,
261
- token: token,
262
- proxy_url: proxy_url,
263
- departments: departments,
264
- popup_view: popup_view,
265
- brand_name: BRAND_NAME
266
- };
267
- var html = template(replacements);
268
- res.send(html);
269
- });
276
+ } catch (err) {
277
+ winston.error("(wab) register error: ", err.response?.data || err.message);
278
+ if (err.response?.data?.code === MFA_MISMATCH.CODE) {
279
+ res.status(202).send({
280
+ success: false,
281
+ code: MFA_MISMATCH.CODE,
282
+ message: "PIN Mismatch"
283
+ })
284
+ return;
285
+ } else {
286
+ res.status(400).send({
287
+ success: false,
288
+ message: "Error registering phone number"
289
+ })
290
+ return;
270
291
  }
271
292
  }
272
- });
293
+
294
+ res.status(200).send("Phone number registered");
295
+
296
+ })
297
+
298
+ router.put("/update/department", async (req, res) => {
299
+ let project_id = req.query.project_id;
300
+ let token = req.query.token;
301
+
302
+ let department_id = req.body.department_id;
303
+ winston.verbose("(wab) changing department with: ", department_id);
304
+
305
+ let settings = await utils.getSettingsByProjectId(project_id);
306
+ settings.department_id = department_id;
307
+
308
+ let CONTENT_KEY = "whatsapp-" + settings.waba_id;
309
+ await db.set(CONTENT_KEY, settings);
310
+
311
+ res.status(200).send("Department Id updated")
312
+ })
273
313
 
274
314
  router.post("/update", async (req, res) => {
275
315
  winston.verbose("(wab) /update");
@@ -291,30 +331,27 @@ router.post("/update", async (req, res) => {
291
331
  settings: { project_id: project_id, token: token },
292
332
  API_URL: API_URL,
293
333
  });
294
- let departments = await tdChannel.getDepartments().catch((err) => {
295
- winston.error("(wab) Error getting deparments");
296
- return []
297
- })
334
+ let departments = await tdChannel.getDepartments();
298
335
 
299
336
  if (settings) {
300
337
  settings.wab_token = wab_token;
301
338
  settings.verify_token = verify_token;
302
339
  settings.department_id = department_id;
303
340
  settings.business_account_id = business_account_id;
304
-
341
+
305
342
  const twClient = new TiledeskWhatsapp({
306
343
  token: settings.wab_token,
307
344
  GRAPH_URL: GRAPH_URL,
308
345
  API_URL: API_URL,
309
346
  BASE_FILE_URL: BASE_FILE_URL,
310
347
  });
311
-
348
+
312
349
  await twClient.getBusinessAccountInfo(business_account_id).then((info) => {
313
350
  settings.phone_number_id = info.data[0].id;
314
351
  settings.phone_number = info.data[0].display_phone_number;
315
352
  settings.verified_name = info.data[0].verified_name;
316
353
  }).catch((err) => {
317
- winston.error("Error getting WAB Account Info");
354
+ winston.error("(wab) Error getting WAB Account Info");
318
355
  })
319
356
 
320
357
  await db.set(CONTENT_KEY, settings);
@@ -359,6 +396,7 @@ router.post("/update", async (req, res) => {
359
396
  winston.debug("\n(wab) Subscription: ", subscription);
360
397
 
361
398
  let settings = {
399
+ source: 'manual',
362
400
  project_id: project_id,
363
401
  token: token,
364
402
  proxy_url: proxy_url,
@@ -409,207 +447,27 @@ router.post("/update", async (req, res) => {
409
447
  };
410
448
  var html = template(replacements);
411
449
  res.send(html);
412
- });
413
- });
414
- }
415
- });
416
-
417
- router.post("/getinfo", async (req, res) => {
418
-
419
- winston.verbose("/getinfo");
420
- let project_id = req.body.project_id;
421
- let token = req.body.token;
422
- let proxy_url = BASE_URL + "/webhook/" + project_id;
423
-
424
- let CONTENT_KEY = "whatsapp-" + project_id;
425
- let settings = await db.get(CONTENT_KEY);
426
-
427
- const tdChannel = new TiledeskChannel({
428
- settings: { project_id: project_id, token: token },
429
- API_URL: API_URL,
430
- });
431
- let departments = await tdChannel.getDepartments().catch((err) => {
432
- winston.error("(wab) Error getting deparments");
433
- return []
434
- })
435
-
436
- const twClient = new TiledeskWhatsapp({
437
- token: settings.wab_token,
438
- GRAPH_URL: GRAPH_URL,
439
- API_URL: API_URL,
440
- BASE_FILE_URL: BASE_FILE_URL,
441
- });
442
-
443
- await twClient.getBusinessAccountInfo(settings.business_account_id).then((info) => {
444
- settings.phone_number_id = info.data[0].id;
445
- settings.phone_number = info.data[0].display_phone_number;
446
- settings.verified_name = info.data[0].verified_name;
447
- winston.debug("(wab) Business account info: ", info);
448
- }).catch((err) => {
449
- winston.error("Error getting WAB Account Info");
450
- })
451
-
452
- await db.set(CONTENT_KEY, settings);
453
-
454
- readHTMLFile("/configure.html", (err, html) => {
455
- var template = handlebars.compile(html);
456
- var replacements = {
457
- app_version: pjson.version,
458
- project_id: project_id,
459
- token: token,
460
- proxy_url: proxy_url,
461
- wab_token: settings.wab_token,
462
- show_success_modal: true,
463
- verify_token: settings.verify_token,
464
- business_account_id: settings.business_account_id,
465
- phone_number: settings.phone_number,
466
- phone_number_id: settings.phone_number_id,
467
- verified_name: settings.verified_name,
468
- subscription_id: settings.subscriptionId,
469
- department_id: settings.department_id,
470
- departments: departments,
471
- brand_name: BRAND_NAME
472
- };
473
- var html = template(replacements);
474
- res.send(html);
475
- });
476
-
477
- })
478
-
479
- router.post("/disconnect", async (req, res) => {
480
- winston.verbose("(wab) /disconnect");
481
-
482
- let project_id = req.body.project_id;
483
- let token = req.body.token;
484
- let subscriptionId = req.body.subscription_id;
485
-
486
- let CONTENT_KEY = "whatsapp-" + project_id;
487
- await db.remove(CONTENT_KEY);
488
- winston.verbose("(wab) Content deleted.");
489
-
490
- let proxy_url = BASE_URL + "/webhook/" + project_id;
491
-
492
- const tdClient = new TiledeskSubscriptionClient({
493
- API_URL: API_URL,
494
- project_id: project_id,
495
- token: token,
496
- });
497
- // get departments
498
- const tdChannel = new TiledeskChannel({
499
- settings: { project_id: project_id, token: token },
500
- API_URL: API_URL,
501
- });
502
- let departments = await tdChannel.getDepartments().catch((err) => {
503
- winston.error("(wab) Error getting deparments");
504
- return []
505
- })
506
-
507
- /*
508
- // callback
509
- tdClient.unsubsribe(project_id, subscriptionId, (err, data) => {
510
- // code here
511
- })
512
- */
513
-
514
- tdClient.unsubscribe(subscriptionId).then((data) => {
515
- readHTMLFile("/configure.html", (err, html) => {
516
- var template = handlebars.compile(html);
517
- var replacements = {
518
- app_version: pjson.version,
519
- project_id: project_id,
520
- token: token,
521
- proxy_url: proxy_url,
522
- departments: departments,
523
- brand_name: BRAND_NAME
524
- };
525
- var html = template(replacements);
526
- res.send(html);
527
- });
528
- }).catch((err) => {
529
- winston.error("(wab) unsubscribe error: " + err);
530
- });
531
- });
532
-
533
- router.get("/direct/tiledesk", async (req, res) => {
534
- winston.verbose("/direct/tiledesk");
535
-
536
- let project_id = req.query.project_id;
537
- let whatsapp_receiver = req.query.whatsapp_receiver;
538
- let phone_number_id = req.query.phone_number_id;
539
-
540
- let CONTENT_KEY = "whatsapp-" + project_id;
541
- let settings = await db.get(CONTENT_KEY);
542
-
543
- let tiledeskChannelMessage = {
544
- text: "Sample text",
545
- attributes: {
546
- attachment: {
547
- type: "wa_template",
548
- template: {
549
- language: "en_US",
550
- name: "hello_world",
551
- }
552
- }
553
- }
554
- };
555
- const tlr = new TiledeskWhatsappTranslator();
556
- const twClient = new TiledeskWhatsapp({
557
- token: settings.wab_token,
558
- GRAPH_URL: GRAPH_URL,
559
- API_URL: API_URL,
560
- BASE_FILE_URL: BASE_FILE_URL,
561
- });
562
-
563
- let whatsappJsonMessage = await tlr.toWhatsapp(
564
- tiledeskChannelMessage,
565
- whatsapp_receiver
566
- );
567
- winston.verbose("(wab) whatsappJsonMessage", whatsappJsonMessage);
568
-
569
- twClient.sendMessage(phone_number_id, whatsappJsonMessage).then((response) => {
570
- winston.verbose(
571
- "(wab) Message sent to WhatsApp! " +
572
- response.status +
573
- " " +
574
- response.statusText
575
- );
576
- }).catch((err) => {
577
- //winston.error("(wab) error send message: ", err);
578
- return res.status(400).send({ success: false, error: err });
579
- });
580
-
581
- res.status(200).send("Message sent");
582
- });
583
-
584
-
585
- // Can be deleted?
586
- router.post("/tiledesk/broadcast", async (req, res) => {
587
- winston.verbose("(wab) Message received from Tiledesk (Broadcast)");
588
-
589
- let tiledeskChannelMessage = req.body.payload;
590
- winston.verbose("(wab) tiledeskChannelMessage: ", tiledeskChannelMessage);
591
- let project_id = req.body.payload.id_project;
592
-
593
- let CONTENT_KEY = "whatsapp-" + project_id;
594
- let settings = await db.get(CONTENT_KEY);
595
-
596
- if (!settings) {
597
- return res.status(400).send({ success: false, error: "WhatsApp is not installed for the project_id: " + project_id });
450
+ });
451
+ });
598
452
  }
453
+ });
599
454
 
600
- if (!settings.business_account_id) {
601
- return res.status(400).send({ success: false, error: "Missing parameter 'WhatsApp Business Account ID'. Please update your app." });
602
- }
455
+ router.post("/getinfo", async (req, res) => {
603
456
 
604
- let receiver_list = req.body.receiver_list;
605
- let phone_number_id = req.body.phone_number_id;
457
+ winston.verbose("(wab) /getinfo");
458
+ let project_id = req.body.project_id;
459
+ let token = req.body.token;
460
+ let proxy_url = BASE_URL + "/webhook/" + project_id;
606
461
 
607
- let tm = new TemplateManager({
608
- token: settings.wab_token,
609
- business_account_id: settings.business_account_id,
610
- GRAPH_URL: GRAPH_URL,
462
+ let CONTENT_KEY = "whatsapp-" + project_id;
463
+ let settings = await db.get(CONTENT_KEY);
464
+
465
+ const tdChannel = new TiledeskChannel({
466
+ settings: { project_id: project_id, token: token },
467
+ API_URL: API_URL,
611
468
  });
612
- const tlr = new TiledeskWhatsappTranslator();
469
+ let departments = await tdChannel.getDepartments();
470
+
613
471
  const twClient = new TiledeskWhatsapp({
614
472
  token: settings.wab_token,
615
473
  GRAPH_URL: GRAPH_URL,
@@ -617,68 +475,104 @@ router.post("/tiledesk/broadcast", async (req, res) => {
617
475
  BASE_FILE_URL: BASE_FILE_URL,
618
476
  });
619
477
 
620
- let response = await tm.getTemplates();
621
- let templates = response.data;
478
+ await twClient.getBusinessAccountInfo(settings.business_account_id).then((info) => {
479
+ settings.phone_number_id = info.data[0].id;
480
+ settings.phone_number = info.data[0].display_phone_number;
481
+ settings.verified_name = info.data[0].verified_name;
482
+ winston.debug("(wab) Business account info: ", info);
483
+ }).catch((err) => {
484
+ winston.error("(wab) Error getting WAB Account Info");
485
+ })
622
486
 
623
- let selected_template = templates.find(
624
- (t) => t.name === tiledeskChannelMessage.attributes.attachment.template.name
625
- );
487
+ await db.set(CONTENT_KEY, settings);
626
488
 
627
- let params_object = await tm.generateParamsObject(selected_template);
489
+ readHTMLFile("/configure.html", (err, html) => {
490
+ var template = handlebars.compile(html);
491
+ var replacements = {
492
+ app_version: pjson.version,
493
+ project_id: project_id,
494
+ token: token,
495
+ proxy_url: proxy_url,
496
+ wab_token: settings.wab_token,
497
+ show_success_modal: true,
498
+ verify_token: settings.verify_token,
499
+ business_account_id: settings.business_account_id,
500
+ phone_number: settings.phone_number,
501
+ phone_number_id: settings.phone_number_id,
502
+ verified_name: settings.verified_name,
503
+ subscription_id: settings.subscriptionId,
504
+ department_id: settings.department_id,
505
+ departments: departments,
506
+ brand_name: BRAND_NAME
507
+ };
508
+ var html = template(replacements);
509
+ res.send(html);
510
+ });
628
511
 
629
- tiledeskChannelMessage.attributes.attachment.template.params = params_object;
630
- winston.verbose("(wab) --> tiledeskChannelMessage: ", tiledeskChannelMessage);
512
+ })
631
513
 
632
- let messages_sent = 0;
633
- let errors = [];
634
- let error_count = 0;
514
+ router.post("/disconnect", async (req, res) => {
515
+ winston.verbose("(wab) /disconnect");
635
516
 
636
- if (receiver_list) {
637
- let i = 0;
638
- async function execute(whatsapp_receiver) {
639
- let customTiledeskJsonMessage = await tlr.sanitizeTiledeskMessage(tiledeskChannelMessage, whatsapp_receiver);
640
- winston.verbose("customTiledeskJsonMessage: ", customTiledeskJsonMessage);
517
+ let project_id = req.body.project_id;
518
+ let token = req.body.token;
519
+ let subscriptionId = req.body.subscription_id;
641
520
 
642
- let whatsappJsonMessage = await tlr.toWhatsapp(customTiledeskJsonMessage, whatsapp_receiver.phone_number);
643
- winston.verbose("(wab) message created for receiver: " + whatsapp_receiver.phone_number);
644
- winston.verbose("(wab) whatsappJsonMessage", whatsappJsonMessage);
521
+ let deleted = await deleteSettingsByProjectId(project_id);
522
+ if (!deleted) {
523
+ deleted = deleteSettings(project_id, null)
524
+ }
645
525
 
646
- await twClient.sendMessage(phone_number_id, whatsappJsonMessage).then((response) => {
647
- winston.verbose("(wab) Message sent to WhatsApp! " + response.status + " " + response.statusText);
648
- messages_sent += 1;
649
- i += 1;
650
- if (i < receiver_list.length) {
651
- execute(receiver_list[i]);
652
- } else {
653
- winston.debug("(wab) End of list");
654
- return res.status(200).send({ success: true, message: "Broadcast terminated", messages_sent: messages_sent, errors: errors });
655
- }
656
- }).catch((err) => {
657
- winston.error("(wab) error send message: " + err.response.data.error.message);
658
- errors.push({ receiver: whatsapp_receiver.phone_number, error: err.response.data.error.message });
659
- i += 1;
660
- if (i < receiver_list.length) {
661
- execute(receiver_list[i]);
662
- } else {
663
- winston.debug("(wab) End of list");
664
- return res.status(200).send({ success: true, message: "Broadcast terminated", messages_sent: messages_sent, errors: errors });
665
- }
666
- });
667
- }
668
- execute(receiver_list[0]);
526
+ if (!deleted) {
527
+ let error_message = "Unable to disconnect WhatsApp";
528
+ return readHTMLFile("/error.html", (err, html) => {
529
+ let template = handlebars.compile(html);
530
+ let replacements = {
531
+ app_version: pjson.version,
532
+ error_message: error_message,
533
+ };
534
+ let renderedHtml = template(replacements);
535
+ res.send(renderedHtml);
536
+ });
669
537
  }
538
+
539
+ const tdClient = new TiledeskSubscriptionClient({
540
+ API_URL: API_URL,
541
+ project_id: project_id,
542
+ token: token,
543
+ });
544
+
545
+ tdClient.unsubscribe(subscriptionId).then((data) => {
546
+ res.redirect(`/configure?project_id=${project_id}&token=${token}`);
547
+ }).catch((err) => {
548
+ winston.error("(wab) unsubscribe error: " + err);
549
+ });
670
550
  });
551
+ // ***************************************
552
+ // ****** MANAGEMENT SECTION - END *******
553
+ // ***************************************
671
554
 
555
+ // ---------------------------------------
556
+
557
+ // ***************************************
558
+ // ****** MESSAGING SECTION - START ******
559
+ // ***************************************
672
560
  router.post("/tiledesk", async (req, res) => {
673
561
  winston.verbose("(wab) Message received from Tiledesk");
674
562
 
675
- var tiledeskChannelMessage = req.body.payload;
563
+ let tiledeskChannelMessage = req.body.payload;
676
564
  winston.debug("(wab) tiledeskChannelMessage: ", tiledeskChannelMessage);
677
- var project_id = req.body.payload.id_project;
678
565
 
679
- // get settings from mongo
680
- let CONTENT_KEY = "whatsapp-" + project_id;
681
- let settings = await db.get(CONTENT_KEY);
566
+ let project_id = req.body.payload.id_project;
567
+ let recipient = req.body.payload.recipient;
568
+ let waba_id = recipient.split('-')[5];
569
+
570
+ let settings = await utils.getSettingsByProjectId(project_id);
571
+
572
+ if (!settings) {
573
+ settings = await utils.getSettings(project_id, waba_id);
574
+ }
575
+
682
576
  let wab_token = settings.wab_token;
683
577
 
684
578
  var text = req.body.payload.text;
@@ -725,17 +619,9 @@ router.post("/tiledesk", async (req, res) => {
725
619
  return res.status(400).send({ success: false, message: "Phone number id undefined" });
726
620
  }
727
621
 
728
- /*
729
- if (settings.expired &&
730
- settings.expired === true) {
731
- winston.info("settings expired: " + settings.expired);
732
- return res.status(200).send({ success: 'false', message: "plan expired"})
733
-
734
- */
735
-
736
622
  // Return an info message option
737
623
  if (settings.expired && settings.expired === true) {
738
- winston.verbose("settings expired: " + settings.expired);
624
+ winston.verbose("(wab) settings expired: " + settings.expired);
739
625
  let tiledeskJsonMessage = {
740
626
  text: "Expired. Upgrade Plan.",
741
627
  sender: "system",
@@ -843,7 +729,7 @@ router.post("/tiledesk", async (req, res) => {
843
729
 
844
730
  if (whatsappJsonMessage) {
845
731
  const twClient = new TiledeskWhatsapp({
846
- token: settings.wab_token,
732
+ token: wab_token,
847
733
  GRAPH_URL: GRAPH_URL,
848
734
  API_URL: API_URL,
849
735
  BASE_FILE_URL: BASE_FILE_URL,
@@ -871,8 +757,157 @@ router.post("/tiledesk", async (req, res) => {
871
757
  }
872
758
  });
873
759
 
760
+ // Endpoint for Whatsapp Business connected via OAuth
761
+ router.post('/webhook', async (req, res) => {
762
+
763
+ if (req.body.object !== "whatsapp_business_account") {
764
+ winston.verbose("(wab) Event not from whatsapp");
765
+ return res.status(400).send({ error: "Invalid request" });
766
+ }
767
+
768
+ if (!req.body.entry?.[0]?.id) {
769
+ winston.warn("(wab) Invalid entry for body: ", req.body)
770
+ return res.status(400).send({ error: "Invalid request object" })
771
+ }
772
+
773
+ const waba_id = req.body.entry[0].id;
774
+
775
+ const CONTENT_KEY = "whatsapp-" + waba_id;
776
+ const settings = await db.get(CONTENT_KEY);
777
+ winston.debug("(wab) settings: ", settings);
778
+
779
+ if (!settings) {
780
+ winston.warn("(wab) settings not found for waba: " + waba_id);
781
+ winston.warn("(wab) settings not found for request: ", req.body);
782
+ return res.sendStatus(200);
783
+ }
784
+
785
+ const tdClient = new TiledeskClient({
786
+ projectId: settings.project_id,
787
+ token: settings.token,
788
+ APIURL: API_URL,
789
+ APIKEY: "___",
790
+ log: false,
791
+ });
792
+
793
+ const wl = new WhatsappLogger({ tdClient: tdClient });
794
+ wl.forwardMessage(req.body);
795
+
796
+ // Message incoming
797
+ if (req.body.entry?.[0]?.changes?.[0]?.value?.messages?.[0]) {
798
+
799
+ let whatsappChannelMessage = req.body.entry[0].changes[0].value.messages[0];
800
+ if (whatsappChannelMessage.type == "system") {
801
+ winston.verbose("(wab) Skip system message");
802
+ return res.sendStatus(200);
803
+ }
804
+
805
+ const tlr = new TiledeskWhatsappTranslator();
806
+ const tdChannel = new TiledeskChannel({ settings: settings, API_URL: API_URL });
807
+
808
+ // Try on Whatsapp - Initialize conversation with chatbot
809
+ if (whatsappChannelMessage.text && whatsappChannelMessage.text.body.startsWith("#td")) {
810
+ let code = whatsappChannelMessage.text.body.split(" ")[0];
811
+ const bottester = new TiledeskBotTester({
812
+ project_id: project_id,
813
+ redis_client: redis_client,
814
+ db: db,
815
+ tdChannel: tdChannel,
816
+ tlr: tlr,
817
+ });
818
+ bottester.startBotConversation(req.body, code).then((result) => {
819
+ winston.verbose("(wab) test conversation started");
820
+ winston.debug("(wab) startBotConversation result: ", result);
821
+ }).catch((err) => {
822
+ winston.error("(wab) start test onversation error: ", err);
823
+ });
824
+ }
825
+ // Standard message
826
+ else {
827
+ let firstname = req.body.entry[0].changes[0].value.contacts[0].profile.name;
828
+ let message_info = {
829
+ channel: "whatsapp",
830
+ whatsapp: {
831
+ phone_number_id: req.body.entry[0].changes[0].value.metadata.phone_number_id,
832
+ from: req.body.entry[0].changes[0].value.messages[0].from,
833
+ firstname: req.body.entry[0].changes[0].value.contacts[0].profile.name,
834
+ lastname: " "
835
+ },
836
+ };
837
+
838
+ let tiledeskJsonMessage;
839
+
840
+ let metadata_type = ["image", "video", "document", "audio"];
841
+ if (metadata_type.includes(whatsappChannelMessage.type)) {
842
+
843
+ const util = new TiledeskWhatsapp({
844
+ token: settings.wab_token,
845
+ GRAPH_URL: GRAPH_URL,
846
+ API_URL: API_URL,
847
+ BASE_FILE_URL: BASE_FILE_URL,
848
+ });
849
+
850
+ const media = whatsappChannelMessage[whatsappChannelMessage.type];
851
+ const filename = await util.downloadMedia(media.id);
852
+
853
+ if (!filename) {
854
+ winston.debug(`(wab) Unable to download media with id ${media.id}. Message not sent.`);
855
+ return res.status(500).send({ success: false, error: "unable to download media" });
856
+ }
857
+
858
+ const file_path = path.join(__dirname, "tmp", filename);
859
+ const uploadFolder = whatsappChannelMessage.type === "image" ? "images" : "files";
860
+ const media_url = await util.uploadMedia(file_path, uploadFolder);
861
+ winston.debug(`(wab) media_url: ${media_url}`);
862
+
863
+ tiledeskJsonMessage = await tlr.toTiledesk(whatsappChannelMessage, firstname, media_url);
864
+ } else {
865
+ winston.debug("(wab) message type: ", whatsappChannelMessage.type);
866
+ tiledeskJsonMessage = await tlr.toTiledesk(whatsappChannelMessage, firstname);
867
+ }
868
+
869
+ if (tiledeskJsonMessage) {
870
+ winston.verbose("(wab) tiledeskJsonMessage: ", tiledeskJsonMessage);
871
+ const response = await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
872
+ winston.verbose("(wab) Message sent to Tiledesk!");
873
+ winston.debug("(wab) response: ", response);
874
+ } else {
875
+ winston.verbose("(wab) tiledeskJsonMessage is undefined");
876
+ }
877
+ }
878
+ }
879
+
880
+
881
+ // Message status update incoming
882
+ if (req.body.entry?.[0]?.changes?.[0]?.value?.statuses?.[0]) {
883
+ let whatsappStatusMessage = req.body.entry[0].changes[0].value.statuses[0];
884
+ winston.verbose("(wab) whatsappStatusMessage: ", whatsappStatusMessage);
885
+
886
+ let message_id = whatsappStatusMessage.id;
887
+ let status = whatsappStatusMessage.status;
888
+ let error = null;
889
+ if (whatsappStatusMessage.errors) {
890
+ error = whatsappStatusMessage.errors[0].title;
891
+ }
892
+ winston.verbose("(wab) update status in " + status + " for message_id " + message_id);
893
+
894
+ const tdClient = new TiledeskClient({
895
+ projectId: settings.project_id,
896
+ token: settings.token,
897
+ APIURL: API_URL,
898
+ APIKEY: "___",
899
+ log: false,
900
+ });
901
+
902
+ const wl = new WhatsappLogger({ tdClient: tdClient });
903
+ wl.updateMessageStatus(message_id, status, error);
904
+ }
905
+
906
+ res.sendStatus(200);
907
+
908
+ })
909
+
874
910
  // Endpoint for Whatsapp Business
875
- // Accepts POST requests at /webhook endpoint
876
911
  router.post("/webhook/:project_id", async (req, res) => {
877
912
  // Parse the request body from the POST
878
913
  let project_id = req.params.project_id;
@@ -1196,7 +1231,7 @@ router.post("/webhook/:project_id", async (req, res) => {
1196
1231
 
1197
1232
  // Accepts GET requests at the /webhook endpoint. You need this URL to setup webhook initially.
1198
1233
  // info on verification request payload: https://developers.facebook.com/docs/graph-api/webhooks/getting-started#verification-requests
1199
- router.get("/webhook/:project_id", async (req, res) => {
1234
+ router.get("/webhook", async (req, res) => {
1200
1235
  /**
1201
1236
  * UPDATE YOUR VERIFY TOKEN
1202
1237
  *This will be the Verify Token value when you set up webhook
@@ -1209,71 +1244,241 @@ router.get("/webhook/:project_id", async (req, res) => {
1209
1244
  let token = req.query["hub.verify_token"];
1210
1245
  let challenge = req.query["hub.challenge"];
1211
1246
 
1212
- let CONTENT_KEY = "whatsapp-" + req.params.project_id;
1247
+ //let CONTENT_KEY = "whatsapp-" + req.params.project_id;
1248
+
1249
+ //let settings = await db.get(CONTENT_KEY);
1250
+
1251
+ // if (!settings || !settings.verify_token) {
1252
+ // winston.error("(wab) No settings found! Unable to verify token.");
1253
+ // res.sendStatus(403);
1254
+ // } else {
1255
+ //let VERIFY_TOKEN = settings.verify_token;
1256
+ let VERIFY_TOKEN = process.env.VERIFY_TOKEN || "tiledesk-verify-token";
1257
+ // Check if a token and mode were sent
1258
+ if (mode && token) {
1259
+ // Check the mode and token sent are correct
1260
+ if (mode === "subscribe" && token === VERIFY_TOKEN) {
1261
+ // Respond with 200 OK and challenge token from the request
1262
+ winston.verbose("(wab) Webhook verified");
1263
+ res.status(200).send(challenge);
1264
+ } else {
1265
+ // Responds with '403 Forbidden' if verify tokens do not match
1266
+ winston.error("(wab) mode is not 'subscribe' or token do not match");
1267
+ res.sendStatus(403);
1268
+ }
1269
+ } else {
1270
+ winston.error("(wab) mode or token undefined");
1271
+ res.status(400).send("impossible to verify the webhook: mode or token undefined.");
1272
+ }
1273
+ //}
1274
+ });
1213
1275
 
1214
- let settings = await db.get(CONTENT_KEY);
1276
+ // Accepts GET requests at the /webhook endpoint. You need this URL to setup webhook initially.
1277
+ // info on verification request payload: https://developers.facebook.com/docs/graph-api/webhooks/getting-started#verification-requests
1278
+ router.get("/webhook/:project_id", async (req, res) => {
1279
+ /**
1280
+ * UPDATE YOUR VERIFY TOKEN
1281
+ *This will be the Verify Token value when you set up webhook
1282
+ */
1283
+ winston.verbose("(wab) Verify the webhook... ");
1284
+ winston.debug("(wab) req.query: " + req.query);
1285
+
1286
+ // Parse params from the webhook verification request
1287
+ let mode = req.query["hub.mode"];
1288
+ let token = req.query["hub.verify_token"];
1289
+ let challenge = req.query["hub.challenge"];
1215
1290
 
1216
- if (!settings || !settings.verify_token) {
1217
- winston.error("(wab) No settings found! Unable to verify token.");
1218
- res.sendStatus(403);
1291
+ //let CONTENT_KEY = "whatsapp-" + req.params.project_id;
1292
+
1293
+ //let settings = await db.get(CONTENT_KEY);
1294
+
1295
+ // if (!settings || !settings.verify_token) {
1296
+ // winston.error("(wab) No settings found! Unable to verify token.");
1297
+ // res.sendStatus(403);
1298
+ // } else {
1299
+ //let VERIFY_TOKEN = settings.verify_token;
1300
+ let VERIFY_TOKEN = "tiledesk-verify-token"
1301
+ // Check if a token and mode were sent
1302
+ if (mode && token) {
1303
+ // Check the mode and token sent are correct
1304
+ if (mode === "subscribe" && token === VERIFY_TOKEN) {
1305
+ // Respond with 200 OK and challenge token from the request
1306
+ winston.verbose("(wab) Webhook verified");
1307
+ res.status(200).send(challenge);
1308
+ } else {
1309
+ // Responds with '403 Forbidden' if verify tokens do not match
1310
+ winston.error("(wab) mode is not 'subscribe' or token do not match");
1311
+ res.sendStatus(403);
1312
+ }
1219
1313
  } else {
1220
- let VERIFY_TOKEN = settings.verify_token;
1221
-
1222
- // Check if a token and mode were sent
1223
- if (mode && token) {
1224
- // Check the mode and token sent are correct
1225
- if (mode === "subscribe" && token === VERIFY_TOKEN) {
1226
- // Respond with 200 OK and challenge token from the request
1227
- winston.verbose("(wab) Webhook verified");
1228
- res.status(200).send(challenge);
1229
- } else {
1230
- // Responds with '403 Forbidden' if verify tokens do not match
1231
- winston.error("(wab) mode is not 'subscribe' or token do not match");
1232
- res.sendStatus(403);
1233
- }
1314
+ winston.error("(wab) mode or token undefined");
1315
+ res.status(400).send("impossible to verify the webhook: mode or token undefined.");
1316
+ }
1317
+ //}
1318
+ });
1319
+ // ***************************************
1320
+ // ******* MESSAGING SECTION - END *******
1321
+ // ***************************************
1322
+
1323
+ // ---------------------------------------
1324
+
1325
+ // ***************************************
1326
+ // ****** TRY ON WA SECTION - START ******
1327
+ // ***************************************
1328
+ router.post("/newtest", async (req, res) => {
1329
+ winston.verbose("(wab) initializing new test..");
1330
+
1331
+ let project_id = req.body.project_id;
1332
+ let bot_id = req.body.bot_id;
1333
+
1334
+ let info = {
1335
+ project_id: project_id,
1336
+ bot_id: bot_id,
1337
+ };
1338
+
1339
+ let short_uid = uuidv4().substring(0, 8);
1340
+ let key = "bottest:" + short_uid;
1341
+
1342
+ if (!redis_client) {
1343
+ return res.status(500).send({ message: "Test it out on Whatsapp not available. Redis not ready." });
1344
+ }
1345
+
1346
+ await redis_client.set(key, JSON.stringify(info), "EX", 604800);
1347
+ redis_client.get(key, (err, value) => {
1348
+ if (err) {
1349
+ winston.error("(wab) redis get err: ", err);
1350
+ return res.status(500).send({ success: "false", message: "Testing info could not be saved" });
1234
1351
  } else {
1235
- winston.error("(wab) mode or token undefined");
1236
- res.status(400).send("impossible to verify the webhook: mode or token undefined.");
1352
+ winston.debug("(wab) new test initialized with id: " + short_uid);
1353
+ return res.status(200).send({ short_uid: short_uid });
1237
1354
  }
1355
+ });
1356
+ });
1357
+ // ***************************************
1358
+ // ******* TRY ON WA SECTION - END *******
1359
+ // ***************************************
1360
+
1361
+ // ---------------------------------------
1362
+
1363
+ // ***************************************
1364
+ // ******* APP STORE SECTION - END *******
1365
+ // ***************************************
1366
+ router.get("/detail", async (req, res) => {
1367
+ winston.verbose("(wab) /detail");
1368
+
1369
+ let project_id = req.query.project_id;
1370
+ let token = req.query.token;
1371
+ let app_id = req.query.app_id;
1372
+
1373
+ const tdChannel = new TiledeskChannel({
1374
+ settings: { project_id: project_id, token: token },
1375
+ API_URL: API_URL,
1376
+ });
1377
+ let isAvailable = await tdChannel.getProjectDetail();
1378
+ winston.debug("(wab) app is available: ", isAvailable);
1379
+ if (!project_id || !token || !app_id) {
1380
+ return res.status(500).send("<p>Ops! An error occured.</p><p>Missing query params! project_id, token and app_id are required.</p>");
1381
+ }
1382
+
1383
+ const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL });
1384
+ let installation = await appClient.getInstallations(project_id, app_id);
1385
+
1386
+ let installed = false;
1387
+ if (installation) {
1388
+ installed = true;
1238
1389
  }
1390
+
1391
+ readHTMLFile("/detail.html", (err, html) => {
1392
+ var template = handlebars.compile(html);
1393
+ var replacements = {
1394
+ app_version: pjson.version,
1395
+ project_id: project_id,
1396
+ token: token,
1397
+ app_id: app_id,
1398
+ installed: installed,
1399
+ isAvailable: isAvailable,
1400
+ };
1401
+ var html = template(replacements);
1402
+ res.send(html);
1403
+ });
1239
1404
  });
1240
1405
 
1241
- router.post("/newtest", async (req, res) => {
1242
- winston.verbose("(wab) initializing new test..");
1406
+ router.post("/install", async (req, res) => {
1407
+ winston.verbose("(wab) /install");
1243
1408
 
1244
1409
  let project_id = req.body.project_id;
1245
- let bot_id = req.body.bot_id;
1410
+ let app_id = req.body.app_id;
1411
+ let token = req.body.token;
1246
1412
 
1247
- let info = {
1413
+ winston.debug("(wab) Install app " + app_id + " for project id " + project_id);
1414
+ let installation_info = {
1248
1415
  project_id: project_id,
1249
- bot_id: bot_id,
1416
+ app_id: app_id,
1417
+ createdAt: Date.now(),
1250
1418
  };
1251
1419
 
1252
- let short_uid = uuidv4().substring(0, 8);
1253
- let key = "bottest:" + short_uid;
1420
+ const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL });
1421
+ appClient
1422
+ .install(installation_info)
1423
+ .then((installation) => {
1424
+ winston.debug("(wab) installation response: ", installation);
1254
1425
 
1255
- if (!redis_client) {
1256
- return res.status(500).send({ message: "Test it out on Whatsapp not available. Redis not ready." });
1257
- }
1426
+ res.redirect(
1427
+ url.format({
1428
+ pathname: BASE_URL + "/detail",
1429
+ query: {
1430
+ project_id: project_id,
1431
+ app_id: app_id,
1432
+ token: token,
1433
+ },
1434
+ })
1435
+ );
1436
+ })
1437
+ .catch((err) => {
1438
+ winston.error("(wab) installation error: " + err.data);
1439
+ res.send("An error occurred during the installation");
1440
+ });
1441
+ });
1258
1442
 
1259
- await redis_client.set(key, JSON.stringify(info), "EX", 604800);
1260
- redis_client.get(key, (err, value) => {
1261
- if (err) {
1262
- winston.error("(wab) redis get err: ", err);
1263
- return res.status(500).send({ success: "false", message: "Testing info could not be saved" });
1264
- } else {
1265
- winston.debug("(wab) new test initialized with id: " + short_uid);
1266
- return res.status(200).send({ short_uid: short_uid });
1267
- }
1443
+ router.post("/uninstall", async (req, res) => {
1444
+ winston.verbose("(wab) /uninstall");
1445
+ let project_id = req.body.project_id;
1446
+ let app_id = req.body.app_id;
1447
+ let token = req.body.token;
1448
+
1449
+ const appClient = new TiledeskAppsClient({ APPS_API_URL: APPS_API_URL });
1450
+ appClient.uninstall(project_id, app_id).then((response) => {
1451
+ winston.debug("(wab) uninstallation response: ", response);
1452
+ res.redirect(
1453
+ url.format({
1454
+ pathname: BASE_URL + "/detail",
1455
+ query: {
1456
+ project_id: project_id,
1457
+ app_id: app_id,
1458
+ token: token,
1459
+ },
1460
+ })
1461
+ );
1462
+ }).catch((err) => {
1463
+ winston.error("(wab) uninstallation error: " + err.data);
1464
+ res.send("An error occurred during the uninstallation");
1268
1465
  });
1269
1466
  });
1467
+ // ***************************************
1468
+ // ****** APP STORE SECTION - START ******
1469
+ // ***************************************
1470
+
1471
+ // ---------------------------------------
1270
1472
 
1473
+ // ***************************************
1474
+ // ******* IN DEPRECATION - START ********
1475
+ // ***************************************
1271
1476
  router.get("/templates/detail", async (req, res) => {
1272
1477
  let project_id = req.query.project_id;
1273
1478
  let token = req.query.token;
1274
1479
  let app_id = req.query.app_id;
1275
1480
  let template_id = req.query.id_template;
1276
- winston.debug("get template_id: " + template_id);
1481
+ winston.debug("(wab) get template_id: " + template_id);
1277
1482
 
1278
1483
  let CONTENT_KEY = "whatsapp-" + project_id;
1279
1484
  let settings = await db.get(CONTENT_KEY);
@@ -1323,97 +1528,148 @@ router.get("/templates/detail", async (req, res) => {
1323
1528
  }
1324
1529
  });
1325
1530
 
1326
- /*
1327
- router.get("/templates/:project_id", async (req, res) => {
1328
- winston.verbose("(wab) /templates");
1531
+ router.get("/direct/tiledesk", async (req, res) => {
1532
+ winston.verbose("(wab) /direct/tiledesk");
1329
1533
 
1330
- let project_id = req.params.project_id;
1331
- let token = req.query.token;
1332
- let app_id = req.query.app_id;
1534
+ let project_id = req.query.project_id;
1535
+ let whatsapp_receiver = req.query.whatsapp_receiver;
1536
+ let phone_number_id = req.query.phone_number_id;
1333
1537
 
1334
1538
  let CONTENT_KEY = "whatsapp-" + project_id;
1335
1539
  let settings = await db.get(CONTENT_KEY);
1336
- winston.debug("(wab) settings: ", settings);
1337
1540
 
1338
- if (settings) {
1339
- let tm = new TemplateManager({
1340
- token: settings.wab_token,
1341
- business_account_id: settings.business_account_id,
1342
- GRAPH_URL: GRAPH_URL,
1343
- });
1344
- let templates = await tm.getTemplates();
1541
+ let tiledeskChannelMessage = {
1542
+ text: "Sample text",
1543
+ attributes: {
1544
+ attachment: {
1545
+ type: "wa_template",
1546
+ template: {
1547
+ language: "en_US",
1548
+ name: "hello_world",
1549
+ }
1550
+ }
1551
+ }
1552
+ };
1553
+ const tlr = new TiledeskWhatsappTranslator();
1554
+ const twClient = new TiledeskWhatsapp({
1555
+ token: settings.wab_token,
1556
+ GRAPH_URL: GRAPH_URL,
1557
+ API_URL: API_URL,
1558
+ BASE_FILE_URL: BASE_FILE_URL,
1559
+ });
1345
1560
 
1346
- readHTMLFile("/templates.html", (err, html) => {
1347
- var template = handlebars.compile(html);
1348
- var replacements = {
1349
- app_version: pjson.version,
1350
- project_id: project_id,
1351
- token: token,
1352
- app_id: app_id,
1353
- templates: templates.data,
1354
- };
1355
- var html = template(replacements);
1356
- res.send(html);
1357
- });
1358
- } else {
1359
- winston.verbose("No settings found.");
1360
- return res
1361
- .status(404)
1362
- .send({
1363
- success: false,
1364
- error: "whatsapp not installed for the project id " + project_id,
1365
- });
1366
- }
1561
+ let whatsappJsonMessage = await tlr.toWhatsapp(
1562
+ tiledeskChannelMessage,
1563
+ whatsapp_receiver
1564
+ );
1565
+ winston.verbose("(wab) whatsappJsonMessage", whatsappJsonMessage);
1566
+
1567
+ twClient.sendMessage(phone_number_id, whatsappJsonMessage).then((response) => {
1568
+ winston.verbose(
1569
+ "(wab) Message sent to WhatsApp! " +
1570
+ response.status +
1571
+ " " +
1572
+ response.statusText
1573
+ );
1574
+ }).catch((err) => {
1575
+ //winston.error("(wab) error send message: ", err);
1576
+ return res.status(400).send({ success: false, error: err });
1577
+ });
1578
+
1579
+ res.status(200).send("Message sent");
1367
1580
  });
1368
- */
1369
1581
 
1370
- /*
1371
- router.get("/ext/templates/:project_id", async (req, res) => {
1372
- winston.verbose("(wab) /ext/templates");
1582
+ // Can be deleted?
1583
+ router.post("/tiledesk/broadcast", async (req, res) => {
1584
+ winston.verbose("(wab) Message received from Tiledesk (Broadcast)");
1373
1585
 
1374
- let project_id = req.params.project_id;
1586
+ let tiledeskChannelMessage = req.body.payload;
1587
+ winston.verbose("(wab) tiledeskChannelMessage: ", tiledeskChannelMessage);
1588
+ let project_id = req.body.payload.id_project;
1375
1589
 
1376
1590
  let CONTENT_KEY = "whatsapp-" + project_id;
1377
1591
  let settings = await db.get(CONTENT_KEY);
1378
1592
 
1379
- if (settings) {
1593
+ if (!settings) {
1594
+ return res.status(400).send({ success: false, error: "WhatsApp is not installed for the project_id: " + project_id });
1595
+ }
1380
1596
 
1381
- if (settings.business_account_id) {
1382
- let tm = new TemplateManager({ token: settings.wab_token, business_account_id: settings.business_account_id, GRAPH_URL: GRAPH_URL })
1383
- let templates = await tm.getTemplates();
1384
- if (templates) {
1385
- res.status(200).send(templates.data);
1386
- } else {
1387
- res.status(500).send({ success: false, code: '02', message: "A problem occurred while getting templates from WhatsApp" })
1388
- }
1597
+ if (!settings.business_account_id) {
1598
+ return res.status(400).send({ success: false, error: "Missing parameter 'WhatsApp Business Account ID'. Please update your app." });
1599
+ }
1389
1600
 
1390
- } else {
1391
- res.status(500).send({ success: false, code: '03', message: "Missing parameter 'WhatsApp Business Account ID'. Please update your app." })
1392
- }
1601
+ let receiver_list = req.body.receiver_list;
1602
+ let phone_number_id = req.body.phone_number_id;
1393
1603
 
1394
- } else {
1395
- res.status(400).send({ success: false, code: '01', message: "WhatsApp not installed for the project_id " + project_id })
1396
- }
1604
+ let tm = new TemplateManager({
1605
+ token: settings.wab_token,
1606
+ business_account_id: settings.business_account_id,
1607
+ GRAPH_URL: GRAPH_URL,
1608
+ });
1609
+ const tlr = new TiledeskWhatsappTranslator();
1610
+ const twClient = new TiledeskWhatsapp({
1611
+ token: settings.wab_token,
1612
+ GRAPH_URL: GRAPH_URL,
1613
+ API_URL: API_URL,
1614
+ BASE_FILE_URL: BASE_FILE_URL,
1615
+ });
1397
1616
 
1398
- })
1399
- */
1617
+ let response = await tm.getTemplates();
1618
+ let templates = response.data;
1400
1619
 
1401
- //router.delete("/ext/")
1620
+ let selected_template = templates.find(
1621
+ (t) => t.name === tiledeskChannelMessage.attributes.attachment.template.name
1622
+ );
1402
1623
 
1403
- /*
1404
- router.get("/ext/:project_id", async (req, res) => {
1405
- winston.verbose("(wab) /ext/");
1624
+ let params_object = await tm.generateParamsObject(selected_template);
1406
1625
 
1407
- let project_id = req.params.project_id;
1408
- let CONTENT_KEY = "whatsapp-" + project_id;
1409
- let settings = await db.get(CONTENT_KEY);
1626
+ tiledeskChannelMessage.attributes.attachment.template.params = params_object;
1627
+ winston.verbose("(wab) --> tiledeskChannelMessage: ", tiledeskChannelMessage);
1410
1628
 
1411
- if (!settings) {
1412
- return res.status(200).send({ success: false, message: "WhatsApp not installed for the project_id " + project_id })
1629
+ let messages_sent = 0;
1630
+ let errors = [];
1631
+ let error_count = 0;
1632
+
1633
+ if (receiver_list) {
1634
+ let i = 0;
1635
+ async function execute(whatsapp_receiver) {
1636
+ let customTiledeskJsonMessage = await tlr.sanitizeTiledeskMessage(tiledeskChannelMessage, whatsapp_receiver);
1637
+ winston.verbose("(wab) customTiledeskJsonMessage: ", customTiledeskJsonMessage);
1638
+
1639
+ let whatsappJsonMessage = await tlr.toWhatsapp(customTiledeskJsonMessage, whatsapp_receiver.phone_number);
1640
+ winston.verbose("(wab) message created for receiver: " + whatsapp_receiver.phone_number);
1641
+ winston.verbose("(wab) whatsappJsonMessage", whatsappJsonMessage);
1642
+
1643
+ await twClient.sendMessage(phone_number_id, whatsappJsonMessage).then((response) => {
1644
+ winston.verbose("(wab) Message sent to WhatsApp! " + response.status + " " + response.statusText);
1645
+ messages_sent += 1;
1646
+ i += 1;
1647
+ if (i < receiver_list.length) {
1648
+ execute(receiver_list[i]);
1649
+ } else {
1650
+ winston.debug("(wab) End of list");
1651
+ return res.status(200).send({ success: true, message: "Broadcast terminated", messages_sent: messages_sent, errors: errors });
1652
+ }
1653
+ }).catch((err) => {
1654
+ winston.error("(wab) error send message: " + err.response.data.error.message);
1655
+ errors.push({ receiver: whatsapp_receiver.phone_number, error: err.response.data.error.message });
1656
+ i += 1;
1657
+ if (i < receiver_list.length) {
1658
+ execute(receiver_list[i]);
1659
+ } else {
1660
+ winston.debug("(wab) End of list");
1661
+ return res.status(200).send({ success: true, message: "Broadcast terminated", messages_sent: messages_sent, errors: errors });
1662
+ }
1663
+ });
1664
+ }
1665
+ execute(receiver_list[0]);
1413
1666
  }
1414
- return res.status(200).send({ success: true, settings: settings })
1415
- })
1416
- */
1667
+ });
1668
+ // ***************************************
1669
+ // ******** IN DEPRECATION - END *********
1670
+ // ***************************************
1671
+
1672
+ // ---------------------------------------
1417
1673
 
1418
1674
  // *****************************
1419
1675
  // ********* FUNCTIONS *********
@@ -1421,7 +1677,7 @@ router.get("/ext/:project_id", async (req, res) => {
1421
1677
 
1422
1678
  async function startApp(settings, callback) {
1423
1679
  winston.info("(wab) Starting Whatsapp App");
1424
-
1680
+
1425
1681
  if (!settings.MONGODB_URL) {
1426
1682
  winston.error("(wab) MONGODB_URL is mandatory. Exit...");
1427
1683
  return callback("Missing parameter: MONGODB_URL");
@@ -1495,6 +1751,20 @@ async function startApp(settings, callback) {
1495
1751
  BRAND_NAME = settings.BRAND_NAME;
1496
1752
  }
1497
1753
 
1754
+ if (!settings.FB_APP_ID) {
1755
+ winston.info("(wab) Missing FB_APP_ID parameter");
1756
+ } else {
1757
+ FB_APP_ID = process.env.FB_APP_ID;
1758
+ winston.info("(wab) FB_APP_ID is present");
1759
+ }
1760
+
1761
+ if (!settings.CONFIGURATION_ID) {
1762
+ winston.info("(wab) Missing CONFIGURATION_ID parameter");
1763
+ } else {
1764
+ CONFIGURATION_ID = process.env.CONFIGURATION_ID;
1765
+ winston.info("(wab) CONFIGURATION_ID is present");
1766
+ }
1767
+
1498
1768
  // // For test only
1499
1769
  // if (settings.LOG_MONGODB_URL) {
1500
1770
  // /**
@@ -1518,9 +1788,9 @@ async function startApp(settings, callback) {
1518
1788
  }
1519
1789
 
1520
1790
  mongoose.connect(LOG_MONGODB_URL).then(() => {
1521
- winston.info("Mongoose DB Connected");
1791
+ winston.info("(wab) Mongoose DB Connected");
1522
1792
  }).catch((err) => {
1523
- winston.error("(Mongoose) Unable to connect with MongoDB ", err);
1793
+ winston.error("(wab) (Mongoose) Unable to connect with MongoDB ", err);
1524
1794
  });
1525
1795
 
1526
1796
  if (settings.dbconnection) {
@@ -1595,4 +1865,19 @@ function readHTMLFile(templateName, callback) {
1595
1865
  );
1596
1866
  }
1597
1867
 
1868
+ async function getDepartments(project_id, token) {
1869
+ try {
1870
+ const tdChannel = new TiledeskChannel({
1871
+ settings: { project_id, token },
1872
+ API_URL,
1873
+ });
1874
+
1875
+ const departments = await tdChannel.getDepartments();
1876
+ return departments;
1877
+ } catch (err) {
1878
+ winston.error("(wab) Error getting departments:", err?.response?.data || err.message || err);
1879
+ return [];
1880
+ }
1881
+ }
1882
+
1598
1883
  module.exports = { router: router, startApp: startApp };