@tiledesk/tiledesk-voice-twilio-connector 0.1.25 → 0.1.26-rc10
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 +291 -160
- package/logs/app.log +40 -0
- package/package.json +3 -3
- package/routes/manageApp.js +8 -8
- package/template/configure.html +15 -15
- package/template/css/configure.css +22 -1
- package/template/error.html +15 -16
- package/tiledesk/KVBaseMongo.js +1 -1
- package/tiledesk/TiledeskChannel.js +66 -0
- package/tiledesk/TiledeskTwilioTranslator.js +3 -3
- package/tiledesk/VoiceChannel.js +11 -11
package/index.js
CHANGED
|
@@ -101,23 +101,18 @@ router.post("/tiledesk", async (req, res) => {
|
|
|
101
101
|
/*SKIP INFO MESSAGES*/
|
|
102
102
|
/*SKIP CURRENT USER MESSAGES*/
|
|
103
103
|
if(!utilsMess.messageType(TYPE_MESSAGE.INFO, tiledeskMessage) && !(tiledeskMessage.sender.indexOf("vxml") > -1) ){
|
|
104
|
-
winston.
|
|
105
|
-
start1 = new Date().getTime();
|
|
106
|
-
console.log("(WH) time: ", new Date().getTime() - time)
|
|
107
|
-
console.log("(WH) received message: ", tiledeskMessage.text, ' --- at time:', new Date(), start1)
|
|
104
|
+
winston.debug("> whook SAVE MESSAGE TO QUEUE " + JSON.stringify(tiledeskMessage) );
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
await tdChannel.addMessageToQueue(tiledeskMessage)
|
|
111
108
|
|
|
112
109
|
res.send("(voice) Message received from Voice Twilio Proxy");
|
|
113
|
-
|
|
114
|
-
|
|
115
110
|
});
|
|
116
111
|
|
|
117
112
|
// TWILIO WEBHOOK : message from user to tiledesk
|
|
118
113
|
router.post('/webhook/:id_project', async (req, res) => {
|
|
119
|
-
winston.debug('(voice) called POST /webhook/:id_project '+ new Date(), req.params)
|
|
120
114
|
let start_call = new Date().getTime();
|
|
115
|
+
winston.debug('(voice) called POST /webhook/:id_project '+ new Date(), req.params)
|
|
121
116
|
|
|
122
117
|
let project_id = req.params.id_project;
|
|
123
118
|
let callSid = req.body.CallSid;
|
|
@@ -137,6 +132,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
137
132
|
}
|
|
138
133
|
|
|
139
134
|
let vxmlAttributes = {
|
|
135
|
+
TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
|
|
136
|
+
TTS_VOICE_NAME: VOICE_NAME,
|
|
140
137
|
callSid: callSid
|
|
141
138
|
};
|
|
142
139
|
|
|
@@ -158,7 +155,7 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
158
155
|
return;
|
|
159
156
|
}
|
|
160
157
|
let end2 = new Date().getTime();
|
|
161
|
-
console.log('Time after signIn: ', end2-start2, '[ms]')
|
|
158
|
+
// console.log('Time after signIn: ', end2-start2, '[ms]')
|
|
162
159
|
|
|
163
160
|
//let conversation_id = await tdChannel.getConversation(ani, callId, user.token);
|
|
164
161
|
let conversation_id = await tdChannel.generateConversation(from, callSid, user.token);
|
|
@@ -200,13 +197,12 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
200
197
|
return res.status(500).send({ message: "Redis not ready. Check redis connection..." })
|
|
201
198
|
}
|
|
202
199
|
//for (const [key, value] of Object.entries(redis_data)){
|
|
203
|
-
// await redis_client.hSet('tiledesk:
|
|
200
|
+
// await redis_client.hSet('tiledesk:voice:'+callId, key, JSON.stringify(value))
|
|
204
201
|
//}
|
|
205
|
-
//await redis_client.expire('tiledesk:
|
|
202
|
+
//await redis_client.expire('tiledesk:voice:'+callId, 86400)
|
|
206
203
|
|
|
207
|
-
await redis_client.set('tiledesk:
|
|
204
|
+
await redis_client.set('tiledesk:voice:'+callSid+':session', JSON.stringify(session_data), {'EX': 86400});
|
|
208
205
|
|
|
209
|
-
let start4 = new Date().getTime();
|
|
210
206
|
let tiledeskMessage= {
|
|
211
207
|
text:'/start',
|
|
212
208
|
senderFullname: from,
|
|
@@ -221,35 +217,37 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
221
217
|
departmentid: settings.department_id
|
|
222
218
|
};
|
|
223
219
|
|
|
224
|
-
let
|
|
225
|
-
if(!
|
|
220
|
+
let response = await tdChannel.send(tiledeskMessage, user.token, conversation_id)
|
|
221
|
+
if(!response){
|
|
226
222
|
return res.status(503).send({ message: "Bad response: Quota exceeded" })
|
|
227
223
|
}
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
|
|
225
|
+
//await for a response message from tiledesk queue
|
|
226
|
+
let start_time_get_message = new Date()
|
|
227
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
228
|
+
let end_time_get_message = new Date()
|
|
229
|
+
winston.verbose('Time to getMessage from queue in /webhook/:project_id : ' + (end_time_get_message-start_time_get_message) + '[ms] --- at time:' + new Date())
|
|
230
230
|
|
|
231
|
-
//generate Tiledesk wait message
|
|
232
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
233
|
-
let waitTiledeskMessage = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
234
|
-
//update delayIndex for wait command message time
|
|
235
|
-
await voiceChannel.saveDelayIndexForCallId(from)
|
|
231
|
+
// //generate Tiledesk wait message
|
|
232
|
+
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
233
|
+
// let waitTiledeskMessage = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
234
|
+
// //update delayIndex for wait command message time
|
|
235
|
+
// await voiceChannel.saveDelayIndexForCallId(from)
|
|
236
236
|
|
|
237
237
|
// send standard wait vxml message
|
|
238
|
-
let messageToVXML = await tdTranslator.toVXML(
|
|
239
|
-
winston.debug('(voice) messageVXML-->'+ messageToVXML)
|
|
238
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, session_data)
|
|
239
|
+
winston.debug('(voice) /webhook/:id_project messageVXML-->'+ messageToVXML)
|
|
240
240
|
|
|
241
|
+
let end_call = new Date().getTime();
|
|
242
|
+
winston.info(`Time to respond to /webhook/${project_id} : ${(end_call-start_call)}[ms]`)
|
|
241
243
|
|
|
242
244
|
// Render the response as XML in reply to the webhook request
|
|
243
245
|
res.set('Content-Type', 'text/xml');
|
|
244
246
|
res.status(200).send(messageToVXML);
|
|
245
|
-
|
|
246
|
-
let end_call = new Date().getTime();
|
|
247
|
-
console.log('Time to responde to /webhook/:id_project : ', end_call-start_call, '[ms]')
|
|
248
|
-
|
|
249
247
|
});
|
|
250
248
|
|
|
251
249
|
|
|
252
|
-
router.post('/
|
|
250
|
+
router.post('/nextblock_old/:callSid/', async(req, res) => {
|
|
253
251
|
let start_call = new Date()
|
|
254
252
|
winston.debug("(voice) called POST /nextblock ", req.body);
|
|
255
253
|
winston.debug("(voice) called POST /nextblock query ", req.query);
|
|
@@ -263,8 +261,8 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
263
261
|
let project_id, conversation_id, user;
|
|
264
262
|
let from, to;
|
|
265
263
|
|
|
266
|
-
let redis_data = await redis_client.get('tiledesk:
|
|
267
|
-
//let redis_data = await redis_client.hGetAll('tiledesk:
|
|
264
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
265
|
+
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
268
266
|
if (!redis_data) {
|
|
269
267
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
270
268
|
}
|
|
@@ -327,18 +325,119 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
327
325
|
|
|
328
326
|
// convert response to vxml
|
|
329
327
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
330
|
-
winston.
|
|
328
|
+
winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
|
|
331
329
|
|
|
332
330
|
let end_call = new Date()
|
|
333
331
|
console.log('Time to responde to /nextblock/:callSid : ', end_call-start_call, '[ms]')
|
|
334
332
|
|
|
335
333
|
// Render the response as XML in reply to the webhook request
|
|
336
|
-
res.set('Content-Type', 'application/xml
|
|
334
|
+
res.set('Content-Type', 'application/xml');
|
|
337
335
|
res.status(200).send(messageToVXML);
|
|
338
336
|
|
|
339
337
|
})
|
|
340
338
|
|
|
341
|
-
|
|
339
|
+
router.post('/nextblock/:callSid/', async(req, res) => {
|
|
340
|
+
let start_call = new Date()
|
|
341
|
+
winston.debug("(voice) called POST /nextblock ", req.body);
|
|
342
|
+
winston.debug("(voice) called POST /nextblock query ", req.query);
|
|
343
|
+
winston.verbose("(voice) called POST /nextblock at" + new Date() + "with text: "+ req.body.SpeechResult);
|
|
344
|
+
|
|
345
|
+
let usertext = req.body.SpeechResult;
|
|
346
|
+
let confidence = req.body.Confidence
|
|
347
|
+
let callSid = req.params.callSid;
|
|
348
|
+
|
|
349
|
+
let sessionInfo;
|
|
350
|
+
let project_id, conversation_id, user;
|
|
351
|
+
let from, to;
|
|
352
|
+
|
|
353
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
354
|
+
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
355
|
+
if (!redis_data) {
|
|
356
|
+
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
357
|
+
}
|
|
358
|
+
sessionInfo = JSON.parse(redis_data)
|
|
359
|
+
project_id = sessionInfo.project_id;
|
|
360
|
+
from = sessionInfo.from;
|
|
361
|
+
to = sessionInfo.to;
|
|
362
|
+
conversation_id = sessionInfo.conversation_id;
|
|
363
|
+
user = sessionInfo.user;
|
|
364
|
+
|
|
365
|
+
let vxmlAttributes = {
|
|
366
|
+
TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
|
|
367
|
+
TTS_VOICE_NAME: VOICE_NAME,
|
|
368
|
+
callSid: callSid,
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
const tdChannel = new TiledeskChannel({
|
|
373
|
+
API_URL: API_URL,
|
|
374
|
+
redis_client: redis_client
|
|
375
|
+
})
|
|
376
|
+
tdChannel.setProjectId(project_id);
|
|
377
|
+
|
|
378
|
+
const tdTranslator = new TiledeskTwilioTranslator({
|
|
379
|
+
BASE_URL: BASE_URL,
|
|
380
|
+
aiService: aiService,
|
|
381
|
+
uploadService: uploadService
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
let start_promise_message= new Date();
|
|
386
|
+
let message = await new Promise(async (resolve, reject) => {
|
|
387
|
+
winston.debug('(voice) ******* user text -->'+ usertext)
|
|
388
|
+
if(usertext === '' || !usertext){
|
|
389
|
+
|
|
390
|
+
let start_time_get_message = new Date()
|
|
391
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
392
|
+
let end_time_get_message = new Date()
|
|
393
|
+
winston.verbose(`(if) Time to getMessage from queue in /nextblock/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]`)
|
|
394
|
+
resolve(message)
|
|
395
|
+
}else{
|
|
396
|
+
//CASE:usertext is not empty and queue is empty --> send message to tiledesk and manage delayTime
|
|
397
|
+
let tiledeskMessage= {
|
|
398
|
+
text:usertext,
|
|
399
|
+
senderFullname: from,
|
|
400
|
+
type: 'text',
|
|
401
|
+
channel: { name: CHANNEL_NAME }
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
let start_time_send_message = new Date()
|
|
405
|
+
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
406
|
+
let end_time_send_message = new Date()
|
|
407
|
+
winston.debug("message sent : ", tdMessage);
|
|
408
|
+
winston.verbose(`(else) Time to send message to tiledesk in /nextblock/${callSid} : ${(end_time_send_message-start_time_send_message)}[ms] with text ` + tdMessage.text + ' --- at time:' + new Date())
|
|
409
|
+
|
|
410
|
+
let start_time_get_message = new Date()
|
|
411
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
412
|
+
let end_time_get_message = new Date()
|
|
413
|
+
winston.verbose(`(else) Time to getMessage from queue in /nextblock/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
|
|
414
|
+
resolve(message)
|
|
415
|
+
|
|
416
|
+
//generate Tiledesk wait message
|
|
417
|
+
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
418
|
+
// let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
419
|
+
// //update delayIndex for wait command message time
|
|
420
|
+
// await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
421
|
+
// resolve(message)
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
let end_promise_message = new Date()
|
|
425
|
+
winston.verbose(`Time to manage message in Promise /nextblock/${callSid}: ${(end_promise_message-start_promise_message)}[ms]` + ' with text' + message.text + ' --- at time --' + new Date())
|
|
426
|
+
|
|
427
|
+
// convert response to vxml
|
|
428
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
429
|
+
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
430
|
+
|
|
431
|
+
let end_call = new Date()
|
|
432
|
+
winston.info(`Time to respond to /nextblock/${callSid} : ${(end_call-start_call)} [ms]`)
|
|
433
|
+
|
|
434
|
+
// Render the response as XML in reply to the webhook request
|
|
435
|
+
res.set('Content-Type', 'application/xml');
|
|
436
|
+
res.status(200).send(messageToVXML);
|
|
437
|
+
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
async function getMessage(callSid, ani, project_id, conversation_id){
|
|
342
441
|
const startTime = Date.now();
|
|
343
442
|
|
|
344
443
|
const tdChannel = new TiledeskChannel({
|
|
@@ -348,71 +447,86 @@ async function getMessage(callSid, from, project_id, conversation_id){
|
|
|
348
447
|
tdChannel.setProjectId(project_id);
|
|
349
448
|
|
|
350
449
|
let message = {}, queue = []
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
//
|
|
450
|
+
|
|
451
|
+
//get queue
|
|
452
|
+
//if queue exist and has at least one message
|
|
453
|
+
//get message from queue, remove it from queue and clear delayTime
|
|
454
|
+
//else
|
|
455
|
+
//subscribe to topic
|
|
456
|
+
//create promise that resolve after MAX_POLLING_TIME s
|
|
457
|
+
//race between subscription and timeout and return a message
|
|
458
|
+
//if subscription resolve first and queue has at least one message
|
|
355
459
|
//get message from queue, remove it from queue and clear delayTime
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
460
|
+
|
|
461
|
+
try{
|
|
462
|
+
|
|
463
|
+
// 1. First attempt: read from queue
|
|
464
|
+
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
465
|
+
winston.debug('[getMessage] /NEXT check queue length--> '+ queue.length)
|
|
466
|
+
|
|
467
|
+
if (queue && queue.length > 0) {
|
|
468
|
+
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue and reset delayTime
|
|
469
|
+
message = queue[0]
|
|
470
|
+
winston.debug('[getMessage] QUEUE --> '+ queue[0].text, queue[0])
|
|
471
|
+
|
|
472
|
+
// remove message from queue and reset delayIndex
|
|
473
|
+
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
474
|
+
await voiceChannel.clearDelayTimeForCallId(callSid)
|
|
475
|
+
|
|
476
|
+
return message;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 2. If queue is empty: subscribe with timeout
|
|
480
|
+
if(queue && queue.length === 0){
|
|
481
|
+
winston.debug("[getMessage] Queue is empty, starting subscription...");
|
|
482
|
+
const subscriptionPromise = (async () => {
|
|
483
|
+
await tdChannel.subscribeToTopic(conversation_id);
|
|
484
|
+
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
485
|
+
|
|
486
|
+
if (!queue || queue.length === 0) {
|
|
487
|
+
throw new Error("No message received after subscription");
|
|
488
|
+
}
|
|
489
|
+
|
|
367
490
|
message = queue[0]
|
|
368
|
-
winston.debug(
|
|
369
|
-
|
|
491
|
+
winston.debug(`[getMessage] Message received from subscription: ${message.text}`, message);
|
|
492
|
+
|
|
493
|
+
// remove message from queue and reset delayIndex
|
|
370
494
|
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
371
|
-
//reset delayIndex for wait command message time
|
|
372
495
|
await voiceChannel.clearDelayTimeForCallId(callSid)
|
|
373
|
-
//manage attributes for current callId
|
|
374
|
-
await voiceChannel.saveSettingsForCallId(message.attributes, callSid)
|
|
375
496
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
497
|
+
return message;
|
|
498
|
+
})();
|
|
379
499
|
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
384
|
-
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
385
|
-
//update delayIndex for wait command message time
|
|
386
|
-
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
500
|
+
const timeoutPromise = new Promise(async (resolve) => {
|
|
501
|
+
setTimeout(async () => {
|
|
502
|
+
winston.debug("[getMessage] Subscription timeout, generating waitTdMessage...");
|
|
387
503
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
504
|
+
//CASE: queue is empty --> generate Tiledesk wait message and manage delayTime
|
|
505
|
+
const delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid);
|
|
506
|
+
const waitMessage = await tdChannel.generateWaitTdMessage(ani, delayTime);
|
|
507
|
+
//update delayIndex for wait command message time
|
|
508
|
+
await voiceChannel.saveDelayIndexForCallId(callSid);
|
|
391
509
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
messageTimeout = setTimeout(checkQueue, delayTime);
|
|
510
|
+
resolve(waitMessage);
|
|
511
|
+
}, MAX_POLLING_TIME * 1000); // MAX_POLLING_TIME is in seconds
|
|
512
|
+
});
|
|
396
513
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
reject(err);
|
|
400
|
-
return;
|
|
514
|
+
// Vince chi arriva prima: subscription o timeout
|
|
515
|
+
return await Promise.race([subscriptionPromise, timeoutPromise]);
|
|
401
516
|
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
517
|
+
}catch(err){
|
|
518
|
+
winston.debug("[getMessage] Error occurred: ", err);
|
|
519
|
+
reject(err);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
407
522
|
|
|
408
523
|
}
|
|
409
524
|
|
|
410
525
|
router.post('/speechresult/:callSid', async (req, res) => {
|
|
411
|
-
|
|
412
526
|
let start_call = new Date();
|
|
413
|
-
winston.
|
|
414
|
-
winston.
|
|
415
|
-
|
|
527
|
+
winston.debug("(voice) called POST /speechresult ", req.body);
|
|
528
|
+
winston.debug("(voice) called POST /speechresult query ", req.query);
|
|
529
|
+
winston.verbose("(voice) called POST /speechresult at" + new Date() + "with text: "+ req.body.SpeechResult);
|
|
416
530
|
|
|
417
531
|
let usertext = req.body.SpeechResult;
|
|
418
532
|
let confidence = req.body.Confidence
|
|
@@ -422,8 +536,8 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
422
536
|
let project_id, conversation_id, user;
|
|
423
537
|
let from, to;
|
|
424
538
|
|
|
425
|
-
let redis_data = await redis_client.get('tiledesk:
|
|
426
|
-
//let redis_data = await redis_client.hGetAll('tiledesk:
|
|
539
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
540
|
+
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
427
541
|
if (!redis_data) {
|
|
428
542
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
429
543
|
}
|
|
@@ -452,54 +566,67 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
452
566
|
uploadService: uploadService
|
|
453
567
|
});
|
|
454
568
|
|
|
455
|
-
winston.verbose("(voice) usertext "+usertext);
|
|
456
|
-
let message = {};
|
|
457
|
-
if(usertext){
|
|
458
|
-
let tiledeskMessage= {
|
|
459
|
-
text:usertext,
|
|
460
|
-
senderFullname: from,
|
|
461
|
-
type: 'text',
|
|
462
|
-
channel: { name: CHANNEL_NAME }
|
|
463
|
-
};
|
|
464
|
-
let startSend= new Date().getTime()
|
|
465
|
-
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
466
|
-
winston.debug("message sent : ", tdMessage);
|
|
467
|
-
let endSend = new Date().getTime();
|
|
468
|
-
console.log('Time to send messagge ( ', usertext, '): ', endSend - startSend, '[ms] at time', new Date())
|
|
469
|
-
//generate Tiledesk wait message
|
|
470
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
471
|
-
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
472
|
-
//update delayIndex for wait command message time
|
|
473
|
-
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
474
|
-
|
|
475
|
-
}else {
|
|
476
|
-
|
|
477
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
478
|
-
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
479
|
-
//update delayIndex for wait command message time
|
|
480
|
-
await voiceChannel.saveDelayIndexForCallId(from)
|
|
481
|
-
}
|
|
482
569
|
|
|
570
|
+
let start_promise_message= new Date();
|
|
571
|
+
let message = await new Promise(async (resolve, reject) => {
|
|
572
|
+
winston.debug('(voice) ******* user text -->'+ usertext)
|
|
573
|
+
if(usertext === '' || !usertext){
|
|
574
|
+
|
|
575
|
+
let start_time_get_message = new Date()
|
|
576
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
577
|
+
let end_time_get_message = new Date()
|
|
578
|
+
winston.verbose(`(if) Time to getMessage from queue in /speechresult/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]`)
|
|
579
|
+
resolve(message)
|
|
580
|
+
}else{
|
|
581
|
+
//CASE:usertext is not empty and queue is empty --> send message to tiledesk and manage delayTime
|
|
582
|
+
let tiledeskMessage= {
|
|
583
|
+
text:usertext,
|
|
584
|
+
senderFullname: from,
|
|
585
|
+
type: 'text',
|
|
586
|
+
channel: { name: CHANNEL_NAME }
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
let start_time_send_message = new Date()
|
|
590
|
+
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
591
|
+
let end_time_send_message = new Date()
|
|
592
|
+
winston.debug("message sent : ", tdMessage);
|
|
593
|
+
winston.verbose(`(else) Time to send message to tiledesk in /speechresult/${callSid} : ${(end_time_send_message-start_time_send_message)}[ms] with text ` + tdMessage.text + ' --- at time:' + new Date())
|
|
594
|
+
|
|
595
|
+
let start_time_get_message = new Date()
|
|
596
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
597
|
+
let end_time_get_message = new Date()
|
|
598
|
+
winston.verbose(`(else) Time to getMessage from queue in /speechresult/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
|
|
599
|
+
resolve(message)
|
|
600
|
+
|
|
601
|
+
//generate Tiledesk wait message
|
|
602
|
+
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
603
|
+
// let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
604
|
+
// //update delayIndex for wait command message time
|
|
605
|
+
// await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
606
|
+
// resolve(message)
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
let end_promise_message = new Date()
|
|
610
|
+
winston.verbose(`Time to manage message in Promise /speechresult/${callSid}: ${(end_promise_message-start_promise_message)}[ms]` + ' with text' + message.text + ' --- at time --' + new Date())
|
|
611
|
+
|
|
483
612
|
// convert response to vxml
|
|
484
613
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
485
|
-
winston.
|
|
614
|
+
winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
|
|
486
615
|
|
|
616
|
+
let end_call = new Date()
|
|
617
|
+
winston.info(`Time to respond to /speechresult/${callId} : ${(end_call-start_call)} [ms]`)
|
|
487
618
|
|
|
488
619
|
// Render the response as XML in reply to the webhook request
|
|
489
|
-
res.set('Content-Type', '
|
|
620
|
+
res.set('Content-Type', 'application/xml');
|
|
490
621
|
res.status(200).send(messageToVXML);
|
|
491
|
-
|
|
492
|
-
let end_call = new Date()
|
|
493
|
-
console.log('Time to responde to /speechresult/:callSid : ', end_call-start_call, '[ms]')
|
|
494
|
-
|
|
495
622
|
})
|
|
496
623
|
|
|
497
624
|
|
|
498
625
|
router.post('/menublock/:callSid', async (req, res) => {
|
|
499
|
-
winston.verbose("(voice) called POST /menu", req.body);
|
|
500
|
-
winston.verbose("(voice) called POST /menu query" , req.query);
|
|
501
626
|
let start_call = new Date().getTime();
|
|
502
|
-
|
|
627
|
+
winston.debug("(voice) called POST /menu", req.body);
|
|
628
|
+
winston.debug("(voice) called POST /menu query" , req.query);
|
|
629
|
+
winston.verbose('/menublock at: ', new Date(), 'with text:', req.body.Digits)
|
|
503
630
|
|
|
504
631
|
let message_text = '';
|
|
505
632
|
let attributes = {};
|
|
@@ -548,7 +675,7 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
548
675
|
let project_id, conversation_id, user;
|
|
549
676
|
let from, to;
|
|
550
677
|
|
|
551
|
-
let redis_data = await redis_client.get('tiledesk:
|
|
678
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
552
679
|
if (!redis_data) {
|
|
553
680
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
554
681
|
}
|
|
@@ -577,7 +704,6 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
577
704
|
uploadService: uploadService
|
|
578
705
|
});
|
|
579
706
|
|
|
580
|
-
let startSend = new Date().getTime();
|
|
581
707
|
//send message to tiledesk
|
|
582
708
|
let tiledeskMessage= {
|
|
583
709
|
text:message_text,
|
|
@@ -586,30 +712,35 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
586
712
|
channel: { name: CHANNEL_NAME },
|
|
587
713
|
attributes: attributes
|
|
588
714
|
};
|
|
589
|
-
let
|
|
590
|
-
|
|
591
|
-
|
|
715
|
+
let response = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
716
|
+
if(!response){
|
|
717
|
+
return res.status(503).send({ message: "Bad response: Quota exceeded" })
|
|
718
|
+
}
|
|
592
719
|
|
|
593
|
-
|
|
594
|
-
let
|
|
595
|
-
let
|
|
596
|
-
|
|
597
|
-
|
|
720
|
+
let start_time_get_message = new Date()
|
|
721
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
722
|
+
let end_time_get_message = new Date()
|
|
723
|
+
winston.verbose(`Time to getMessage from queue in /menublock/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
|
|
724
|
+
|
|
725
|
+
// //generate Tiledesk wait message
|
|
726
|
+
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
727
|
+
// let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
728
|
+
// //update delayIndex for wait command message time
|
|
729
|
+
// await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
598
730
|
|
|
599
731
|
// convert response to vxml
|
|
600
732
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
601
|
-
winston.
|
|
733
|
+
winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
|
|
602
734
|
|
|
603
|
-
res.set('Content-Type', 'text/xml');
|
|
604
|
-
res.status(200).send(messageToVXML);
|
|
605
|
-
|
|
606
735
|
let end_call = new Date().getTime();
|
|
607
|
-
|
|
736
|
+
winston.info(`Time to respond to /menublock/${callSid} : ${(end_call-start_call)} [ms]`)
|
|
608
737
|
|
|
738
|
+
res.set('Content-Type', 'application/xml');
|
|
739
|
+
res.status(200).send(messageToVXML);
|
|
609
740
|
});
|
|
610
741
|
|
|
611
742
|
router.post('/handle/:callSid/:event', async (req, res) => {
|
|
612
|
-
winston.
|
|
743
|
+
winston.debug("(voice) called POST /handle", req.body);
|
|
613
744
|
winston.debug("(voice) called POST /handle query -->", req.query);
|
|
614
745
|
winston.debug("(voice) called POST /handle params-->", req.params);
|
|
615
746
|
|
|
@@ -622,7 +753,7 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
622
753
|
let project_id, conversation_id, user;
|
|
623
754
|
let from, to;
|
|
624
755
|
|
|
625
|
-
let redis_data = await redis_client.get('tiledesk:
|
|
756
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
626
757
|
if (!redis_data) {
|
|
627
758
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
628
759
|
}
|
|
@@ -678,9 +809,9 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
678
809
|
|
|
679
810
|
// convert response to vxml
|
|
680
811
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes,sessionInfo)
|
|
681
|
-
winston.
|
|
812
|
+
winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
|
|
682
813
|
|
|
683
|
-
res.set('Content-Type', '
|
|
814
|
+
res.set('Content-Type', 'application/xml');
|
|
684
815
|
res.status(200).send(messageToVXML);
|
|
685
816
|
|
|
686
817
|
});
|
|
@@ -688,7 +819,7 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
688
819
|
|
|
689
820
|
/* ----> catch Event block <----- */
|
|
690
821
|
router.post('/event/:callSid/:event', async(req, res)=> {
|
|
691
|
-
winston.
|
|
822
|
+
winston.debug("(voice) called POST /event" , req.params);
|
|
692
823
|
winston.debug("(voice) called POST /event query" , req.query);
|
|
693
824
|
winston.debug("(voice) called POST /event body" , req.body);
|
|
694
825
|
|
|
@@ -707,7 +838,7 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
707
838
|
let project_id, conversation_id, user;
|
|
708
839
|
let from, to;
|
|
709
840
|
|
|
710
|
-
let redis_data = await redis_client.get('tiledesk:
|
|
841
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
711
842
|
if (!redis_data) {
|
|
712
843
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
713
844
|
}
|
|
@@ -783,16 +914,16 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
783
914
|
|
|
784
915
|
// convert response to vxml
|
|
785
916
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
786
|
-
winston.
|
|
917
|
+
winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
|
|
787
918
|
|
|
788
|
-
res.set('Content-Type', '
|
|
919
|
+
res.set('Content-Type', 'application/xml');
|
|
789
920
|
res.status(200).send(messageToVXML);
|
|
790
921
|
})
|
|
791
922
|
|
|
792
923
|
|
|
793
924
|
/* ----> catch Twilio Events <----- */
|
|
794
925
|
router.post('/twilio/status',async (req, res) => {
|
|
795
|
-
winston.
|
|
926
|
+
winston.debug('+++++++++++(voice) called POST twilio/status ', req.body);
|
|
796
927
|
|
|
797
928
|
let event = req.body.CallStatus;
|
|
798
929
|
let callSid = req.body.CallSid;
|
|
@@ -806,7 +937,7 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
806
937
|
let project_id, conversation_id, user;
|
|
807
938
|
let from, to;
|
|
808
939
|
|
|
809
|
-
let redis_data = await redis_client.get('tiledesk:
|
|
940
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
810
941
|
if (!redis_data) {
|
|
811
942
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
812
943
|
}
|
|
@@ -858,8 +989,8 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
858
989
|
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
859
990
|
|
|
860
991
|
//remove session data for current callId and relative queue data
|
|
861
|
-
await redis_client.del('tiledesk:
|
|
862
|
-
await redis_client.del('tiledesk:
|
|
992
|
+
await redis_client.del('tiledesk:voice:'+callSid+':session');
|
|
993
|
+
await redis_client.del('tiledesk:voice:'+callSid+':delayIndex');
|
|
863
994
|
await tdChannel.clearQueue(conversation_id);
|
|
864
995
|
break;
|
|
865
996
|
}
|
|
@@ -872,17 +1003,17 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
872
1003
|
|
|
873
1004
|
|
|
874
1005
|
router.post('/twilio/fail',async (req, res) => {
|
|
875
|
-
winston.
|
|
1006
|
+
winston.debug('+++++++++++(voice) called POST twilio/fail ', req.params)
|
|
876
1007
|
winston.debug('+++++++++++(voice) called POST twilio/fail ', req.body)
|
|
877
1008
|
|
|
878
|
-
res.set('Content-Type', '
|
|
1009
|
+
res.set('Content-Type', 'application/xml');
|
|
879
1010
|
res.status(200).send('<Response></Response>');
|
|
880
1011
|
})
|
|
881
1012
|
|
|
882
1013
|
|
|
883
1014
|
/* ----> catch Twilio Events <----- */
|
|
884
1015
|
router.post('/record/:callSid/',async (req, res) => {
|
|
885
|
-
winston.
|
|
1016
|
+
winston.debug('+++++++++++(voice) called POST record/:callSid ', req.body);
|
|
886
1017
|
|
|
887
1018
|
let callSid = req.body.CallSid;
|
|
888
1019
|
let audioFileUrl = req.body.RecordingUrl;
|
|
@@ -891,7 +1022,7 @@ router.post('/record/:callSid/',async (req, res) => {
|
|
|
891
1022
|
let project_id, conversation_id, user;
|
|
892
1023
|
let from, to;
|
|
893
1024
|
|
|
894
|
-
let redis_data = await redis_client.get('tiledesk:
|
|
1025
|
+
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
895
1026
|
if (!redis_data) {
|
|
896
1027
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
897
1028
|
}
|
|
@@ -968,9 +1099,9 @@ router.post('/record/:callSid/',async (req, res) => {
|
|
|
968
1099
|
|
|
969
1100
|
// convert response to vxml
|
|
970
1101
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
971
|
-
winston.
|
|
1102
|
+
winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
|
|
972
1103
|
|
|
973
|
-
res.set('Content-Type', '
|
|
1104
|
+
res.set('Content-Type', 'application/xml');
|
|
974
1105
|
res.status(200).send(messageToVXML);
|
|
975
1106
|
|
|
976
1107
|
})
|
|
@@ -988,7 +1119,7 @@ async function generateSTT(audioFileUrl, attributes, sessionInfo, settings){
|
|
|
988
1119
|
let keep_going = await aiService.checkQuoteAvailability(sessionInfo.project_id, settings.token).catch((err)=>{
|
|
989
1120
|
winston.error('errr while checkQuoteAvailability for project:', sessionInfo.project_id, err.response?.data)
|
|
990
1121
|
})
|
|
991
|
-
winston.
|
|
1122
|
+
winston.debug('(voice) checkQuoteAvailability return: '+ keep_going);
|
|
992
1123
|
if(!keep_going){
|
|
993
1124
|
//no token is available --> close conversation
|
|
994
1125
|
return tiledeskMessage= {
|
|
@@ -1048,8 +1179,8 @@ async function generateSTT(audioFileUrl, attributes, sessionInfo, settings){
|
|
|
1048
1179
|
|
|
1049
1180
|
|
|
1050
1181
|
router.get('/addon/transcript', async (req, res) => {
|
|
1051
|
-
winston.
|
|
1052
|
-
winston.debug("(
|
|
1182
|
+
winston.debug("(voice) called GET /transcript query-->" , req.query);
|
|
1183
|
+
winston.debug("(voice) called GET /transcript body -->" , req.body);
|
|
1053
1184
|
|
|
1054
1185
|
res.status(200).send('ok');
|
|
1055
1186
|
|
|
@@ -1057,7 +1188,7 @@ router.get('/addon/transcript', async (req, res) => {
|
|
|
1057
1188
|
|
|
1058
1189
|
/** --> only for test purpose <-- **/
|
|
1059
1190
|
router.get('/test', async (req, res) => {
|
|
1060
|
-
winston.
|
|
1191
|
+
winston.debug("(voice) called GET /test" , req.query);
|
|
1061
1192
|
|
|
1062
1193
|
let project_id = req.query.id_project;
|
|
1063
1194
|
let callSid = req.body.CallSid;
|
|
@@ -1203,7 +1334,7 @@ router.get('/test', async (req, res) => {
|
|
|
1203
1334
|
//let disconnect = await tdChannel.disconnect();
|
|
1204
1335
|
|
|
1205
1336
|
|
|
1206
|
-
res.set('Content-Type', '
|
|
1337
|
+
res.set('Content-Type', 'application/xml');
|
|
1207
1338
|
res.status(200).send(messageToVXML);
|
|
1208
1339
|
|
|
1209
1340
|
//res.status(200).send(message);
|
|
@@ -1297,7 +1428,7 @@ async function startApp(settings, callback) {
|
|
|
1297
1428
|
}
|
|
1298
1429
|
|
|
1299
1430
|
if(settings.MAX_POLLING_TIME){
|
|
1300
|
-
MAX_POLLING_TIME = settings.MAX_POLLING_TIME/
|
|
1431
|
+
MAX_POLLING_TIME = settings.MAX_POLLING_TIME/1000; //convert in seconds
|
|
1301
1432
|
}
|
|
1302
1433
|
|
|
1303
1434
|
if (settings.REDIS_HOST && settings.REDIS_PORT) {
|
package/logs/app.log
CHANGED
|
@@ -2948,3 +2948,43 @@ info: (voice) Starting Manage Route
|
|
|
2948
2948
|
info: (voice)-MANAGE API_URL: https://tiledesk-server-pre.herokuapp.com
|
|
2949
2949
|
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
2950
2950
|
info: (voice)-MANAGE redis_client: [object Object]
|
|
2951
|
+
info: (voice) Starting VOICE TWILIO App
|
|
2952
|
+
info: (voice) Starting Manage Route
|
|
2953
|
+
info: (voice)-MANAGE API_URL: https://tiledesk-server-pre.herokuapp.com
|
|
2954
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
2955
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
2956
|
+
error: uncaughtException: listen EADDRINUSE: address already in use :::3000
|
|
2957
|
+
Error: listen EADDRINUSE: address already in use :::3000
|
|
2958
|
+
at Server.setupListenHandle [as _listen2] (node:net:1463:16)
|
|
2959
|
+
at listenInCluster (node:net:1511:12)
|
|
2960
|
+
at Server.listen (node:net:1599:7)
|
|
2961
|
+
at Function.listen (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/application.js:635:24)
|
|
2962
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/index.js:48:9
|
|
2963
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/index.js:1344:9
|
|
2964
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/tiledesk/KVBaseMongo.js:34:9
|
|
2965
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/utils.js:704:5
|
|
2966
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/mongo_client.js:286:7
|
|
2967
|
+
at connectCallback (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/operations/connect.js:367:5)
|
|
2968
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/operations/connect.js:557:5
|
|
2969
|
+
at connectHandler (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/topology.js:298:43)
|
|
2970
|
+
at cb (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/topology.js:690:26)
|
|
2971
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/cmap/connection_pool.js:352:13
|
|
2972
|
+
at handleOperationResult (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/server.js:567:5)
|
|
2973
|
+
at MessageStream.messageHandler (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/cmap/connection.js:308:5) {"date":"Tue Sep 16 2025 14:57:01 GMT+0200 (Ora legale dell’Europa centrale)","error":{"address":"::","code":"EADDRINUSE","errno":-48,"port":3000,"syscall":"listen"},"exception":true,"os":{"loadavg":[4.11376953125,4.04150390625,3.8125],"uptime":415993},"process":{"argv":["/Users/gabriele95/.nvm/versions/node/v16.19.0/bin/node","/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/index.js"],"cwd":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector","execPath":"/Users/gabriele95/.nvm/versions/node/v16.19.0/bin/node","gid":20,"memoryUsage":{"arrayBuffers":18326832,"external":19286742,"heapTotal":74727424,"heapUsed":44398688,"rss":119177216},"pid":64036,"uid":501,"version":"v16.19.0"},"stack":"Error: listen EADDRINUSE: address already in use :::3000\n at Server.setupListenHandle [as _listen2] (node:net:1463:16)\n at listenInCluster (node:net:1511:12)\n at Server.listen (node:net:1599:7)\n at Function.listen (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/application.js:635:24)\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/index.js:48:9\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/index.js:1344:9\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/tiledesk/KVBaseMongo.js:34:9\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/utils.js:704:5\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/mongo_client.js:286:7\n at connectCallback (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/operations/connect.js:367:5)\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/operations/connect.js:557:5\n at connectHandler (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/topology.js:298:43)\n at cb (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/topology.js:690:26)\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/cmap/connection_pool.js:352:13\n at handleOperationResult (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/server.js:567:5)\n at MessageStream.messageHandler (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/cmap/connection.js:308:5)","trace":[{"column":16,"file":"node:net","function":"Server.setupListenHandle [as _listen2]","line":1463,"method":"setupListenHandle [as _listen2]","native":false},{"column":12,"file":"node:net","function":"listenInCluster","line":1511,"method":null,"native":false},{"column":7,"file":"node:net","function":"Server.listen","line":1599,"method":"listen","native":false},{"column":24,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/application.js","function":"Function.listen","line":635,"method":"listen","native":false},{"column":9,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/index.js","function":null,"line":48,"method":null,"native":false},{"column":9,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/index.js","function":null,"line":1344,"method":null,"native":false},{"column":9,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/tiledesk/KVBaseMongo.js","function":null,"line":34,"method":null,"native":false},{"column":5,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/utils.js","function":null,"line":704,"method":null,"native":false},{"column":7,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/mongo_client.js","function":null,"line":286,"method":null,"native":false},{"column":5,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/operations/connect.js","function":"connectCallback","line":367,"method":null,"native":false},{"column":5,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/operations/connect.js","function":null,"line":557,"method":null,"native":false},{"column":43,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/topology.js","function":"connectHandler","line":298,"method":null,"native":false},{"column":26,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/topology.js","function":"cb","line":690,"method":null,"native":false},{"column":13,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/cmap/connection_pool.js","function":null,"line":352,"method":null,"native":false},{"column":5,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/core/sdam/server.js","function":"handleOperationResult","line":567,"method":null,"native":false},{"column":5,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/node_modules/mongodb/lib/cmap/connection.js","function":"MessageStream.messageHandler","line":308,"method":"messageHandler","native":false}]}
|
|
2974
|
+
info: (voice) Starting VOICE TWILIO App
|
|
2975
|
+
info: (voice) Starting Manage Route
|
|
2976
|
+
info: (voice)-MANAGE API_URL: https://tiledesk-server-pre.herokuapp.com
|
|
2977
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
2978
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
2979
|
+
error: uncaughtException: invalid parameter format
|
|
2980
|
+
TypeError: invalid parameter format
|
|
2981
|
+
at Object.parse (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/content-type/index.js:162:13)
|
|
2982
|
+
at setCharset (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/utils.js:252:28)
|
|
2983
|
+
at ServerResponse.send (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/response.js:175:32)
|
|
2984
|
+
at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/index.js:337:19
|
|
2985
|
+
at processTicksAndRejections (node:internal/process/task_queues:96:5) {"date":"Tue Sep 16 2025 14:58:17 GMT+0200 (Ora legale dell’Europa centrale)","error":{},"exception":true,"os":{"loadavg":[4.119140625,4.09326171875,3.8505859375],"uptime":416069},"process":{"argv":["/Users/gabriele95/.nvm/versions/node/v16.19.0/bin/node","/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/index.js"],"cwd":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector","execPath":"/Users/gabriele95/.nvm/versions/node/v16.19.0/bin/node","gid":20,"memoryUsage":{"arrayBuffers":18358478,"external":19478426,"heapTotal":37765120,"heapUsed":34590424,"rss":55885824},"pid":64617,"uid":501,"version":"v16.19.0"},"stack":"TypeError: invalid parameter format\n at Object.parse (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/content-type/index.js:162:13)\n at setCharset (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/utils.js:252:28)\n at ServerResponse.send (/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/response.js:175:32)\n at /Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/index.js:337:19\n at processTicksAndRejections (node:internal/process/task_queues:96:5)","trace":[{"column":13,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/content-type/index.js","function":"Object.parse","line":162,"method":"parse","native":false},{"column":28,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/utils.js","function":"setCharset","line":252,"method":null,"native":false},{"column":32,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/node_modules/express/lib/response.js","function":"ServerResponse.send","line":175,"method":"send","native":false},{"column":19,"file":"/Users/gabriele95/Desktop/Tiledesk/channels-connector/tiledesk-voice-twilio-connector/voiceRoute/index.js","function":null,"line":337,"method":null,"native":false},{"column":5,"file":"node:internal/process/task_queues","function":"processTicksAndRejections","line":96,"method":null,"native":false}]}
|
|
2986
|
+
info: (voice) Starting VOICE TWILIO App
|
|
2987
|
+
info: (voice) Starting Manage Route
|
|
2988
|
+
info: (voice)-MANAGE API_URL: https://tiledesk-server-pre.herokuapp.com
|
|
2989
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
2990
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
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.26-rc10",
|
|
4
4
|
"description": "Tiledesk VOICE Twilio connector",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Gabriele Panico",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"start": "node index.js"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": "
|
|
18
|
-
"npm": "8.15.0"
|
|
17
|
+
"node": ">=18.0.0",
|
|
18
|
+
"npm": ">=8.15.0"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [],
|
|
21
21
|
"dependencies": {
|
package/routes/manageApp.js
CHANGED
|
@@ -55,7 +55,7 @@ router.get("/", async (req, res) => {
|
|
|
55
55
|
|
|
56
56
|
router.get('/configure', async (req, res) => {
|
|
57
57
|
|
|
58
|
-
winston.
|
|
58
|
+
winston.debug("(voice) /configure :params", req.query);
|
|
59
59
|
|
|
60
60
|
let project_id = req.query.project_id;
|
|
61
61
|
let token = req.query.token;
|
|
@@ -89,7 +89,7 @@ router.get('/configure', async (req, res) => {
|
|
|
89
89
|
let CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
|
|
90
90
|
|
|
91
91
|
let settings = await db.get(CONTENT_KEY);
|
|
92
|
-
winston.
|
|
92
|
+
winston.debug("(voice) settings: ", settings);
|
|
93
93
|
|
|
94
94
|
// get departments
|
|
95
95
|
const tdChannel = new TiledeskChannel({
|
|
@@ -157,7 +157,7 @@ router.get('/configure', async (req, res) => {
|
|
|
157
157
|
})
|
|
158
158
|
|
|
159
159
|
router.post('/update', async (req, res) => {
|
|
160
|
-
winston.
|
|
160
|
+
winston.debug("(voice) /update", req.body);
|
|
161
161
|
|
|
162
162
|
let project_id = req.body.project_id;
|
|
163
163
|
let token = req.body.token;
|
|
@@ -168,7 +168,7 @@ router.post('/update', async (req, res) => {
|
|
|
168
168
|
let CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
|
|
169
169
|
let settings = await db.get(CONTENT_KEY);
|
|
170
170
|
|
|
171
|
-
winston.
|
|
171
|
+
winston.debug("(voice) /update settings", settings);
|
|
172
172
|
|
|
173
173
|
let proxy_url = BASE_URL + "/webhook/" + project_id;
|
|
174
174
|
let status_url = BASE_URL + "/twilio/status";
|
|
@@ -264,7 +264,7 @@ router.post('/update', async (req, res) => {
|
|
|
264
264
|
|
|
265
265
|
router.post('/disconnect', async (req, res) => {
|
|
266
266
|
|
|
267
|
-
winston.
|
|
267
|
+
winston.debug("(voice) /disconnect")
|
|
268
268
|
|
|
269
269
|
let project_id = req.body.project_id;
|
|
270
270
|
let token = req.body.token;
|
|
@@ -273,7 +273,7 @@ router.post('/disconnect', async (req, res) => {
|
|
|
273
273
|
|
|
274
274
|
let CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
|
|
275
275
|
await db.remove(CONTENT_KEY);
|
|
276
|
-
winston.
|
|
276
|
+
winston.debug("(voice) Content deleted.");
|
|
277
277
|
|
|
278
278
|
let proxy_url = BASE_URL + "/webhook/" + project_id;
|
|
279
279
|
let status_url = BASE_URL + "/twilio/status";
|
|
@@ -327,7 +327,7 @@ async function subscribe(token, project_id){
|
|
|
327
327
|
return new Promise((resolve, reject)=> {
|
|
328
328
|
tdClient.subscribe(subscription_info).then(async (data)=> {
|
|
329
329
|
let subscription = data;
|
|
330
|
-
winston.
|
|
330
|
+
winston.debug("(voice) Subscription: ", subscription)
|
|
331
331
|
|
|
332
332
|
let settings = {
|
|
333
333
|
project_id: project_id,
|
|
@@ -352,7 +352,7 @@ async function unsubscribe(token, project_id, subscriptionId){
|
|
|
352
352
|
|
|
353
353
|
return new Promise((resolve, reject)=> {
|
|
354
354
|
tdClient.unsubscribe(subscriptionId).then(async (data)=> {
|
|
355
|
-
winston.
|
|
355
|
+
winston.debug("(voice) Subscription: ", data)
|
|
356
356
|
resolve(data)
|
|
357
357
|
}).catch((error)=> {reject(error) })
|
|
358
358
|
|
package/template/configure.html
CHANGED
|
@@ -6,26 +6,26 @@
|
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
8
|
|
|
9
|
-
<!--
|
|
10
|
-
<link rel="stylesheet" href="https://
|
|
11
|
-
|
|
9
|
+
<!-- Bootstrap 5.3 CSS -->
|
|
10
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
|
11
|
+
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
|
12
|
+
crossorigin="anonymous">
|
|
12
13
|
|
|
13
|
-
<!--
|
|
14
|
-
<link rel="stylesheet" href="https://
|
|
15
|
-
integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
|
|
16
|
-
|
|
17
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
14
|
+
<!-- Font Awesome 7 (latest version) -->
|
|
15
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css" crossorigin="anonymous" />
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
<
|
|
17
|
+
<!-- Font Poppins -->
|
|
18
|
+
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet" />
|
|
19
|
+
|
|
20
|
+
<!-- Custom CSS -->
|
|
21
|
+
<link rel="stylesheet" href="./css/configure.css">
|
|
21
22
|
|
|
22
|
-
<!--
|
|
23
|
-
<script src="https://
|
|
24
|
-
|
|
23
|
+
<!-- Bootstrap 5.3 JavaScript Bundle (include Popper) -->
|
|
24
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
|
25
|
+
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
|
26
|
+
crossorigin="anonymous">
|
|
25
27
|
</script>
|
|
26
28
|
|
|
27
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
28
|
-
<link rel="stylesheet" href="./css/configure.css">
|
|
29
29
|
|
|
30
30
|
<style>
|
|
31
31
|
body {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
body {
|
|
4
4
|
padding: 20px 0px;
|
|
5
|
+
font-size: 14px;
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
.header {
|
|
@@ -105,6 +106,9 @@ ul {
|
|
|
105
106
|
margin-top: 20px;
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
.form-group {
|
|
110
|
+
margin-bottom: 15px;
|
|
111
|
+
}
|
|
108
112
|
|
|
109
113
|
.input-label {
|
|
110
114
|
margin-bottom: 5px;
|
|
@@ -132,7 +136,7 @@ ul {
|
|
|
132
136
|
padding: 0px 8px;
|
|
133
137
|
cursor: pointer;
|
|
134
138
|
margin-top: 12px;
|
|
135
|
-
margin-left: -
|
|
139
|
+
margin-left: -66px;
|
|
136
140
|
font-size: 18px;
|
|
137
141
|
}
|
|
138
142
|
|
|
@@ -145,6 +149,12 @@ ul {
|
|
|
145
149
|
padding: 6px 36px 6px 14px;
|
|
146
150
|
}
|
|
147
151
|
|
|
152
|
+
input[readonly],
|
|
153
|
+
textarea[readonly] {
|
|
154
|
+
background-color: #eee !important;
|
|
155
|
+
opacity: 1;
|
|
156
|
+
}
|
|
157
|
+
|
|
148
158
|
.custom-input {
|
|
149
159
|
height: 45px;
|
|
150
160
|
box-shadow: 0px 0px 7px 0px #bdbdbd;
|
|
@@ -168,6 +178,15 @@ ul {
|
|
|
168
178
|
box-shadow: 0px 0px 7px 0px #bdbdbd;
|
|
169
179
|
border: solid 2px transparent;
|
|
170
180
|
border-radius: 6px;
|
|
181
|
+
appearance: none; /* Rimuove lo stile di default */
|
|
182
|
+
-webkit-appearance: none; /* Safari */
|
|
183
|
+
-moz-appearance: none; /* Firefox */
|
|
184
|
+
background-color: #fff;
|
|
185
|
+
background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='gray' class='bi bi-caret-down-fill' viewBox='0 0 16 16'%3E%3Cpath d='M7.247 11.14l-4.796-5.481C2.122 5.332 2.482 4.5 3.252 4.5h9.496c.77 0 1.13.832.801 1.159l-4.796 5.481a1 1 0 0 1-1.506 0z'/%3E%3C/svg%3E");
|
|
186
|
+
background-repeat: no-repeat;
|
|
187
|
+
background-position: right 0.75rem center; /* posizionamento freccia */
|
|
188
|
+
background-size: 12px;
|
|
189
|
+
padding-right: 2rem; /* spazio per la freccia */
|
|
171
190
|
}
|
|
172
191
|
|
|
173
192
|
.custom-select:hover {
|
|
@@ -199,6 +218,7 @@ ul {
|
|
|
199
218
|
width: 300px;
|
|
200
219
|
background-color: #8c8e8fbd;
|
|
201
220
|
font-size: 13px;
|
|
221
|
+
font-weight: 400;
|
|
202
222
|
color: #fff;
|
|
203
223
|
text-align: center;
|
|
204
224
|
padding: 8px 8px;
|
|
@@ -340,6 +360,7 @@ ul {
|
|
|
340
360
|
flex-direction: column;
|
|
341
361
|
align-items: center;
|
|
342
362
|
justify-content: center;
|
|
363
|
+
font-size: 14px;
|
|
343
364
|
}
|
|
344
365
|
|
|
345
366
|
.error-modal {
|
package/template/error.html
CHANGED
|
@@ -6,27 +6,26 @@
|
|
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
8
|
|
|
9
|
-
<!--
|
|
10
|
-
<link rel="stylesheet" href="https://
|
|
11
|
-
|
|
9
|
+
<!-- Bootstrap 5.3 CSS -->
|
|
10
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
|
|
11
|
+
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
|
|
12
|
+
crossorigin="anonymous">
|
|
12
13
|
|
|
13
|
-
<!--
|
|
14
|
-
<link rel="stylesheet" href="https://
|
|
15
|
-
integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
|
|
16
|
-
|
|
17
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
14
|
+
<!-- Font Awesome 7 (latest version) -->
|
|
15
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css" crossorigin="anonymous" />
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
<!-- Latest compiled and minified JavaScript -->
|
|
23
|
-
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"
|
|
24
|
-
integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous">
|
|
25
|
-
</script>
|
|
17
|
+
<!-- Font Poppins -->
|
|
18
|
+
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap" rel="stylesheet" />
|
|
26
19
|
|
|
27
|
-
|
|
20
|
+
<!-- Custom CSS -->
|
|
28
21
|
<link rel="stylesheet" href="./css/error.css">
|
|
29
22
|
|
|
23
|
+
<!-- Bootstrap 5.3 JavaScript Bundle (include Popper) -->
|
|
24
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
|
|
25
|
+
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
|
|
26
|
+
crossorigin="anonymous">
|
|
27
|
+
</script>
|
|
28
|
+
|
|
30
29
|
<style>
|
|
31
30
|
body {
|
|
32
31
|
font-family: 'Poppins', serif;
|
package/tiledesk/KVBaseMongo.js
CHANGED
|
@@ -221,6 +221,7 @@ class TiledeskChannel {
|
|
|
221
221
|
//push element to queue list and set expiration time
|
|
222
222
|
await this.redis_client.rPush('tiledesk:queue:'+conversation_id, JSON.stringify(message));
|
|
223
223
|
this.redis_client.expire('tiledesk:queue:'+conversation_id, 86400)
|
|
224
|
+
await this.redis_client.publish('tiledesk:conversation:'+conversation_id, JSON.stringify(message));
|
|
224
225
|
|
|
225
226
|
}
|
|
226
227
|
|
|
@@ -239,6 +240,71 @@ class TiledeskChannel {
|
|
|
239
240
|
}
|
|
240
241
|
return messages;
|
|
241
242
|
}
|
|
243
|
+
|
|
244
|
+
/** PUBLISH MESSAGE TO REDIS TOPIC **/
|
|
245
|
+
async publishMessageToTopic(message){
|
|
246
|
+
|
|
247
|
+
/*SKIP INFO MESSAGES*/
|
|
248
|
+
if(utils.messageType(TYPE_MESSAGE.INFO, message)){
|
|
249
|
+
winston.debug("> SKIPPING INFO message: " + JSON.stringify(message) );
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/*SKIP CURRENT USER MESSAGES*/
|
|
254
|
+
if (message.sender.indexOf("vxml") > -1) {
|
|
255
|
+
winston.debug("> SKIPPING ECHO message: " + JSON.stringify(message) );
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
winston.debug("> SAVE message TO QUEUE: " + JSON.stringify(message) );
|
|
260
|
+
|
|
261
|
+
let conversation_id = message.recipient
|
|
262
|
+
//PUBLISH MESSAGE TO REDIS TOPIC WITH KET tiledesk:queue:+conversation_id
|
|
263
|
+
await this.redis_client.publish('tiledesk:conversation:'+conversation_id, JSON.stringify(message));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** SUBSCRIBE TO REDIS TOPIC */
|
|
267
|
+
async subscribeToTopic(conversation_id){
|
|
268
|
+
const topic = `tiledesk:conversation:${conversation_id}`;
|
|
269
|
+
console.log("subscribeToTopic: " + topic);
|
|
270
|
+
|
|
271
|
+
// duplichi il client principale
|
|
272
|
+
const subscriber = this.redis_client.duplicate();
|
|
273
|
+
await subscriber.connect();
|
|
274
|
+
|
|
275
|
+
let resolved = false; // flag per assicurarsi che risolva solo una volta
|
|
276
|
+
|
|
277
|
+
return new Promise((resolve, reject) => {
|
|
278
|
+
const cleanup = async () => {
|
|
279
|
+
try {
|
|
280
|
+
await subscriber.unsubscribe(topic);
|
|
281
|
+
await subscriber.quit();
|
|
282
|
+
} catch (err) {
|
|
283
|
+
// ignora errori di chiusura
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
subscriber.subscribe(topic, async (message) => {
|
|
288
|
+
if (resolved) return; // se già risolto, ignora altri messaggi
|
|
289
|
+
resolved = true;
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
const json = JSON.parse(message);
|
|
293
|
+
resolve(json);
|
|
294
|
+
} catch (err) {
|
|
295
|
+
reject(err);
|
|
296
|
+
} finally {
|
|
297
|
+
cleanup(); // chiudi il subscriber appena arriva il primo messaggio
|
|
298
|
+
}
|
|
299
|
+
}).catch(async (err) => {
|
|
300
|
+
if (!resolved) {
|
|
301
|
+
resolved = true;
|
|
302
|
+
reject(err);
|
|
303
|
+
await cleanup();
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
}
|
|
242
308
|
|
|
243
309
|
/** REMOVE MESSAGE FROM REDIS QUEUE LIST **/
|
|
244
310
|
async removeMessageFromQueue(conversation_id, message_id){
|
|
@@ -315,9 +315,9 @@ class TiledeskTwilioTranslator {
|
|
|
315
315
|
//if(xmlAttributes && xmlAttributes.noInputTimeout){
|
|
316
316
|
// gather.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
|
|
317
317
|
//}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
if(xmlAttributes && xmlAttributes.incompleteSpeechTimeout){
|
|
319
|
+
gather.att("speechTimeout", xmlAttributes.incompleteSpeechTimeout/1000 ).up();
|
|
320
|
+
}
|
|
321
321
|
|
|
322
322
|
const prompt = this.promptVXML(gather, message, xmlAttributes);
|
|
323
323
|
|
package/tiledesk/VoiceChannel.js
CHANGED
|
@@ -53,7 +53,7 @@ class VoiceChannel {
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
async getNextDelayTimeForCallId(callId){
|
|
56
|
-
const index = await this.redis_client.get('tiledesk:
|
|
56
|
+
const index = await this.redis_client.get('tiledesk:voice:'+callId + ':delayIndex');
|
|
57
57
|
if(index){
|
|
58
58
|
return this.getNextDelayTime(index)
|
|
59
59
|
}
|
|
@@ -64,31 +64,31 @@ class VoiceChannel {
|
|
|
64
64
|
async saveDelayIndexForCallId(callId){
|
|
65
65
|
|
|
66
66
|
//get value for current callId
|
|
67
|
-
const index = await this.redis_client.get('tiledesk:
|
|
67
|
+
const index = await this.redis_client.get('tiledesk:voice:'+callId + ':delayIndex');
|
|
68
68
|
if(index){
|
|
69
69
|
//increment
|
|
70
70
|
const delayIndex = (+index) +1
|
|
71
71
|
//save new index to redis
|
|
72
|
-
await this.redis_client.set('tiledesk:
|
|
72
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', delayIndex, {'EX': 86400});
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
//if index is not present: set to default (0)
|
|
76
|
-
await this.redis_client.set('tiledesk:
|
|
76
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, {'EX': 86400});
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/** RESET INDEX INTO REDIS DATA FOR CURRENT CALLID **/
|
|
80
80
|
async clearDelayTimeForCallId(callId){
|
|
81
81
|
|
|
82
82
|
//get value for current callId
|
|
83
|
-
const index = await this.redis_client.get('tiledesk:
|
|
83
|
+
const index = await this.redis_client.get('tiledesk:voice:'+callId + ':delayIndex');
|
|
84
84
|
winston.debug('clearDelayTimeForCallId: index -->'+index)
|
|
85
85
|
if(index){
|
|
86
86
|
//set index to default (0)
|
|
87
|
-
await this.redis_client.set('tiledesk:
|
|
87
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, {'EX': 86400});
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
//if index is not present: set to default (0)
|
|
91
|
-
await this.redis_client.set('tiledesk:
|
|
91
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, {'EX': 86400});
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
|
|
@@ -118,21 +118,21 @@ class VoiceChannel {
|
|
|
118
118
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
const index = await this.redis_client.get('tiledesk:
|
|
121
|
+
const index = await this.redis_client.get('tiledesk:voice:'+callId + ':attributes');
|
|
122
122
|
winston.debug('saveSettingsForCallId: attributes found -->'+index)
|
|
123
123
|
if(index){
|
|
124
124
|
//set index to default (0)
|
|
125
|
-
await this.redis_client.set('tiledesk:
|
|
125
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(flowAttributes), {'EX': 86400});
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
//if index is not present: set to default (0)
|
|
129
|
-
await this.redis_client.set('tiledesk:
|
|
129
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(flowAttributes), {'EX': 86400});
|
|
130
130
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
|
|
134
134
|
async getSettingsForCallId(callId){
|
|
135
|
-
const attributes = await this.redis_client.get('tiledesk:
|
|
135
|
+
const attributes = await this.redis_client.get('tiledesk:voice:'+callId + ':attributes');
|
|
136
136
|
if(attributes){
|
|
137
137
|
return JSON.parse(attributes)
|
|
138
138
|
}
|