@tiledesk/tiledesk-voice-twilio-connector 0.1.26-rc11 → 0.1.26-rc13
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 +234 -190
- package/logs/app.log +92 -0
- package/package.json +1 -1
- package/tiledesk/TiledeskChannel.js +1 -1
- package/tiledesk/TiledeskTwilioTranslator.js +67 -33
- package/tiledesk/VoiceChannel.js +42 -37
- 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 +20 -1
package/logs/app.log
CHANGED
|
@@ -2988,3 +2988,95 @@ info: (voice) Starting Manage Route
|
|
|
2988
2988
|
info: (voice)-MANAGE API_URL: https://tiledesk-server-pre.herokuapp.com
|
|
2989
2989
|
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
2990
2990
|
info: (voice)-MANAGE redis_client: [object Object]
|
|
2991
|
+
info: (voice) Starting VOICE TWILIO App
|
|
2992
|
+
info: (voice) Starting Manage Route
|
|
2993
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
2994
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
2995
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
2996
|
+
info: (voice) Starting VOICE TWILIO App
|
|
2997
|
+
info: (voice) Starting Manage Route
|
|
2998
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
2999
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3000
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3001
|
+
error: undefined {"name":"MongoServerSelectionError","reason":{"commonWireVersion":null,"compatibilityError":null,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"logicalSessionTimeoutMinutes":null,"maxElectionId":null,"maxSetVersion":null,"servers":{},"setName":null,"stale":false,"type":"Single"}}
|
|
3002
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3003
|
+
info: (voice) Starting Manage Route
|
|
3004
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3005
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3006
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3007
|
+
error: undefined {"name":"MongoServerSelectionError","reason":{"commonWireVersion":null,"compatibilityError":null,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"logicalSessionTimeoutMinutes":null,"maxElectionId":null,"maxSetVersion":null,"servers":{},"setName":null,"stale":false,"type":"Single"}}
|
|
3008
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3009
|
+
info: (voice) Starting Manage Route
|
|
3010
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3011
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3012
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3013
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3014
|
+
info: (voice) Starting Manage Route
|
|
3015
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3016
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3017
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3018
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3019
|
+
info: (voice) Starting Manage Route
|
|
3020
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3021
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3022
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3023
|
+
error: undefined {"name":"MongoServerSelectionError","reason":{"commonWireVersion":null,"compatibilityError":null,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"logicalSessionTimeoutMinutes":null,"maxElectionId":null,"maxSetVersion":null,"servers":{},"setName":null,"stale":false,"type":"Single"}}
|
|
3024
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3025
|
+
info: (voice) Starting Manage Route
|
|
3026
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3027
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3028
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3029
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3030
|
+
info: (voice) Starting Manage Route
|
|
3031
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3032
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3033
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3034
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3035
|
+
info: (voice) Starting Manage Route
|
|
3036
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3037
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3038
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3039
|
+
error: undefined {"name":"MongoServerSelectionError","reason":{"commonWireVersion":null,"compatibilityError":null,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"logicalSessionTimeoutMinutes":null,"maxElectionId":null,"maxSetVersion":null,"servers":{},"setName":null,"stale":false,"type":"Single"}}
|
|
3040
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3041
|
+
info: (voice) Starting Manage Route
|
|
3042
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3043
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3044
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3045
|
+
error: undefined {"name":"MongoServerSelectionError","reason":{"commonWireVersion":null,"compatibilityError":null,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"logicalSessionTimeoutMinutes":null,"maxElectionId":null,"maxSetVersion":null,"servers":{},"setName":null,"stale":false,"type":"Single"}}
|
|
3046
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3047
|
+
info: (voice) Starting Manage Route
|
|
3048
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3049
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3050
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3051
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3052
|
+
info: (voice) Starting Manage Route
|
|
3053
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3054
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3055
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3056
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3057
|
+
info: (voice) Starting Manage Route
|
|
3058
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3059
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3060
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3061
|
+
error: undefined {"name":"MongoServerSelectionError","reason":{"commonWireVersion":null,"compatibilityError":null,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"logicalSessionTimeoutMinutes":null,"maxElectionId":null,"maxSetVersion":null,"servers":{},"setName":null,"stale":false,"type":"Single"}}
|
|
3062
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3063
|
+
info: (voice) Starting Manage Route
|
|
3064
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3065
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3066
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3067
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3068
|
+
info: (voice) Starting Manage Route
|
|
3069
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3070
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3071
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3072
|
+
error: undefined {"name":"MongoServerSelectionError","reason":{"commonWireVersion":null,"compatibilityError":null,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"logicalSessionTimeoutMinutes":null,"maxElectionId":null,"maxSetVersion":null,"servers":{},"setName":null,"stale":false,"type":"Single"}}
|
|
3073
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3074
|
+
info: (voice) Starting Manage Route
|
|
3075
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3076
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3077
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
|
3078
|
+
info: (voice) Starting VOICE TWILIO App
|
|
3079
|
+
info: (voice) Starting Manage Route
|
|
3080
|
+
info: (voice)-MANAGE API_URL: https://stage.eks.tiledesk.com/api
|
|
3081
|
+
info: (voice)-MANAGE BASE_URL: https://pw99h2hr-3000.euw.devtunnels.ms
|
|
3082
|
+
info: (voice)-MANAGE redis_client: [object Object]
|
package/package.json
CHANGED
|
@@ -266,7 +266,7 @@ class TiledeskChannel {
|
|
|
266
266
|
/** SUBSCRIBE TO REDIS TOPIC */
|
|
267
267
|
async subscribeToTopic(conversation_id){
|
|
268
268
|
const topic = `tiledesk:conversation:${conversation_id}`;
|
|
269
|
-
console.log("subscribeToTopic: " + topic);
|
|
269
|
+
// console.log("subscribeToTopic: " + topic);
|
|
270
270
|
|
|
271
271
|
// duplichi il client principale
|
|
272
272
|
const subscriber = this.redis_client.duplicate();
|
|
@@ -11,11 +11,16 @@ const SETTING_MESSAGE = require('./constants').SETTING_MESSAGE
|
|
|
11
11
|
const CHANNEL_NAME = require('./constants').CHANNEL_NAME
|
|
12
12
|
const VOICE_PROVIDER = require('./constants').VOICE_PROVIDER;
|
|
13
13
|
const OPENAI_SETTINGS = require('./constants').OPENAI_SETTINGS;
|
|
14
|
+
const ELEVENLABS_SETTINGS = require('./constants').ELEVENLABS_SETTINGS;
|
|
14
15
|
|
|
15
16
|
const TYPE_ACTION_VXML = require('./constants').TYPE_ACTION_VXML
|
|
16
17
|
const TYPE_MESSAGE = require('./constants').TYPE_MESSAGE
|
|
17
18
|
const INFO_MESSAGE_TYPE = require('./constants').INFO_MESSAGE_TYPE
|
|
18
19
|
|
|
20
|
+
const voiceEventEmitter = require('./services/voiceEventEmitter');
|
|
21
|
+
|
|
22
|
+
const { SttError } = require('./errors');
|
|
23
|
+
|
|
19
24
|
class TiledeskTwilioTranslator {
|
|
20
25
|
/**
|
|
21
26
|
* Constructor for TiledeskVXMLTranslator
|
|
@@ -67,8 +72,8 @@ class TiledeskTwilioTranslator {
|
|
|
67
72
|
this.voiceProvider = VOICE_PROVIDER.TWILIO
|
|
68
73
|
if(flowAttributes.VOICE_PROVIDER){
|
|
69
74
|
this.voiceProvider = flowAttributes.VOICE_PROVIDER
|
|
70
|
-
|
|
71
75
|
}
|
|
76
|
+
vxmlAttributes.VOICE_PROVIDER = this.voiceProvider;
|
|
72
77
|
|
|
73
78
|
// IF VOICE_PROVIDER is TWILIO --> default values is on user account twilio settings
|
|
74
79
|
// IF VOICE_PROVIDER is OPENAI --> set default values from constants
|
|
@@ -77,13 +82,21 @@ class TiledeskTwilioTranslator {
|
|
|
77
82
|
vxmlAttributes.TTS_MODEL = flowAttributes.TTS_MODEL? flowAttributes.TTS_MODEL : OPENAI_SETTINGS.TTS_MODEL;
|
|
78
83
|
vxmlAttributes.STT_MODEL = flowAttributes.STT_MODEL? flowAttributes.STT_MODEL : OPENAI_SETTINGS.STT_MODEL;
|
|
79
84
|
}
|
|
80
|
-
|
|
85
|
+
|
|
86
|
+
// IF VOICE_PROVIDER is ELEVENLABS --> default values is on user account twilio settings
|
|
87
|
+
// IF VOICE_PROVIDER is ELEVENLABS --> set default values from constants
|
|
88
|
+
if(this.voiceProvider === VOICE_PROVIDER.ELEVENLABS){
|
|
89
|
+
vxmlAttributes.TTS_VOICE_NAME = flowAttributes.TTS_VOICE_NAME? flowAttributes.TTS_VOICE_NAME : ELEVENLABS_SETTINGS.TTS_VOICE_NAME;
|
|
90
|
+
vxmlAttributes.TTS_MODEL = flowAttributes.TTS_MODEL? flowAttributes.TTS_MODEL : ELEVENLABS_SETTINGS.TTS_MODEL;
|
|
91
|
+
vxmlAttributes.TTS_VOICE_LANGUAGE = flowAttributes.TTS_VOICE_LANGUAGE? flowAttributes.TTS_VOICE_LANGUAGE : ELEVENLABS_SETTINGS.TTS_VOICE_LANGUAGE;
|
|
92
|
+
vxmlAttributes.STT_MODEL = flowAttributes.STT_MODEL? flowAttributes.STT_MODEL : ELEVENLABS_SETTINGS.STT_MODEL;
|
|
93
|
+
}
|
|
81
94
|
|
|
82
95
|
}
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
|
|
86
97
|
winston.debug("[TiledeskVXMLTranslator] manageVoiceAttributes: vxmlAttributes returned:", vxmlAttributes);
|
|
98
|
+
voiceEventEmitter.emit('saveSettings', vxmlAttributes);
|
|
99
|
+
|
|
87
100
|
return vxmlAttributes
|
|
88
101
|
}
|
|
89
102
|
|
|
@@ -281,6 +294,9 @@ class TiledeskTwilioTranslator {
|
|
|
281
294
|
|
|
282
295
|
async delayVXMLConverter(rootEle, message, xmlAttributes){
|
|
283
296
|
const command = message.attributes.commands[0]
|
|
297
|
+
|
|
298
|
+
const prompt = this.promptVXML(rootEle, message, xmlAttributes);
|
|
299
|
+
|
|
284
300
|
rootEle.ele("Redirect", {method: "POST"}, this.BASE_URL + '/nextblock/' + xmlAttributes.callSid).up()
|
|
285
301
|
|
|
286
302
|
return rootEle.end({ pretty: true });
|
|
@@ -289,7 +305,7 @@ class TiledeskTwilioTranslator {
|
|
|
289
305
|
|
|
290
306
|
async playPromptVXMLConverter(rootEle, message, xmlAttributes){
|
|
291
307
|
|
|
292
|
-
const prompt = this.promptVXML(rootEle, message, xmlAttributes);
|
|
308
|
+
const prompt = await this.promptVXML(rootEle, message, xmlAttributes);
|
|
293
309
|
|
|
294
310
|
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + '&previousIntentTimestamp='+Date.now();
|
|
295
311
|
rootEle.ele("Redirect", {method: "POST"}, this.BASE_URL + '/nextblock/' + xmlAttributes.callSid + queryUrl).up()
|
|
@@ -307,7 +323,8 @@ class TiledeskTwilioTranslator {
|
|
|
307
323
|
const gather = rootEle.ele("Gather", { input: "speech"})
|
|
308
324
|
|
|
309
325
|
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
|
|
310
|
-
gather.att("action", this.BASE_URL + '/
|
|
326
|
+
gather.att("action", this.BASE_URL + '/nextBlock/' + xmlAttributes.callSid + queryUrl)
|
|
327
|
+
// gather.att("action", this.BASE_URL + '/speechresult/' + xmlAttributes.callSid + queryUrl)
|
|
311
328
|
.att("method", "POST")
|
|
312
329
|
.att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
|
|
313
330
|
.att('speechTimeout', "auto")
|
|
@@ -339,15 +356,16 @@ class TiledeskTwilioTranslator {
|
|
|
339
356
|
}
|
|
340
357
|
|
|
341
358
|
record
|
|
342
|
-
|
|
359
|
+
.att("action", this.BASE_URL + '/record/action/' + xmlAttributes.callSid + queryUrl)
|
|
343
360
|
.att("method", "POST")
|
|
344
361
|
.att("trim", "trim-silence")
|
|
345
|
-
.att("
|
|
362
|
+
.att("timeout", "2")
|
|
363
|
+
.att("recordingStatusCallback", this.BASE_URL + '/record/callback/' + xmlAttributes.callSid + queryUrl)
|
|
346
364
|
.att("recordingStatusCallbackMethod", "POST")
|
|
347
365
|
|
|
348
|
-
if(xmlAttributes && xmlAttributes.noInputTimeout){
|
|
349
|
-
|
|
350
|
-
}
|
|
366
|
+
// if(xmlAttributes && xmlAttributes.noInputTimeout){
|
|
367
|
+
// record.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
|
|
368
|
+
// }
|
|
351
369
|
|
|
352
370
|
}
|
|
353
371
|
|
|
@@ -366,7 +384,7 @@ class TiledeskTwilioTranslator {
|
|
|
366
384
|
let queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + '&previousIntentTimestamp='+Date.now() + '&menu_options=' + menu_options;
|
|
367
385
|
const handleNoInputNoMatchQuery = await this.handleNoInputNoMatch(rootEle, message, xmlAttributes);
|
|
368
386
|
if(handleNoInputNoMatchQuery && handleNoInputNoMatchQuery.queryNoMatch){
|
|
369
|
-
queryUrl += '&
|
|
387
|
+
queryUrl += '&'+ handleNoInputNoMatchQuery.queryNoMatch
|
|
370
388
|
}
|
|
371
389
|
|
|
372
390
|
const gather = rootEle.ele("Gather", { input: "dtmf"})
|
|
@@ -470,7 +488,7 @@ class TiledeskTwilioTranslator {
|
|
|
470
488
|
}
|
|
471
489
|
|
|
472
490
|
|
|
473
|
-
queryNoInput = '
|
|
491
|
+
queryNoInput = 'button_action='+button_noIput.action.substring(1);
|
|
474
492
|
//rootEle.ele("Redirect", {}, this.BASE_URL + '/handle/' + attributes.callSid + '/no_input'+ queryNoInput)
|
|
475
493
|
|
|
476
494
|
|
|
@@ -497,7 +515,7 @@ class TiledeskTwilioTranslator {
|
|
|
497
515
|
value: 'no_match'
|
|
498
516
|
}
|
|
499
517
|
|
|
500
|
-
queryNoMatch = '
|
|
518
|
+
queryNoMatch = 'button_action='+button_noMatch.action.substring(1); //remove '#' from intentId because is not a valid char for XML lang
|
|
501
519
|
//rootEle.ele("Redirect", {}, this.BASE_URL + '/handle/' + attributes.callSid + '/no_match'+ queryNoMatch)
|
|
502
520
|
|
|
503
521
|
/*element.ele("nomatch")
|
|
@@ -622,29 +640,45 @@ class TiledeskTwilioTranslator {
|
|
|
622
640
|
}
|
|
623
641
|
|
|
624
642
|
async generateTTS(text, attributes){
|
|
625
|
-
|
|
626
643
|
let audioData = null;
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
644
|
+
try {
|
|
645
|
+
switch(this.voiceProvider){
|
|
646
|
+
case VOICE_PROVIDER.OPENAI:
|
|
647
|
+
let GPT_KEY = this.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.key
|
|
648
|
+
audioData = await this.aiService.textToSpeech(text, attributes.TTS_VOICE_NAME, attributes.TTS_MODEL, GPT_KEY)
|
|
649
|
+
break;
|
|
650
|
+
case VOICE_PROVIDER.ELEVENLABS:
|
|
651
|
+
let ELEVENLABS_APIKEY = this.integrations.find((el => el.type === VOICE_PROVIDER.ELEVENLABS))?.key
|
|
652
|
+
audioData = await this.aiService.textToSpeechElevenLabs(text, attributes.TTS_VOICE_NAME, attributes.TTS_MODEL, attributes.TTS_VOICE_LANGUAGE, ELEVENLABS_APIKEY)
|
|
653
|
+
break;
|
|
654
|
+
default:
|
|
655
|
+
throw new SttError('TTS_FAILED', 'Unsupported voice provider: ' + this.voiceProvider);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (!audioData) {
|
|
659
|
+
throw new SttError('TTS_FAILED', 'TTS returned no audio data');
|
|
660
|
+
}
|
|
640
661
|
|
|
662
|
+
let fileUrl = await this.uploadService.upload(attributes.callSid, audioData, this.user)
|
|
663
|
+
winston.debug('(voice) Audio Message url captured after TTS -->', fileUrl)
|
|
664
|
+
return fileUrl
|
|
665
|
+
} catch (error) {
|
|
666
|
+
winston.error('(voice) TTS generation error:', error);
|
|
667
|
+
switch (error.code) {
|
|
668
|
+
case 'TTS_FAILED':
|
|
669
|
+
winston.error('(voice) TTS_FAILED:', error.message);
|
|
670
|
+
break;
|
|
671
|
+
case 'AI_SERVICE_ERROR':
|
|
672
|
+
winston.error('(voice) AI_SERVICE_ERROR:', error.message);
|
|
673
|
+
break;
|
|
674
|
+
case 'UPLOAD_SERVICE_ERROR':
|
|
675
|
+
winston.error('(voice) UPLOAD_SERVICE_ERROR:', error.message);
|
|
676
|
+
break;
|
|
677
|
+
default:
|
|
678
|
+
throw new SttError('TTS_FAILED', 'TTS generation failed: ' + error.message);
|
|
679
|
+
}
|
|
641
680
|
}
|
|
642
681
|
|
|
643
|
-
let fileUrl = await this.uploadService.upload(attributes.callSid, audioData, this.user).catch((err)=>{
|
|
644
|
-
console.log('errr while uploading audioData', err.response)
|
|
645
|
-
})
|
|
646
|
-
console.log('(voice) Audio Message url captured after TTS -->', fileUrl)
|
|
647
|
-
return fileUrl
|
|
648
682
|
}
|
|
649
683
|
|
|
650
684
|
|
package/tiledesk/VoiceChannel.js
CHANGED
|
@@ -4,17 +4,10 @@ const jwt = require("jsonwebtoken");
|
|
|
4
4
|
const { v4: uuidv4 } = require("uuid");
|
|
5
5
|
const { promisify } = require('util');
|
|
6
6
|
|
|
7
|
-
/*UTILS*/
|
|
8
|
-
const utils = require('./utils-message.js')
|
|
9
|
-
const TYPE_MESSAGE = require('./constants').TYPE_MESSAGE
|
|
10
|
-
const MESSAGE_TYPE_MINE = require('./constants').MESSAGE_TYPE_MINE
|
|
11
|
-
const MESSAGE_TYPE_OTHERS = require('./constants').MESSAGE_TYPE_OTHERS
|
|
12
|
-
const CHANNEL_NAME = require('./constants').CHANNEL_NAME
|
|
13
|
-
const VOICE_PROVIDER = require('./constants').VOICE_PROVIDER;
|
|
14
|
-
const OPENAI_SETTINGS = require('./constants').OPENAI_SETTINGS;
|
|
15
|
-
|
|
16
7
|
const winston = require("../winston");
|
|
17
8
|
|
|
9
|
+
const voiceEventEmitter = require('./services/voiceEventEmitter');
|
|
10
|
+
|
|
18
11
|
class VoiceChannel {
|
|
19
12
|
|
|
20
13
|
|
|
@@ -48,8 +41,21 @@ class VoiceChannel {
|
|
|
48
41
|
}
|
|
49
42
|
|
|
50
43
|
this.redis_client = config.redis_client
|
|
44
|
+
|
|
45
|
+
this.listenToVoiceEvents();
|
|
51
46
|
|
|
52
47
|
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
listenToVoiceEvents(){
|
|
51
|
+
|
|
52
|
+
voiceEventEmitter.on('saveSettings', async (data) => {
|
|
53
|
+
winston.debug('[VoiceChannel] listenToVoiceEvents: saveSettings event received -->', data)
|
|
54
|
+
if(data){
|
|
55
|
+
await this.saveSettingsForCallId(data, data.callSid);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
53
59
|
|
|
54
60
|
|
|
55
61
|
async getNextDelayTimeForCallId(callId){
|
|
@@ -69,11 +75,11 @@ class VoiceChannel {
|
|
|
69
75
|
//increment
|
|
70
76
|
const delayIndex = (+index) +1
|
|
71
77
|
//save new index to redis
|
|
72
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', delayIndex,
|
|
78
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', delayIndex, 'EX', 86400);
|
|
73
79
|
return;
|
|
74
80
|
}
|
|
75
81
|
//if index is not present: set to default (0)
|
|
76
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0,
|
|
82
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, 'EX', 86400);
|
|
77
83
|
}
|
|
78
84
|
|
|
79
85
|
/** RESET INDEX INTO REDIS DATA FOR CURRENT CALLID **/
|
|
@@ -94,50 +100,49 @@ class VoiceChannel {
|
|
|
94
100
|
|
|
95
101
|
async saveSettingsForCallId(attributes, callId){
|
|
96
102
|
|
|
97
|
-
winston.debug('saveSettingsForCallId: attributes -->', attributes)
|
|
98
|
-
let flowAttributes = {}
|
|
99
|
-
if(attributes && attributes.flowAttributes){
|
|
100
|
-
|
|
101
|
-
flowAttributes = attributes.flowAttributes;
|
|
102
|
-
|
|
103
|
-
//MANAGE VOICE SETTINGS from globals attributes
|
|
104
|
-
let voiceProvider = VOICE_PROVIDER.TWILIO
|
|
105
|
-
if(flowAttributes.VOICE_PROVIDER){
|
|
106
|
-
voiceProvider = flowAttributes.VOICE_PROVIDER
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// IF VOICE_PROVIDER is TWILIO --> default values is on user account twilio settings
|
|
111
|
-
// IF VOICE_PROVIDER is OPENAI --> set default values from constants
|
|
112
|
-
if(voiceProvider === VOICE_PROVIDER.OPENAI){
|
|
113
|
-
flowAttributes.TTS_VOICE_NAME = flowAttributes.TTS_VOICE_NAME? flowAttributes.TTS_VOICE_NAME : OPENAI_SETTINGS.TTS_VOICE_NAME;
|
|
114
|
-
flowAttributes.TTS_MODEL = flowAttributes.TTS_MODEL? flowAttributes.TTS_MODEL : OPENAI_SETTINGS.TTS_MODEL;
|
|
115
|
-
flowAttributes.STT_MODEL = flowAttributes.STT_MODEL? flowAttributes.STT_MODEL : OPENAI_SETTINGS.STT_MODEL;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
103
|
+
winston.debug('[VoiceChannel] saveSettingsForCallId: attributes -->', attributes)
|
|
120
104
|
|
|
121
105
|
const index = await this.redis_client.get('tiledesk:voice:'+callId + ':attributes');
|
|
122
|
-
winston.debug('saveSettingsForCallId: attributes found -->'+index)
|
|
106
|
+
winston.debug('[VoiceChannel] saveSettingsForCallId: attributes found -->'+index)
|
|
123
107
|
if(index){
|
|
124
108
|
//set index to default (0)
|
|
125
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(
|
|
109
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), 'EX', 86400);
|
|
126
110
|
return;
|
|
127
111
|
}
|
|
128
112
|
//if index is not present: set to default (0)
|
|
129
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(
|
|
113
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), 'EX', 86400);
|
|
130
114
|
|
|
131
115
|
}
|
|
132
116
|
|
|
133
117
|
|
|
134
118
|
async getSettingsForCallId(callId){
|
|
135
119
|
const attributes = await this.redis_client.get('tiledesk:voice:'+callId + ':attributes');
|
|
120
|
+
winston.debug('[VoiceChannel] getSettingsForCallId: attributes found -->', attributes, callId)
|
|
136
121
|
if(attributes){
|
|
137
122
|
return JSON.parse(attributes)
|
|
138
123
|
}
|
|
139
124
|
return {};
|
|
140
125
|
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
async deleteCallKeys(callSid) {
|
|
129
|
+
const pattern = `tiledesk:voice:${callSid}:*`;
|
|
130
|
+
let cursor = 0;
|
|
131
|
+
|
|
132
|
+
do {
|
|
133
|
+
const reply = await this.redis_client.scan(cursor, {
|
|
134
|
+
MATCH: pattern,
|
|
135
|
+
COUNT: 100
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
cursor = reply.cursor;
|
|
139
|
+
const keys = reply.keys;
|
|
140
|
+
|
|
141
|
+
if (keys.length > 0) {
|
|
142
|
+
await this.redis_client.del(keys);
|
|
143
|
+
}
|
|
144
|
+
} while (cursor !== 0);
|
|
145
|
+
}
|
|
141
146
|
|
|
142
147
|
|
|
143
148
|
|
package/tiledesk/constants.js
CHANGED
|
@@ -58,5 +58,21 @@ module.exports = {
|
|
|
58
58
|
TTS_VOICE_NAME: 'alloy',
|
|
59
59
|
TTS_MODEL: 'tts-1',
|
|
60
60
|
STT_MODEL: 'whisper-1'
|
|
61
|
-
}
|
|
61
|
+
},
|
|
62
|
+
ELEVENLABS_SETTINGS:{
|
|
63
|
+
TTS_VOICE_NAME: '21m00Tcm4TlvDq8ikWAM',
|
|
64
|
+
TTS_MODEL: 'eleven_multilingual_v2',
|
|
65
|
+
TTS_VOICE_LANGUAGE: 'en',
|
|
66
|
+
STT_MODEL: 'scribe_v1'
|
|
67
|
+
},
|
|
68
|
+
NON_SPEECH_TOKENS: [
|
|
69
|
+
'(music)',
|
|
70
|
+
'(noise)',
|
|
71
|
+
'(silence)',
|
|
72
|
+
'(background noise)',
|
|
73
|
+
'(applause)',
|
|
74
|
+
'(breathing)',
|
|
75
|
+
'(laughs)',
|
|
76
|
+
'(laughter)'
|
|
77
|
+
]
|
|
62
78
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class SttError extends Error {
|
|
2
|
+
constructor(code, message, extra = {}) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.code = code; // es. 'AUDIO_DOWNLOAD_FAILED'
|
|
5
|
+
this.extra = extra; // opzionale, eventuali dati aggiuntivi
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class RedisError extends Error {
|
|
10
|
+
constructor(code, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class ServiceError extends Error {
|
|
17
|
+
constructor(code, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.code = code;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// esporta tutte insieme
|
|
24
|
+
module.exports = {
|
|
25
|
+
SttError,
|
|
26
|
+
RedisError,
|
|
27
|
+
ServiceError
|
|
28
|
+
};
|