@tiledesk/tiledesk-voice-twilio-connector 0.1.14-rc7 → 0.1.15
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 +240 -134
- package/package.json +2 -1
- package/routes/manageApp.js +11 -1
- package/template/configure.html +26 -8
- package/template/css/configure.css +12 -1
- package/tiledesk/TiledeskChannel.js +18 -83
- package/tiledesk/TiledeskTwilioTranslator.js +115 -49
- package/tiledesk/VoiceChannel.js +0 -1
- package/tiledesk/constants.js +12 -2
- package/tiledesk/fileUtils.js +54 -0
- package/tiledesk/services/AiService.js +93 -0
- package/tiledesk/services/IntegrationService.js +82 -0
- package/tiledesk/services/UploadService.js +79 -0
- package/tiledesk/services/speech_voice-twilio-393892661914_CAa837741db2a05b0cfa946d034c0c4048.wav +0 -0
- package/tiledesk/services/speech_voice-twilio-393892661914_CAcb69e06f3ea491f99778d58ddce7d70d.wav +0 -0
package/index.js
CHANGED
|
@@ -22,6 +22,14 @@ router.use('/manage', manageRoute);
|
|
|
22
22
|
const { TiledeskChannel } = require("./tiledesk/TiledeskChannel");
|
|
23
23
|
const { TiledeskTwilioTranslator } = require('./tiledesk/TiledeskTwilioTranslator');
|
|
24
24
|
|
|
25
|
+
//services
|
|
26
|
+
const { IntegrationService } = require('./tiledesk/services/IntegrationService');
|
|
27
|
+
const { AiService } = require('./tiledesk/services/AiService')
|
|
28
|
+
const { UploadService } = require('./tiledesk/services/UploadService')
|
|
29
|
+
let integrationService = null;
|
|
30
|
+
let aiService = null;
|
|
31
|
+
let uploadService = null;
|
|
32
|
+
|
|
25
33
|
//voice clients
|
|
26
34
|
const { VoiceChannel } = require("./tiledesk/VoiceChannel");
|
|
27
35
|
let voiceChannel = null;
|
|
@@ -40,14 +48,21 @@ const CHANNEL_NAME = require('./tiledesk/constants').CHANNEL_NAME
|
|
|
40
48
|
const CALL_STATUS = require('./tiledesk/constants').CALL_STATUS
|
|
41
49
|
const VOICE_NAME = require('./tiledesk/constants').VOICE_NAME
|
|
42
50
|
const VOICE_LANGUAGE = require('./tiledesk/constants').VOICE_LANGUAGE
|
|
51
|
+
const OPENAI_SETTINGS = require('./tiledesk/constants').OPENAI_SETTINGS
|
|
52
|
+
const VOICE_PROVIDER = require('./tiledesk/constants').VOICE_PROVIDER
|
|
53
|
+
let BASE_POOLING_DELAY = require('./tiledesk/constants').BASE_POOLING_DELAY
|
|
54
|
+
let MAX_POLLING_TIME = require('./tiledesk/constants').MAX_POLLING_TIME
|
|
43
55
|
const utils = require('./tiledesk/utils.js')
|
|
44
56
|
|
|
45
57
|
let API_URL = null;
|
|
46
58
|
let BASE_URL = null;
|
|
59
|
+
let BASE_FILE_URL = null;
|
|
60
|
+
let OPENAI_ENDPOINT = null;
|
|
47
61
|
let REDIS_HOST = null;
|
|
48
62
|
let REDIS_PORT = null;
|
|
49
63
|
let REDIS_PASSWORD = null;
|
|
50
64
|
let BRAND_NAME = null;
|
|
65
|
+
let GPT_KEY = null;
|
|
51
66
|
|
|
52
67
|
let twilio = require('twilio');
|
|
53
68
|
const VoiceResponse = require('twilio').twiml.VoiceResponse;
|
|
@@ -119,8 +134,6 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
119
134
|
}
|
|
120
135
|
|
|
121
136
|
let vxmlAttributes = {
|
|
122
|
-
voiceName: VOICE_NAME,
|
|
123
|
-
voiceLanguage: VOICE_LANGUAGE,
|
|
124
137
|
callSid: callSid
|
|
125
138
|
};
|
|
126
139
|
|
|
@@ -131,7 +144,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
131
144
|
tdChannel.setProjectId(project_id)
|
|
132
145
|
|
|
133
146
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
134
|
-
BASE_URL: BASE_URL
|
|
147
|
+
BASE_URL: BASE_URL,
|
|
148
|
+
aiService: aiService
|
|
135
149
|
});
|
|
136
150
|
|
|
137
151
|
let start2 = new Date().getTime();
|
|
@@ -147,6 +161,20 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
147
161
|
let conversation_id = await tdChannel.generateConversation(from, callSid, user.token);
|
|
148
162
|
winston.debug("(voice) conversation returned:"+ conversation_id);
|
|
149
163
|
|
|
164
|
+
|
|
165
|
+
//GET AND SAVE GPT-KET IF
|
|
166
|
+
let integrations = []
|
|
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
|
+
}
|
|
176
|
+
integrations.push({type: 'openai', key: key})
|
|
177
|
+
|
|
150
178
|
//save data to redis
|
|
151
179
|
let session_data = {
|
|
152
180
|
from: from,
|
|
@@ -154,7 +182,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
154
182
|
callSid: callSid,
|
|
155
183
|
project_id: project_id,
|
|
156
184
|
user: user,
|
|
157
|
-
conversation_id: conversation_id
|
|
185
|
+
conversation_id: conversation_id,
|
|
186
|
+
integrations: settings.integrations
|
|
158
187
|
}
|
|
159
188
|
if (!redis_client) {
|
|
160
189
|
return res.status(500).send({ message: "Redis not ready. Check redis connection..." })
|
|
@@ -194,7 +223,7 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
194
223
|
await voiceChannel.saveDelayIndexForCallId(from)
|
|
195
224
|
|
|
196
225
|
// send standard wait vxml message
|
|
197
|
-
let messageToVXML = await tdTranslator.toVXML(waitTiledeskMessage, callSid, vxmlAttributes)
|
|
226
|
+
let messageToVXML = await tdTranslator.toVXML(waitTiledeskMessage, callSid, vxmlAttributes, session_data)
|
|
198
227
|
winston.debug('(voice) messageVXML-->'+ messageToVXML)
|
|
199
228
|
|
|
200
229
|
|
|
@@ -235,10 +264,11 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
235
264
|
user = sessionInfo.user;
|
|
236
265
|
|
|
237
266
|
let vxmlAttributes = {
|
|
238
|
-
|
|
239
|
-
|
|
267
|
+
TTS_VOICE_LANGUAGE: null,
|
|
268
|
+
TTS_VOICE_NAME: null,
|
|
240
269
|
callSid: callSid,
|
|
241
270
|
};
|
|
271
|
+
|
|
242
272
|
|
|
243
273
|
const tdChannel = new TiledeskChannel({
|
|
244
274
|
API_URL: API_URL,
|
|
@@ -247,70 +277,44 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
247
277
|
tdChannel.setProjectId(project_id);
|
|
248
278
|
|
|
249
279
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
250
|
-
BASE_URL: BASE_URL
|
|
280
|
+
BASE_URL: BASE_URL,
|
|
281
|
+
aiService: aiService,
|
|
282
|
+
uploadService: uploadService
|
|
251
283
|
});
|
|
284
|
+
|
|
252
285
|
|
|
253
286
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
//CASE: queue is empty --> generate Tiledesk wait message and manage delayTime
|
|
270
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
271
|
-
console.log('delay /nextblock -->', delayTime)
|
|
272
|
-
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
273
|
-
//update delayIndex for wait command message time
|
|
274
|
-
await voiceChannel.saveDelayIndexForCallId(from)
|
|
275
|
-
|
|
276
|
-
} else if(queue.length > 0 && (usertext === '' || !usertext)){
|
|
277
|
-
|
|
278
|
-
let end1 = new Date().getTime()
|
|
279
|
-
console.log('Time between wh message and /nextblock queue message:', end1-start1, '[ms] (message:', queue[0].text , ') at time', new Date())
|
|
280
|
-
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue and reset delayTime
|
|
281
|
-
message = queue[0]
|
|
282
|
-
winston.debug('QUEUE --> ',queue[0])
|
|
283
|
-
//remove message from queue
|
|
284
|
-
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
285
|
-
//reset delayIndex for wait command message time
|
|
286
|
-
await voiceChannel.clearDelayTimeForCallId(callSid)
|
|
287
|
-
|
|
288
|
-
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
289
|
-
winston.debug('QUEUE after remove --> ',queue.length)
|
|
290
|
-
}else{
|
|
291
|
-
//CASE:usertext is not empty and queue is empty --> send message to tiledesk and manage delayTime
|
|
292
|
-
let tiledeskMessage= {
|
|
293
|
-
text:usertext,
|
|
294
|
-
senderFullname: from,
|
|
295
|
-
type: 'text',
|
|
296
|
-
channel: { name: CHANNEL_NAME }
|
|
297
|
-
};
|
|
298
|
-
let startSend= new Date().getTime()
|
|
299
|
-
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
300
|
-
winston.debug("message sent : ", tdMessage);
|
|
301
|
-
let endSend = new Date().getTime();
|
|
302
|
-
console.log('Time to send messagge ( ', usertext, '): ', endSend - startSend, '[ms] at time', new Date())
|
|
303
|
-
//generate Tiledesk wait message
|
|
304
|
-
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
305
|
-
console.log('(message sent) delay /nextblock -->', delayTime)
|
|
306
|
-
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
307
|
-
//update delayIndex for wait command message time
|
|
308
|
-
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
309
|
-
}
|
|
287
|
+
let message = await new Promise(async (resolve, reject) => {
|
|
288
|
+
winston.debug('******* user text -->', usertext)
|
|
289
|
+
if(usertext === '' || !usertext){
|
|
290
|
+
|
|
291
|
+
let message = await getMessage(callSid, from, project_id, conversation_id)
|
|
292
|
+
|
|
293
|
+
resolve(message)
|
|
294
|
+
}else{
|
|
295
|
+
//CASE:usertext is not empty and queue is empty --> send message to tiledesk and manage delayTime
|
|
296
|
+
let tiledeskMessage= {
|
|
297
|
+
text:usertext,
|
|
298
|
+
senderFullname: from,
|
|
299
|
+
type: 'text',
|
|
300
|
+
channel: { name: CHANNEL_NAME }
|
|
301
|
+
};
|
|
310
302
|
|
|
303
|
+
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
304
|
+
winston.debug("message sent : ", tdMessage);
|
|
305
|
+
//generate Tiledesk wait message
|
|
306
|
+
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
307
|
+
let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
308
|
+
//update delayIndex for wait command message time
|
|
309
|
+
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
310
|
+
|
|
311
|
+
resolve(message)
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
|
|
311
315
|
|
|
312
316
|
// convert response to vxml
|
|
313
|
-
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
317
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
314
318
|
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
315
319
|
|
|
316
320
|
let end_call = new Date()
|
|
@@ -322,6 +326,73 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
322
326
|
|
|
323
327
|
})
|
|
324
328
|
|
|
329
|
+
async function getMessage(callSid, from, project_id, conversation_id){
|
|
330
|
+
const startTime = Date.now();
|
|
331
|
+
|
|
332
|
+
const tdChannel = new TiledeskChannel({
|
|
333
|
+
API_URL: API_URL,
|
|
334
|
+
redis_client: redis_client
|
|
335
|
+
})
|
|
336
|
+
tdChannel.setProjectId(project_id);
|
|
337
|
+
|
|
338
|
+
let message = {}, queue = []
|
|
339
|
+
return new Promise(async (resolve, reject)=>{
|
|
340
|
+
/********function checkQueue ************ */
|
|
341
|
+
//get queue
|
|
342
|
+
//if queue exist and has at least one message
|
|
343
|
+
//get message from queue, remove it from queue and clear delayTime
|
|
344
|
+
//if call duration is more than MAX_WAITING_TIME
|
|
345
|
+
//generate wait command messaget time
|
|
346
|
+
// recall yourself after delayTime s
|
|
347
|
+
const checkQueue = async ()=>{
|
|
348
|
+
winston.debug('(checkQueue FN ) ******* call checkQUEUE')
|
|
349
|
+
try{
|
|
350
|
+
queue = await tdChannel.getMessagesFromQueue(conversation_id)
|
|
351
|
+
winston.debug('(checkQueue FN ) /NEXT controllo coda--> '+ queue.length)
|
|
352
|
+
|
|
353
|
+
if (queue && queue.length > 0) {
|
|
354
|
+
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue and reset delayTime
|
|
355
|
+
message = queue[0]
|
|
356
|
+
winston.debug('(checkQueue FN ) QUEUE --> '+ queue[0].text, queue[0])
|
|
357
|
+
//remove message from queue
|
|
358
|
+
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
359
|
+
//reset delayIndex for wait command message time
|
|
360
|
+
await voiceChannel.clearDelayTimeForCallId(callSid)
|
|
361
|
+
|
|
362
|
+
resolve(message);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const elapsedTime = (Date.now() - startTime) / 1000;
|
|
367
|
+
if (elapsedTime >= MAX_POLLING_TIME) {
|
|
368
|
+
//CASE: queue is empty --> generate Tiledesk wait message and manage delayTime
|
|
369
|
+
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
370
|
+
message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
371
|
+
//update delayIndex for wait command message time
|
|
372
|
+
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
373
|
+
|
|
374
|
+
resolve(message);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
379
|
+
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
380
|
+
winston.debug('(checkQueue FN ) RECALL checkQueue FN in: '+ delayTime + '[ms]')
|
|
381
|
+
setTimeout(checkQueue, delayTime);
|
|
382
|
+
|
|
383
|
+
}catch(err){
|
|
384
|
+
winston.debug("Error occurred while check message in queue: ", err);
|
|
385
|
+
reject(err);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
winston.debug('(checkQueue FN ) INIT POLLING . . . ')
|
|
390
|
+
checkQueue();
|
|
391
|
+
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
}
|
|
395
|
+
|
|
325
396
|
router.post('/speechresult/:callSid', async (req, res) => {
|
|
326
397
|
|
|
327
398
|
let start_call = new Date();
|
|
@@ -350,8 +421,8 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
350
421
|
user = sessionInfo.user;
|
|
351
422
|
|
|
352
423
|
let vxmlAttributes = {
|
|
353
|
-
|
|
354
|
-
|
|
424
|
+
TTS_VOICE_LANGUAGE: null,
|
|
425
|
+
TTS_VOICE_NAME: null,
|
|
355
426
|
callSid: callSid,
|
|
356
427
|
};
|
|
357
428
|
|
|
@@ -362,7 +433,9 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
362
433
|
tdChannel.setProjectId(project_id);
|
|
363
434
|
|
|
364
435
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
365
|
-
BASE_URL: BASE_URL
|
|
436
|
+
BASE_URL: BASE_URL,
|
|
437
|
+
aiService: aiService,
|
|
438
|
+
uploadService: uploadService
|
|
366
439
|
});
|
|
367
440
|
|
|
368
441
|
winston.verbose("(vxml) usertext "+usertext);
|
|
@@ -394,7 +467,7 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
394
467
|
}
|
|
395
468
|
|
|
396
469
|
// convert response to vxml
|
|
397
|
-
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
470
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
398
471
|
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
399
472
|
|
|
400
473
|
|
|
@@ -473,8 +546,8 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
473
546
|
user = sessionInfo.user;
|
|
474
547
|
|
|
475
548
|
let vxmlAttributes = {
|
|
476
|
-
|
|
477
|
-
|
|
549
|
+
TTS_VOICE_LANGUAGE: null,
|
|
550
|
+
TTS_VOICE_NAME: null,
|
|
478
551
|
callSid: callSid,
|
|
479
552
|
};
|
|
480
553
|
|
|
@@ -485,7 +558,9 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
485
558
|
tdChannel.setProjectId(project_id);
|
|
486
559
|
|
|
487
560
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
488
|
-
BASE_URL: BASE_URL
|
|
561
|
+
BASE_URL: BASE_URL,
|
|
562
|
+
aiService: aiService,
|
|
563
|
+
uploadService: uploadService
|
|
489
564
|
});
|
|
490
565
|
|
|
491
566
|
let startSend = new Date().getTime();
|
|
@@ -508,7 +583,7 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
508
583
|
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
509
584
|
|
|
510
585
|
// convert response to vxml
|
|
511
|
-
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
586
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
512
587
|
winston.debug("(voice) VXML to SEND: "+ messageToVXML);
|
|
513
588
|
|
|
514
589
|
res.set('Content-Type', 'text/xml');
|
|
@@ -545,8 +620,8 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
545
620
|
user = sessionInfo.user;
|
|
546
621
|
|
|
547
622
|
let vxmlAttributes = {
|
|
548
|
-
|
|
549
|
-
|
|
623
|
+
TTS_VOICE_LANGUAGE: null,
|
|
624
|
+
TTS_VOICE_NAME: null,
|
|
550
625
|
callSid: callSid,
|
|
551
626
|
};
|
|
552
627
|
|
|
@@ -557,7 +632,9 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
557
632
|
tdChannel.setProjectId(project_id);
|
|
558
633
|
|
|
559
634
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
560
|
-
BASE_URL: BASE_URL
|
|
635
|
+
BASE_URL: BASE_URL,
|
|
636
|
+
aiService: aiService,
|
|
637
|
+
uploadService: uploadService
|
|
561
638
|
});
|
|
562
639
|
|
|
563
640
|
|
|
@@ -586,7 +663,7 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
586
663
|
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
587
664
|
|
|
588
665
|
// convert response to vxml
|
|
589
|
-
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
666
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes,sessionInfo)
|
|
590
667
|
winston.debug("(vxml) VXML to SEND: "+ messageToVXML);
|
|
591
668
|
|
|
592
669
|
res.set('Content-Type', 'text/xml');
|
|
@@ -623,8 +700,8 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
623
700
|
user = sessionInfo.user;
|
|
624
701
|
|
|
625
702
|
let vxmlAttributes = {
|
|
626
|
-
|
|
627
|
-
|
|
703
|
+
TTS_VOICE_LANGUAGE: null,
|
|
704
|
+
TTS_VOICE_NAME: null,
|
|
628
705
|
callSid: callSid,
|
|
629
706
|
};
|
|
630
707
|
|
|
@@ -635,7 +712,9 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
635
712
|
tdChannel.setProjectId(project_id);
|
|
636
713
|
|
|
637
714
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
638
|
-
BASE_URL: BASE_URL
|
|
715
|
+
BASE_URL: BASE_URL,
|
|
716
|
+
aiService: aiService,
|
|
717
|
+
uploadService: uploadService
|
|
639
718
|
});
|
|
640
719
|
|
|
641
720
|
let button_action = ''
|
|
@@ -684,7 +763,7 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
684
763
|
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
685
764
|
|
|
686
765
|
// convert response to vxml
|
|
687
|
-
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes)
|
|
766
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
688
767
|
winston.debug("(vxml) VXML to SEND: "+ messageToVXML);
|
|
689
768
|
|
|
690
769
|
res.set('Content-Type', 'text/xml');
|
|
@@ -715,8 +794,8 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
715
794
|
user = sessionInfo.user;
|
|
716
795
|
|
|
717
796
|
let vxmlAttributes = {
|
|
718
|
-
|
|
719
|
-
|
|
797
|
+
TTS_VOICE_LANGUAGE: null,
|
|
798
|
+
TTS_VOICE_NAME: null,
|
|
720
799
|
callSid: callSid,
|
|
721
800
|
};
|
|
722
801
|
|
|
@@ -727,7 +806,9 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
727
806
|
tdChannel.setProjectId(project_id)
|
|
728
807
|
|
|
729
808
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
730
|
-
BASE_URL: BASE_URL
|
|
809
|
+
BASE_URL: BASE_URL,
|
|
810
|
+
aiService: aiService,
|
|
811
|
+
uploadService: uploadService
|
|
731
812
|
});
|
|
732
813
|
|
|
733
814
|
|
|
@@ -766,39 +847,6 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
766
847
|
|
|
767
848
|
|
|
768
849
|
|
|
769
|
-
router.post('/testbase/:id_project',async (req, res) => {
|
|
770
|
-
let start_call = new Date().getTime();
|
|
771
|
-
console.log('/testbase at: ', new Date())
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
let twiml = '<Response>' +
|
|
775
|
-
'<Gather input="speech" action="https://tiledesk-voice-twilio-connector.glitch.me/speechresult2/CAc6aec6904b5b9c7341d0b95a687c5dd9" method="POST" language="it-IT" speechTimeout="1" >' +
|
|
776
|
-
'<Say voice="Polly.Bianca" language="it-IT">Dimmi qualcosa</Say>' +
|
|
777
|
-
'</Gather>' +
|
|
778
|
-
'</Response>'
|
|
779
|
-
|
|
780
|
-
res.set('Content-Type', 'text/xml');
|
|
781
|
-
res.status(200).send(twiml);
|
|
782
|
-
|
|
783
|
-
let end_call = new Date().getTime();
|
|
784
|
-
console.log('Time to responde to /testbase/<id_project>/ : ', end_call-start_call, '[ms] at time', new Date())
|
|
785
|
-
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
router.post('/speechresult2/:callSid',async (req, res) => {
|
|
789
|
-
winston.verbose('+++++++++++(voice) called POST /speechresult2/ ', req.body)
|
|
790
|
-
let start_call = new Date();
|
|
791
|
-
console.log('/speechresult2 at: ', new Date(), 'with text:', req.body.SpeechResult)
|
|
792
|
-
|
|
793
|
-
let twiml = '<Response>' +
|
|
794
|
-
'<Say voice="Polly.Bianca" language="it-IT">Ciao</Say>' +
|
|
795
|
-
'</Response>'
|
|
796
|
-
|
|
797
|
-
res.set('Content-Type', 'text/xml');
|
|
798
|
-
res.status(200).send(twiml);
|
|
799
|
-
|
|
800
|
-
});
|
|
801
|
-
|
|
802
850
|
router.post('/twilio/fail',async (req, res) => {
|
|
803
851
|
winston.verbose('+++++++++++(voice) called POST twilio/fail ', req.params)
|
|
804
852
|
winston.debug('+++++++++++(voice) called POST twilio/fail ', req.body)
|
|
@@ -813,6 +861,7 @@ router.post('/record/:callSid/',async (req, res) => {
|
|
|
813
861
|
winston.verbose('+++++++++++(voice) called POST record/:callSid ', req.body);
|
|
814
862
|
|
|
815
863
|
let callSid = req.body.CallSid;
|
|
864
|
+
let audioFileUrl = req.body.RecordingUrl;
|
|
816
865
|
|
|
817
866
|
let sessionInfo;
|
|
818
867
|
let project_id, conversation_id, user;
|
|
@@ -830,10 +879,11 @@ router.post('/record/:callSid/',async (req, res) => {
|
|
|
830
879
|
user = sessionInfo.user;
|
|
831
880
|
|
|
832
881
|
let vxmlAttributes = {
|
|
833
|
-
|
|
834
|
-
|
|
882
|
+
TTS_VOICE_LANGUAGE: null,
|
|
883
|
+
TTS_VOICE_NAME: null,
|
|
835
884
|
callSid: callSid,
|
|
836
885
|
};
|
|
886
|
+
|
|
837
887
|
|
|
838
888
|
const tdChannel = new TiledeskChannel({
|
|
839
889
|
API_URL: API_URL,
|
|
@@ -842,14 +892,23 @@ router.post('/record/:callSid/',async (req, res) => {
|
|
|
842
892
|
tdChannel.setProjectId(project_id)
|
|
843
893
|
|
|
844
894
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
845
|
-
BASE_URL: BASE_URL
|
|
895
|
+
BASE_URL: BASE_URL,
|
|
896
|
+
aiService: aiService,
|
|
897
|
+
uploadService: uploadService
|
|
846
898
|
});
|
|
847
899
|
|
|
900
|
+
const CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
|
|
901
|
+
let settings = await db.get(CONTENT_KEY);
|
|
902
|
+
if(!settings){
|
|
903
|
+
return res.status(404).send({error: "VOICE Channel not already connected"})
|
|
904
|
+
}
|
|
848
905
|
|
|
849
|
-
//
|
|
850
|
-
let
|
|
851
|
-
let textMessage = await
|
|
852
|
-
|
|
906
|
+
//SPEECH TO TEXT
|
|
907
|
+
let key = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPEN_AI))?.key
|
|
908
|
+
let textMessage = await aiService.speechToText(audioFileUrl, vxmlAttributes.STT_MODEL, key).catch((err)=>{
|
|
909
|
+
console.log('errr while transcript', err.response?.data)
|
|
910
|
+
})
|
|
911
|
+
console.log('(voice) Message captured after STT -->', textMessage)
|
|
853
912
|
|
|
854
913
|
if(!textMessage){
|
|
855
914
|
//case NO_INPUT
|
|
@@ -876,19 +935,23 @@ router.post('/record/:callSid/',async (req, res) => {
|
|
|
876
935
|
senderFullname: from,
|
|
877
936
|
type: 'text',
|
|
878
937
|
channel: { name: CHANNEL_NAME }
|
|
879
|
-
|
|
938
|
+
};
|
|
880
939
|
let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
|
|
881
940
|
winston.debug("message sent : ", tdMessage);
|
|
941
|
+
|
|
942
|
+
|
|
882
943
|
//generate Tiledesk wait message
|
|
883
944
|
let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
|
|
884
945
|
let message = await tdChannel.generateWaitTdMessage(from, delayTime)
|
|
885
946
|
//update delayIndex for wait command message time
|
|
886
947
|
await voiceChannel.saveDelayIndexForCallId(callSid)
|
|
948
|
+
|
|
949
|
+
// convert response to vxml
|
|
950
|
+
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
|
|
951
|
+
winston.debug("(vxml) VXML to SEND: "+ messageToVXML);
|
|
887
952
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
res.set('Content-Type', 'application/xml');
|
|
891
|
-
res.status(200).send(message);
|
|
953
|
+
res.set('Content-Type', 'text/xml');
|
|
954
|
+
res.status(200).send(messageToVXML);
|
|
892
955
|
|
|
893
956
|
})
|
|
894
957
|
|
|
@@ -923,7 +986,9 @@ router.get('/test', async (req, res) => {
|
|
|
923
986
|
tdChannel.setProjectId(project_id)
|
|
924
987
|
|
|
925
988
|
const tdTranslator = new TiledeskTwilioTranslator({
|
|
926
|
-
BASE_URL: BASE_URL
|
|
989
|
+
BASE_URL: BASE_URL,
|
|
990
|
+
aiService: aiService,
|
|
991
|
+
uploadService: uploadService
|
|
927
992
|
});
|
|
928
993
|
|
|
929
994
|
//let result = await tdChannel.signIn(ani)
|
|
@@ -1027,15 +1092,23 @@ router.get('/test', async (req, res) => {
|
|
|
1027
1092
|
|
|
1028
1093
|
|
|
1029
1094
|
let vxmlAttributes = {
|
|
1030
|
-
|
|
1031
|
-
|
|
1095
|
+
TTS_VOICE_LANGUAGE: null,
|
|
1096
|
+
TTS_VOICE_NAME: null,
|
|
1032
1097
|
callSid: callSid
|
|
1033
1098
|
};
|
|
1034
1099
|
|
|
1100
|
+
let sessionInfo = {
|
|
1101
|
+
user: {
|
|
1102
|
+
_id: 'id_user'
|
|
1103
|
+
},
|
|
1104
|
+
integrations: [
|
|
1105
|
+
{ type: 'openai', key: GPT_KEY}
|
|
1106
|
+
]
|
|
1107
|
+
}
|
|
1035
1108
|
|
|
1036
1109
|
|
|
1037
1110
|
// convert response to vxml
|
|
1038
|
-
let messageToVXML = await tdTranslator.toVXML(message_buttons, callSid, vxmlAttributes)
|
|
1111
|
+
let messageToVXML = await tdTranslator.toVXML(message_buttons, callSid, vxmlAttributes, sessionInfo )
|
|
1039
1112
|
//let disconnect = await tdChannel.disconnect();
|
|
1040
1113
|
|
|
1041
1114
|
|
|
@@ -1101,6 +1174,14 @@ async function startApp(settings, callback) {
|
|
|
1101
1174
|
}
|
|
1102
1175
|
winston.debug("(voice) API_URL: " + API_URL)
|
|
1103
1176
|
|
|
1177
|
+
if (!settings.BASE_FILE_URL) {
|
|
1178
|
+
let port = process.env.PORT || 3000;
|
|
1179
|
+
BASE_FILE_URL = 'http://localhost:' + port;
|
|
1180
|
+
} else {
|
|
1181
|
+
BASE_FILE_URL = settings.BASE_FILE_URL;
|
|
1182
|
+
}
|
|
1183
|
+
winston.debug("(voice) API_URL: " + API_URL)
|
|
1184
|
+
|
|
1104
1185
|
if (!settings.BASE_URL) {
|
|
1105
1186
|
winston.error("(voice) BASE_URL is mandatory. Exit...");
|
|
1106
1187
|
return callback('Missing parameter: BASE_URL');
|
|
@@ -1113,6 +1194,19 @@ async function startApp(settings, callback) {
|
|
|
1113
1194
|
BRAND_NAME = settings.BRAND_NAME
|
|
1114
1195
|
}
|
|
1115
1196
|
|
|
1197
|
+
if (settings.GPT_KEY) {
|
|
1198
|
+
GPT_KEY = settings.GPT_KEY;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if(settings.OPENAI_ENDPOINT){
|
|
1202
|
+
OPENAI_ENDPOINT = settings.OPENAI_ENDPOINT
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
if(settings.MAX_POLLING_TIME){
|
|
1206
|
+
MAX_POLLING_TIME = settings.MAX_POLLING_TIME;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
|
|
1116
1210
|
if (settings.REDIS_HOST && settings.REDIS_PORT) {
|
|
1117
1211
|
REDIS_HOST = settings.REDIS_HOST;
|
|
1118
1212
|
REDIS_PORT = settings.REDIS_PORT;
|
|
@@ -1124,10 +1218,22 @@ async function startApp(settings, callback) {
|
|
|
1124
1218
|
|
|
1125
1219
|
//init VOICE CHANNEL
|
|
1126
1220
|
voiceChannel = new VoiceChannel({
|
|
1127
|
-
BASE_POOLING_DELAY: settings.BASE_POOLING_DELAY ||
|
|
1221
|
+
BASE_POOLING_DELAY: settings.BASE_POOLING_DELAY || BASE_POOLING_DELAY,
|
|
1128
1222
|
redis_client: redis_client,
|
|
1129
1223
|
})
|
|
1130
1224
|
|
|
1225
|
+
//init Services
|
|
1226
|
+
aiService = new AiService({
|
|
1227
|
+
OPENAI_ENDPOINT: OPENAI_ENDPOINT
|
|
1228
|
+
})
|
|
1229
|
+
integrationService = new IntegrationService({
|
|
1230
|
+
API_URL: API_URL,
|
|
1231
|
+
})
|
|
1232
|
+
uploadService = new UploadService({
|
|
1233
|
+
API_URL: BASE_FILE_URL,
|
|
1234
|
+
})
|
|
1235
|
+
|
|
1236
|
+
|
|
1131
1237
|
if (settings.dbconnection) {
|
|
1132
1238
|
db.reuseConnection(settings.dbconnection, () => {
|
|
1133
1239
|
winston.debug("(voice) KVBaseMongo reused exsisting db connection");
|
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.15",
|
|
4
4
|
"description": "Tiledesk VOICE Twilio connector",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Gabriele Panico",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"app-root-path": "^3.0.0",
|
|
23
23
|
"axios": "^0.27.2",
|
|
24
|
+
"axios-retry": "^4.5.0",
|
|
24
25
|
"bluebird": "^3.7.2",
|
|
25
26
|
"body-parser": "^1.20.2",
|
|
26
27
|
"dotenv": "^16.0.2",
|
package/routes/manageApp.js
CHANGED
|
@@ -13,6 +13,12 @@ const handlebars = require('handlebars');
|
|
|
13
13
|
const { TiledeskChannel } = require("../tiledesk/TiledeskChannel")
|
|
14
14
|
const { TiledeskSubscriptionClient } = require('../tiledesk/TiledeskSubscriptionClient');
|
|
15
15
|
|
|
16
|
+
//services
|
|
17
|
+
const { IntegrationService } = require('../tiledesk/services/IntegrationService');
|
|
18
|
+
const { AiService } = require('../tiledesk/services/AiService')
|
|
19
|
+
let integrationService = null;
|
|
20
|
+
let aiService = null;
|
|
21
|
+
|
|
16
22
|
//constant
|
|
17
23
|
const CHANNEL_NAME = require('../tiledesk/constants').CHANNEL_NAME;
|
|
18
24
|
|
|
@@ -23,6 +29,8 @@ router.use(express.static(path.join(__dirname, '..', '/template')));
|
|
|
23
29
|
|
|
24
30
|
let API_URL = null;
|
|
25
31
|
let BASE_URL = null;
|
|
32
|
+
let OPENAI_ENDPOINT = null;
|
|
33
|
+
let GPT_KEY = null;
|
|
26
34
|
let BRAND_NAME = null;
|
|
27
35
|
let redis_client = null;
|
|
28
36
|
let db = null;
|
|
@@ -174,6 +182,8 @@ router.post('/update', async (req, res) => {
|
|
|
174
182
|
});
|
|
175
183
|
winston.debug("(voice) found departments", departments)
|
|
176
184
|
|
|
185
|
+
|
|
186
|
+
|
|
177
187
|
if (settings) {
|
|
178
188
|
|
|
179
189
|
settings.department_id = department_id;
|
|
@@ -377,8 +387,8 @@ async function startApp(settings, callback) {
|
|
|
377
387
|
|
|
378
388
|
|
|
379
389
|
if (settings.BRAND_NAME) {
|
|
380
|
-
winston.info("(voice)-MANAGE BRAND_NAME: " + BRAND_NAME);
|
|
381
390
|
BRAND_NAME = settings.BRAND_NAME
|
|
391
|
+
winston.info("(voice)-MANAGE BRAND_NAME: " + BRAND_NAME);
|
|
382
392
|
}
|
|
383
393
|
|
|
384
394
|
|