@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 CHANGED
@@ -98,12 +98,6 @@ router.post("/tiledesk", async (req, res) => {
98
98
  });
99
99
  tdChannel.setProjectId(project_id)
100
100
 
101
- /*SKIP INFO MESSAGES*/
102
- /*SKIP CURRENT USER MESSAGES*/
103
- if(!utilsMess.messageType(TYPE_MESSAGE.INFO, tiledeskMessage) && !(tiledeskMessage.sender.indexOf("vxml") > -1) ){
104
- winston.debug("> whook SAVE MESSAGE TO QUEUE " + JSON.stringify(tiledeskMessage) );
105
- }
106
-
107
101
  await tdChannel.addMessageToQueue(tiledeskMessage)
108
102
 
109
103
  res.send("(voice) Message received from Voice Twilio Proxy");
@@ -145,7 +139,8 @@ router.post('/webhook/:id_project', async (req, res) => {
145
139
 
146
140
  const tdTranslator = new TiledeskTwilioTranslator({
147
141
  BASE_URL: BASE_URL,
148
- aiService: aiService
142
+ aiService: aiService,
143
+ uploadService: uploadService
149
144
  });
150
145
 
151
146
  let start2 = new Date().getTime();
@@ -155,32 +150,33 @@ router.post('/webhook/:id_project', async (req, res) => {
155
150
  return;
156
151
  }
157
152
  let end2 = new Date().getTime();
158
- // console.log('Time after signIn: ', end2-start2, '[ms]')
159
153
 
160
154
  //let conversation_id = await tdChannel.getConversation(ani, callId, user.token);
161
155
  let conversation_id = await tdChannel.generateConversation(from, callSid, user.token);
162
156
  winston.debug("(voice) conversation returned:"+ conversation_id);
163
157
 
164
-
165
- //GET AND SAVE GPT-KET IF
166
158
  let integrations = [], publicKey = false;
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;
159
+ try {
160
+ //GET AND SAVE GPT-KET IF
161
+ let key = await integrationService.getKeyFromIntegrations(project_id, 'openai', settings.token)
162
+ if (!key) {
163
+ winston.debug("(voice) - Key not found in Integrations. Searching in kb settings...");
164
+ key = await integrationService.getKeyFromKbSettings(project_id, settings.token);
165
+ }
166
+ if (!key) {
167
+ winston.debug("(voice) - Retrieve public gptkey")
168
+ key = GPT_KEY;
169
+ publicKey = true;
170
+ }
171
+ integrations.push({type: 'openai', key: key, publicKey: publicKey})
176
172
 
177
- }
178
- integrations.push({type: 'openai', key: key, publicKey: publicKey})
179
-
180
- let eleven_labs = await integrationService.getKeyFromIntegrations(project_id, 'elevenlabs', settings.token)
181
- if (eleven_labs) {
182
- winston.debug("(voice) - Key found in Integrations: "+ eleven_labs);
183
- integrations.push({type: 'elevenlabs', key: eleven_labs, publicKey: false})
173
+ let eleven_labs = await integrationService.getKeyFromIntegrations(project_id, 'elevenlabs', settings.token)
174
+ if (eleven_labs) {
175
+ winston.debug("(voice) - Key found in Integrations: "+ eleven_labs);
176
+ integrations.push({type: 'elevenlabs', key: eleven_labs, publicKey: false})
177
+ }
178
+ } catch (error) {
179
+ winston.error('(voice) - Error retrieving integrations keys:', error);
184
180
  }
185
181
 
186
182
  //save data to redis
@@ -226,7 +222,7 @@ router.post('/webhook/:id_project', async (req, res) => {
226
222
  let start_time_get_message = new Date()
227
223
  let message = await getMessage(callSid, from, project_id, conversation_id)
228
224
  let end_time_get_message = new Date()
229
- winston.verbose('Time to getMessage from queue in /webhook/:project_id : ' + (end_time_get_message-start_time_get_message) + '[ms] --- at time:' + new Date())
225
+ winston.verbose(`Time to getMessage from queue in /webhook/:${project_id} : ${(end_time_get_message-start_time_get_message)}[ms] --- at time:` + new Date())
230
226
 
231
227
  // //generate Tiledesk wait message
232
228
  // let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
@@ -239,7 +235,7 @@ router.post('/webhook/:id_project', async (req, res) => {
239
235
  winston.debug('(voice) /webhook/:id_project messageVXML-->'+ messageToVXML)
240
236
 
241
237
  let end_call = new Date().getTime();
242
- winston.info(`Time to respond to /webhook/${project_id} : ${(end_call-start_call)}[ms]`)
238
+ winston.info(`Time to respond to /webhook/${project_id}: ${(end_call-start_call)}[ms]`)
243
239
 
244
240
  // Render the response as XML in reply to the webhook request
245
241
  res.set('Content-Type', 'text/xml');
@@ -251,7 +247,6 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
251
247
  let start_call = new Date()
252
248
  winston.debug("(voice) called POST /nextblock ", req.body);
253
249
  winston.debug("(voice) called POST /nextblock query ", req.query);
254
- console.log('/nextblock at: ', new Date(), 'with text:', req.body.SpeechResult)
255
250
 
256
251
  let usertext = req.body.SpeechResult;
257
252
  let confidence = req.body.Confidence
@@ -325,7 +320,7 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
325
320
 
326
321
  // convert response to vxml
327
322
  let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
328
- winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
323
+ winston.debug("(voice) VXML to SEND: "+ messageToVXML);
329
324
 
330
325
  let end_call = new Date()
331
326
  console.log('Time to responde to /nextblock/:callSid : ', end_call-start_call, '[ms]')
@@ -338,8 +333,6 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
338
333
 
339
334
  router.post('/nextblock/:callSid/', async(req, res) => {
340
335
  let start_call = new Date()
341
- winston.debug("(voice) called POST /nextblock ", req.body);
342
- winston.debug("(voice) called POST /nextblock query ", req.query);
343
336
  winston.verbose("(voice) called POST /nextblock at" + new Date() + "with text: "+ req.body.SpeechResult);
344
337
 
345
338
  let usertext = req.body.SpeechResult;
@@ -404,7 +397,6 @@ router.post('/nextblock/:callSid/', async(req, res) => {
404
397
  let start_time_send_message = new Date()
405
398
  let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
406
399
  let end_time_send_message = new Date()
407
- winston.debug("message sent : ", tdMessage);
408
400
  winston.verbose(`(else) Time to send message to tiledesk in /nextblock/${callSid} : ${(end_time_send_message-start_time_send_message)}[ms] with text ` + tdMessage.text + ' --- at time:' + new Date())
409
401
 
410
402
  let start_time_get_message = new Date()
@@ -422,7 +414,7 @@ router.post('/nextblock/:callSid/', async(req, res) => {
422
414
  }
423
415
  })
424
416
  let end_promise_message = new Date()
425
- winston.verbose(`Time to manage message in Promise /nextblock/${callSid}: ${(end_promise_message-start_promise_message)}[ms]` + ' with text' + message.text + ' --- at time --' + new Date())
417
+ winston.verbose(`Time to manage message in Promise /nextblock/${callSid}: ${(end_promise_message-start_promise_message)}[ms]` + ' with text:' + message.text + ' --- at time --' + new Date())
426
418
 
427
419
  // convert response to vxml
428
420
  let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
@@ -524,8 +516,6 @@ async function getMessage(callSid, ani, project_id, conversation_id){
524
516
 
525
517
  router.post('/speechresult/:callSid', async (req, res) => {
526
518
  let start_call = new Date();
527
- winston.debug("(voice) called POST /speechresult ", req.body);
528
- winston.debug("(voice) called POST /speechresult query ", req.query);
529
519
  winston.verbose("(voice) called POST /speechresult at" + new Date() + "with text: "+ req.body.SpeechResult);
530
520
 
531
521
  let usertext = req.body.SpeechResult;
@@ -611,7 +601,7 @@ router.post('/speechresult/:callSid', async (req, res) => {
611
601
 
612
602
  // convert response to vxml
613
603
  let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
614
- winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
604
+ winston.debug("(voice) VXML to SEND: "+ messageToVXML);
615
605
 
616
606
  let end_call = new Date()
617
607
  winston.info(`Time to respond to /speechresult/${callSid} : ${(end_call-start_call)} [ms]`)
@@ -621,12 +611,142 @@ router.post('/speechresult/:callSid', async (req, res) => {
621
611
  res.status(200).send(messageToVXML);
622
612
  })
623
613
 
614
+ /* ----> called with Record tag in action property <----- */
615
+ router.post('/record/action/:callSid/',async (req, res) => {
616
+ winston.verbose('+++++++++++(voice) called POST record/action/:callSid at time ' + new Date());
617
+
618
+ let callSid = req.body.CallSid;
619
+ let sessionInfo;
620
+ let project_id, conversation_id, user;
621
+ let from, to;
622
+
623
+ let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
624
+ if (!redis_data) {
625
+ return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
626
+ }
627
+ sessionInfo = JSON.parse(redis_data)
628
+ project_id = sessionInfo.project_id;
629
+ from = sessionInfo.from;
630
+ to = sessionInfo.to;
631
+ conversation_id = sessionInfo.conversation_id;
632
+ user = sessionInfo.user;
633
+
634
+ let vxmlAttributes = {
635
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
636
+ TTS_VOICE_NAME: VOICE_NAME,
637
+ callSid: callSid,
638
+ };
639
+
640
+ const tdChannel = new TiledeskChannel({
641
+ API_URL: API_URL,
642
+ redis_client: redis_client
643
+ })
644
+ tdChannel.setProjectId(project_id)
645
+
646
+ const tdTranslator = new TiledeskTwilioTranslator({
647
+ BASE_URL: BASE_URL,
648
+ aiService: aiService,
649
+ uploadService: uploadService
650
+ });
651
+
652
+
653
+ let start_time_get_message = new Date()
654
+ let message = await getMessage(callSid, from, project_id, conversation_id)
655
+ winston.debug('message from getMessage in /record/action/: ', message)
656
+ let end_time_get_message = new Date()
657
+ winston.verbose(`Time to getMessage from queue in /record/action/${callSid} : ${(end_time_get_message-start_time_get_message)}[ms]` + ' --- at time:' + new Date())
658
+
659
+
660
+ //generate Tiledesk wait message
661
+ // let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
662
+ // let message = await tdChannel.generateWaitTdMessage(from, delayTime)
663
+ // //update delayIndex for wait command message time
664
+ // await voiceChannel.saveDelayIndexForCallId(callSid)
665
+
666
+ // convert response to vxml
667
+ let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
668
+ winston.debug("(voice) /record/action VXML to SEND: "+ messageToVXML);
669
+
670
+ res.set('Content-Type', 'application/xml');
671
+ res.status(200).send(messageToVXML);
672
+
673
+ });
674
+
675
+ /* ----> called with Record tag in recordingStatusCallback property <----- */
676
+ router.post('/record/callback/:callSid/',async (req, res) => {
677
+ winston.verbose('+++++++++++(voice) called POST record/callback/:callSid at time', new Date());
678
+ let start_call = new Date();
679
+
680
+ let callSid = req.params.callSid || req.body.CallSid;
681
+ let audioFileUrl = req.body.RecordingUrl;
682
+ let audioFileDuration = req.body.RecordingDuration;
683
+ let button_action = req.query.button_action ? '#' + req.query.button_action : '';
684
+ let previousIntentName = req.query.intentName || '';
685
+
686
+
687
+ let sessionInfo;
688
+ let project_id, conversation_id, user;
689
+ let from, to;
690
+
691
+ let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
692
+ if (!redis_data) {
693
+ return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
694
+ }
695
+ sessionInfo = JSON.parse(redis_data)
696
+ project_id = sessionInfo.project_id;
697
+ from = sessionInfo.from;
698
+ to = sessionInfo.to;
699
+ conversation_id = sessionInfo.conversation_id;
700
+ user = sessionInfo.user;
701
+
702
+ const tdChannel = new TiledeskChannel({
703
+ API_URL: API_URL,
704
+ redis_client: redis_client
705
+ })
706
+ tdChannel.setProjectId(project_id)
707
+
708
+ const CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
709
+ let settings = await db.get(CONTENT_KEY);
710
+ if(!settings){
711
+ return res.status(404).send({error: "VOICE Channel not already connected"})
712
+ }
713
+
714
+ let tiledeskMessage = null;
715
+ // tiledeskMessage = buildNoInputMessage('no_input', { from, button_action, payload: { event: 'no_input', lastBlock: previousIntentName, lastTimestamp: Date.now()} });
716
+
717
+ //SPEECH TO TEXT
718
+ const attributes = await voiceChannel.getSettingsForCallId(callSid);
719
+ winston.debug(`[VOICE] getting text message from STT: ${audioFileUrl}, model: ${attributes.STT_MODEL}`);
720
+ // generateSTT ritorna sempre un oggetto coerente (anche vuoto o /close)
721
+ tiledeskMessage = await generateSTT(audioFileUrl, attributes, sessionInfo, settings)
722
+ winston.debug('[VOICE] tiledeskMessage from STT: ', tiledeskMessage)
723
+ if (!tiledeskMessage || Object.keys(tiledeskMessage).length === 0) {
724
+ winston.debug(`[VOICE] STT result empty, fallback to no_input branch for callSid ${callSid}`);
725
+ tiledeskMessage = buildNoInputMessage('no_input', { from, button_action, payload: { event: 'no_input', lastBlock: previousIntentName, lastTimestamp: Date.now()} });
726
+ }else {
727
+ const normalizedText = utils.normalizeSTT(tiledeskMessage.text);
728
+ winston.verbose(`[VOICE] normalized STT text: ${normalizedText} for callSid ${callSid}`);
729
+ if(!normalizedText){
730
+ tiledeskMessage = buildNoInputMessage('no_input', { from, button_action, payload: { event: 'no_input', lastBlock: previousIntentName, lastTimestamp: Date.now()} });
731
+ }else{
732
+ tiledeskMessage.text = normalizedText;
733
+ }
734
+ }
735
+
736
+ //send message to tiledesk
737
+ let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
738
+ let end_call = new Date();
739
+ winston.info(`Time to respond to /record/callback/${callSid} : ${(end_call-start_call)} [ms]`)
740
+
741
+ res.status(200).send({ success: true , message: "Message sent to Tiledesk for callSid " + callSid});
742
+ })
743
+
624
744
 
625
745
  router.post('/menublock/:callSid', async (req, res) => {
626
746
  let start_call = new Date().getTime();
627
747
  winston.debug("(voice) called POST /menu", req.body);
628
748
  winston.debug("(voice) called POST /menu query" , req.query);
629
- winston.verbose('/menublock at: ', new Date(), 'with text:', req.body.Digits)
749
+ winston.verbose('/menublock at: ' + new Date() + 'with text:'+ req.body.Digits)
630
750
 
631
751
  let message_text = '';
632
752
  let attributes = {};
@@ -730,7 +850,7 @@ router.post('/menublock/:callSid', async (req, res) => {
730
850
 
731
851
  // convert response to vxml
732
852
  let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
733
- winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
853
+ winston.debug("(voice) VXML to SEND: "+ messageToVXML);
734
854
 
735
855
  let end_call = new Date().getTime();
736
856
  winston.info(`Time to respond to /menublock/${callSid} : ${(end_call-start_call)} [ms]`)
@@ -809,7 +929,7 @@ router.post('/handle/:callSid/:event', async (req, res) => {
809
929
 
810
930
  // convert response to vxml
811
931
  let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes,sessionInfo)
812
- winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
932
+ winston.debug("(voice) VXML to SEND: "+ messageToVXML);
813
933
 
814
934
  res.set('Content-Type', 'application/xml');
815
935
  res.status(200).send(messageToVXML);
@@ -914,7 +1034,7 @@ router.post('/event/:callSid/:event', async(req, res)=> {
914
1034
 
915
1035
  // convert response to vxml
916
1036
  let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
917
- winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
1037
+ winston.debug("(voice) VXML to SEND: "+ messageToVXML);
918
1038
 
919
1039
  res.set('Content-Type', 'application/xml');
920
1040
  res.status(200).send(messageToVXML);
@@ -989,8 +1109,7 @@ router.post('/twilio/status',async (req, res) => {
989
1109
  let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
990
1110
 
991
1111
  //remove session data for current callId and relative queue data
992
- await redis_client.del('tiledesk:voice:'+callSid+':session');
993
- await redis_client.del('tiledesk:voice:'+callSid+':delayIndex');
1112
+ await voiceChannel.deleteCallKeys(callSid);
994
1113
  await tdChannel.clearQueue(conversation_id);
995
1114
  break;
996
1115
  }
@@ -1011,172 +1130,97 @@ router.post('/twilio/fail',async (req, res) => {
1011
1130
  })
1012
1131
 
1013
1132
 
1014
- /* ----> catch Twilio Events <----- */
1015
- router.post('/record/:callSid/',async (req, res) => {
1016
- winston.debug('+++++++++++(voice) called POST record/:callSid ', req.body);
1017
-
1018
- let callSid = req.body.CallSid;
1019
- let audioFileUrl = req.body.RecordingUrl;
1020
-
1021
- let sessionInfo;
1022
- let project_id, conversation_id, user;
1023
- let from, to;
1024
-
1025
- let redis_data = await redis_client.get('tiledesk:voice:'+callSid+':session');
1026
- if (!redis_data) {
1027
- return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
1028
- }
1029
- sessionInfo = JSON.parse(redis_data)
1030
- project_id = sessionInfo.project_id;
1031
- from = sessionInfo.from;
1032
- to = sessionInfo.to;
1033
- conversation_id = sessionInfo.conversation_id;
1034
- user = sessionInfo.user;
1035
-
1036
- let vxmlAttributes = {
1037
- TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
1038
- TTS_VOICE_NAME: VOICE_NAME,
1039
- callSid: callSid,
1040
- };
1041
-
1042
-
1043
- const tdChannel = new TiledeskChannel({
1044
- API_URL: API_URL,
1045
- redis_client: redis_client
1046
- })
1047
- tdChannel.setProjectId(project_id)
1048
-
1049
- const tdTranslator = new TiledeskTwilioTranslator({
1050
- BASE_URL: BASE_URL,
1051
- aiService: aiService,
1052
- uploadService: uploadService
1053
- });
1054
-
1055
- const CONTENT_KEY = CHANNEL_NAME + "-" + project_id;
1056
- let settings = await db.get(CONTENT_KEY);
1057
- if(!settings){
1058
- return res.status(404).send({error: "VOICE Channel not already connected"})
1059
- }
1060
-
1061
-
1062
- let attributes = await voiceChannel.getSettingsForCallId(callSid);
1063
- console.log('attributessss', attributes)
1064
-
1065
- //SPEECH TO TEXT
1066
- console.log('getting text message . . . ', audioFileUrl, attributes.STT_MODEL)
1067
- let tiledeskMessage = await generateSTT(audioFileUrl, attributes, sessionInfo, settings)
1068
- console.log('(voice) Message captured after STT -->', tiledeskMessage)
1069
-
1070
- if(!tiledeskMessage){
1071
- //case NO_INPUT
1072
- const queryString = utils.buildQueryString(req.query);
1073
- winston.debug('case no input.. redirect '+ queryString)
1074
-
1075
- return await axios({
1076
- url: "http://localhost:3000/handle/" + callSid + '/no_input'+ queryString,
1077
- headers: req.headers,
1078
- data: req.body,
1079
- method: 'POST'
1080
- }).then((response) => {
1081
- winston.debug("[TiledeskChannel] speechToText response : ", response.data);
1082
- return res.status(response.status).send(response.data);
1083
- }).catch((err) => {
1084
- winston.error("[TiledeskChannel] speechToText error: ", err);
1085
- return res.status(500).send({ success: false, message: "Errore while redirect to /handle for callSid " + callSid});;
1086
- })
1087
- }
1088
-
1089
-
1090
- let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
1091
- winston.debug("message sent : ", tdMessage);
1092
-
1093
-
1094
- //generate Tiledesk wait message
1095
- let delayTime = await voiceChannel.getNextDelayTimeForCallId(callSid)
1096
- let message = await tdChannel.generateWaitTdMessage(from, delayTime)
1097
- //update delayIndex for wait command message time
1098
- await voiceChannel.saveDelayIndexForCallId(callSid)
1099
-
1100
- // convert response to vxml
1101
- let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, sessionInfo)
1102
- winston.verbose("(voice) VXML to SEND: "+ messageToVXML);
1103
-
1104
- res.set('Content-Type', 'application/xml');
1105
- res.status(200).send(messageToVXML);
1106
-
1107
- })
1108
1133
 
1109
1134
  async function generateSTT(audioFileUrl, attributes, sessionInfo, settings){
1110
1135
 
1111
1136
  winston.debug("(voice) generateSTT: "+ attributes.VOICE_PROVIDER);
1112
1137
 
1113
- let tiledeskMessage = {}, text = null;
1114
- switch(attributes.VOICE_PROVIDER){
1115
- case VOICE_PROVIDER.OPENAI:
1116
- let GPT_KEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.key
1117
- let publicKey = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.publicKey
1118
- if(publicKey){
1119
- let keep_going = await aiService.checkQuoteAvailability(sessionInfo.project_id, settings.token).catch((err)=>{
1120
- winston.error('errr while checkQuoteAvailability for project:', sessionInfo.project_id, err.response?.data)
1121
- })
1122
- winston.debug('(voice) checkQuoteAvailability return: '+ keep_going);
1123
- if(!keep_going){
1124
- //no token is available --> close conversation
1125
- return tiledeskMessage= {
1126
- //text:'\\close',
1127
- text:'/close',
1128
- senderFullname: sessionInfo.from,
1129
- type: 'text',
1130
- channel: { name: CHANNEL_NAME },
1131
- attributes: {
1132
- subtype: "info",
1133
- action: 'close'+JSON.stringify({event: 'quota_exceeded'}),
1134
- payload: {
1135
- catchEvent: 'quota_exceeded'
1136
- },
1137
- timestamp: 'xxxxxx'
1138
- }
1139
- };
1138
+ let tiledeskMessage = {};
1139
+ let text = null;
1140
+
1141
+ try {
1142
+ switch(attributes.VOICE_PROVIDER){
1143
+ case VOICE_PROVIDER.OPENAI: {
1144
+ let GPT_KEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.key
1145
+ let publicKey = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.publicKey
1146
+ if(publicKey){
1147
+ let keep_going = await aiService.checkQuoteAvailability(sessionInfo.project_id, settings.token)
1148
+ winston.debug('(voice) checkQuoteAvailability return: '+ keep_going);
1149
+ if(!keep_going){
1150
+ //no token is available --> close conversation
1151
+ return tiledeskMessage= {
1152
+ //text:'\\close',
1153
+ text:'/close',
1154
+ senderFullname: sessionInfo.from,
1155
+ type: 'text',
1156
+ channel: { name: CHANNEL_NAME },
1157
+ attributes: {
1158
+ subtype: "info",
1159
+ action: 'close'+JSON.stringify({event: 'quota_exceeded'}),
1160
+ payload: {
1161
+ catchEvent: 'quota_exceeded'
1162
+ },
1163
+ timestamp: 'xxxxxx'
1164
+ }
1165
+ };
1140
1166
 
1167
+ }
1141
1168
  }
1169
+
1170
+ text = await aiService.speechToText(audioFileUrl, attributes.STT_MODEL, GPT_KEY)
1171
+ break;
1142
1172
  }
1143
-
1144
- text = await aiService.speechToText(audioFileUrl, attributes.STT_MODEL, GPT_KEY).catch((err)=>{
1145
- winston.error('errr while transcript', err.response?.data)
1146
- })
1147
- tiledeskMessage= {
1148
- text: text,
1149
- senderFullname: sessionInfo.from,
1150
- type: 'text',
1151
- channel: { name: CHANNEL_NAME }
1152
- };
1153
- break;
1154
- case VOICE_PROVIDER.ELEVENLABS:
1155
- let ELEVENLABS_APIKEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.ELEVENLABS))?.key
1156
- // La condizione negli input del metodo è corretta, ma può essere scritta in modo più leggibile:
1157
- const ttsLanguage = attributes.TTS_LANGUAGE || 'en';
1158
- text = await this.aiService.speechToTextElevenLabs(
1159
- audioFileUrl,
1160
- attributes.STT_MODEL,
1161
- ttsLanguage,
1162
- ELEVENLABS_APIKEY
1163
- ).catch((err) => {
1164
- winston.error('errr while creating elevenlabs audio message', err?.response?.data);
1165
- });
1166
- tiledeskMessage= {
1173
+ case VOICE_PROVIDER.ELEVENLABS: {
1174
+ let ELEVENLABS_APIKEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.ELEVENLABS))?.key
1175
+ const ttsLanguage = attributes.TTS_LANGUAGE || 'en';
1176
+ text = await aiService.speechToTextElevenLabs( audioFileUrl, attributes.STT_MODEL, ttsLanguage, ELEVENLABS_APIKEY )
1177
+ break;
1178
+ }
1179
+ default:
1180
+ throw new Error('Unsupported VOICE_PROVIDER: ' + attributes.VOICE_PROVIDER);
1181
+ }
1182
+
1183
+ if(text){
1184
+ winston.debug('[STT] text empty → fallback no_input');
1185
+ tiledeskMessage = {
1167
1186
  text: text,
1168
1187
  senderFullname: sessionInfo.from,
1169
1188
  type: 'text',
1170
1189
  channel: { name: CHANNEL_NAME }
1171
1190
  };
1172
- break;
1191
+ }
1192
+ } catch (error) {
1193
+ winston.error('[STT] generateSTT error:', error);
1194
+ switch (error.code) {
1195
+ case 'AISERVICE_FAILED':
1196
+ winston.error('[STT] AISERVICE_FAILED → ', error.message);
1197
+ break;
1198
+ }
1173
1199
 
1200
+ // fallback: tiledeskMessage vuoto
1201
+ tiledeskMessage = {};
1202
+
1174
1203
  }
1175
1204
 
1176
1205
  return tiledeskMessage
1177
1206
  }
1178
1207
 
1179
1208
 
1209
+ async function buildNoInputMessage(event, { from, button_action, payload }) {
1210
+ return {
1211
+ text: `/${event}`,
1212
+ senderFullname: from,
1213
+ type: 'text',
1214
+ channel: { name: CHANNEL_NAME },
1215
+ attributes: {
1216
+ type: 'info',
1217
+ action: button_action,
1218
+ payload: payload
1219
+ }
1220
+ };
1221
+ }
1222
+
1223
+
1180
1224
 
1181
1225
  router.get('/addon/transcript', async (req, res) => {
1182
1226
  winston.debug("(voice) called GET /transcript query-->" , req.query);
@@ -1365,7 +1409,7 @@ async function connectRedis() {
1365
1409
 
1366
1410
 
1367
1411
  redis_client.on('error', err => {
1368
- winston.debug('(voice) Connect Redis Error ' + err);
1412
+ winston.error('(voice) Connect Redis Error ' + err);
1369
1413
  })
1370
1414
  /*
1371
1415
  redis_client.on('connect', () => {