@tiledesk/tiledesk-voice-twilio-connector 0.1.13 → 0.1.14-rc1
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 +126 -8
- package/package.json +1 -1
- package/tiledesk/TiledeskChannel.js +1 -1
- package/tiledesk/TiledeskTwilioTranslator.js +10 -10
package/index.js
CHANGED
|
@@ -37,7 +37,6 @@ var redis_client;
|
|
|
37
37
|
|
|
38
38
|
//UTILS
|
|
39
39
|
const CHANNEL_NAME = require('./tiledesk/constants').CHANNEL_NAME
|
|
40
|
-
const TYPE_MESSAGE = require('./tiledesk/constants').TYPE_MESSAGE
|
|
41
40
|
const CALL_STATUS = require('./tiledesk/constants').CALL_STATUS
|
|
42
41
|
const VOICE_NAME = require('./tiledesk/constants').VOICE_NAME
|
|
43
42
|
const VOICE_LANGUAGE = require('./tiledesk/constants').VOICE_LANGUAGE
|
|
@@ -54,6 +53,13 @@ let twilio = require('twilio');
|
|
|
54
53
|
const VoiceResponse = require('twilio').twiml.VoiceResponse;
|
|
55
54
|
const { MessagingResponse } = require('twilio').twiml;
|
|
56
55
|
|
|
56
|
+
let start1= ''
|
|
57
|
+
let time = null;
|
|
58
|
+
|
|
59
|
+
/*UTILS*/
|
|
60
|
+
const utilsMess = require('./tiledesk/utils-message.js')
|
|
61
|
+
const TYPE_MESSAGE = require('./tiledesk/constants').TYPE_MESSAGE
|
|
62
|
+
|
|
57
63
|
router.get("/", async (req, res) => {
|
|
58
64
|
res.send("Welcome to Tiledesk-voice-twilio connector");
|
|
59
65
|
});
|
|
@@ -61,6 +67,7 @@ router.get("/", async (req, res) => {
|
|
|
61
67
|
|
|
62
68
|
// TILEDESK WEBHOOK: message from tiledesk to user (chatbot/agent messagges)
|
|
63
69
|
router.post("/tiledesk", async (req, res) => {
|
|
70
|
+
winston.debug("(vxml) Message received from Tiledesk in projectID: "+ req.body.payload.id_project + ' ---- and text: '+req.body.payload.text )
|
|
64
71
|
let tiledeskMessage = req.body.payload
|
|
65
72
|
let project_id = tiledeskMessage.id_project
|
|
66
73
|
|
|
@@ -73,6 +80,15 @@ router.post("/tiledesk", async (req, res) => {
|
|
|
73
80
|
});
|
|
74
81
|
tdChannel.setProjectId(project_id)
|
|
75
82
|
|
|
83
|
+
/*SKIP INFO MESSAGES*/
|
|
84
|
+
/*SKIP CURRENT USER MESSAGES*/
|
|
85
|
+
if(!utilsMess.messageType(TYPE_MESSAGE.INFO, tiledeskMessage) && !(tiledeskMessage.sender.indexOf("vxml") > -1) ){
|
|
86
|
+
winston.verbose("> whook SAVE MESSAGE TO QUEUE " + JSON.stringify(tiledeskMessage) );
|
|
87
|
+
start1 = new Date().getTime();
|
|
88
|
+
console.log("(WH) time: ", new Date().getTime() - time)
|
|
89
|
+
console.log("(WH) received message: ", tiledeskMessage.text, ' --- at time:', new Date(), start1)
|
|
90
|
+
}
|
|
91
|
+
|
|
76
92
|
await tdChannel.addMessageToQueue(tiledeskMessage)
|
|
77
93
|
|
|
78
94
|
res.send("(vxml) Message received from Voice VXML Proxy");
|
|
@@ -82,7 +98,8 @@ router.post("/tiledesk", async (req, res) => {
|
|
|
82
98
|
|
|
83
99
|
// TWILIO WEBHOOK : message from user to tiledesk
|
|
84
100
|
router.post('/webhook/:id_project', async (req, res) => {
|
|
85
|
-
winston.
|
|
101
|
+
winston.debug('(voice) called POST /webhook/:id_project '+ new Date(), req.params)
|
|
102
|
+
let start_call = new Date().getTime();
|
|
86
103
|
|
|
87
104
|
let project_id = req.params.id_project;
|
|
88
105
|
let callSid = req.body.CallSid;
|
|
@@ -117,11 +134,14 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
117
134
|
BASE_URL: BASE_URL
|
|
118
135
|
});
|
|
119
136
|
|
|
137
|
+
let start2 = new Date().getTime();
|
|
120
138
|
let user = await tdChannel.signIn(from, settings);
|
|
121
139
|
if(!user){
|
|
122
140
|
res.status(401).send({message: "Cannot able to signIn with current caller phone :" + from});
|
|
123
141
|
return;
|
|
124
142
|
}
|
|
143
|
+
let end2 = new Date().getTime();
|
|
144
|
+
console.log('Time after signIn: ', end2-start2, '[ms]')
|
|
125
145
|
|
|
126
146
|
//let conversation_id = await tdChannel.getConversation(ani, callId, user.token);
|
|
127
147
|
let conversation_id = await tdChannel.generateConversation(from, callSid, user.token);
|
|
@@ -146,7 +166,7 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
146
166
|
|
|
147
167
|
await redis_client.set('tiledesk:vxml:'+callSid+':session', JSON.stringify(session_data), {'EX': 86400});
|
|
148
168
|
|
|
149
|
-
|
|
169
|
+
let start4 = new Date().getTime();
|
|
150
170
|
let tiledeskMessage= {
|
|
151
171
|
text:'/start',
|
|
152
172
|
senderFullname: from,
|
|
@@ -164,7 +184,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
164
184
|
if(!message){
|
|
165
185
|
return res.status(503).send({ message: "Bad response: Quota exceeded" })
|
|
166
186
|
}
|
|
167
|
-
|
|
187
|
+
let end4 = new Date().getTime();
|
|
188
|
+
console.log('Time after sent message /start: ', end4-start4, '[ms]')
|
|
168
189
|
|
|
169
190
|
//generate Tiledesk wait message
|
|
170
191
|
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
@@ -181,12 +202,17 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
181
202
|
res.set('Content-Type', 'text/xml');
|
|
182
203
|
res.status(200).send(messageToVXML);
|
|
183
204
|
|
|
205
|
+
let end_call = new Date().getTime();
|
|
206
|
+
console.log('Time to responde to /webhook/:id_project : ', end_call-start_call, '[ms]')
|
|
207
|
+
|
|
184
208
|
});
|
|
185
209
|
|
|
186
210
|
|
|
187
211
|
router.post('/nextblock/:callSid/', async(req, res) => {
|
|
188
|
-
|
|
189
|
-
winston.
|
|
212
|
+
let start_call = new Date()
|
|
213
|
+
winston.debug("(vxml) called POST /nextblock ", req.body);
|
|
214
|
+
winston.debug("(vxml) called POST /nextblock query ", req.query);
|
|
215
|
+
console.log('/nextblock at: ', new Date(), 'with text:', req.body.SpeechResult)
|
|
190
216
|
|
|
191
217
|
let usertext = req.body.SpeechResult;
|
|
192
218
|
let confidence = req.body.Confidence
|
|
@@ -236,18 +262,21 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
236
262
|
|
|
237
263
|
let message = {}, queue = []
|
|
238
264
|
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
239
|
-
winston.
|
|
265
|
+
winston.verbose('/NEXT controllo coda--> '+ queue.length + ' ' + usertext)
|
|
240
266
|
if(queue.length === 0 && (usertext === '' || !usertext)){
|
|
241
267
|
winston.debug('QUEUE is empty--> ',queue)
|
|
242
268
|
|
|
243
269
|
//CASE: queue is empty --> generate Tiledesk wait message and manage delayTime
|
|
244
270
|
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
271
|
+
console.log('delay /nextblock -->', delayTime)
|
|
245
272
|
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
246
273
|
//update delayIndex for wait command message time
|
|
247
274
|
await voiceChannel.saveDelayIndexForCallId(from)
|
|
248
275
|
|
|
249
276
|
} else if(queue.length > 0 && (usertext === '' || !usertext)){
|
|
250
277
|
|
|
278
|
+
let end1 = new Date().getTime()
|
|
279
|
+
console.log('Time between wh message and /nextblock queue message:', end1-start1, '[ms] (message:', queue[0].text , ') at time', new Date())
|
|
251
280
|
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue and reset delayTime
|
|
252
281
|
message = queue[0]
|
|
253
282
|
winston.debug('QUEUE --> ',queue[0])
|
|
@@ -266,10 +295,14 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
266
295
|
type: 'text',
|
|
267
296
|
channel: { name: CHANNEL_NAME }
|
|
268
297
|
};
|
|
298
|
+
let startSend= new Date().getTime()
|
|
269
299
|
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
270
300
|
winston.debug("message sent : ", tdMessage);
|
|
301
|
+
let endSend = new Date().getTime();
|
|
302
|
+
console.log('Time to send messagge ( ', usertext, '): ', endSend - startSend, '[ms] at time', new Date())
|
|
271
303
|
//generate Tiledesk wait message
|
|
272
304
|
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
305
|
+
console.log('(message sent) delay /nextblock -->', delayTime)
|
|
273
306
|
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
274
307
|
//update delayIndex for wait command message time
|
|
275
308
|
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
@@ -279,11 +312,96 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
279
312
|
// convert response to vxml
|
|
280
313
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
281
314
|
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
282
|
-
|
|
315
|
+
|
|
316
|
+
let end_call = new Date()
|
|
317
|
+
console.log('Time to responde to /nextblock/:callSid : ', end_call-start_call, '[ms]')
|
|
283
318
|
|
|
284
319
|
// Render the response as XML in reply to the webhook request
|
|
285
320
|
res.set('Content-Type', 'text/xml');
|
|
286
321
|
res.status(200).send(messageToVXML);
|
|
322
|
+
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
router.post('/speechresult/:callSid', async (req, res) => {
|
|
326
|
+
|
|
327
|
+
let startTime = new Date();
|
|
328
|
+
winston.verbose("(vxml) called POST /speechresult ", req.body);
|
|
329
|
+
winston.verbose("(vxml) called POST /speechresult query ", req.query);
|
|
330
|
+
|
|
331
|
+
let usertext = req.body.SpeechResult;
|
|
332
|
+
let confidence = req.body.Confidence
|
|
333
|
+
let callSid = req.params.callSid;
|
|
334
|
+
|
|
335
|
+
let sessionInfo;
|
|
336
|
+
let project_id, conversation_id, user;
|
|
337
|
+
let from, to;
|
|
338
|
+
|
|
339
|
+
let redis_data = await redis_client.get('tiledesk:vxml:'+callSid+':session');
|
|
340
|
+
//let redis_data = await redis_client.hGetAll('tiledesk:vxml:'+callId);
|
|
341
|
+
if (!redis_data) {
|
|
342
|
+
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
343
|
+
}
|
|
344
|
+
sessionInfo = JSON.parse(redis_data)
|
|
345
|
+
project_id = sessionInfo.project_id;
|
|
346
|
+
from = sessionInfo.from;
|
|
347
|
+
to = sessionInfo.to;
|
|
348
|
+
conversation_id = sessionInfo.conversation_id;
|
|
349
|
+
user = sessionInfo.user;
|
|
350
|
+
|
|
351
|
+
let vxmlAttributes = {
|
|
352
|
+
voiceName: VOICE_NAME,
|
|
353
|
+
voiceLanguage: VOICE_LANGUAGE,
|
|
354
|
+
callSid: callSid,
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const tdChannel = new TiledeskChannel({
|
|
358
|
+
API_URL: API_URL,
|
|
359
|
+
redis_client: redis_client
|
|
360
|
+
})
|
|
361
|
+
tdChannel.setProjectId(project_id);
|
|
362
|
+
|
|
363
|
+
const tdTranslator = new TiledeskTwilioTranslator({
|
|
364
|
+
BASE_URL: BASE_URL
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
winston.verbose("(vxml) usertext "+usertext);
|
|
368
|
+
let message = {};
|
|
369
|
+
if(usertext){
|
|
370
|
+
let tiledeskMessage= {
|
|
371
|
+
text:usertext,
|
|
372
|
+
senderFullname: from,
|
|
373
|
+
type: 'text',
|
|
374
|
+
channel: { name: CHANNEL_NAME }
|
|
375
|
+
};
|
|
376
|
+
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
377
|
+
winston.debug("message sent : ", tdMessage);
|
|
378
|
+
//generate Tiledesk wait message
|
|
379
|
+
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
380
|
+
message = await tdChannel.generateWaitTdMessage(from, 50)
|
|
381
|
+
//update delayIndex for wait command message time
|
|
382
|
+
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
383
|
+
|
|
384
|
+
}else {
|
|
385
|
+
|
|
386
|
+
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
387
|
+
message = await tdChannel.generateWaitTdMessage(from, 50)
|
|
388
|
+
//update delayIndex for wait command message time
|
|
389
|
+
await voiceChannel.saveDelayIndexForCallId(from)
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// convert response to vxml
|
|
393
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
394
|
+
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
395
|
+
|
|
396
|
+
let endTime = new Date()
|
|
397
|
+
let diffTIme = endTime - startTime
|
|
398
|
+
winston.verbose("(voice) send response to TWILIO in : " + diffTIme + "ms");
|
|
399
|
+
|
|
400
|
+
// Render the response as XML in reply to the webhook request
|
|
401
|
+
res.set('Content-Type', 'text/xml');
|
|
402
|
+
res.status(200).send(messageToVXML);
|
|
403
|
+
|
|
404
|
+
|
|
287
405
|
})
|
|
288
406
|
|
|
289
407
|
|
package/package.json
CHANGED
|
@@ -179,7 +179,7 @@ class TiledeskChannel {
|
|
|
179
179
|
winston.debug("[TiledeskChannel] send message response: ", response.data);
|
|
180
180
|
return response.data;
|
|
181
181
|
}).catch((err) => {
|
|
182
|
-
winston.error("[TiledeskChannel] send message: ", err);
|
|
182
|
+
winston.error("[TiledeskChannel] send message: ", err.response?.data);
|
|
183
183
|
return null;
|
|
184
184
|
})
|
|
185
185
|
|
|
@@ -69,7 +69,7 @@ class TiledeskTwilioTranslator {
|
|
|
69
69
|
|
|
70
70
|
//MANAGE CLOSE info message
|
|
71
71
|
const isInfoSupport = utils.messageType(TYPE_MESSAGE.INFO_SUPPORT, msg)
|
|
72
|
-
winston.
|
|
72
|
+
winston.debug("[TiledeskVXMLTranslator] isInfoSupport:"+ isInfoSupport);
|
|
73
73
|
if(isInfoSupport && utils.infoMessageType(msg) === INFO_MESSAGE_TYPE.CHAT_CLOSED){
|
|
74
74
|
const hangUp = this.hangupCall(xml)
|
|
75
75
|
return hangUp;
|
|
@@ -83,7 +83,7 @@ class TiledeskTwilioTranslator {
|
|
|
83
83
|
|
|
84
84
|
/** check for WAIT **/
|
|
85
85
|
const isWait = this.checkIfIsWait(msg);
|
|
86
|
-
winston.
|
|
86
|
+
winston.debug("[TiledeskVXMLTranslator] toVXML: isWait:"+ isWait);
|
|
87
87
|
if(isWait){
|
|
88
88
|
const delayForm = await this.delayVXMLConverter(xml, msg, vxmlAttributes);
|
|
89
89
|
return delayForm;
|
|
@@ -91,7 +91,7 @@ class TiledeskTwilioTranslator {
|
|
|
91
91
|
|
|
92
92
|
/** check for DTMF FORM **/
|
|
93
93
|
const isDtmfForm = this.checkIfIsDTMFForm(msg);
|
|
94
|
-
winston.
|
|
94
|
+
winston.debug("[TiledeskVXMLTranslator] toVXML: isDtmfForm: "+ isDtmfForm);
|
|
95
95
|
if(isDtmfForm){
|
|
96
96
|
const DTMFForm = await this.dtmfFormVXMLConverter(xml, msg, vxmlAttributes);
|
|
97
97
|
return DTMFForm;
|
|
@@ -99,7 +99,7 @@ class TiledeskTwilioTranslator {
|
|
|
99
99
|
|
|
100
100
|
/** check for BLIND TRANSFER **/
|
|
101
101
|
const isBlindFransfer = this.checkIfIsBlindFransfer(msg);
|
|
102
|
-
winston.
|
|
102
|
+
winston.debug("[TiledeskVXMLTranslator] toVXML: isBlindFransfer: "+ isBlindFransfer);
|
|
103
103
|
if(isBlindFransfer){
|
|
104
104
|
const blindTransfer = await this.blindTransferVXMLConverter(xml, msg, vxmlAttributes);
|
|
105
105
|
return blindTransfer;
|
|
@@ -107,7 +107,7 @@ class TiledeskTwilioTranslator {
|
|
|
107
107
|
|
|
108
108
|
/** check for DTMF MENU **/
|
|
109
109
|
const isMenu = this.checkIfIsDTMFMenuMessage(msg);
|
|
110
|
-
winston.
|
|
110
|
+
winston.debug("[TiledeskVXMLTranslator] toVXML: isMenu: "+ isMenu);
|
|
111
111
|
if(isMenu){
|
|
112
112
|
const menu = await this.menuVXMLConverter(xml, msg, vxmlAttributes);
|
|
113
113
|
return menu;
|
|
@@ -115,14 +115,14 @@ class TiledeskTwilioTranslator {
|
|
|
115
115
|
|
|
116
116
|
/** check for SPEECH FORM **/
|
|
117
117
|
const isSpeechForm = this.checkIfIsSpeechFormMessage(msg);
|
|
118
|
-
winston.
|
|
118
|
+
winston.debug("[TiledeskVXMLTranslator] toVXML: isSpeechForm: "+ isSpeechForm);
|
|
119
119
|
if(isSpeechForm){
|
|
120
120
|
const form = await this.speechFormVXMLConverter(xml, msg, vxmlAttributes);
|
|
121
121
|
return form;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/** check for FORM (PlayPrompt action) **/
|
|
125
|
-
winston.
|
|
125
|
+
winston.debug("[TiledeskVXMLTranslator] toVXML: isPrompt: true");
|
|
126
126
|
const prompt = await this.playPromptVXMLConverter(xml, msg, vxmlAttributes);
|
|
127
127
|
return prompt;
|
|
128
128
|
|
|
@@ -264,7 +264,7 @@ class TiledeskTwilioTranslator {
|
|
|
264
264
|
const gather = rootEle.ele("Gather", { input: "speech"})
|
|
265
265
|
|
|
266
266
|
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
|
|
267
|
-
gather.att("action", this.BASE_URL + '/
|
|
267
|
+
gather.att("action", this.BASE_URL + '/speechresult/' + xmlAttributes.callSid + queryUrl)
|
|
268
268
|
.att("method", "POST")
|
|
269
269
|
.att("language", xmlAttributes.voiceLanguage)
|
|
270
270
|
.att('speechTimeout', "auto")
|
|
@@ -272,9 +272,9 @@ class TiledeskTwilioTranslator {
|
|
|
272
272
|
if(xmlAttributes && xmlAttributes.noInputTimeout){
|
|
273
273
|
gather.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
|
|
274
274
|
}
|
|
275
|
-
if(xmlAttributes && xmlAttributes.incompleteSpeechTimeout){
|
|
275
|
+
/*if(xmlAttributes && xmlAttributes.incompleteSpeechTimeout){
|
|
276
276
|
gather.att("speechTimeout", xmlAttributes.incompleteSpeechTimeout/1000 ).up();
|
|
277
|
-
}
|
|
277
|
+
}*/
|
|
278
278
|
|
|
279
279
|
const prompt = this.promptVXML(gather, message, xmlAttributes);
|
|
280
280
|
|