@tiledesk/tiledesk-voice-twilio-connector 0.1.26-rc8 → 0.1.26

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/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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-voice-twilio-connector",
3
- "version": "0.1.26-rc8",
3
+ "version": "0.1.26",
4
4
  "description": "Tiledesk VOICE Twilio connector",
5
5
  "license": "MIT",
6
6
  "author": "Gabriele Panico",
@@ -55,7 +55,7 @@ router.get("/", async (req, res) => {
55
55
 
56
56
  router.get('/configure', async (req, res) => {
57
57
 
58
- winston.verbose("(voice) /configure :params", req.query);
58
+ winston.debug("(voice) /configure :params", req.query);
59
59
 
60
60
  let project_id = req.query.project_id;
61
61
  let token = req.query.token;
@@ -89,7 +89,7 @@ router.get('/configure', async (req, res) => {
89
89
  let CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
90
90
 
91
91
  let settings = await db.get(CONTENT_KEY);
92
- winston.verbose("(voice) settings: ", settings);
92
+ winston.debug("(voice) settings: ", settings);
93
93
 
94
94
  // get departments
95
95
  const tdChannel = new TiledeskChannel({
@@ -157,7 +157,7 @@ router.get('/configure', async (req, res) => {
157
157
  })
158
158
 
159
159
  router.post('/update', async (req, res) => {
160
- winston.verbose("(voice) /update", req.body);
160
+ winston.debug("(voice) /update", req.body);
161
161
 
162
162
  let project_id = req.body.project_id;
163
163
  let token = req.body.token;
@@ -168,7 +168,7 @@ router.post('/update', async (req, res) => {
168
168
  let CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
169
169
  let settings = await db.get(CONTENT_KEY);
170
170
 
171
- winston.verbose("(voice) /update settings", settings);
171
+ winston.debug("(voice) /update settings", settings);
172
172
 
173
173
  let proxy_url = BASE_URL + "/webhook/" + project_id;
174
174
  let status_url = BASE_URL + "/twilio/status";
@@ -264,7 +264,7 @@ router.post('/update', async (req, res) => {
264
264
 
265
265
  router.post('/disconnect', async (req, res) => {
266
266
 
267
- winston.verbose("(voice) /disconnect")
267
+ winston.debug("(voice) /disconnect")
268
268
 
269
269
  let project_id = req.body.project_id;
270
270
  let token = req.body.token;
@@ -273,7 +273,7 @@ router.post('/disconnect', async (req, res) => {
273
273
 
274
274
  let CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
275
275
  await db.remove(CONTENT_KEY);
276
- winston.verbose("(voice) Content deleted.");
276
+ winston.debug("(voice) Content deleted.");
277
277
 
278
278
  let proxy_url = BASE_URL + "/webhook/" + project_id;
279
279
  let status_url = BASE_URL + "/twilio/status";
@@ -327,7 +327,7 @@ async function subscribe(token, project_id){
327
327
  return new Promise((resolve, reject)=> {
328
328
  tdClient.subscribe(subscription_info).then(async (data)=> {
329
329
  let subscription = data;
330
- winston.verbose("(voice) Subscription: ", subscription)
330
+ winston.debug("(voice) Subscription: ", subscription)
331
331
 
332
332
  let settings = {
333
333
  project_id: project_id,
@@ -352,7 +352,7 @@ async function unsubscribe(token, project_id, subscriptionId){
352
352
 
353
353
  return new Promise((resolve, reject)=> {
354
354
  tdClient.unsubscribe(subscriptionId).then(async (data)=> {
355
- winston.verbose("(voice) Subscription: ", data)
355
+ winston.debug("(voice) Subscription: ", data)
356
356
  resolve(data)
357
357
  }).catch((error)=> {reject(error) })
358
358
 
@@ -76,7 +76,7 @@ class KVBaseMongo {
76
76
  resolve(doc.value);
77
77
  }
78
78
  else {
79
- winston.verbose("No Doc found!");
79
+ winston.debug("No Doc found!");
80
80
  resolve(null);
81
81
  }
82
82
  }
@@ -246,17 +246,17 @@ class TiledeskChannel {
246
246
 
247
247
  /*SKIP INFO MESSAGES*/
248
248
  if(utils.messageType(TYPE_MESSAGE.INFO, message)){
249
- winston.verbose("> SKIPPING INFO message: " + JSON.stringify(message) );
249
+ winston.debug("> SKIPPING INFO message: " + JSON.stringify(message) );
250
250
  return;
251
251
  }
252
252
 
253
253
  /*SKIP CURRENT USER MESSAGES*/
254
254
  if (message.sender.indexOf("vxml") > -1) {
255
- winston.verbose("> SKIPPING ECHO message: " + JSON.stringify(message) );
255
+ winston.debug("> SKIPPING ECHO message: " + JSON.stringify(message) );
256
256
  return;
257
257
  }
258
258
 
259
- winston.verbose("> SAVE message TO QUEUE: " + JSON.stringify(message) );
259
+ winston.debug("> SAVE message TO QUEUE: " + JSON.stringify(message) );
260
260
 
261
261
  let conversation_id = message.recipient
262
262
  //PUBLISH MESSAGE TO REDIS TOPIC WITH KET tiledesk:queue:+conversation_id
@@ -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,16 +323,17 @@ 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 + '/speechresult/' + xmlAttributes.callSid + queryUrl)
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")
314
331
 
315
332
  //if(xmlAttributes && xmlAttributes.noInputTimeout){
316
- // gather.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
333
+ // gather.att("timeout", Math.round(xmlAttributes.noInputTimeout/1000) ).up();
317
334
  //}
318
335
  if(xmlAttributes && xmlAttributes.incompleteSpeechTimeout){
319
- gather.att("speechTimeout", xmlAttributes.incompleteSpeechTimeout/1000 ).up();
336
+ gather.att("speechTimeout", Math.round(xmlAttributes.incompleteSpeechTimeout/1000) ).up();
320
337
  }
321
338
 
322
339
  const prompt = this.promptVXML(gather, message, xmlAttributes);
@@ -339,15 +356,16 @@ class TiledeskTwilioTranslator {
339
356
  }
340
357
 
341
358
  record
342
- //.att("action", this.BASE_URL + '/record/' + xmlAttributes.callSid + queryUrl)
359
+ .att("action", this.BASE_URL + '/record/action/' + xmlAttributes.callSid + queryUrl)
343
360
  .att("method", "POST")
344
361
  .att("trim", "trim-silence")
345
- .att("recordingStatusCallback", this.BASE_URL + '/record/' + xmlAttributes.callSid + queryUrl)
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
- record.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
350
- }
366
+ // if(xmlAttributes && xmlAttributes.noInputTimeout){
367
+ // record.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
368
+ // }
351
369
 
352
370
  }
353
371
 
@@ -366,13 +384,13 @@ 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 += '&button_action='+ handleNoInputNoMatchQuery.queryNoMatch.split('button_action=')[1]
387
+ queryUrl += '&'+ handleNoInputNoMatchQuery.queryNoMatch
370
388
  }
371
389
 
372
390
  const gather = rootEle.ele("Gather", { input: "dtmf"})
373
391
 
374
392
 
375
- gather.att("timeout", xmlAttributes.noInputTimeout/1000 )
393
+ gather.att("timeout", Math.round(xmlAttributes.noInputTimeout/1000) )
376
394
  .att("numDigits", "1" )
377
395
  .att("action", this.BASE_URL + '/menublock/' + xmlAttributes.callSid + queryUrl)
378
396
  .att("method", "POST")
@@ -394,7 +412,7 @@ class TiledeskTwilioTranslator {
394
412
  const gather = rootEle.ele("Gather", { input: "dtmf"})
395
413
 
396
414
  const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
397
- gather.att("timeout", xmlAttributes.noInputTimeout/1000 )
415
+ gather.att("timeout", Math.round(xmlAttributes.noInputTimeout/1000) )
398
416
  .att("action", this.BASE_URL + '/menublock/' + xmlAttributes.callSid + queryUrl)
399
417
  .att("method", "POST")
400
418
  .att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
@@ -470,7 +488,7 @@ class TiledeskTwilioTranslator {
470
488
  }
471
489
 
472
490
 
473
- queryNoInput = 'intentName='+ querystring.encode(attributes.intentName) + '&previousIntentTimestamp='+Date.now() + '&button_action='+button_noIput.action.substring(1);
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 = 'intentName='+ querystring.encode(attributes.intentName) + '&previousIntentTimestamp='+Date.now() + '&button_action='+button_noMatch.action.substring(1); //remove '#' from intentId because is not a valid char for XML lang
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
- switch(this.voiceProvider){
628
- case VOICE_PROVIDER.OPENAI:
629
- let GPT_KEY = this.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.key
630
- audioData = await this.aiService.textToSpeech(text, attributes.TTS_VOICE_NAME, attributes.TTS_MODEL, GPT_KEY).catch((err)=>{
631
- console.log('errr while creating audio message', err.response?.data)
632
- })
633
- break;
634
- case VOICE_PROVIDER.ELEVENLABS:
635
- let ELEVENLABS_APIKEY = this.integrations.find((el => el.type === VOICE_PROVIDER.ELEVENLABS))?.key
636
- audioData = await this.aiService.textToSpeechElevenLabs(text, attributes.TTS_VOICE_NAME, attributes.TTS_MODEL, ELEVENLABS_APIKEY).catch((err)=>{
637
- console.log('errr while creating elevenlabs audio message', err.response?.data)
638
- })
639
- break;
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
 
@@ -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, {'EX': 86400});
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, {'EX': 86400});
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(flowAttributes), {'EX': 86400});
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(flowAttributes), {'EX': 86400});
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
 
@@ -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
+ };