@tiledesk/tiledesk-voice-twilio-connector 0.1.8 → 0.1.10
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 +5 -38
- package/package.json +1 -1
- package/template/configure.html +3 -3
- package/tiledesk/TiledeskTwilioTranslator.js +30 -12
- package/tiledesk/constants.js +12 -1
- package/tiledesk/utils-message.js +36 -1
package/index.js
CHANGED
|
@@ -274,44 +274,11 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
274
274
|
//update delayIndex for wait command message time
|
|
275
275
|
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
276
276
|
}
|
|
277
|
-
|
|
278
|
-
/*
|
|
279
|
-
if(usertext === '' || !usertext){
|
|
280
|
-
//get message from queue
|
|
281
|
-
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
282
|
-
winston.debug('QUEUE --> ',queue.length)
|
|
283
|
-
if(queue.length === 0){
|
|
284
|
-
//CASE: queue is empty --> generate Tiledesk wait message
|
|
285
|
-
message = await tdChannel.generateWaitTdMessage(ani, callId)
|
|
286
|
-
winston.debug('QUEUE is empty--> ',queue)
|
|
287
|
-
}else{
|
|
288
|
-
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue
|
|
289
|
-
message = queue[0]
|
|
290
|
-
winston.debug('QUEUE --> ',queue[0])
|
|
291
|
-
//remove message from queue
|
|
292
|
-
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
293
|
-
|
|
294
|
-
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
295
|
-
winston.debug('QUEUE after remove --> ',queue.length)
|
|
296
|
-
}
|
|
297
|
-
}else{
|
|
298
|
-
//send message to tiledesk
|
|
299
|
-
let tiledeskMessage= {
|
|
300
|
-
text:usertext,
|
|
301
|
-
senderFullname: ani,
|
|
302
|
-
type: 'text',
|
|
303
|
-
channel: { name: CHANNEL_NAME }
|
|
304
|
-
};
|
|
305
|
-
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
306
|
-
winston.debug("message sent : ", tdMessage);
|
|
307
|
-
//generate Tiledesk wait message
|
|
308
|
-
message = await tdChannel.generateWaitTdMessage(ani, callId)
|
|
309
|
-
}
|
|
310
|
-
*/
|
|
277
|
+
|
|
311
278
|
|
|
312
279
|
// convert response to vxml
|
|
313
280
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
314
|
-
winston.debug("(
|
|
281
|
+
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
315
282
|
|
|
316
283
|
// Render the response as XML in reply to the webhook request
|
|
317
284
|
res.set('Content-Type', 'text/xml');
|
|
@@ -962,12 +929,12 @@ async function startApp(settings, callback) {
|
|
|
962
929
|
}
|
|
963
930
|
|
|
964
931
|
if (!settings.API_URL) {
|
|
965
|
-
|
|
966
|
-
|
|
932
|
+
let port = process.env.PORT || 3000;
|
|
933
|
+
API_URL = 'http://localhost:' + port;
|
|
967
934
|
} else {
|
|
968
935
|
API_URL = settings.API_URL;
|
|
969
|
-
winston.info("(voice) API_URL: " + API_URL);
|
|
970
936
|
}
|
|
937
|
+
winston.info("(voice) API_URL: " + API_URL)
|
|
971
938
|
|
|
972
939
|
if (!settings.BASE_URL) {
|
|
973
940
|
winston.error("(voice) BASE_URL is mandatory. Exit...");
|
package/package.json
CHANGED
package/template/configure.html
CHANGED
|
@@ -112,15 +112,15 @@
|
|
|
112
112
|
<ul>
|
|
113
113
|
<div class="list-element">
|
|
114
114
|
<img src="https://cdn-icons-png.flaticon.com/512/1443/1443000.png" width="20" height="20" style="margin-right: 10px;"/>
|
|
115
|
-
<h7>
|
|
115
|
+
<h7>Real-time notifications: get instant alerts on your Tiledesk account whenever a client calls you, ensuring you never miss a message</h7>
|
|
116
116
|
</div>
|
|
117
117
|
<div class="list-element">
|
|
118
118
|
<img src="https://cdn-icons-png.flaticon.com/512/1443/1443000.png" width="20" height="20" style="margin-right: 10px;"/>
|
|
119
|
-
<h7>
|
|
119
|
+
<h7>Effortless responses: Quickly reply to clients directly from your Tiledesk account with ease</h7>
|
|
120
120
|
</div>
|
|
121
121
|
<div class="list-element">
|
|
122
122
|
<img src="https://cdn-icons-png.flaticon.com/512/1443/1443000.png" width="20" height="20" style="margin-right: 10px;"/>
|
|
123
|
-
<h7>
|
|
123
|
+
<h7>Streamlined messaging: Manage multiple conversations simultaneously, all from one convenient platform</h7>
|
|
124
124
|
</div>
|
|
125
125
|
</ul>
|
|
126
126
|
{{/unless}}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const { v4: uuidv4 } = require("uuid");
|
|
2
2
|
const winston = require("../winston");
|
|
3
3
|
const xmlbuilder = require("xmlbuilder");
|
|
4
|
+
const querystring = require("querystring");
|
|
5
|
+
|
|
4
6
|
const utils = require("./utils-message.js");
|
|
5
7
|
const MENU_CHOICE = require("./constants.js").MENU_CHOICE;
|
|
6
8
|
const WAIT_MESSAGE = require("./constants.js").WAIT_MESSAGE;
|
|
@@ -9,6 +11,8 @@ const SETTING_MESSAGE = require('./constants').SETTING_MESSAGE
|
|
|
9
11
|
const CHANNEL_NAME = require('./constants').CHANNEL_NAME
|
|
10
12
|
|
|
11
13
|
const TYPE_ACTION_VXML = require('./constants').TYPE_ACTION_VXML
|
|
14
|
+
const TYPE_MESSAGE = require('./constants').TYPE_MESSAGE
|
|
15
|
+
const INFO_MESSAGE_TYPE = require('./constants').INFO_MESSAGE_TYPE
|
|
12
16
|
|
|
13
17
|
class TiledeskTwilioTranslator {
|
|
14
18
|
/**
|
|
@@ -63,6 +67,14 @@ class TiledeskTwilioTranslator {
|
|
|
63
67
|
vxmlAttributes = this.manageVoiceAttributes(msg, vxmlAttributes)
|
|
64
68
|
//const header = this.headerVXML(xml, vxmlAttributes);
|
|
65
69
|
|
|
70
|
+
//MANAGE CLOSE info message
|
|
71
|
+
const isInfoSupport = utils.messageType(TYPE_MESSAGE.INFO_SUPPORT, msg)
|
|
72
|
+
winston.verbose("[TiledeskVXMLTranslator] isInfoSupport::"+ isInfoSupport);
|
|
73
|
+
if(isInfoSupport && utils.infoMessageType(msg) === INFO_MESSAGE_TYPE.CHAT_CLOSED){
|
|
74
|
+
const hangUp = this.hangupCall(xml)
|
|
75
|
+
return hangUp;
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
|
|
67
79
|
if (msg.attributes && msg.attributes.commands && msg.attributes.commands.length > 0 ) {
|
|
68
80
|
const commands = msg.attributes.commands;
|
|
@@ -218,6 +230,12 @@ class TiledeskTwilioTranslator {
|
|
|
218
230
|
return buttons;
|
|
219
231
|
}
|
|
220
232
|
|
|
233
|
+
async hangupCall(rootEle){
|
|
234
|
+
rootEle.ele("Hangup").up()
|
|
235
|
+
|
|
236
|
+
return rootEle.end({ pretty: true });
|
|
237
|
+
}
|
|
238
|
+
|
|
221
239
|
|
|
222
240
|
async delayVXMLConverter(rootEle, message, xmlAttributes){
|
|
223
241
|
const command = message.attributes.commands[0]
|
|
@@ -231,7 +249,7 @@ class TiledeskTwilioTranslator {
|
|
|
231
249
|
|
|
232
250
|
const prompt = this.promptVXML(rootEle, message, xmlAttributes);
|
|
233
251
|
|
|
234
|
-
const queryUrl = '?intentName='+ xmlAttributes.intentName + '&previousIntentTimestamp='+Date.now();
|
|
252
|
+
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + '&previousIntentTimestamp='+Date.now();
|
|
235
253
|
rootEle.ele("Redirect", {Method: "POST"}, this.BASE_URL + '/nextblock/' + xmlAttributes.callSid + queryUrl).up()
|
|
236
254
|
//prompt.ele("submit", { fetchhint: "safe", expr: "proxyBaseUrl +'/nextblock/' + session.connection.calltoken", method: "post", namelist: "usertext session intentName previousIntentTimestamp" });
|
|
237
255
|
|
|
@@ -242,14 +260,14 @@ class TiledeskTwilioTranslator {
|
|
|
242
260
|
/** DONE **/
|
|
243
261
|
async speechFormVXMLConverter(rootEle, message, xmlAttributes) {
|
|
244
262
|
|
|
245
|
-
|
|
263
|
+
|
|
246
264
|
const gather = rootEle.ele("Gather", { input: "speech"})
|
|
247
265
|
|
|
248
266
|
if(xmlAttributes && xmlAttributes.noInputTimeout){
|
|
249
267
|
gather.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
|
|
250
268
|
}
|
|
251
269
|
|
|
252
|
-
const queryUrl = '?intentName='+ xmlAttributes.intentName + "&previousIntentTimestamp="+Date.now();
|
|
270
|
+
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
|
|
253
271
|
gather.att("action", this.BASE_URL + '/nextblock/' + xmlAttributes.callSid + queryUrl)
|
|
254
272
|
.att("method", "POST")
|
|
255
273
|
.att("language", xmlAttributes.voiceLanguage)
|
|
@@ -261,9 +279,9 @@ class TiledeskTwilioTranslator {
|
|
|
261
279
|
if(handleNoInputNoMatchQuery && handleNoInputNoMatchQuery.queryNoInput){
|
|
262
280
|
rootEle.ele("Redirect", {}, this.BASE_URL + '/handle/' + xmlAttributes.callSid + '/no_input?'+ handleNoInputNoMatchQuery.queryNoInput)
|
|
263
281
|
}
|
|
264
|
-
*/
|
|
265
282
|
|
|
266
|
-
|
|
283
|
+
|
|
284
|
+
/*let queryUrl = '?intentName='+ xmlAttributes.intentName + "&previousIntentTimestamp="+Date.now();
|
|
267
285
|
const prompt = this.promptVXML(rootEle, message, xmlAttributes);
|
|
268
286
|
|
|
269
287
|
const record = rootEle.ele("Record", { playBeep: "false"})
|
|
@@ -281,7 +299,7 @@ class TiledeskTwilioTranslator {
|
|
|
281
299
|
.att("method", "POST")
|
|
282
300
|
.att("trim", "trim-silence")
|
|
283
301
|
|
|
284
|
-
|
|
302
|
+
*/
|
|
285
303
|
|
|
286
304
|
|
|
287
305
|
return rootEle.end({ pretty: true });
|
|
@@ -296,7 +314,7 @@ class TiledeskTwilioTranslator {
|
|
|
296
314
|
options.forEach((option) => menu_options += option.value + ':' + option.action.substring(1) + ';')
|
|
297
315
|
|
|
298
316
|
|
|
299
|
-
let queryUrl = '?intentName='+ xmlAttributes.intentName + '&previousIntentTimestamp='+Date.now() + '&menu_options=' + menu_options;
|
|
317
|
+
let queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + '&previousIntentTimestamp='+Date.now() + '&menu_options=' + menu_options;
|
|
300
318
|
const handleNoInputNoMatchQuery = await this.handleNoInputNoMatch(rootEle, message, xmlAttributes);
|
|
301
319
|
if(handleNoInputNoMatchQuery && handleNoInputNoMatchQuery.queryNoMatch){
|
|
302
320
|
queryUrl += '&button_action='+ handleNoInputNoMatchQuery.queryNoMatch.split('button_action=')[1]
|
|
@@ -326,7 +344,7 @@ class TiledeskTwilioTranslator {
|
|
|
326
344
|
|
|
327
345
|
const gather = rootEle.ele("Gather", { input: "dtmf"})
|
|
328
346
|
|
|
329
|
-
const queryUrl = '?intentName='+ xmlAttributes.intentName + "&previousIntentTimestamp="+Date.now();
|
|
347
|
+
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
|
|
330
348
|
gather.att("timeout", xmlAttributes.noInputTimeout/1000 )
|
|
331
349
|
.att("action", this.BASE_URL + '/menublock/' + xmlAttributes.callSid + queryUrl)
|
|
332
350
|
.att("method", "POST")
|
|
@@ -402,8 +420,8 @@ class TiledeskTwilioTranslator {
|
|
|
402
420
|
value: 'no_input'
|
|
403
421
|
}
|
|
404
422
|
|
|
405
|
-
|
|
406
|
-
queryNoInput = 'intentName='+ attributes.intentName + '&previousIntentTimestamp='+Date.now() + '&button_action='+button_noIput.action.substring(1);
|
|
423
|
+
|
|
424
|
+
queryNoInput = 'intentName='+ querystring.encode(attributes.intentName) + '&previousIntentTimestamp='+Date.now() + '&button_action='+button_noIput.action.substring(1);
|
|
407
425
|
//rootEle.ele("Redirect", {}, this.BASE_URL + '/handle/' + attributes.callSid + '/no_input'+ queryNoInput)
|
|
408
426
|
|
|
409
427
|
|
|
@@ -430,7 +448,7 @@ class TiledeskTwilioTranslator {
|
|
|
430
448
|
value: 'no_match'
|
|
431
449
|
}
|
|
432
450
|
|
|
433
|
-
queryNoMatch = 'intentName='+ attributes.intentName + '&previousIntentTimestamp='+Date.now() + '&button_action='+button_noMatch.action.substring(1); //remove '#' from intentId because is not a valid char for XML lang
|
|
451
|
+
queryNoMatch = 'intentName='+ querystring.encode(attributes.intentName) + '&previousIntentTimestamp='+Date.now() + '&button_action='+button_noMatch.action.substring(1); //remove '#' from intentId because is not a valid char for XML lang
|
|
434
452
|
//rootEle.ele("Redirect", {}, this.BASE_URL + '/handle/' + attributes.callSid + '/no_match'+ queryNoMatch)
|
|
435
453
|
|
|
436
454
|
/*element.ele("nomatch")
|
|
@@ -518,7 +536,7 @@ class TiledeskTwilioTranslator {
|
|
|
518
536
|
|
|
519
537
|
const transfer = rootEle.ele("Dial");
|
|
520
538
|
|
|
521
|
-
let queryUrl = '?intentName='+ attributes.intentName + '&previousIntentTimestamp='+Date.now()
|
|
539
|
+
let queryUrl = '?intentName='+ querystring.encode(attributes.intentName) + '&previousIntentTimestamp='+Date.now()
|
|
522
540
|
/* <-- transfer error --> */
|
|
523
541
|
if(lastCommand.settings && lastCommand.settings.falseIntent){
|
|
524
542
|
queryUrl += '&' + 'button_success=' + attributes.trueIntent.substring(1)
|
package/tiledesk/constants.js
CHANGED
|
@@ -7,7 +7,18 @@ module.exports = {
|
|
|
7
7
|
FRAME: 'frame',
|
|
8
8
|
GALLERY: 'gallery',
|
|
9
9
|
INFO: 'info',
|
|
10
|
-
SUPPORT_INFO: 'support/info'
|
|
10
|
+
SUPPORT_INFO: 'support/info',
|
|
11
|
+
INFO_SUPPORT: 'info/support'
|
|
12
|
+
},
|
|
13
|
+
INFO_MESSAGE_TYPE: {
|
|
14
|
+
CHAT_REOPENED : 'CHAT_REOPENED',
|
|
15
|
+
CHAT_CLOSED : 'CHAT_CLOSED',
|
|
16
|
+
MEMBER_JOINED_GROUP : 'MEMBER_JOINED_GROUP',
|
|
17
|
+
MEMBER_LEFT_GROUP : "MEMBER_LEFT_GROUP",
|
|
18
|
+
LEAD_UPDATED : "LEAD_UPDATED",
|
|
19
|
+
TOUCHING_OPERATOR : "TOUCHING_OPERATOR",
|
|
20
|
+
LIVE_PAGE : "LIVE_PAGE",
|
|
21
|
+
PLAN_EXPIRED : "PLAN_EXPIRED"
|
|
11
22
|
},
|
|
12
23
|
MESSAGE_TYPE_MINE: 'MINE',
|
|
13
24
|
MESSAGE_TYPE_OTHERS: 'OTHERS',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
const TYPE_MESSAGE = require('./constants').TYPE_MESSAGE
|
|
3
|
+
const INFO_MESSAGE_TYPE = require('./constants').INFO_MESSAGE_TYPE
|
|
3
4
|
const MESSAGE_TYPE_MINE = require('./constants').MESSAGE_TYPE_MINE
|
|
4
5
|
const MESSAGE_TYPE_OTHERS = require('./constants').MESSAGE_TYPE_OTHERS
|
|
5
6
|
|
|
@@ -38,6 +39,13 @@ function isInfo(message) {
|
|
|
38
39
|
return false;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
function isInfoSupport(message) {
|
|
43
|
+
if (message && message.attributes && message.attributes.subtype === TYPE_MESSAGE.INFO_SUPPORT) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
function isMine(message, user) {
|
|
42
50
|
if (message && message.sender && message.sender === user.userid) {
|
|
43
51
|
return true;
|
|
@@ -50,6 +58,9 @@ function messageType(msgType, message, user) {
|
|
|
50
58
|
if (msgType === TYPE_MESSAGE.INFO) {
|
|
51
59
|
return this.isInfo(message);
|
|
52
60
|
}
|
|
61
|
+
if (msgType === TYPE_MESSAGE.INFO_SUPPORT) {
|
|
62
|
+
return this.isInfoSupport(message);
|
|
63
|
+
}
|
|
53
64
|
if (msgType === MESSAGE_TYPE_MINE) {
|
|
54
65
|
return isMine(message, user);
|
|
55
66
|
}
|
|
@@ -61,4 +72,28 @@ function messageType(msgType, message, user) {
|
|
|
61
72
|
}
|
|
62
73
|
}
|
|
63
74
|
|
|
64
|
-
|
|
75
|
+
function infoMessageType(msg){
|
|
76
|
+
if(msg && msg.attributes.messagelabel && msg.attributes.messagelabel.key === INFO_MESSAGE_TYPE.MEMBER_JOINED_GROUP){
|
|
77
|
+
return INFO_MESSAGE_TYPE.MEMBER_JOINED_GROUP
|
|
78
|
+
}
|
|
79
|
+
if(msg && msg.attributes.messagelabel && msg.attributes.messagelabel.key === INFO_MESSAGE_TYPE.CHAT_CLOSED){
|
|
80
|
+
return INFO_MESSAGE_TYPE.CHAT_CLOSED
|
|
81
|
+
}
|
|
82
|
+
if(msg && msg.attributes.messagelabel && msg.attributes.messagelabel.key === INFO_MESSAGE_TYPE.CHAT_REOPENED){
|
|
83
|
+
return INFO_MESSAGE_TYPE.CHAT_REOPENED
|
|
84
|
+
}
|
|
85
|
+
if(msg && msg.attributes.messagelabel && msg.attributes.messagelabel.key === INFO_MESSAGE_TYPE.TOUCHING_OPERATOR){
|
|
86
|
+
return INFO_MESSAGE_TYPE.TOUCHING_OPERATOR
|
|
87
|
+
}
|
|
88
|
+
if(msg && msg.attributes.messagelabel && msg.attributes.messagelabel.key === INFO_MESSAGE_TYPE.LEAD_UPDATED){
|
|
89
|
+
return INFO_MESSAGE_TYPE.LEAD_UPDATED
|
|
90
|
+
}
|
|
91
|
+
if(msg && msg.attributes.messagelabel && msg.attributes.messagelabel.key === INFO_MESSAGE_TYPE.MEMBER_LEFT_GROUP){
|
|
92
|
+
return INFO_MESSAGE_TYPE.MEMBER_LEFT_GROUP
|
|
93
|
+
}
|
|
94
|
+
if(msg && msg.attributes.messagelabel && msg.attributes.messagelabel.key === INFO_MESSAGE_TYPE.LIVE_PAGE){
|
|
95
|
+
return INFO_MESSAGE_TYPE.LIVE_PAGE
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {isCarousel, isImage, isFile, isAudio, isInfo, isInfoSupport, messageType, infoMessageType}
|