@tiledesk/tiledesk-tybot-connector 0.2.16 → 0.2.19
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/CHANGELOG.md +11 -0
- package/TiledeskExpression.js +15 -8
- package/index.js +75 -1
- package/models/IntentsMachineFactory.js +7 -1
- package/models/TiledeskChatbot.js +14 -5
- package/models/TiledeskChatbotUtil.js +19 -0
- package/models/TiledeskIntentsMachine.js +6 -5
- package/package.json +1 -1
- package/tiledeskChatbotPlugs/directives/DirIntent.js +1 -0
- package/tiledeskChatbotPlugs/directives/DirReply.js +12 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
available on:
|
|
6
6
|
▶️ https://www.npmjs.com/package/@tiledesk/tiledesk-tybot-connector
|
|
7
7
|
|
|
8
|
+
### v0.2.19
|
|
9
|
+
- Added native attribute lastUserMessageType
|
|
10
|
+
- Added integration with external intents decode engine
|
|
11
|
+
- Added support for automatic wait time on each reply
|
|
12
|
+
|
|
13
|
+
### v0.2.17
|
|
14
|
+
- Added lastUserMessage JSON native attribute
|
|
15
|
+
- Added attributes to access image and file properties from incoming messages
|
|
16
|
+
- Added support for JSON attributes in conditions
|
|
17
|
+
- Added backupIntentsFinder based on Fulltext in TiledeskChatbot
|
|
18
|
+
|
|
8
19
|
### v0.2.16
|
|
9
20
|
- update whatsapp api url for pre environment
|
|
10
21
|
|
package/TiledeskExpression.js
CHANGED
|
@@ -230,9 +230,9 @@ class TiledeskExpression {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
evaluateJavascriptExpression(expression, context) {
|
|
233
|
-
// console.log("evaluating:", expression)
|
|
233
|
+
// console.log("(evaluateJavascriptExpression) evaluating:", expression)
|
|
234
234
|
// console.log("context:", context)
|
|
235
|
-
let res;
|
|
235
|
+
let res = null;
|
|
236
236
|
try {
|
|
237
237
|
const vm = new VM({
|
|
238
238
|
timeout: 200,
|
|
@@ -240,9 +240,10 @@ class TiledeskExpression {
|
|
|
240
240
|
sandbox: context
|
|
241
241
|
});
|
|
242
242
|
res = vm.run(`let $data = this;${expression}`);
|
|
243
|
+
// console.log("res=", res)
|
|
243
244
|
}
|
|
244
245
|
catch (err) {
|
|
245
|
-
console.error("TiledeskExpression.evaluate() error:", err.message, "evaluating expression: '" + expression + "'");
|
|
246
|
+
console.error("(evaluateJavascriptExpression) TiledeskExpression.evaluate() error:", err.message, "evaluating expression: '" + expression + "'");
|
|
246
247
|
}
|
|
247
248
|
return res;
|
|
248
249
|
}
|
|
@@ -324,11 +325,13 @@ class TiledeskExpression {
|
|
|
324
325
|
// console.log("applyPattern:", applyPattern);
|
|
325
326
|
let operand1_s;
|
|
326
327
|
let is_valid_operand1 = TiledeskExpression.validateVariableName(condition.operand1);
|
|
328
|
+
// console.log("is_valid_operand1:", condition.operand1, is_valid_operand1);
|
|
327
329
|
if (is_valid_operand1) {
|
|
328
330
|
operand1_s = TiledeskExpression.variableOperand(condition.operand1);
|
|
331
|
+
// console.log("operand1_s:", operand1_s);
|
|
329
332
|
}
|
|
330
333
|
else {
|
|
331
|
-
|
|
334
|
+
console.error("Condition evaluation stopped because of invalid operand", condition.operand1);
|
|
332
335
|
return null;
|
|
333
336
|
}
|
|
334
337
|
|
|
@@ -343,24 +346,26 @@ class TiledeskExpression {
|
|
|
343
346
|
operand2_s = TiledeskExpression.variableOperand(condition.operand2.name);
|
|
344
347
|
}
|
|
345
348
|
else {
|
|
346
|
-
|
|
349
|
+
console.error("Condition evaluation stopped because of invalid operand2", condition.operand2);
|
|
347
350
|
return null;
|
|
348
351
|
}
|
|
349
352
|
}
|
|
350
353
|
else {
|
|
351
|
-
|
|
354
|
+
console.error("Condition evaluation stopped because of: No operand2", JSON.stringify(condition));
|
|
352
355
|
return null;
|
|
353
356
|
}
|
|
354
357
|
|
|
355
|
-
// console.log("operand2_s:", operand2_s);
|
|
358
|
+
// console.log("operand1_s, operand2_s:",operand1_s, operand2_s);
|
|
356
359
|
const expression =
|
|
357
360
|
applyPattern
|
|
358
361
|
.replace("#1", operand1_s)
|
|
359
362
|
.replace("#2", operand2_s);
|
|
363
|
+
// console.log("expression is:", expression);
|
|
360
364
|
return expression;
|
|
361
365
|
}
|
|
362
366
|
|
|
363
367
|
static JSONGroupToExpression(group, variables) {
|
|
368
|
+
// console.log("attributes:", variables);
|
|
364
369
|
let conditions = group.conditions;
|
|
365
370
|
let group_expression = "";
|
|
366
371
|
// console.log("conditions:", conditions)
|
|
@@ -368,6 +373,7 @@ class TiledeskExpression {
|
|
|
368
373
|
let part = conditions[i];
|
|
369
374
|
if (part.type === "condition") {
|
|
370
375
|
let expression = TiledeskExpression.JSONConditionToExpression(part, variables);
|
|
376
|
+
// console.log("returned expression:", expression);
|
|
371
377
|
if (expression === null) {
|
|
372
378
|
// console.error("Invalid JSON expression", JSON.stringify(part));
|
|
373
379
|
return null;
|
|
@@ -412,7 +418,8 @@ class TiledeskExpression {
|
|
|
412
418
|
static validateVariableName(variableName) {
|
|
413
419
|
// console.log("variableName", variableName)
|
|
414
420
|
// console.log("type of variableName:", typeof variableName);
|
|
415
|
-
let matches = variableName.match(/^[a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_]*$/gm);
|
|
421
|
+
// let matches = variableName.match(/^[a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_]*$/gm);
|
|
422
|
+
let matches = variableName.match(/^[a-zA-Z_]+.*$/gm);
|
|
416
423
|
// console.log("matches:", matches)
|
|
417
424
|
if (matches !== null) {
|
|
418
425
|
return true;
|
package/index.js
CHANGED
|
@@ -102,8 +102,11 @@ router.post('/ext/:botid', async (req, res) => {
|
|
|
102
102
|
if (log) {console.log("bot found:", JSON.stringify(bot));}
|
|
103
103
|
|
|
104
104
|
let intentsMachine;
|
|
105
|
+
let backupMachine;
|
|
105
106
|
if (!staticBots) {
|
|
106
107
|
intentsMachine = IntentsMachineFactory.getMachine(bot, botId, projectId, log);
|
|
108
|
+
backupMachine = IntentsMachineFactory.getBackupMachine(bot, botId, projectId, log);
|
|
109
|
+
console.log("Created backupMachine:", backupMachine);
|
|
107
110
|
}
|
|
108
111
|
else {
|
|
109
112
|
intentsMachine = {}
|
|
@@ -132,6 +135,7 @@ router.post('/ext/:botid', async (req, res) => {
|
|
|
132
135
|
const chatbot = new TiledeskChatbot({
|
|
133
136
|
botsDataSource: botsDS,
|
|
134
137
|
intentsFinder: intentsMachine,
|
|
138
|
+
backupIntentsFinder: backupMachine,
|
|
135
139
|
botId: botId,
|
|
136
140
|
bot: bot,
|
|
137
141
|
token: token,
|
|
@@ -257,7 +261,60 @@ async function updateRequestVariables(chatbot, message, projectId, requestId) {
|
|
|
257
261
|
}
|
|
258
262
|
if (message.text && message.sender !== "_tdinternal") {
|
|
259
263
|
await chatbot.addParameter(TiledeskChatbotConst.REQ_LAST_USER_TEXT_KEY, message.text);
|
|
264
|
+
await chatbot.addParameter("lastUserMessageType", message.type);
|
|
265
|
+
await chatbot.addParameter("lastUserMessage", lastUserMessageFrom(message)); // JSON TYPE *NEW
|
|
266
|
+
// get image
|
|
267
|
+
if (message.type && message.type === "image" && message.metadata) {
|
|
268
|
+
// "text": "\nimage text",
|
|
269
|
+
// "id_project": "65203e12f8c0cf002cf4110b",
|
|
270
|
+
// "createdBy": "8ac52a30-133f-4ee1-8b4b-96055bb81757",
|
|
271
|
+
// "metadata": {
|
|
272
|
+
// "height": 905,
|
|
273
|
+
// "name": "tiledesk_Open graph_general.png",
|
|
274
|
+
// "src": "https://firebasestorage.googleapis.com/v0/b/chat21-pre-01.appspot.com/o/public%2Fimages%2F8ac52a30-133f-4ee1-8b4b-96055bb81757%2Fda5bbc8d-5174-49a8-a041-3d9355242da5%2Ftiledesk_Open%20graph_general.png?alt=media&token=be82fecb-3cd1-45b9-a135-c2c57a932862",
|
|
275
|
+
// "type": "image/png",
|
|
276
|
+
// "uid": "lo68iyq5",
|
|
277
|
+
// "width": 1724
|
|
278
|
+
// }
|
|
279
|
+
if (message.metadata.src) {
|
|
280
|
+
await chatbot.addParameter("lastUserImageURL", message.metadata.src);
|
|
281
|
+
await chatbot.addParameter("lastUserImageName", message.metadata.name);
|
|
282
|
+
await chatbot.addParameter("lastUserImageWidth", message.metadata.width);
|
|
283
|
+
await chatbot.addParameter("lastUserImageHeight", message.metadata.height);
|
|
284
|
+
await chatbot.addParameter("lastUserImageType", message.metadata.type);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
await chatbot.addParameter("lastUserImageURL", null);
|
|
289
|
+
await chatbot.addParameter("lastUserImageName", null);
|
|
290
|
+
await chatbot.addParameter("lastUserImageWidth", null);
|
|
291
|
+
await chatbot.addParameter("lastUserImageHeight", null);
|
|
292
|
+
await chatbot.addParameter("lastUserImageType", null);
|
|
293
|
+
}
|
|
294
|
+
// get document
|
|
295
|
+
if (message.type && message.type === "file" && message.metadata) {
|
|
296
|
+
// "type": "file",
|
|
297
|
+
// "text": "[LIBRETTO-WEB-ISTRUZIONI-GENITORI.pdf](https://firebasestorage.googleapis.com/v0/b/chat21-pre-01.appspot.com/o/public%2Fimages%2F8ac52a30-133f-4ee1-8b4b-96055bb81757%2F502265ee-4f4a-47a4-9375-172bb0e6bf39%2FLIBRETTO-WEB-ISTRUZIONI-GENITORI.pdf?alt=media&token=a09d065a-9b56-4507-8960-344cc294e4d1)\nistruzioni",
|
|
298
|
+
// "metadata": {
|
|
299
|
+
// "name": "LIBRETTO-WEB-ISTRUZIONI-GENITORI.pdf",
|
|
300
|
+
// "src": "https://firebasestorage.googleapis.com/v0/b/chat21-pre-01.appspot.com/o/public%2Fimages%2F8ac52a30-133f-4ee1-8b4b-96055bb81757%2F502265ee-4f4a-47a4-9375-172bb0e6bf39%2FLIBRETTO-WEB-ISTRUZIONI-GENITORI.pdf?alt=media&token=a09d065a-9b56-4507-8960-344cc294e4d1",
|
|
301
|
+
// "type": "application/pdf",
|
|
302
|
+
// "uid": "lo68oz8i"
|
|
303
|
+
// }
|
|
304
|
+
if (message.metadata.src) {
|
|
305
|
+
await chatbot.addParameter("lastUserDocumentURL", message.metadata.src);
|
|
306
|
+
await chatbot.addParameter("lastUserDocumentName", message.metadata.name);
|
|
307
|
+
await chatbot.addParameter("lastUserDocumentType", message.metadata.type);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
await chatbot.addParameter("lastUserDocumentURL", null);
|
|
312
|
+
await chatbot.addParameter("lastUserDocumentName", null);
|
|
313
|
+
await chatbot.addParameter("lastUserDocumentType", null);
|
|
314
|
+
}
|
|
260
315
|
}
|
|
316
|
+
|
|
317
|
+
|
|
261
318
|
await chatbot.addParameter(TiledeskChatbotConst.REQ_LAST_MESSAGE_ID_KEY, messageId);
|
|
262
319
|
if (message.request && message.request.location && message.request.location.country) {
|
|
263
320
|
await chatbot.addParameter(TiledeskChatbotConst.REQ_COUNTRY_KEY, message.request.location.country);
|
|
@@ -325,7 +382,7 @@ async function updateRequestVariables(chatbot, message, projectId, requestId) {
|
|
|
325
382
|
}
|
|
326
383
|
}
|
|
327
384
|
if (chatbot.log) {
|
|
328
|
-
console.log("tdcache:", chatbot.tdcache);
|
|
385
|
+
// console.log("tdcache:", chatbot.tdcache);
|
|
329
386
|
console.log("requestId:", requestId);
|
|
330
387
|
console.log("KEY:", TiledeskChatbotConst.REQ_PROJECT_ID_KEY);
|
|
331
388
|
let proj_ = await TiledeskChatbot.getParameterStatic(chatbot.tdcache, requestId, TiledeskChatbotConst.REQ_PROJECT_ID_KEY);
|
|
@@ -366,6 +423,23 @@ async function updateRequestVariables(chatbot, message, projectId, requestId) {
|
|
|
366
423
|
// }
|
|
367
424
|
}
|
|
368
425
|
|
|
426
|
+
function lastUserMessageFrom(msg) {
|
|
427
|
+
let message = {};
|
|
428
|
+
message["senderFullname"] = msg["senderFullname"]; // ex. "Bot"
|
|
429
|
+
message["type"] = msg["type"]; // ex. "text",
|
|
430
|
+
message["channel_type"] = msg["channel_type"]; // ex. "group",
|
|
431
|
+
message["status"] = msg["status"]; // ex. 0,
|
|
432
|
+
message["id"] = msg["_id"]; // ex. "6538cda46cb4d8002cf2317a",
|
|
433
|
+
message["sender"] = msg["sender"]; // ex. "system",
|
|
434
|
+
message["recipient"] = msg["recipient"]; // ex. "support-group-65203e12f8c0cf002cf4110b-4066a69c8b464646a3ff25f9f41575bb",
|
|
435
|
+
message["text"] = msg["text"]; // ex. "\\start",
|
|
436
|
+
message["createdBy"] = msg["createdBy"]; // ex. "system",
|
|
437
|
+
message["attributes"] = msg["attributes"]; // ex. { "subtype": "info" }
|
|
438
|
+
message["metadata"] = msg["metadata"];
|
|
439
|
+
message["channel"] = msg["channel"]; // ex. { "name": "chat21" }
|
|
440
|
+
return message;
|
|
441
|
+
}
|
|
442
|
+
|
|
369
443
|
function actionsToDirectives(actions) {
|
|
370
444
|
let directives = [];
|
|
371
445
|
if (actions && actions.length > 0) {
|
|
@@ -13,12 +13,18 @@ class IntentsMachineFactory {
|
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
else {
|
|
16
|
-
console.log("bot.intentsEngine is null");
|
|
17
16
|
if (log) {console.log("Setting MongodbIntentsMachine with bot:", JSON.stringify(bot));}
|
|
18
17
|
machine = new MongodbIntentsMachine({projectId: projectId, language: bot.language, log});
|
|
19
18
|
}
|
|
20
19
|
return machine;
|
|
21
20
|
}
|
|
21
|
+
|
|
22
|
+
static getBackupMachine(bot, botId, projectId, log) {
|
|
23
|
+
let machine;
|
|
24
|
+
if (log) {console.log("Setting MongodbIntentsMachine as Backup Intents Machine on bot:", JSON.stringify(bot));}
|
|
25
|
+
machine = new MongodbIntentsMachine({projectId: projectId, language: bot.language, log});
|
|
26
|
+
return machine;
|
|
27
|
+
}
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
module.exports = { IntentsMachineFactory }
|
|
@@ -29,6 +29,7 @@ class TiledeskChatbot {
|
|
|
29
29
|
}
|
|
30
30
|
this.botsDataSource = config.botsDataSource;
|
|
31
31
|
this.intentsFinder = config.intentsFinder;
|
|
32
|
+
this.backupIntentsFinder = config.backupIntentsFinder;
|
|
32
33
|
this.botId = config.botId;
|
|
33
34
|
this.bot = config.bot;
|
|
34
35
|
this.token = config.token;
|
|
@@ -79,7 +80,7 @@ class TiledeskChatbot {
|
|
|
79
80
|
if (this.log) {console.log("RESETTING LOCKED INTENT. Intent was explicitly invoked with an action:", message.attributes.action);}
|
|
80
81
|
await this.unlockIntent(this.requestId);
|
|
81
82
|
await this.unlockAction(this.requestId);
|
|
82
|
-
if (this.log) {console.log("RESET LOCKED INTENT. Intent was explicitly
|
|
83
|
+
if (this.log) {console.log("RESET LOCKED INTENT. Intent was explicitly invoked with an action:", message.attributes.action);}
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
catch(error) {
|
|
@@ -151,7 +152,7 @@ class TiledeskChatbot {
|
|
|
151
152
|
|
|
152
153
|
let explicit_intent_name = null;
|
|
153
154
|
// Explicit intent invocation
|
|
154
|
-
if (message.text.startsWith("/")) {
|
|
155
|
+
if (message.text && message.text.startsWith("/")) {
|
|
155
156
|
if (this.log) {console.log("Intent was explicitly invoked:", message.text);}
|
|
156
157
|
let intent_name = message.text.substring(message.text.indexOf("/") + 1);
|
|
157
158
|
if (this.log) {console.log("Invoked Intent:", intent_name);}
|
|
@@ -274,16 +275,24 @@ class TiledeskChatbot {
|
|
|
274
275
|
return;
|
|
275
276
|
}
|
|
276
277
|
else { // NLP
|
|
277
|
-
if (this.log) {console.log("Chatbot NLP
|
|
278
|
+
if (this.log) {console.log("Chatbot NLP decoding intent...");}
|
|
278
279
|
let intents;
|
|
279
280
|
try {
|
|
280
281
|
intents = await this.intentsFinder.decode(this.botId, message.text);
|
|
282
|
+
if (this.log) {console.log("Tiledesk AI intents found:", intents);}
|
|
281
283
|
}
|
|
282
284
|
catch(error) {
|
|
283
|
-
console.error("An error occurred:", error);
|
|
285
|
+
console.error("An error occurred on IntentsFinder.decode() (/model/parse error):", error.message);
|
|
286
|
+
// recover on fulltext
|
|
287
|
+
if (this.backupIntentsFinder) {
|
|
288
|
+
if (this.log) {console.log("using backup Finder:", this.backupIntentsFinder);}
|
|
289
|
+
intents = await this.backupIntentsFinder.decode(this.botId, message.text);
|
|
290
|
+
if (this.log) {console.log("Got intents from backup finder:", intents);}
|
|
291
|
+
}
|
|
284
292
|
}
|
|
285
|
-
if (this.log) {console.log("NLP
|
|
293
|
+
if (this.log) {console.log("NLP intents found:", intents);}
|
|
286
294
|
if (intents && intents.length > 0) {
|
|
295
|
+
console.log("Matching intents found.");
|
|
287
296
|
// let faq = await this.botsDataSource.getByIntentDisplayName(this.botId, intents[0].intent_display_name);
|
|
288
297
|
let faq = await this.botsDataSource.getByIntentDisplayNameCache(this.botId, intents[0].intent_display_name, this.tdcache);
|
|
289
298
|
let reply;
|
|
@@ -233,6 +233,25 @@ class TiledeskChatbotUtil {
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
static totalMessageWait(message) {
|
|
237
|
+
if (!message) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
console.log("compute delay...", message)
|
|
241
|
+
if (message.attributes.commands.length > 0) {
|
|
242
|
+
console.log("going on delay")
|
|
243
|
+
let commands = message.attributes.commands;
|
|
244
|
+
console.log("got commands", commands)
|
|
245
|
+
let totalWaitTime = 0;
|
|
246
|
+
for (let i = commands.length - 1; i >= 0; i--) {
|
|
247
|
+
if (commands[i].type === "wait") { // is a wait
|
|
248
|
+
totalWaitTime += commands[i].time;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return totalWaitTime;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
236
255
|
static fillCommandAttachments(command, variables, log) {
|
|
237
256
|
if (log) {
|
|
238
257
|
console.log("filling command button:", JSON.stringify(command))
|
|
@@ -37,13 +37,14 @@ class TiledeskIntentsMachine {
|
|
|
37
37
|
};
|
|
38
38
|
this.myrequest(
|
|
39
39
|
HTTPREQUEST,
|
|
40
|
-
|
|
40
|
+
(err, resbody) => {
|
|
41
41
|
if (err) {
|
|
42
|
-
console.error("error:", err)
|
|
42
|
+
// console.error("An error occurred on /model/parse:", err)
|
|
43
43
|
reject(err);
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
46
|
-
|
|
46
|
+
console.log("Tiledesk AI replied:", resbody)
|
|
47
|
+
resolve(this.translateForTiledesk(resbody));
|
|
47
48
|
}
|
|
48
49
|
}, false
|
|
49
50
|
);
|
|
@@ -83,7 +84,7 @@ class TiledeskIntentsMachine {
|
|
|
83
84
|
// }
|
|
84
85
|
let intents_array = intents.intent_ranking;
|
|
85
86
|
let tiledesk_intents = [];
|
|
86
|
-
for (i = 0; i < intents_array.length; i++) {
|
|
87
|
+
for (let i = 0; i < intents_array.length; i++) {
|
|
87
88
|
let td_intent = {
|
|
88
89
|
"intent_display_name": intents_array[i].name
|
|
89
90
|
}
|
|
@@ -123,7 +124,7 @@ class TiledeskIntentsMachine {
|
|
|
123
124
|
}
|
|
124
125
|
})
|
|
125
126
|
.catch( (error) => {
|
|
126
|
-
console.error("An error occurred:", error);
|
|
127
|
+
// console.error("An error occurred:", error);
|
|
127
128
|
if (callback) {
|
|
128
129
|
callback(error, null, null);
|
|
129
130
|
}
|
package/package.json
CHANGED
|
@@ -106,7 +106,18 @@ class DirReply {
|
|
|
106
106
|
console.error("Error sending reply:", err);
|
|
107
107
|
}
|
|
108
108
|
if (this.log) {console.log("Reply message sent");}
|
|
109
|
-
|
|
109
|
+
const delay = TiledeskChatbotUtil.totalMessageWait(message);
|
|
110
|
+
console.log("got total delay:", delay)
|
|
111
|
+
if (delay > 0 && delay <= 30000) { // prevent long delays
|
|
112
|
+
setTimeout(() => {
|
|
113
|
+
console.log("callback after delay")
|
|
114
|
+
callback();
|
|
115
|
+
}, delay);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log("invalid delay.")
|
|
119
|
+
callback();
|
|
120
|
+
}
|
|
110
121
|
});
|
|
111
122
|
|
|
112
123
|
// this.sendSupportMessage(
|