@tiledesk/tiledesk-whatsapp-connector 1.0.31 → 1.0.33

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/.env CHANGED
@@ -1,2 +1,2 @@
1
1
  # CUSTOM ENV
2
- NPM_PUBLISH_TOKEN=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
2
+ NPM_PUBLISH_TOKEN=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
package/index.js CHANGED
@@ -31,6 +31,7 @@ const { TemplateManager } = require("./tiledesk/TemplateManager");
31
31
  const { WhatsappLogger } = require("./tiledesk/WhatsappLogger");
32
32
  const Utils = require("./tiledesk/Utils");
33
33
  const whatsappService = require("./tiledesk/WhatsappService");
34
+ const { ensureGlobalSigninSubscription, ensureProjectTestSubscription, getActiveTest, isProjectId } = require("./tiledesk/BotTestSubscriptions");
34
35
 
35
36
  // mongo
36
37
  const { KVBaseMongo } = require("./tiledesk/KVBaseMongo");
@@ -560,22 +561,22 @@ router.post("/disconnect", async (req, res) => {
560
561
  // ***************************************
561
562
  // ****** MESSAGING SECTION - START ******
562
563
  // ***************************************
563
- router.post("/tiledesk", async (req, res) => {
564
+ function phoneNumberIdFromTiledeskRecipient(recipient, attributes) {
565
+ if (attributes && attributes.whatsapp_phone_number_id) {
566
+ return attributes.whatsapp_phone_number_id;
567
+ }
568
+ if (!recipient || recipient.indexOf("wab-") === -1) {
569
+ return null;
570
+ }
571
+ return recipient.substring(recipient.lastIndexOf("wab-") + 4, recipient.lastIndexOf("-"));
572
+ }
573
+
574
+ async function handleTiledeskOutbound(req, res, settings) {
564
575
  winston.verbose("(wab) Message received from Tiledesk");
565
576
 
566
577
  let tiledeskChannelMessage = req.body.payload;
567
578
  winston.debug("(wab) tiledeskChannelMessage: ", tiledeskChannelMessage);
568
579
 
569
- let project_id = req.body.payload.id_project;
570
- let recipient = req.body.payload.recipient;
571
- let waba_id = recipient.split('-')[5];
572
-
573
- let settings = await utils.getSettingsByProjectId(project_id);
574
-
575
- if (!settings) {
576
- settings = await utils.getSettings(project_id, waba_id);
577
- }
578
-
579
580
  let wab_token = settings.wab_token;
580
581
 
581
582
  var text = req.body.payload.text;
@@ -758,6 +759,55 @@ router.post("/tiledesk", async (req, res) => {
758
759
  winston.debug("(wab) no command, no text --> skip");
759
760
  return res.status(400).send({ success: false, error: "No command or text specified. Skip message." });
760
761
  }
762
+ }
763
+
764
+ router.post("/tiledesk", async (req, res) => {
765
+ const payload = req.body && req.body.payload;
766
+ if (!payload || !payload.recipient) {
767
+ return res.status(400).send({ success: false, error: "Invalid Tiledesk payload" });
768
+ }
769
+
770
+ const project_id = payload.id_project;
771
+ const waba_id = payload.recipient.split("-")[5];
772
+ let settings = await utils.getSettingsByProjectId(project_id);
773
+ if (!settings) {
774
+ settings = await utils.getSettings(project_id, waba_id);
775
+ }
776
+ if (!settings) {
777
+ winston.warn("(wab) settings not found for project " + project_id);
778
+ return res.sendStatus(200);
779
+ }
780
+
781
+ const phoneNumberId = phoneNumberIdFromTiledeskRecipient(payload.recipient, payload.attributes);
782
+ if (phoneNumberId) {
783
+ const official = await db.get("whatsapp-phone-" + phoneNumberId);
784
+ if (official && official.project_id && String(official.project_id) !== String(project_id)) {
785
+ winston.verbose("(wab) Skip Try on WhatsApp reply on the project subscription");
786
+ return res.sendStatus(200);
787
+ }
788
+ }
789
+
790
+ return handleTiledeskOutbound(req, res, settings);
791
+ });
792
+
793
+ router.post("/tiledesk/test", async (req, res) => {
794
+ const payload = req.body && req.body.payload;
795
+ if (!payload || !payload.recipient) {
796
+ return res.sendStatus(200);
797
+ }
798
+
799
+ const phoneNumberId = phoneNumberIdFromTiledeskRecipient(payload.recipient, payload.attributes);
800
+ if (!phoneNumberId) {
801
+ return res.sendStatus(200);
802
+ }
803
+
804
+ const settings = await db.get("whatsapp-phone-" + phoneNumberId);
805
+ if (!settings || !settings.wab_token) {
806
+ winston.verbose("(wab) No official WhatsApp settings for test reply " + phoneNumberId);
807
+ return res.sendStatus(200);
808
+ }
809
+
810
+ return handleTiledeskOutbound(req, res, settings);
761
811
  });
762
812
 
763
813
  // Endpoint for Whatsapp Business connected via OAuth
@@ -817,6 +867,7 @@ router.post('/webhook', async (req, res) => {
817
867
  db: db,
818
868
  tdChannel: tdChannel,
819
869
  tlr: tlr,
870
+ baseUrl: BASE_URL,
820
871
  });
821
872
  bottester.startBotConversation(req.body, code).then((result) => {
822
873
  winston.verbose("(wab) test conversation started");
@@ -880,7 +931,7 @@ router.post('/webhook', async (req, res) => {
880
931
  winston.verbose("(wab) tiledeskJsonMessage: ", tiledeskJsonMessage);
881
932
 
882
933
  try {
883
- const response = await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
934
+ const response = await deliverInboundMessage(tdChannel, tiledeskJsonMessage, message_info, settings);
884
935
  winston.verbose("(wab) Message sent to Tiledesk!");
885
936
  winston.debug("(wab) response: ", response);
886
937
  } catch (err) {
@@ -998,6 +1049,7 @@ router.post("/webhook/:project_id", async (req, res) => {
998
1049
  db: db,
999
1050
  tdChannel: tdChannel,
1000
1051
  tlr: tlr,
1052
+ baseUrl: BASE_URL,
1001
1053
  });
1002
1054
  bottester.startBotConversation(req.body, code).then((result) => {
1003
1055
  winston.verbose("(wab) test conversation started");
@@ -1114,7 +1166,7 @@ router.post("/webhook/:project_id", async (req, res) => {
1114
1166
 
1115
1167
  if (tiledeskJsonMessage) {
1116
1168
  winston.verbose("(wab) tiledeskJsonMessage: ", tiledeskJsonMessage);
1117
- const response = await tdChannel.send(tiledeskJsonMessage, message_info, settings.department_id);
1169
+ const response = await deliverInboundMessage(tdChannel, tiledeskJsonMessage, message_info, settings);
1118
1170
  winston.verbose("(wab) Message sent to Tiledesk!");
1119
1171
  winston.debug("(wab) response: ", response);
1120
1172
  } else {
@@ -1268,6 +1320,22 @@ router.post("/newtest", async (req, res) => {
1268
1320
  let project_id = req.body.project_id;
1269
1321
  let bot_id = req.body.bot_id;
1270
1322
 
1323
+ if (!project_id || !bot_id) {
1324
+ return res.status(400).send({ success: false, message: "project_id and bot_id are required" });
1325
+ }
1326
+
1327
+ if (!redis_client) {
1328
+ return res.status(500).send({ message: "Test it out on Whatsapp not available. Redis not ready." });
1329
+ }
1330
+
1331
+ try {
1332
+ await ensureGlobalSigninSubscription(db, BASE_URL);
1333
+ await ensureProjectTestSubscription(db, project_id, BASE_URL, redis_client);
1334
+ } catch (err) {
1335
+ winston.error("(wab) unable to prepare Try on WhatsApp: " + err);
1336
+ return res.status(500).send({ success: false, message: "Try on WhatsApp is not available" });
1337
+ }
1338
+
1271
1339
  let info = {
1272
1340
  project_id: project_id,
1273
1341
  bot_id: bot_id,
@@ -1276,10 +1344,6 @@ router.post("/newtest", async (req, res) => {
1276
1344
  let short_uid = uuidv4().substring(0, 8);
1277
1345
  let key = "bottest:" + short_uid;
1278
1346
 
1279
- if (!redis_client) {
1280
- return res.status(500).send({ message: "Test it out on Whatsapp not available. Redis not ready." });
1281
- }
1282
-
1283
1347
  await redis_client.set(key, JSON.stringify(info), "EX", 604800);
1284
1348
  redis_client.get(key, (err, value) => {
1285
1349
  if (err) {
@@ -1811,6 +1875,22 @@ async function getDepartments(project_id, token) {
1811
1875
  }
1812
1876
  }
1813
1877
 
1878
+ async function deliverInboundMessage(tdChannel, tiledeskJsonMessage, messageInfo, settings) {
1879
+ const whatsapp = messageInfo && messageInfo.whatsapp;
1880
+ const session = await getActiveTest(redis_client, whatsapp && whatsapp.phone_number_id, whatsapp && whatsapp.from);
1881
+ if (session && session.request_id && isProjectId(session.project_id)) {
1882
+ winston.verbose("(wab) Continue Try on WhatsApp in project " + session.project_id);
1883
+ const signinSubscription = await ensureGlobalSigninSubscription(db, BASE_URL);
1884
+ return tdChannel.sendToExistingRequest(tiledeskJsonMessage, messageInfo, {
1885
+ project_id: session.project_id,
1886
+ request_id: session.request_id,
1887
+ subscriptionId: String(signinSubscription._id),
1888
+ secret: signinSubscription.secret
1889
+ });
1890
+ }
1891
+ return tdChannel.send(tiledeskJsonMessage, messageInfo, settings.department_id);
1892
+ }
1893
+
1814
1894
  /**
1815
1895
  * Helper function to get or create user token with Redis caching
1816
1896
  * @param {Object} tdChannel - TiledeskChannel instance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-whatsapp-connector",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "Tiledesk Whatsapp connector",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,164 @@
1
+ const { v4: uuidv4 } = require("uuid");
2
+ const winston = require("../winston");
3
+
4
+ const BOTTEST_SIGNIN_EVENT = "whatsapp.bottest.signin";
5
+ const WHATSAPP_MESSAGE_EVENT = "message.create.request.channel.whatsapp";
6
+ const BOTTEST_CREATED_BY = "whatsapp-bottest";
7
+
8
+ function isProjectId(value) {
9
+ return typeof value === "string" && /^[a-fA-F0-9]{24}$/.test(value);
10
+ }
11
+
12
+ function subscriptions(kv) {
13
+ if (!kv || !kv.db || typeof kv.db.collection !== "function") {
14
+ throw new Error("WhatsApp storage is not connected");
15
+ }
16
+ return kv.db.collection("subscriptions");
17
+ }
18
+
19
+ async function ensureGlobalSigninSubscription(kv, baseUrl) {
20
+ if (!baseUrl) {
21
+ throw new Error("BASE_URL is required to prepare Try on WhatsApp");
22
+ }
23
+ const collection = subscriptions(kv);
24
+ const query = { event: BOTTEST_SIGNIN_EVENT, id_project: "*", global: true };
25
+ let existing = await collection.findOne(query);
26
+ if (existing) {
27
+ return existing;
28
+ }
29
+
30
+ const now = new Date();
31
+ const doc = {
32
+ event: BOTTEST_SIGNIN_EVENT,
33
+ target: baseUrl + "/tiledesk/test",
34
+ secret: uuidv4(),
35
+ id_project: "*",
36
+ global: true,
37
+ createdBy: BOTTEST_CREATED_BY,
38
+ createdAt: now,
39
+ updatedAt: now
40
+ };
41
+
42
+ try {
43
+ const result = await collection.insertOne(doc);
44
+ doc._id = result.insertedId;
45
+ return doc;
46
+ } catch (err) {
47
+ if (err && err.code === 11000) {
48
+ existing = await collection.findOne(query);
49
+ if (existing) {
50
+ return existing;
51
+ }
52
+ }
53
+ throw err;
54
+ }
55
+ }
56
+
57
+ async function ensureProjectTestSubscription(kv, projectId, baseUrl, redisClient) {
58
+ if (!isProjectId(projectId)) {
59
+ throw new Error("Try on WhatsApp project_id is invalid");
60
+ }
61
+ if (!baseUrl) {
62
+ throw new Error("BASE_URL is required to prepare Try on WhatsApp");
63
+ }
64
+
65
+ const collection = subscriptions(kv);
66
+ const target = baseUrl + "/tiledesk/test";
67
+ const query = { id_project: projectId, event: WHATSAPP_MESSAGE_EVENT, target: target };
68
+ let existing = await collection.findOne(query);
69
+ if (!existing) {
70
+ const now = new Date();
71
+ const doc = {
72
+ event: WHATSAPP_MESSAGE_EVENT,
73
+ target: target,
74
+ secret: uuidv4(),
75
+ id_project: projectId,
76
+ global: false,
77
+ createdBy: BOTTEST_CREATED_BY,
78
+ createdAt: now,
79
+ updatedAt: now
80
+ };
81
+ try {
82
+ const result = await collection.insertOne(doc);
83
+ doc._id = result.insertedId;
84
+ existing = doc;
85
+ } catch (err) {
86
+ if (err && err.code === 11000) {
87
+ existing = await collection.findOne(query);
88
+ } else {
89
+ throw err;
90
+ }
91
+ }
92
+ }
93
+
94
+ await invalidateSubscriptionCache(redisClient, projectId, WHATSAPP_MESSAGE_EVENT);
95
+ return existing;
96
+ }
97
+
98
+ function activeTestKey(phoneNumberId, from) {
99
+ return "bottest:active:" + phoneNumberId + ":" + from;
100
+ }
101
+
102
+ function saveActiveTest(redisClient, phoneNumberId, from, session) {
103
+ if (!redisClient || !phoneNumberId || !from || !session) {
104
+ return Promise.resolve();
105
+ }
106
+ const key = activeTestKey(phoneNumberId, from);
107
+ return new Promise((resolve, reject) => {
108
+ redisClient.set(key, JSON.stringify(session), "EX", 604800, function (err) {
109
+ if (err) {
110
+ return reject(err);
111
+ }
112
+ resolve();
113
+ });
114
+ });
115
+ }
116
+
117
+ function getActiveTest(redisClient, phoneNumberId, from) {
118
+ if (!redisClient || !phoneNumberId || !from) {
119
+ return Promise.resolve(null);
120
+ }
121
+ const key = activeTestKey(phoneNumberId, from);
122
+ return new Promise((resolve) => {
123
+ redisClient.get(key, function (err, value) {
124
+ if (err || !value) {
125
+ return resolve(null);
126
+ }
127
+ try {
128
+ resolve(JSON.parse(value));
129
+ } catch (parseErr) {
130
+ resolve(null);
131
+ }
132
+ });
133
+ });
134
+ }
135
+
136
+ function invalidateSubscriptionCache(redisClient, projectId, event) {
137
+ if (!redisClient || !projectId || !event) {
138
+ return Promise.resolve();
139
+ }
140
+ const key = "cacheman:cachegoose-cache:" + projectId + ":subscriptions:event:" + event;
141
+ return new Promise((resolve) => {
142
+ try {
143
+ redisClient.del(key, function (err) {
144
+ if (err) {
145
+ winston.warn("(wab) unable to refresh subscription cache for " + projectId);
146
+ }
147
+ resolve();
148
+ });
149
+ } catch (err) {
150
+ winston.warn("(wab) unable to refresh subscription cache for " + projectId);
151
+ resolve();
152
+ }
153
+ });
154
+ }
155
+
156
+ module.exports = {
157
+ BOTTEST_SIGNIN_EVENT,
158
+ WHATSAPP_MESSAGE_EVENT,
159
+ isProjectId,
160
+ ensureGlobalSigninSubscription,
161
+ ensureProjectTestSubscription,
162
+ saveActiveTest,
163
+ getActiveTest
164
+ };
@@ -7,6 +7,7 @@ const { TiledeskClient } = require('@tiledesk/tiledesk-client');
7
7
  const { TiledeskWhatsappTranslator } = require('./TiledeskWhatsappTranslator');
8
8
  const { TiledeskChannel } = require('./TiledeskChannel');
9
9
  const Utils = require("./Utils");
10
+ const { ensureGlobalSigninSubscription, ensureProjectTestSubscription, isProjectId, saveActiveTest } = require("./BotTestSubscriptions");
10
11
 
11
12
  class TiledeskBotTester {
12
13
 
@@ -46,6 +47,7 @@ class TiledeskBotTester {
46
47
  this.db = config.db;
47
48
  this.tdChannel = config.tdChannel;
48
49
  this.tlr = config.tlr;
50
+ this.baseUrl = config.baseUrl;
49
51
 
50
52
  this.utils = new Utils({ db: this.db })
51
53
 
@@ -79,7 +81,7 @@ class TiledeskBotTester {
79
81
  test_info = JSON.parse(value)
80
82
  } catch(err) {
81
83
  winston.error("(wab) Error parsing value: " + value);
82
- reject(err);
84
+ return reject(err);
83
85
  }
84
86
  let message_info = {
85
87
  channel: "whatsapp",
@@ -103,14 +105,36 @@ class TiledeskBotTester {
103
105
 
104
106
  whatsappChannelMessage.text.body = "/start";
105
107
  let tiledeskJsonMessage = await this.tlr.toTiledesk(whatsappChannelMessage, whatsappContact.profile.name);
106
-
107
- if (test_info) {
108
- const response = await this.tdChannel.sendAndAddBot(tiledeskJsonMessage, message_info, test_info.bot_id)
109
- return resolve(response)
110
- } else {
108
+
109
+ if (!test_info || !test_info.bot_id || !isProjectId(test_info.project_id)) {
111
110
  winston.verbose("(wab) No bot selected for test. Exit..")
112
111
  return reject("Test skipped")
113
112
  }
113
+
114
+ try {
115
+ const phoneNumberId = message_info.whatsapp.phone_number_id;
116
+ if (phoneNumberId && this.tdChannel && this.tdChannel.settings) {
117
+ await this.db.set("whatsapp-phone-" + phoneNumberId, this.tdChannel.settings);
118
+ }
119
+ const signinSubscription = await ensureGlobalSigninSubscription(this.db, this.baseUrl);
120
+ await ensureProjectTestSubscription(this.db, test_info.project_id, this.baseUrl, this.redis_client);
121
+ const response = await this.tdChannel.sendAndAddBot(tiledeskJsonMessage, message_info, test_info.bot_id, {
122
+ project_id: test_info.project_id,
123
+ subscriptionId: String(signinSubscription._id),
124
+ secret: signinSubscription.secret
125
+ });
126
+ if (response && response.request_id) {
127
+ await saveActiveTest(this.redis_client, phoneNumberId, message_info.whatsapp.from, {
128
+ project_id: test_info.project_id,
129
+ bot_id: test_info.bot_id,
130
+ request_id: response.request_id
131
+ });
132
+ }
133
+ return resolve(response);
134
+ } catch (prepareErr) {
135
+ winston.error("(wab) [TiledeskBotTester] unable to open test conversation: " + prepareErr);
136
+ return reject(prepareErr);
137
+ }
114
138
 
115
139
  }
116
140
  }
@@ -181,18 +181,23 @@ class TiledeskChannel {
181
181
  * @param {string} bot_id - The bot ID
182
182
  * @returns {Promise<Object>} - The response from the Tiledesk API
183
183
  */
184
- async sendAndAddBot(tiledeskMessage, messageInfo, bot_id) {
185
-
184
+ async sendAndAddBot(tiledeskMessage, messageInfo, bot_id, targetProject) {
185
+
186
+ const projectId = targetProject && targetProject.project_id ? targetProject.project_id : this.settings.project_id;
187
+ const subscriptionId = targetProject && targetProject.subscriptionId ? targetProject.subscriptionId : this.settings.subscriptionId;
188
+ const secret = targetProject && targetProject.secret ? targetProject.secret : this.settings.secret;
189
+
186
190
  let channel;
187
191
  let new_request_id;
188
192
  tiledeskMessage.participants = ["bot_" + bot_id];
189
193
  tiledeskMessage.attributes = {
190
- sourcePage: "whatsapp://&td_draft=true"
194
+ sourcePage: "whatsapp://&td_draft=true",
195
+ whatsapp_bottest: true
191
196
  }
192
197
 
193
198
  if (messageInfo.channel == "whatsapp") {
194
199
  channel = messageInfo.whatsapp;
195
- new_request_id = "support-group-" + this.settings.project_id + "-" + uuidv4().substring(0, 8) + "-wab-" + channel.phone_number_id + "-" + channel.from;
200
+ new_request_id = "support-group-" + projectId + "-" + uuidv4().substring(0, 8) + "-wab-" + channel.phone_number_id + "-" + channel.from;
196
201
  } else {
197
202
  winston.verbose("(wab) [TiledeskChannel] Channel not supported")
198
203
  return null;
@@ -203,10 +208,11 @@ class TiledeskChannel {
203
208
  first_name: channel.firstname,
204
209
  last_name: channel.lastname,
205
210
  sub: 'userexternal',
206
- aud: 'https://tiledesk.com/subscriptions/' + this.settings.subscriptionId
211
+ aud: 'https://tiledesk.com/subscriptions/' + subscriptionId
207
212
  }
208
213
 
209
- var customToken = jwt.sign(payload, this.settings.secret);
214
+ var customToken = jwt.sign(payload, secret);
215
+ const signinBody = targetProject && targetProject.project_id ? { id_project: projectId } : {};
210
216
 
211
217
  return await axios({
212
218
  url: this.API_URL + "/auth/signinWithCustomToken",
@@ -214,7 +220,7 @@ class TiledeskChannel {
214
220
  'Content-Type': 'application/json',
215
221
  'Authorization': 'JWT ' + customToken
216
222
  },
217
- data: {},
223
+ data: signinBody,
218
224
  method: 'POST'
219
225
  }).then((response) => {
220
226
 
@@ -222,7 +228,7 @@ class TiledeskChannel {
222
228
  token = this.fixToken(token);
223
229
 
224
230
  return axios({
225
- url: this.API_URL + `/${this.settings.project_id}/requests/${new_request_id}/messages`,
231
+ url: this.API_URL + `/${projectId}/requests/${new_request_id}/messages`,
226
232
  headers: {
227
233
  'Content-Type': 'application/json',
228
234
  'Authorization': token
@@ -230,7 +236,9 @@ class TiledeskChannel {
230
236
  data: tiledeskMessage,
231
237
  method: 'POST'
232
238
  }).then((response) => {
233
- return response.data
239
+ const data = response.data || {};
240
+ data.request_id = new_request_id;
241
+ return data;
234
242
  }).catch((err) => {
235
243
  const errorMessage = utils._extractErrorMessage(err);
236
244
  winston.error("(wab) [TiledeskChannel] send message (open conversation) error: " + errorMessage);
@@ -241,6 +249,49 @@ class TiledeskChannel {
241
249
  })
242
250
  }
243
251
 
252
+ /**
253
+ * Append a message to a Try on WhatsApp request that already lives in the bot's project.
254
+ */
255
+ async sendToExistingRequest(tiledeskMessage, messageInfo, target) {
256
+ if (!target || !target.project_id || !target.request_id || !target.subscriptionId || !target.secret) {
257
+ throw new Error("Try on WhatsApp session is incomplete");
258
+ }
259
+
260
+ const channel = messageInfo.whatsapp || {};
261
+ const payload = {
262
+ _id: 'wab-' + channel.from,
263
+ first_name: channel.firstname,
264
+ last_name: channel.lastname,
265
+ sub: 'userexternal',
266
+ aud: 'https://tiledesk.com/subscriptions/' + target.subscriptionId
267
+ };
268
+ const customToken = jwt.sign(payload, target.secret);
269
+
270
+ const signinResponse = await axios({
271
+ url: this.API_URL + "/auth/signinWithCustomToken",
272
+ headers: {
273
+ 'Content-Type': 'application/json',
274
+ 'Authorization': 'JWT ' + customToken
275
+ },
276
+ data: { id_project: target.project_id },
277
+ method: 'POST'
278
+ });
279
+
280
+ const token = this.fixToken(signinResponse.data.token);
281
+ delete tiledeskMessage.participants;
282
+
283
+ const sendResponse = await axios({
284
+ url: this.API_URL + `/${target.project_id}/requests/${target.request_id}/messages`,
285
+ headers: {
286
+ 'Content-Type': 'application/json',
287
+ 'Authorization': token
288
+ },
289
+ data: tiledeskMessage,
290
+ method: 'POST'
291
+ });
292
+ return sendResponse.data;
293
+ }
294
+
244
295
  /**
245
296
  * Get the project detail from Tiledesk
246
297
  * @returns {Promise<Object>} - The response from the Tiledesk API