@tiledesk/tiledesk-voice-twilio-connector 0.1.26 → 0.1.27
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 +21 -51
- package/package.json +2 -1
- package/tiledesk/TiledeskTwilioTranslator.js +9 -8
- package/tiledesk/VoiceChannel.js +23 -4
- package/tiledesk/utils.js +10 -1
package/index.js
CHANGED
|
@@ -195,16 +195,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
195
195
|
conversation_id: conversation_id,
|
|
196
196
|
integrations: integrations
|
|
197
197
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
//for (const [key, value] of Object.entries(redis_data)){
|
|
202
|
-
// await redis_client.hSet('tiledesk:voice:'+callId, key, JSON.stringify(value))
|
|
203
|
-
//}
|
|
204
|
-
//await redis_client.expire('tiledesk:voice:'+callId, 86400)
|
|
205
|
-
|
|
206
|
-
await redis_client.set('tiledesk:voice:'+callSid+':session', JSON.stringify(session_data), {'EX': 86400});
|
|
207
|
-
|
|
198
|
+
voiceChannel.setSessionForCallId(callSid, session_data)
|
|
199
|
+
|
|
208
200
|
let tiledeskMessage= {
|
|
209
201
|
text:'/start',
|
|
210
202
|
senderFullname: from,
|
|
@@ -262,8 +254,7 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
|
|
|
262
254
|
let project_id, conversation_id, user;
|
|
263
255
|
let from, to;
|
|
264
256
|
|
|
265
|
-
let redis_data = await
|
|
266
|
-
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
257
|
+
let redis_data = await voiceChannel.getSessionForCallId(callSid)
|
|
267
258
|
if (!redis_data) {
|
|
268
259
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
269
260
|
}
|
|
@@ -345,16 +336,13 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
345
336
|
let confidence = req.body.Confidence
|
|
346
337
|
let callSid = req.params.callSid;
|
|
347
338
|
|
|
348
|
-
let sessionInfo;
|
|
349
339
|
let project_id, conversation_id, user;
|
|
350
340
|
let from, to;
|
|
351
341
|
|
|
352
|
-
let
|
|
353
|
-
|
|
354
|
-
if (!redis_data) {
|
|
342
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
343
|
+
if (!sessionInfo) {
|
|
355
344
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
356
345
|
}
|
|
357
|
-
sessionInfo = JSON.parse(redis_data)
|
|
358
346
|
project_id = sessionInfo.project_id;
|
|
359
347
|
from = sessionInfo.from;
|
|
360
348
|
to = sessionInfo.to;
|
|
@@ -465,7 +453,7 @@ async function getMessage(callSid, ani, project_id, conversation_id){
|
|
|
465
453
|
if (queue && queue.length > 0) {
|
|
466
454
|
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue and reset delayTime
|
|
467
455
|
message = queue[0]
|
|
468
|
-
winston.
|
|
456
|
+
winston.verbose('[getMessage] QUEUE --> '+ queue[0].text)
|
|
469
457
|
|
|
470
458
|
// remove message from queue and reset delayIndex
|
|
471
459
|
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
@@ -486,7 +474,7 @@ async function getMessage(callSid, ani, project_id, conversation_id){
|
|
|
486
474
|
}
|
|
487
475
|
|
|
488
476
|
message = queue[0]
|
|
489
|
-
winston.
|
|
477
|
+
winston.verbose(`[getMessage] Message received from subscription: ${message.text}`);
|
|
490
478
|
|
|
491
479
|
// remove message from queue and reset delayIndex
|
|
492
480
|
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
@@ -528,16 +516,13 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
528
516
|
let confidence = req.body.Confidence
|
|
529
517
|
let callSid = req.params.callSid;
|
|
530
518
|
|
|
531
|
-
let sessionInfo;
|
|
532
519
|
let project_id, conversation_id, user;
|
|
533
520
|
let from, to;
|
|
534
521
|
|
|
535
|
-
let
|
|
536
|
-
|
|
537
|
-
if (!redis_data) {
|
|
522
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
523
|
+
if (!sessionInfo) {
|
|
538
524
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
539
525
|
}
|
|
540
|
-
sessionInfo = JSON.parse(redis_data)
|
|
541
526
|
project_id = sessionInfo.project_id;
|
|
542
527
|
from = sessionInfo.from;
|
|
543
528
|
to = sessionInfo.to;
|
|
@@ -623,15 +608,13 @@ router.post('/record/action/:callSid/',async (req, res) => {
|
|
|
623
608
|
let start_call = new Date();
|
|
624
609
|
|
|
625
610
|
let callSid = req.body.CallSid;
|
|
626
|
-
let sessionInfo;
|
|
627
611
|
let project_id, conversation_id, user;
|
|
628
612
|
let from, to;
|
|
629
613
|
|
|
630
|
-
let
|
|
631
|
-
if (!
|
|
614
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
615
|
+
if (!sessionInfo) {
|
|
632
616
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
633
617
|
}
|
|
634
|
-
sessionInfo = JSON.parse(redis_data)
|
|
635
618
|
project_id = sessionInfo.project_id;
|
|
636
619
|
from = sessionInfo.from;
|
|
637
620
|
to = sessionInfo.to;
|
|
@@ -692,16 +675,13 @@ router.post('/record/callback/:callSid/',async (req, res) => {
|
|
|
692
675
|
let button_action = req.query.button_action ? '#' + req.query.button_action : '';
|
|
693
676
|
let previousIntentName = req.query.intentName || '';
|
|
694
677
|
|
|
695
|
-
|
|
696
|
-
let sessionInfo;
|
|
697
678
|
let project_id, conversation_id, user;
|
|
698
679
|
let from, to;
|
|
699
680
|
|
|
700
|
-
let
|
|
701
|
-
if (!
|
|
681
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
682
|
+
if (!sessionInfo) {
|
|
702
683
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
703
684
|
}
|
|
704
|
-
sessionInfo = JSON.parse(redis_data)
|
|
705
685
|
project_id = sessionInfo.project_id;
|
|
706
686
|
from = sessionInfo.from;
|
|
707
687
|
to = sessionInfo.to;
|
|
@@ -799,16 +779,13 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
799
779
|
winston.debug("(voice) button menu: ", button);
|
|
800
780
|
winston.debug("(voice) message_text menu: "+ message_text);
|
|
801
781
|
|
|
802
|
-
|
|
803
|
-
let sessionInfo;
|
|
804
782
|
let project_id, conversation_id, user;
|
|
805
783
|
let from, to;
|
|
806
784
|
|
|
807
|
-
let
|
|
808
|
-
if (!
|
|
785
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
786
|
+
if (!sessionInfo) {
|
|
809
787
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
810
788
|
}
|
|
811
|
-
sessionInfo = JSON.parse(redis_data)
|
|
812
789
|
project_id = sessionInfo.project_id;
|
|
813
790
|
from = sessionInfo.from;
|
|
814
791
|
to = sessionInfo.to;
|
|
@@ -878,15 +855,13 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
878
855
|
let button_action = '#' + req.query.button_action;
|
|
879
856
|
let previousIntentName = req.query.intentName;
|
|
880
857
|
|
|
881
|
-
let sessionInfo;
|
|
882
858
|
let project_id, conversation_id, user;
|
|
883
859
|
let from, to;
|
|
884
860
|
|
|
885
|
-
let
|
|
886
|
-
if (!
|
|
861
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
862
|
+
if (!sessionInfo) {
|
|
887
863
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
888
864
|
}
|
|
889
|
-
sessionInfo = JSON.parse(redis_data)
|
|
890
865
|
project_id = sessionInfo.project_id;
|
|
891
866
|
from = sessionInfo.from;
|
|
892
867
|
to = sessionInfo.to;
|
|
@@ -962,16 +937,13 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
962
937
|
let currentIntentName = req.query.intentName;
|
|
963
938
|
let currentIntentTimestamp = req.query.previousIntentTimestamp;
|
|
964
939
|
|
|
965
|
-
|
|
966
|
-
let sessionInfo;
|
|
967
940
|
let project_id, conversation_id, user;
|
|
968
941
|
let from, to;
|
|
969
942
|
|
|
970
|
-
let
|
|
971
|
-
if (!
|
|
943
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
944
|
+
if (!sessionInfo) {
|
|
972
945
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
973
946
|
}
|
|
974
|
-
sessionInfo = JSON.parse(redis_data)
|
|
975
947
|
project_id = sessionInfo.project_id;
|
|
976
948
|
from = sessionInfo.from;
|
|
977
949
|
to = sessionInfo.to;
|
|
@@ -1062,15 +1034,13 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
1062
1034
|
clearTimeout(messageTimeout)
|
|
1063
1035
|
}
|
|
1064
1036
|
|
|
1065
|
-
let sessionInfo;
|
|
1066
1037
|
let project_id, conversation_id, user;
|
|
1067
1038
|
let from, to;
|
|
1068
1039
|
|
|
1069
|
-
let
|
|
1070
|
-
if (!
|
|
1040
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
1041
|
+
if (!sessionInfo) {
|
|
1071
1042
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
1072
1043
|
}
|
|
1073
|
-
sessionInfo = JSON.parse(redis_data)
|
|
1074
1044
|
project_id = sessionInfo.project_id;
|
|
1075
1045
|
from = sessionInfo.from;
|
|
1076
1046
|
to = sessionInfo.to;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiledesk/tiledesk-voice-twilio-connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "Tiledesk VOICE Twilio connector",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Gabriele Panico",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"redis": "^4.6.13",
|
|
37
37
|
"redis-lock": "^1.0.0",
|
|
38
38
|
"redlock": "^4.2.0",
|
|
39
|
+
"remove-markdown": "^0.6.2",
|
|
39
40
|
"twilio": "^5.2.1",
|
|
40
41
|
"uuid": "^8.3.2",
|
|
41
42
|
"winston": "^3.3.3",
|
|
@@ -3,7 +3,8 @@ const winston = require("../winston");
|
|
|
3
3
|
const xmlbuilder = require("xmlbuilder");
|
|
4
4
|
const querystring = require("querystring");
|
|
5
5
|
|
|
6
|
-
const utils = require("./utils
|
|
6
|
+
const utils = require("./utils.js");
|
|
7
|
+
const utils_message = require("./utils-message.js");
|
|
7
8
|
const MENU_CHOICE = require("./constants.js").MENU_CHOICE;
|
|
8
9
|
const WAIT_MESSAGE = require("./constants.js").WAIT_MESSAGE;
|
|
9
10
|
const TEXT_MESSAGE = require("./constants.js").TEXT_MESSAGE;
|
|
@@ -123,9 +124,9 @@ class TiledeskTwilioTranslator {
|
|
|
123
124
|
|
|
124
125
|
|
|
125
126
|
//MANAGE CLOSE info message
|
|
126
|
-
const isInfoSupport =
|
|
127
|
+
const isInfoSupport = utils_message.messageType(TYPE_MESSAGE.INFO_SUPPORT, msg)
|
|
127
128
|
winston.debug("[TiledeskVXMLTranslator] isInfoSupport:"+ isInfoSupport);
|
|
128
|
-
if(isInfoSupport &&
|
|
129
|
+
if(isInfoSupport && utils_message.infoMessageType(msg) === INFO_MESSAGE_TYPE.CHAT_CLOSED){
|
|
129
130
|
const hangUp = this.hangupCall(xml)
|
|
130
131
|
return hangUp;
|
|
131
132
|
}
|
|
@@ -319,7 +320,6 @@ class TiledeskTwilioTranslator {
|
|
|
319
320
|
async speechFormVXMLConverter(rootEle, message, xmlAttributes) {
|
|
320
321
|
|
|
321
322
|
if(this.voiceProvider === VOICE_PROVIDER.TWILIO){
|
|
322
|
-
|
|
323
323
|
const gather = rootEle.ele("Gather", { input: "speech"})
|
|
324
324
|
|
|
325
325
|
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
|
|
@@ -328,6 +328,7 @@ class TiledeskTwilioTranslator {
|
|
|
328
328
|
.att("method", "POST")
|
|
329
329
|
.att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
|
|
330
330
|
.att('speechTimeout', "auto")
|
|
331
|
+
.att("enhanced", "true") // enable enhanced recognition
|
|
331
332
|
|
|
332
333
|
//if(xmlAttributes && xmlAttributes.noInputTimeout){
|
|
333
334
|
// gather.att("timeout", Math.round(xmlAttributes.noInputTimeout/1000) ).up();
|
|
@@ -552,19 +553,19 @@ class TiledeskTwilioTranslator {
|
|
|
552
553
|
if (command.type === "message") {
|
|
553
554
|
//case type: TEXT
|
|
554
555
|
if(command.message.type === 'text'){
|
|
556
|
+
let text = utils.markdownToTwilioSpeech(command.message.text);
|
|
555
557
|
if(that.voiceProvider !== VOICE_PROVIDER.TWILIO){
|
|
556
|
-
let voiceMessageUrl = await that.generateTTS(
|
|
558
|
+
let voiceMessageUrl = await that.generateTTS(text, attributes)
|
|
557
559
|
rootEle.ele('Play', {}, voiceMessageUrl )
|
|
558
560
|
}else{
|
|
559
|
-
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE },
|
|
561
|
+
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE }, text);
|
|
560
562
|
}
|
|
561
563
|
|
|
562
|
-
|
|
563
564
|
}
|
|
564
565
|
//case type: FRAME
|
|
565
566
|
if(command.message.type === 'frame' && command.message.metadata.src !== ""){
|
|
566
567
|
if(command.message.text != ''){
|
|
567
|
-
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE },
|
|
568
|
+
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE }, text);
|
|
568
569
|
}
|
|
569
570
|
rootEle.ele('Play', {}, command.message.metadata.src )
|
|
570
571
|
}
|
package/tiledesk/VoiceChannel.js
CHANGED
|
@@ -75,11 +75,11 @@ class VoiceChannel {
|
|
|
75
75
|
//increment
|
|
76
76
|
const delayIndex = (+index) +1
|
|
77
77
|
//save new index to redis
|
|
78
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', delayIndex, 'EX'
|
|
78
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', delayIndex, {'EX': 86400});
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
//if index is not present: set to default (0)
|
|
82
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, 'EX'
|
|
82
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, {'EX': 86400});
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/** RESET INDEX INTO REDIS DATA FOR CURRENT CALLID **/
|
|
@@ -106,11 +106,11 @@ class VoiceChannel {
|
|
|
106
106
|
winston.debug('[VoiceChannel] saveSettingsForCallId: attributes found -->'+index)
|
|
107
107
|
if(index){
|
|
108
108
|
//set index to default (0)
|
|
109
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), 'EX'
|
|
109
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), {'EX': 86400});
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
//if index is not present: set to default (0)
|
|
113
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), 'EX'
|
|
113
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), {'EX': 86400});
|
|
114
114
|
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -125,6 +125,25 @@ class VoiceChannel {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
|
|
128
|
+
async setSessionForCallId(callSid, session) {
|
|
129
|
+
await this.redis_client.set(`tiledesk:voice:${callSid}:session`, JSON.stringify(session), { 'EX': 86400 });
|
|
130
|
+
//for (const [key, value] of Object.entries(redis_data)){
|
|
131
|
+
// await redis_client.hSet('tiledesk:voice:'+callId, key, JSON.stringify(value))
|
|
132
|
+
//}
|
|
133
|
+
//await redis_client.expire('tiledesk:voice:'+callId, 86400)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async getSessionForCallId(callSid) {
|
|
137
|
+
const sessionData = await this.redis_client.get(`tiledesk:voice:${callSid}:session`);
|
|
138
|
+
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
139
|
+
if (sessionData) {
|
|
140
|
+
return JSON.parse(sessionData);
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
128
147
|
async deleteCallKeys(callSid) {
|
|
129
148
|
const pattern = `tiledesk:voice:${callSid}:*`;
|
|
130
149
|
let cursor = 0;
|
package/tiledesk/utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const NON_SPEECH_TOKENS = require('./constants').NON_SPEECH_TOKENS
|
|
2
|
+
const removeMarkdown = require('remove-markdown');
|
|
2
3
|
|
|
3
4
|
function getNumber(phoneNumber){
|
|
4
5
|
if(phoneNumber.startsWith('+')){
|
|
@@ -31,5 +32,13 @@ function normalizeSTT(text) {
|
|
|
31
32
|
return cleaned;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function markdownToTwilioSpeech(text) {
|
|
36
|
+
return removeMarkdown(text)
|
|
37
|
+
.replace(/:/g, ': ')
|
|
38
|
+
.replace(/\n+/g, '. ')
|
|
39
|
+
.replace(/\s+/g, ' ')
|
|
40
|
+
.trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
|
|
35
|
-
module.exports = {getNumber, buildQueryString, normalizeSTT}
|
|
44
|
+
module.exports = {getNumber, buildQueryString, normalizeSTT, markdownToTwilioSpeech}
|