@tiledesk/tiledesk-voice-twilio-connector 0.1.26-rc9 → 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 +328 -294
- package/logs/app.log +92 -0
- package/package.json +2 -1
- package/routes/manageApp.js +8 -8
- package/tiledesk/KVBaseMongo.js +1 -1
- package/tiledesk/TiledeskChannel.js +4 -4
- package/tiledesk/TiledeskTwilioTranslator.js +80 -45
- package/tiledesk/VoiceChannel.js +59 -35
- package/tiledesk/constants.js +17 -1
- package/tiledesk/errors.js +28 -0
- package/tiledesk/services/AiService.js +91 -74
- package/tiledesk/services/UploadService.js +4 -1
- package/tiledesk/services/voiceEventEmitter.js +6 -0
- package/tiledesk/utils.js +29 -1
package/index.js
CHANGED
|
@@ -73,7 +73,7 @@ let start1= ''
|
|
|
73
73
|
let time = null;
|
|
74
74
|
|
|
75
75
|
/*UTILS*/
|
|
76
|
-
const
|
|
76
|
+
const utilsMessage = require('./tiledesk/utils-message.js')
|
|
77
77
|
const TYPE_MESSAGE = require('./tiledesk/constants').TYPE_MESSAGE
|
|
78
78
|
|
|
79
79
|
let messageTimeout = null;
|
|
@@ -100,24 +100,19 @@ router.post("/tiledesk", async (req, res) => {
|
|
|
100
100
|
|
|
101
101
|
/*SKIP INFO MESSAGES*/
|
|
102
102
|
/*SKIP CURRENT USER MESSAGES*/
|
|
103
|
-
if(!
|
|
104
|
-
winston.verbose(
|
|
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)
|
|
103
|
+
if(!utilsMessage.messageType(TYPE_MESSAGE.INFO, tiledeskMessage) && !(tiledeskMessage.sender.indexOf("vxml") > -1) ){
|
|
104
|
+
winston.verbose(`> whook SAVE MESSAGE "${tiledeskMessage.text}" TO QUEUE at time ` + new Date() );
|
|
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
|
|
|
@@ -148,7 +145,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
148
145
|
|
|
149
146
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
150
147
|
BASE_URL: BASE_URL,
|
|
151
|
-
aiService: aiService
|
|
148
|
+
aiService: aiService,
|
|
149
|
+
uploadService: uploadService
|
|
152
150
|
});
|
|
153
151
|
|
|
154
152
|
let start2 = new Date().getTime();
|
|
@@ -158,32 +156,33 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
158
156
|
return;
|
|
159
157
|
}
|
|
160
158
|
let end2 = new Date().getTime();
|
|
161
|
-
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);
|
|
165
162
|
winston.debug("(voice) conversation returned:"+ conversation_id);
|
|
166
163
|
|
|
167
|
-
|
|
168
|
-
//GET AND SAVE GPT-KET IF
|
|
169
164
|
let integrations = [], publicKey = false;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
165
|
+
try {
|
|
166
|
+
//GET AND SAVE GPT-KET IF
|
|
167
|
+
let key = await integrationService.getKeyFromIntegrations(project_id, 'openai', settings.token)
|
|
168
|
+
if (!key) {
|
|
169
|
+
winston.debug("(voice) - Key not found in Integrations. Searching in kb settings...");
|
|
170
|
+
key = await integrationService.getKeyFromKbSettings(project_id, settings.token);
|
|
171
|
+
}
|
|
172
|
+
if (!key) {
|
|
173
|
+
winston.debug("(voice) - Retrieve public gptkey")
|
|
174
|
+
key = GPT_KEY;
|
|
175
|
+
publicKey = true;
|
|
176
|
+
}
|
|
177
|
+
integrations.push({type: 'openai', key: key, publicKey: publicKey})
|
|
179
178
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
179
|
+
let eleven_labs = await integrationService.getKeyFromIntegrations(project_id, 'elevenlabs', settings.token)
|
|
180
|
+
if (eleven_labs) {
|
|
181
|
+
winston.debug("(voice) - Key found in Integrations: "+ eleven_labs);
|
|
182
|
+
integrations.push({type: 'elevenlabs', key: eleven_labs, publicKey: false})
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
winston.error('(voice) - Error retrieving integrations keys:', error);
|
|
187
186
|
}
|
|
188
187
|
|
|
189
188
|
//save data to redis
|
|
@@ -196,16 +195,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
196
195
|
conversation_id: conversation_id,
|
|
197
196
|
integrations: integrations
|
|
198
197
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
//for (const [key, value] of Object.entries(redis_data)){
|
|
203
|
-
// await redis_client.hSet('tiledesk:voice:'+callId, key, JSON.stringify(value))
|
|
204
|
-
//}
|
|
205
|
-
//await redis_client.expire('tiledesk:voice:'+callId, 86400)
|
|
206
|
-
|
|
207
|
-
await redis_client.set('tiledesk:voice:'+callSid+':session', JSON.stringify(session_data), {'EX': 86400});
|
|
208
|
-
|
|
198
|
+
voiceChannel.setSessionForCallId(callSid, session_data)
|
|
199
|
+
|
|
209
200
|
let tiledeskMessage= {
|
|
210
201
|
text:'/start',
|
|
211
202
|
senderFullname: from,
|
|
@@ -229,7 +220,7 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
229
220
|
let start_time_get_message = new Date()
|
|
230
221
|
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
231
222
|
let end_time_get_message = new Date()
|
|
232
|
-
winston.verbose(
|
|
223
|
+
winston.verbose(`Time to getMessage from queue in /webhook/:${project_id} : ${(end_time_get_message-start_time_get_message)}[ms] --- at time:` + new Date())
|
|
233
224
|
|
|
234
225
|
// //generate Tiledesk wait message
|
|
235
226
|
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
@@ -239,16 +230,14 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
239
230
|
|
|
240
231
|
// send standard wait vxml message
|
|
241
232
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, session_data)
|
|
242
|
-
winston.debug('(voice) messageVXML-->'+ messageToVXML)
|
|
233
|
+
winston.debug('(voice) /webhook/:id_project messageVXML-->'+ messageToVXML)
|
|
243
234
|
|
|
235
|
+
let end_call = new Date().getTime();
|
|
236
|
+
winston.info(`Time to respond to /webhook/${project_id}: ${(end_call-start_call)}[ms]`)
|
|
244
237
|
|
|
245
238
|
// Render the response as XML in reply to the webhook request
|
|
246
239
|
res.set('Content-Type', 'text/xml');
|
|
247
240
|
res.status(200).send(messageToVXML);
|
|
248
|
-
|
|
249
|
-
let end_call = new Date().getTime();
|
|
250
|
-
console.log('Time to responde to /webhook/:id_project : ', end_call-start_call, '[ms]')
|
|
251
|
-
|
|
252
241
|
});
|
|
253
242
|
|
|
254
243
|
|
|
@@ -256,7 +245,6 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
|
|
|
256
245
|
let start_call = new Date()
|
|
257
246
|
winston.debug("(voice) called POST /nextblock ", req.body);
|
|
258
247
|
winston.debug("(voice) called POST /nextblock query ", req.query);
|
|
259
|
-
console.log('/nextblock at: ', new Date(), 'with text:', req.body.SpeechResult)
|
|
260
248
|
|
|
261
249
|
let usertext = req.body.SpeechResult;
|
|
262
250
|
let confidence = req.body.Confidence
|
|
@@ -266,8 +254,7 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
|
|
|
266
254
|
let project_id, conversation_id, user;
|
|
267
255
|
let from, to;
|
|
268
256
|
|
|
269
|
-
let redis_data = await
|
|
270
|
-
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
257
|
+
let redis_data = await voiceChannel.getSessionForCallId(callSid)
|
|
271
258
|
if (!redis_data) {
|
|
272
259
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
273
260
|
}
|
|
@@ -343,24 +330,19 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
|
|
|
343
330
|
|
|
344
331
|
router.post('/nextblock/:callSid/', async(req, res) => {
|
|
345
332
|
let start_call = new Date()
|
|
346
|
-
winston.
|
|
347
|
-
winston.debug("(voice) called POST /nextblock query ", req.query);
|
|
348
|
-
console.log("(voice) called POST /nextblock at" + new Date() + "with text: " + req.body.SpeechResult)
|
|
333
|
+
winston.verbose("(voice) called POST /nextblock at " + new Date() + "with text: "+ req.body.SpeechResult);
|
|
349
334
|
|
|
350
335
|
let usertext = req.body.SpeechResult;
|
|
351
336
|
let confidence = req.body.Confidence
|
|
352
337
|
let callSid = req.params.callSid;
|
|
353
338
|
|
|
354
|
-
let sessionInfo;
|
|
355
339
|
let project_id, conversation_id, user;
|
|
356
340
|
let from, to;
|
|
357
341
|
|
|
358
|
-
let
|
|
359
|
-
|
|
360
|
-
if (!redis_data) {
|
|
342
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
343
|
+
if (!sessionInfo) {
|
|
361
344
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
362
345
|
}
|
|
363
|
-
sessionInfo = JSON.parse(redis_data)
|
|
364
346
|
project_id = sessionInfo.project_id;
|
|
365
347
|
from = sessionInfo.from;
|
|
366
348
|
to = sessionInfo.to;
|
|
@@ -389,13 +371,13 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
389
371
|
|
|
390
372
|
let start_promise_message= new Date();
|
|
391
373
|
let message = await new Promise(async (resolve, reject) => {
|
|
392
|
-
winston.debug('******* user text -->'+ usertext)
|
|
374
|
+
winston.debug('(voice) ******* user text -->'+ usertext)
|
|
393
375
|
if(usertext === '' || !usertext){
|
|
394
376
|
|
|
395
377
|
let start_time_get_message = new Date()
|
|
396
378
|
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
397
379
|
let end_time_get_message = new Date()
|
|
398
|
-
winston.verbose(`(if) Time to getMessage from queue in /nextblock/${callSid} :
|
|
380
|
+
winston.verbose(`(if) Time to getMessage from queue in /nextblock/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]`)
|
|
399
381
|
resolve(message)
|
|
400
382
|
}else{
|
|
401
383
|
//CASE:usertext is not empty and queue is empty --> send message to tiledesk and manage delayTime
|
|
@@ -409,13 +391,12 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
409
391
|
let start_time_send_message = new Date()
|
|
410
392
|
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
411
393
|
let end_time_send_message = new Date()
|
|
412
|
-
winston.
|
|
413
|
-
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())
|
|
394
|
+
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())
|
|
414
395
|
|
|
415
396
|
let start_time_get_message = new Date()
|
|
416
397
|
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
417
398
|
let end_time_get_message = new Date()
|
|
418
|
-
winston.verbose(`(else) Time to getMessage from queue in /nextblock/${callSid} :
|
|
399
|
+
winston.verbose(`(else) Time to getMessage from queue in /nextblock/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
|
|
419
400
|
resolve(message)
|
|
420
401
|
|
|
421
402
|
//generate Tiledesk wait message
|
|
@@ -427,14 +408,14 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
427
408
|
}
|
|
428
409
|
})
|
|
429
410
|
let end_promise_message = new Date()
|
|
430
|
-
winston.verbose(`Time to manage message in Promise /nextblock/${callSid}:
|
|
411
|
+
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())
|
|
431
412
|
|
|
432
413
|
// convert response to vxml
|
|
433
414
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
434
415
|
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
435
416
|
|
|
436
417
|
let end_call = new Date()
|
|
437
|
-
|
|
418
|
+
winston.info(`Time to respond to /nextblock/${callSid} : ${(end_call-start_call)} [ms]`)
|
|
438
419
|
|
|
439
420
|
// Render the response as XML in reply to the webhook request
|
|
440
421
|
res.set('Content-Type', 'application/xml');
|
|
@@ -467,12 +448,12 @@ async function getMessage(callSid, ani, project_id, conversation_id){
|
|
|
467
448
|
|
|
468
449
|
// 1. First attempt: read from queue
|
|
469
450
|
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
470
|
-
|
|
451
|
+
winston.debug('[getMessage] /NEXT check queue length--> '+ queue.length)
|
|
471
452
|
|
|
472
453
|
if (queue && queue.length > 0) {
|
|
473
454
|
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue and reset delayTime
|
|
474
455
|
message = queue[0]
|
|
475
|
-
winston.
|
|
456
|
+
winston.verbose('[getMessage] QUEUE --> '+ queue[0].text)
|
|
476
457
|
|
|
477
458
|
// remove message from queue and reset delayIndex
|
|
478
459
|
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
@@ -493,7 +474,7 @@ async function getMessage(callSid, ani, project_id, conversation_id){
|
|
|
493
474
|
}
|
|
494
475
|
|
|
495
476
|
message = queue[0]
|
|
496
|
-
winston.
|
|
477
|
+
winston.verbose(`[getMessage] Message received from subscription: ${message.text}`);
|
|
497
478
|
|
|
498
479
|
// remove message from queue and reset delayIndex
|
|
499
480
|
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
@@ -528,26 +509,20 @@ async function getMessage(callSid, ani, project_id, conversation_id){
|
|
|
528
509
|
}
|
|
529
510
|
|
|
530
511
|
router.post('/speechresult/:callSid', async (req, res) => {
|
|
531
|
-
|
|
532
512
|
let start_call = new Date();
|
|
533
|
-
winston.verbose("(voice) called POST /speechresult "
|
|
534
|
-
winston.verbose("(voice) called POST /speechresult query ", req.query);
|
|
535
|
-
console.log('/speechresult at: ', new Date(), 'with text:', req.body.SpeechResult)
|
|
513
|
+
winston.verbose("(voice) called POST /speechresult at" + new Date() + "with text: "+ req.body.SpeechResult);
|
|
536
514
|
|
|
537
515
|
let usertext = req.body.SpeechResult;
|
|
538
516
|
let confidence = req.body.Confidence
|
|
539
517
|
let callSid = req.params.callSid;
|
|
540
518
|
|
|
541
|
-
let sessionInfo;
|
|
542
519
|
let project_id, conversation_id, user;
|
|
543
520
|
let from, to;
|
|
544
521
|
|
|
545
|
-
let
|
|
546
|
-
|
|
547
|
-
if (!redis_data) {
|
|
522
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
523
|
+
if (!sessionInfo) {
|
|
548
524
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
549
525
|
}
|
|
550
|
-
sessionInfo = JSON.parse(redis_data)
|
|
551
526
|
project_id = sessionInfo.project_id;
|
|
552
527
|
from = sessionInfo.from;
|
|
553
528
|
to = sessionInfo.to;
|
|
@@ -572,54 +547,195 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
572
547
|
uploadService: uploadService
|
|
573
548
|
});
|
|
574
549
|
|
|
575
|
-
winston.verbose("(voice) usertext "+usertext);
|
|
576
|
-
let message = {};
|
|
577
|
-
if(usertext){
|
|
578
|
-
let tiledeskMessage= {
|
|
579
|
-
text:usertext,
|
|
580
|
-
senderFullname: from,
|
|
581
|
-
type: 'text',
|
|
582
|
-
channel: { name: CHANNEL_NAME }
|
|
583
|
-
};
|
|
584
|
-
let startSend= new Date().getTime()
|
|
585
|
-
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
586
|
-
winston.debug("message sent : ", tdMessage);
|
|
587
|
-
let endSend = new Date().getTime();
|
|
588
|
-
console.log('Time to send messagge ( ', usertext, '): ', endSend - startSend, '[ms] at time', new Date())
|
|
589
|
-
//generate Tiledesk wait message
|
|
590
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
591
|
-
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
592
|
-
//update delayIndex for wait command message time
|
|
593
|
-
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
594
|
-
|
|
595
|
-
}else {
|
|
596
|
-
|
|
597
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
598
|
-
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
599
|
-
//update delayIndex for wait command message time
|
|
600
|
-
await voiceChannel.saveDelayIndexForCallId(from)
|
|
601
|
-
}
|
|
602
550
|
|
|
551
|
+
let start_promise_message= new Date();
|
|
552
|
+
let message = await new Promise(async (resolve, reject) => {
|
|
553
|
+
winston.debug('(voice) ******* user text -->'+ usertext)
|
|
554
|
+
if(usertext === '' || !usertext){
|
|
555
|
+
|
|
556
|
+
let start_time_get_message = new Date()
|
|
557
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
558
|
+
let end_time_get_message = new Date()
|
|
559
|
+
winston.verbose(`(if) Time to getMessage from queue in /speechresult/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]`)
|
|
560
|
+
resolve(message)
|
|
561
|
+
}else{
|
|
562
|
+
//CASE:usertext is not empty and queue is empty --> send message to tiledesk and manage delayTime
|
|
563
|
+
let tiledeskMessage= {
|
|
564
|
+
text:usertext,
|
|
565
|
+
senderFullname: from,
|
|
566
|
+
type: 'text',
|
|
567
|
+
channel: { name: CHANNEL_NAME }
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
let start_time_send_message = new Date()
|
|
571
|
+
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
572
|
+
let end_time_send_message = new Date()
|
|
573
|
+
winston.debug("message sent : ", tdMessage);
|
|
574
|
+
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())
|
|
575
|
+
|
|
576
|
+
let start_time_get_message = new Date()
|
|
577
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
578
|
+
let end_time_get_message = new Date()
|
|
579
|
+
winston.verbose(`(else) Time to getMessage from queue in /speechresult/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
|
|
580
|
+
resolve(message)
|
|
581
|
+
|
|
582
|
+
//generate Tiledesk wait message
|
|
583
|
+
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
584
|
+
// let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
585
|
+
// //update delayIndex for wait command message time
|
|
586
|
+
// await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
587
|
+
// resolve(message)
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
let end_promise_message = new Date()
|
|
591
|
+
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())
|
|
592
|
+
|
|
603
593
|
// convert response to vxml
|
|
604
594
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
605
595
|
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
606
596
|
|
|
597
|
+
let end_call = new Date()
|
|
598
|
+
winston.info(`Time to respond to /speechresult/${callSid} : ${(end_call-start_call)} [ms]`)
|
|
607
599
|
|
|
608
600
|
// Render the response as XML in reply to the webhook request
|
|
609
601
|
res.set('Content-Type', 'application/xml');
|
|
610
602
|
res.status(200).send(messageToVXML);
|
|
603
|
+
})
|
|
604
|
+
|
|
605
|
+
/* ----> called with Record tag in action property <----- */
|
|
606
|
+
router.post('/record/action/:callSid/',async (req, res) => {
|
|
607
|
+
winston.verbose('+++++++++++(voice) called POST record/action/:callSid at time '+ new Date() + "at timestamp " + new Date().getTime());
|
|
608
|
+
let start_call = new Date();
|
|
609
|
+
|
|
610
|
+
let callSid = req.body.CallSid;
|
|
611
|
+
let project_id, conversation_id, user;
|
|
612
|
+
let from, to;
|
|
613
|
+
|
|
614
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
615
|
+
if (!sessionInfo) {
|
|
616
|
+
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
617
|
+
}
|
|
618
|
+
project_id = sessionInfo.project_id;
|
|
619
|
+
from = sessionInfo.from;
|
|
620
|
+
to = sessionInfo.to;
|
|
621
|
+
conversation_id = sessionInfo.conversation_id;
|
|
622
|
+
user = sessionInfo.user;
|
|
611
623
|
|
|
612
|
-
let
|
|
613
|
-
|
|
624
|
+
let vxmlAttributes = {
|
|
625
|
+
TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
|
|
626
|
+
TTS_VOICE_NAME: VOICE_NAME,
|
|
627
|
+
callSid: callSid,
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
const tdChannel = new TiledeskChannel({
|
|
631
|
+
API_URL: API_URL,
|
|
632
|
+
redis_client: redis_client
|
|
633
|
+
})
|
|
634
|
+
tdChannel.setProjectId(project_id)
|
|
635
|
+
|
|
636
|
+
const tdTranslator = new TiledeskTwilioTranslator({
|
|
637
|
+
BASE_URL: BASE_URL,
|
|
638
|
+
aiService: aiService,
|
|
639
|
+
uploadService: uploadService
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
let start_time_get_message = new Date()
|
|
644
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
645
|
+
winston.debug('message from getMessage in /record/action/: ', message)
|
|
646
|
+
let end_time_get_message = new Date()
|
|
647
|
+
winston.verbose(`Time to getMessage from queue in /record/action/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
//generate Tiledesk wait message
|
|
651
|
+
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
652
|
+
// let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
653
|
+
// //update delayIndex for wait command message time
|
|
654
|
+
// await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
655
|
+
|
|
656
|
+
// convert response to vxml
|
|
657
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
658
|
+
winston.debug("(voice) /record/action VXML to SEND: "+ messageToVXML);
|
|
659
|
+
|
|
660
|
+
let end_call = new Date();
|
|
661
|
+
winston.info(`Time to respond to /record/action/${callSid} : ${(end_call-start_call)}[ms]`)
|
|
662
|
+
res.set('Content-Type', 'application/xml');
|
|
663
|
+
res.status(200).send(messageToVXML);
|
|
664
|
+
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
/* ----> called with Record tag in recordingStatusCallback property <----- */
|
|
668
|
+
router.post('/record/callback/:callSid/',async (req, res) => {
|
|
669
|
+
winston.verbose('+++++++++++(voice) called POST record/callback/:callSid at time'+ new Date() + "at timestamp " + new Date().getTime());
|
|
670
|
+
let start_call = new Date();
|
|
671
|
+
|
|
672
|
+
let callSid = req.params.callSid || req.body.CallSid;
|
|
673
|
+
let audioFileUrl = req.body.RecordingUrl;
|
|
674
|
+
let audioFileDuration = req.body.RecordingDuration;
|
|
675
|
+
let button_action = req.query.button_action ? '#' + req.query.button_action : '';
|
|
676
|
+
let previousIntentName = req.query.intentName || '';
|
|
677
|
+
|
|
678
|
+
let project_id, conversation_id, user;
|
|
679
|
+
let from, to;
|
|
680
|
+
|
|
681
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
682
|
+
if (!sessionInfo) {
|
|
683
|
+
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
684
|
+
}
|
|
685
|
+
project_id = sessionInfo.project_id;
|
|
686
|
+
from = sessionInfo.from;
|
|
687
|
+
to = sessionInfo.to;
|
|
688
|
+
conversation_id = sessionInfo.conversation_id;
|
|
689
|
+
user = sessionInfo.user;
|
|
690
|
+
|
|
691
|
+
const tdChannel = new TiledeskChannel({
|
|
692
|
+
API_URL: API_URL,
|
|
693
|
+
redis_client: redis_client
|
|
694
|
+
})
|
|
695
|
+
tdChannel.setProjectId(project_id)
|
|
696
|
+
|
|
697
|
+
const CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
|
|
698
|
+
let settings = await db.get(CONTENT_KEY);
|
|
699
|
+
if(!settings){
|
|
700
|
+
return res.status(404).send({error: "VOICE Channel not already connected"})
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
let tiledeskMessage = null;
|
|
704
|
+
// tiledeskMessage = buildNoInputMessage('no_input', { from, button_action, payload: { event: 'no_input', lastBlock: previousIntentName, lastTimestamp: Date.now()} });
|
|
614
705
|
|
|
706
|
+
//SPEECH TO TEXT
|
|
707
|
+
const attributes = await voiceChannel.getSettingsForCallId(callSid);
|
|
708
|
+
winston.debug(`[VOICE] getting text message from STT: ${audioFileUrl}, model: ${attributes.STT_MODEL}`);
|
|
709
|
+
// generateSTT ritorna sempre un oggetto coerente (anche vuoto o /close)
|
|
710
|
+
tiledeskMessage = await generateSTT(audioFileUrl, attributes, sessionInfo, settings)
|
|
711
|
+
winston.debug('[VOICE] tiledeskMessage from STT: ', tiledeskMessage)
|
|
712
|
+
if (!tiledeskMessage || Object.keys(tiledeskMessage).length === 0) {
|
|
713
|
+
winston.debug(`[VOICE] STT result empty, fallback to no_input branch for callSid ${callSid}`);
|
|
714
|
+
tiledeskMessage = buildNoInputMessage('no_input', { from, button_action, payload: { event: 'no_input', lastBlock: previousIntentName, lastTimestamp: Date.now()} });
|
|
715
|
+
}else {
|
|
716
|
+
const normalizedText = utils.normalizeSTT(tiledeskMessage.text);
|
|
717
|
+
winston.verbose(`[VOICE] normalized STT text: ${normalizedText} for callSid ${callSid}`);
|
|
718
|
+
if(!normalizedText){
|
|
719
|
+
tiledeskMessage = buildNoInputMessage('no_input', { from, button_action, payload: { event: 'no_input', lastBlock: previousIntentName, lastTimestamp: Date.now()} });
|
|
720
|
+
}else{
|
|
721
|
+
tiledeskMessage.text = normalizedText;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
//send message to tiledesk
|
|
726
|
+
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
727
|
+
let end_call = new Date();
|
|
728
|
+
winston.info(`Time to respond to /record/callback/${callSid} : ${(end_call-start_call)} [ms] with text ` + tiledeskMessage.text);
|
|
729
|
+
|
|
730
|
+
res.status(200).send({ success: true , message: "Message sent to Tiledesk for callSid " + callSid});
|
|
615
731
|
})
|
|
616
732
|
|
|
617
733
|
|
|
618
734
|
router.post('/menublock/:callSid', async (req, res) => {
|
|
619
|
-
winston.verbose("(voice) called POST /menu", req.body);
|
|
620
|
-
winston.verbose("(voice) called POST /menu query" , req.query);
|
|
621
735
|
let start_call = new Date().getTime();
|
|
622
|
-
|
|
736
|
+
winston.debug("(voice) called POST /menu", req.body);
|
|
737
|
+
winston.debug("(voice) called POST /menu query" , req.query);
|
|
738
|
+
winston.verbose('/menublock at: ' + new Date() + 'with text:'+ req.body.Digits)
|
|
623
739
|
|
|
624
740
|
let message_text = '';
|
|
625
741
|
let attributes = {};
|
|
@@ -663,16 +779,13 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
663
779
|
winston.debug("(voice) button menu: ", button);
|
|
664
780
|
winston.debug("(voice) message_text menu: "+ message_text);
|
|
665
781
|
|
|
666
|
-
|
|
667
|
-
let sessionInfo;
|
|
668
782
|
let project_id, conversation_id, user;
|
|
669
783
|
let from, to;
|
|
670
784
|
|
|
671
|
-
let
|
|
672
|
-
if (!
|
|
785
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
786
|
+
if (!sessionInfo) {
|
|
673
787
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
674
788
|
}
|
|
675
|
-
sessionInfo = JSON.parse(redis_data)
|
|
676
789
|
project_id = sessionInfo.project_id;
|
|
677
790
|
from = sessionInfo.from;
|
|
678
791
|
to = sessionInfo.to;
|
|
@@ -697,7 +810,6 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
697
810
|
uploadService: uploadService
|
|
698
811
|
});
|
|
699
812
|
|
|
700
|
-
let startSend = new Date().getTime();
|
|
701
813
|
//send message to tiledesk
|
|
702
814
|
let tiledeskMessage= {
|
|
703
815
|
text:message_text,
|
|
@@ -706,30 +818,35 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
706
818
|
channel: { name: CHANNEL_NAME },
|
|
707
819
|
attributes: attributes
|
|
708
820
|
};
|
|
709
|
-
let
|
|
710
|
-
|
|
711
|
-
|
|
821
|
+
let response = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
822
|
+
if(!response){
|
|
823
|
+
return res.status(503).send({ message: "Bad response: Quota exceeded" })
|
|
824
|
+
}
|
|
712
825
|
|
|
713
|
-
|
|
714
|
-
let
|
|
715
|
-
let
|
|
716
|
-
|
|
717
|
-
|
|
826
|
+
let start_time_get_message = new Date()
|
|
827
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
828
|
+
let end_time_get_message = new Date()
|
|
829
|
+
winston.verbose(`Time to getMessage from queue in /menublock/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
|
|
830
|
+
|
|
831
|
+
// //generate Tiledesk wait message
|
|
832
|
+
// let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
833
|
+
// let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
834
|
+
// //update delayIndex for wait command message time
|
|
835
|
+
// await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
718
836
|
|
|
719
837
|
// convert response to vxml
|
|
720
838
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
721
839
|
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
722
840
|
|
|
723
|
-
res.set('Content-Type', 'application/xml');
|
|
724
|
-
res.status(200).send(messageToVXML);
|
|
725
|
-
|
|
726
841
|
let end_call = new Date().getTime();
|
|
727
|
-
|
|
842
|
+
winston.info(`Time to respond to /menublock/${callSid} : ${(end_call-start_call)} [ms]`)
|
|
728
843
|
|
|
844
|
+
res.set('Content-Type', 'application/xml');
|
|
845
|
+
res.status(200).send(messageToVXML);
|
|
729
846
|
});
|
|
730
847
|
|
|
731
848
|
router.post('/handle/:callSid/:event', async (req, res) => {
|
|
732
|
-
winston.
|
|
849
|
+
winston.debug("(voice) called POST /handle", req.body);
|
|
733
850
|
winston.debug("(voice) called POST /handle query -->", req.query);
|
|
734
851
|
winston.debug("(voice) called POST /handle params-->", req.params);
|
|
735
852
|
|
|
@@ -738,15 +855,13 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
738
855
|
let button_action = '#' + req.query.button_action;
|
|
739
856
|
let previousIntentName = req.query.intentName;
|
|
740
857
|
|
|
741
|
-
let sessionInfo;
|
|
742
858
|
let project_id, conversation_id, user;
|
|
743
859
|
let from, to;
|
|
744
860
|
|
|
745
|
-
let
|
|
746
|
-
if (!
|
|
861
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
862
|
+
if (!sessionInfo) {
|
|
747
863
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
748
864
|
}
|
|
749
|
-
sessionInfo = JSON.parse(redis_data)
|
|
750
865
|
project_id = sessionInfo.project_id;
|
|
751
866
|
from = sessionInfo.from;
|
|
752
867
|
to = sessionInfo.to;
|
|
@@ -808,7 +923,7 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
808
923
|
|
|
809
924
|
/* ----> catch Event block <----- */
|
|
810
925
|
router.post('/event/:callSid/:event', async(req, res)=> {
|
|
811
|
-
winston.
|
|
926
|
+
winston.debug("(voice) called POST /event" , req.params);
|
|
812
927
|
winston.debug("(voice) called POST /event query" , req.query);
|
|
813
928
|
winston.debug("(voice) called POST /event body" , req.body);
|
|
814
929
|
|
|
@@ -822,16 +937,13 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
822
937
|
let currentIntentName = req.query.intentName;
|
|
823
938
|
let currentIntentTimestamp = req.query.previousIntentTimestamp;
|
|
824
939
|
|
|
825
|
-
|
|
826
|
-
let sessionInfo;
|
|
827
940
|
let project_id, conversation_id, user;
|
|
828
941
|
let from, to;
|
|
829
942
|
|
|
830
|
-
let
|
|
831
|
-
if (!
|
|
943
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
944
|
+
if (!sessionInfo) {
|
|
832
945
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
833
946
|
}
|
|
834
|
-
sessionInfo = JSON.parse(redis_data)
|
|
835
947
|
project_id = sessionInfo.project_id;
|
|
836
948
|
from = sessionInfo.from;
|
|
837
949
|
to = sessionInfo.to;
|
|
@@ -912,7 +1024,7 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
912
1024
|
|
|
913
1025
|
/* ----> catch Twilio Events <----- */
|
|
914
1026
|
router.post('/twilio/status',async (req, res) => {
|
|
915
|
-
winston.
|
|
1027
|
+
winston.debug('+++++++++++(voice) called POST twilio/status ', req.body);
|
|
916
1028
|
|
|
917
1029
|
let event = req.body.CallStatus;
|
|
918
1030
|
let callSid = req.body.CallSid;
|
|
@@ -922,15 +1034,13 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
922
1034
|
clearTimeout(messageTimeout)
|
|
923
1035
|
}
|
|
924
1036
|
|
|
925
|
-
let sessionInfo;
|
|
926
1037
|
let project_id, conversation_id, user;
|
|
927
1038
|
let from, to;
|
|
928
1039
|
|
|
929
|
-
let
|
|
930
|
-
if (!
|
|
1040
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
1041
|
+
if (!sessionInfo) {
|
|
931
1042
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
932
1043
|
}
|
|
933
|
-
sessionInfo = JSON.parse(redis_data)
|
|
934
1044
|
project_id = sessionInfo.project_id;
|
|
935
1045
|
from = sessionInfo.from;
|
|
936
1046
|
to = sessionInfo.to;
|
|
@@ -978,8 +1088,7 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
978
1088
|
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
979
1089
|
|
|
980
1090
|
//remove session data for current callId and relative queue data
|
|
981
|
-
await
|
|
982
|
-
await redis_client.del('tiledesk:voice:'+callSid+':delayIndex');
|
|
1091
|
+
await voiceChannel.deleteCallKeys(callSid);
|
|
983
1092
|
await tdChannel.clearQueue(conversation_id);
|
|
984
1093
|
break;
|
|
985
1094
|
}
|
|
@@ -992,7 +1101,7 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
992
1101
|
|
|
993
1102
|
|
|
994
1103
|
router.post('/twilio/fail',async (req, res) => {
|
|
995
|
-
winston.
|
|
1104
|
+
winston.debug('+++++++++++(voice) called POST twilio/fail ', req.params)
|
|
996
1105
|
winston.debug('+++++++++++(voice) called POST twilio/fail ', req.body)
|
|
997
1106
|
|
|
998
1107
|
res.set('Content-Type', 'application/xml');
|
|
@@ -1000,175 +1109,100 @@ router.post('/twilio/fail',async (req, res) => {
|
|
|
1000
1109
|
})
|
|
1001
1110
|
|
|
1002
1111
|
|
|
1003
|
-
/* ----> catch Twilio Events <----- */
|
|
1004
|
-
router.post('/record/:callSid/',async (req, res) => {
|
|
1005
|
-
winston.verbose('+++++++++++(voice) called POST record/:callSid ', req.body);
|
|
1006
|
-
|
|
1007
|
-
let callSid = req.body.CallSid;
|
|
1008
|
-
let audioFileUrl = req.body.RecordingUrl;
|
|
1009
|
-
|
|
1010
|
-
let sessionInfo;
|
|
1011
|
-
let project_id, conversation_id, user;
|
|
1012
|
-
let from, to;
|
|
1013
|
-
|
|
1014
|
-
let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
|
|
1015
|
-
if (!redis_data) {
|
|
1016
|
-
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
1017
|
-
}
|
|
1018
|
-
sessionInfo = JSON.parse(redis_data)
|
|
1019
|
-
project_id = sessionInfo.project_id;
|
|
1020
|
-
from = sessionInfo.from;
|
|
1021
|
-
to = sessionInfo.to;
|
|
1022
|
-
conversation_id = sessionInfo.conversation_id;
|
|
1023
|
-
user = sessionInfo.user;
|
|
1024
|
-
|
|
1025
|
-
let vxmlAttributes = {
|
|
1026
|
-
TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
|
|
1027
|
-
TTS_VOICE_NAME: VOICE_NAME,
|
|
1028
|
-
callSid: callSid,
|
|
1029
|
-
};
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
const tdChannel = new TiledeskChannel({
|
|
1033
|
-
API_URL: API_URL,
|
|
1034
|
-
redis_client: redis_client
|
|
1035
|
-
})
|
|
1036
|
-
tdChannel.setProjectId(project_id)
|
|
1037
|
-
|
|
1038
|
-
const tdTranslator = new TiledeskTwilioTranslator({
|
|
1039
|
-
BASE_URL: BASE_URL,
|
|
1040
|
-
aiService: aiService,
|
|
1041
|
-
uploadService: uploadService
|
|
1042
|
-
});
|
|
1043
|
-
|
|
1044
|
-
const CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
|
|
1045
|
-
let settings = await db.get(CONTENT_KEY);
|
|
1046
|
-
if(!settings){
|
|
1047
|
-
return res.status(404).send({error: "VOICE Channel not already connected"})
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
let attributes = await voiceChannel.getSettingsForCallId(callSid);
|
|
1052
|
-
console.log('attributessss', attributes)
|
|
1053
|
-
|
|
1054
|
-
//SPEECH TO TEXT
|
|
1055
|
-
console.log('getting text message . . . ', audioFileUrl, attributes.STT_MODEL)
|
|
1056
|
-
let tiledeskMessage = await generateSTT(audioFileUrl, attributes, sessionInfo, settings)
|
|
1057
|
-
console.log('(voice) Message captured after STT -->', tiledeskMessage)
|
|
1058
|
-
|
|
1059
|
-
if(!tiledeskMessage){
|
|
1060
|
-
//case NO_INPUT
|
|
1061
|
-
const queryString = utils.buildQueryString(req.query);
|
|
1062
|
-
winston.debug('case no input.. redirect '+ queryString)
|
|
1063
|
-
|
|
1064
|
-
return await axios({
|
|
1065
|
-
url: "http://localhost:3000/handle/" + callSid + '/no_input'+ queryString,
|
|
1066
|
-
headers: req.headers,
|
|
1067
|
-
data: req.body,
|
|
1068
|
-
method: 'POST'
|
|
1069
|
-
}).then((response) => {
|
|
1070
|
-
winston.debug("[TiledeskChannel] speechToText response : ", response.data);
|
|
1071
|
-
return res.status(response.status).send(response.data);
|
|
1072
|
-
}).catch((err) => {
|
|
1073
|
-
winston.error("[TiledeskChannel] speechToText error: ", err);
|
|
1074
|
-
return res.status(500).send({ success: false, message: "Errore while redirect to /handle for callSid " + callSid});;
|
|
1075
|
-
})
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
1080
|
-
winston.debug("message sent : ", tdMessage);
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
//generate Tiledesk wait message
|
|
1084
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
1085
|
-
let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
1086
|
-
//update delayIndex for wait command message time
|
|
1087
|
-
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
1088
|
-
|
|
1089
|
-
// convert response to vxml
|
|
1090
|
-
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
1091
|
-
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
1092
|
-
|
|
1093
|
-
res.set('Content-Type', 'application/xml');
|
|
1094
|
-
res.status(200).send(messageToVXML);
|
|
1095
|
-
|
|
1096
|
-
})
|
|
1097
1112
|
|
|
1098
1113
|
async function generateSTT(audioFileUrl, attributes, sessionInfo, settings){
|
|
1099
1114
|
|
|
1100
1115
|
winston.debug("(voice) generateSTT: "+ attributes.VOICE_PROVIDER);
|
|
1101
1116
|
|
|
1102
|
-
let tiledeskMessage = {}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
let
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1117
|
+
let tiledeskMessage = {};
|
|
1118
|
+
let text = null;
|
|
1119
|
+
|
|
1120
|
+
try {
|
|
1121
|
+
switch(attributes.VOICE_PROVIDER){
|
|
1122
|
+
case VOICE_PROVIDER.OPENAI: {
|
|
1123
|
+
let GPT_KEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.key
|
|
1124
|
+
let publicKey = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.publicKey
|
|
1125
|
+
if(publicKey){
|
|
1126
|
+
let keep_going = await aiService.checkQuoteAvailability(sessionInfo.project_id, settings.token)
|
|
1127
|
+
winston.debug('(voice) checkQuoteAvailability return: '+ keep_going);
|
|
1128
|
+
if(!keep_going){
|
|
1129
|
+
//no token is available --> close conversation
|
|
1130
|
+
return tiledeskMessage= {
|
|
1131
|
+
//text:'\\close',
|
|
1132
|
+
text:'/close',
|
|
1133
|
+
senderFullname: sessionInfo.from,
|
|
1134
|
+
type: 'text',
|
|
1135
|
+
channel: { name: CHANNEL_NAME },
|
|
1136
|
+
attributes: {
|
|
1137
|
+
subtype: "info",
|
|
1138
|
+
action: 'close'+JSON.stringify({event: 'quota_exceeded'}),
|
|
1139
|
+
payload: {
|
|
1140
|
+
catchEvent: 'quota_exceeded'
|
|
1141
|
+
},
|
|
1142
|
+
timestamp: 'xxxxxx'
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1129
1145
|
|
|
1146
|
+
}
|
|
1130
1147
|
}
|
|
1148
|
+
|
|
1149
|
+
text = await aiService.speechToText(audioFileUrl, attributes.STT_MODEL, GPT_KEY)
|
|
1150
|
+
break;
|
|
1131
1151
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
// La condizione negli input del metodo è corretta, ma può essere scritta in modo più leggibile:
|
|
1146
|
-
const ttsLanguage = attributes.TTS_LANGUAGE || 'en';
|
|
1147
|
-
text = await this.aiService.speechToTextElevenLabs(
|
|
1148
|
-
audioFileUrl,
|
|
1149
|
-
attributes.STT_MODEL,
|
|
1150
|
-
ttsLanguage,
|
|
1151
|
-
ELEVENLABS_APIKEY
|
|
1152
|
-
).catch((err) => {
|
|
1153
|
-
winston.error('errr while creating elevenlabs audio message', err?.response?.data);
|
|
1154
|
-
});
|
|
1155
|
-
tiledeskMessage= {
|
|
1152
|
+
case VOICE_PROVIDER.ELEVENLABS: {
|
|
1153
|
+
let ELEVENLABS_APIKEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.ELEVENLABS))?.key
|
|
1154
|
+
const ttsLanguage = attributes.TTS_LANGUAGE || 'en';
|
|
1155
|
+
text = await aiService.speechToTextElevenLabs( audioFileUrl, attributes.STT_MODEL, ttsLanguage, ELEVENLABS_APIKEY )
|
|
1156
|
+
break;
|
|
1157
|
+
}
|
|
1158
|
+
default:
|
|
1159
|
+
throw new Error('Unsupported VOICE_PROVIDER: ' + attributes.VOICE_PROVIDER);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
if(text){
|
|
1163
|
+
winston.debug('[STT] text empty → fallback no_input');
|
|
1164
|
+
tiledeskMessage = {
|
|
1156
1165
|
text: text,
|
|
1157
1166
|
senderFullname: sessionInfo.from,
|
|
1158
1167
|
type: 'text',
|
|
1159
1168
|
channel: { name: CHANNEL_NAME }
|
|
1160
1169
|
};
|
|
1161
|
-
|
|
1170
|
+
}
|
|
1171
|
+
} catch (error) {
|
|
1172
|
+
winston.error('[STT] generateSTT error:', error);
|
|
1173
|
+
switch (error.code) {
|
|
1174
|
+
case 'AISERVICE_FAILED':
|
|
1175
|
+
winston.error('[STT] AISERVICE_FAILED → ', error.message);
|
|
1176
|
+
break;
|
|
1177
|
+
}
|
|
1162
1178
|
|
|
1179
|
+
// fallback: tiledeskMessage vuoto
|
|
1180
|
+
tiledeskMessage = {};
|
|
1181
|
+
|
|
1163
1182
|
}
|
|
1164
1183
|
|
|
1165
1184
|
return tiledeskMessage
|
|
1166
1185
|
}
|
|
1167
1186
|
|
|
1168
1187
|
|
|
1188
|
+
async function buildNoInputMessage(event, { from, button_action, payload }) {
|
|
1189
|
+
return {
|
|
1190
|
+
text: `/${event}`,
|
|
1191
|
+
senderFullname: from,
|
|
1192
|
+
type: 'text',
|
|
1193
|
+
channel: { name: CHANNEL_NAME },
|
|
1194
|
+
attributes: {
|
|
1195
|
+
type: 'info',
|
|
1196
|
+
action: button_action,
|
|
1197
|
+
payload: payload
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
|
|
1169
1203
|
|
|
1170
1204
|
router.get('/addon/transcript', async (req, res) => {
|
|
1171
|
-
winston.
|
|
1205
|
+
winston.debug("(voice) called GET /transcript query-->" , req.query);
|
|
1172
1206
|
winston.debug("(voice) called GET /transcript body -->" , req.body);
|
|
1173
1207
|
|
|
1174
1208
|
res.status(200).send('ok');
|
|
@@ -1177,7 +1211,7 @@ router.get('/addon/transcript', async (req, res) => {
|
|
|
1177
1211
|
|
|
1178
1212
|
/** --> only for test purpose <-- **/
|
|
1179
1213
|
router.get('/test', async (req, res) => {
|
|
1180
|
-
winston.
|
|
1214
|
+
winston.debug("(voice) called GET /test" , req.query);
|
|
1181
1215
|
|
|
1182
1216
|
let project_id = req.query.id_project;
|
|
1183
1217
|
let callSid = req.body.CallSid;
|
|
@@ -1354,7 +1388,7 @@ async function connectRedis() {
|
|
|
1354
1388
|
|
|
1355
1389
|
|
|
1356
1390
|
redis_client.on('error', err => {
|
|
1357
|
-
winston.
|
|
1391
|
+
winston.error('(voice) Connect Redis Error ' + err);
|
|
1358
1392
|
})
|
|
1359
1393
|
/*
|
|
1360
1394
|
redis_client.on('connect', () => {
|