@tiledesk/tiledesk-voice-twilio-connector 0.1.21 → 0.1.23

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
@@ -58,6 +58,7 @@ let API_URL = null;
58
58
  let BASE_URL = null;
59
59
  let BASE_FILE_URL = null;
60
60
  let OPENAI_ENDPOINT = null;
61
+ let ELEVENLABS_ENDPOINT = null;
61
62
  let REDIS_HOST = null;
62
63
  let REDIS_PORT = null;
63
64
  let REDIS_PASSWORD = null;
@@ -164,7 +165,7 @@ router.post('/webhook/:id_project', async (req, res) => {
164
165
  winston.debug("(voice) conversation returned:"+ conversation_id);
165
166
 
166
167
 
167
- //GET AND SAVE GPT-KET IF
168
+ //GET AND SAVE GPT-KET IF
168
169
  let integrations = [], publicKey = false;
169
170
  let key = await integrationService.getKeyFromIntegrations(project_id, 'openai', settings.token)
170
171
  if (!key) {
@@ -179,6 +180,12 @@ router.post('/webhook/:id_project', async (req, res) => {
179
180
  }
180
181
  integrations.push({type: 'openai', key: key, publicKey: publicKey})
181
182
 
183
+ let eleven_labs = await integrationService.getKeyFromIntegrations(project_id, 'elevenlabs', settings.token)
184
+ if (eleven_labs) {
185
+ winston.debug("(voice) - Key found in Integrations: "+ eleven_labs);
186
+ integrations.push({type: 'elevenlabs', key: eleven_labs, publicKey: false})
187
+ }
188
+
182
189
  //save data to redis
183
190
  let session_data = {
184
191
  from: from,
@@ -207,7 +214,7 @@ router.post('/webhook/:id_project', async (req, res) => {
207
214
  attributes: {
208
215
  subtype: 'info',
209
216
  payload: {
210
- ... req.query //send all attributes back to chatbot
217
+ ... req.body //send all attributes back to chatbot
211
218
  }
212
219
  },
213
220
  channel: { name: CHANNEL_NAME },
@@ -269,8 +276,8 @@ router.post('/nextblock/:callSid/', async(req, res) => {
269
276
  user = sessionInfo.user;
270
277
 
271
278
  let vxmlAttributes = {
272
- TTS_VOICE_LANGUAGE: null,
273
- TTS_VOICE_NAME: null,
279
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
280
+ TTS_VOICE_NAME: VOICE_NAME,
274
281
  callSid: callSid,
275
282
  };
276
283
 
@@ -363,6 +370,8 @@ async function getMessage(callSid, from, project_id, conversation_id){
363
370
  await tdChannel.removeMessageFromQueue(conversation_id, message._id)
364
371
  //reset delayIndex for wait command message time
365
372
  await voiceChannel.clearDelayTimeForCallId(callSid)
373
+ //manage attributes for current callId
374
+ await voiceChannel.saveSettingsForCallId(message.attributes, callSid)
366
375
 
367
376
  resolve(message);
368
377
  return;
@@ -426,8 +435,8 @@ router.post('/speechresult/:callSid', async (req, res) => {
426
435
  user = sessionInfo.user;
427
436
 
428
437
  let vxmlAttributes = {
429
- TTS_VOICE_LANGUAGE: null,
430
- TTS_VOICE_NAME: null,
438
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
439
+ TTS_VOICE_NAME: VOICE_NAME,
431
440
  callSid: callSid,
432
441
  };
433
442
 
@@ -551,8 +560,8 @@ router.post('/menublock/:callSid', async (req, res) => {
551
560
  user = sessionInfo.user;
552
561
 
553
562
  let vxmlAttributes = {
554
- TTS_VOICE_LANGUAGE: null,
555
- TTS_VOICE_NAME: null,
563
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
564
+ TTS_VOICE_NAME: VOICE_NAME,
556
565
  callSid: callSid,
557
566
  };
558
567
 
@@ -625,8 +634,8 @@ router.post('/handle/:callSid/:event', async (req, res) => {
625
634
  user = sessionInfo.user;
626
635
 
627
636
  let vxmlAttributes = {
628
- TTS_VOICE_LANGUAGE: null,
629
- TTS_VOICE_NAME: null,
637
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
638
+ TTS_VOICE_NAME: VOICE_NAME,
630
639
  callSid: callSid,
631
640
  };
632
641
 
@@ -710,8 +719,8 @@ router.post('/event/:callSid/:event', async(req, res)=> {
710
719
  user = sessionInfo.user;
711
720
 
712
721
  let vxmlAttributes = {
713
- TTS_VOICE_LANGUAGE: null,
714
- TTS_VOICE_NAME: null,
722
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
723
+ TTS_VOICE_NAME: VOICE_NAME,
715
724
  callSid: callSid,
716
725
  };
717
726
 
@@ -809,8 +818,8 @@ router.post('/twilio/status',async (req, res) => {
809
818
  user = sessionInfo.user;
810
819
 
811
820
  let vxmlAttributes = {
812
- TTS_VOICE_LANGUAGE: null,
813
- TTS_VOICE_NAME: null,
821
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
822
+ TTS_VOICE_NAME: VOICE_NAME,
814
823
  callSid: callSid,
815
824
  };
816
825
 
@@ -894,8 +903,8 @@ router.post('/record/:callSid/',async (req, res) => {
894
903
  user = sessionInfo.user;
895
904
 
896
905
  let vxmlAttributes = {
897
- TTS_VOICE_LANGUAGE: null,
898
- TTS_VOICE_NAME: null,
906
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
907
+ TTS_VOICE_NAME: VOICE_NAME,
899
908
  callSid: callSid,
900
909
  };
901
910
 
@@ -918,23 +927,16 @@ router.post('/record/:callSid/',async (req, res) => {
918
927
  return res.status(404).send({error: "VOICE Channel not already connected"})
919
928
  }
920
929
 
921
- //SPEECH TO TEXT
922
- let key = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPEN_AI))?.key
923
- let publicKey = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPEN_AI))?.publicKey
924
- //check quotes if user is using public GPT_KEY
925
- if(publicKey){
926
- let keep_going = await aiService.checkQuoteAvailability(project_id, user.token);
927
- if(!keep_going){
928
- //no toke is available
929
- }
930
- }
931
930
 
932
- let textMessage = await aiService.speechToText(audioFileUrl, vxmlAttributes.STT_MODEL, key).catch((err)=>{
933
- console.log('errr while transcript', err.response?.data)
934
- })
935
- console.log('(voice) Message captured after STT -->', textMessage)
931
+ let attributes = await voiceChannel.getSettingsForCallId(callSid);
932
+ console.log('attributessss', attributes)
936
933
 
937
- if(!textMessage){
934
+ //SPEECH TO TEXT
935
+ console.log('getting text message . . . ', audioFileUrl, attributes.STT_MODEL)
936
+ let tiledeskMessage = await generateSTT(audioFileUrl, attributes, sessionInfo, settings)
937
+ console.log('(voice) Message captured after STT -->', tiledeskMessage)
938
+
939
+ if(!tiledeskMessage){
938
940
  //case NO_INPUT
939
941
  const queryString = utils.buildQueryString(req.query);
940
942
  winston.debug('case no input.. redirect '+ queryString)
@@ -953,13 +955,7 @@ router.post('/record/:callSid/',async (req, res) => {
953
955
  })
954
956
  }
955
957
 
956
-
957
- let tiledeskMessage= {
958
- text:textMessage,
959
- senderFullname: from,
960
- type: 'text',
961
- channel: { name: CHANNEL_NAME }
962
- };
958
+
963
959
  let tdMessage = await tdChannel.send(tiledeskMessage, user.token, conversation_id);
964
960
  winston.debug("message sent : ", tdMessage);
965
961
 
@@ -979,6 +975,77 @@ router.post('/record/:callSid/',async (req, res) => {
979
975
 
980
976
  })
981
977
 
978
+ async function generateSTT(audioFileUrl, attributes, sessionInfo, settings){
979
+
980
+ winston.debug("(voice) generateSTT: "+ attributes.VOICE_PROVIDER);
981
+
982
+ let tiledeskMessage = {}, text = null;
983
+ switch(attributes.VOICE_PROVIDER){
984
+ case VOICE_PROVIDER.OPENAI:
985
+ let GPT_KEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.key
986
+ let publicKey = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))?.publicKey
987
+ if(publicKey){
988
+ let keep_going = await aiService.checkQuoteAvailability(sessionInfo.project_id, settings.token).catch((err)=>{
989
+ winston.error('errr while checkQuoteAvailability for project:', sessionInfo.project_id, err.response?.data)
990
+ })
991
+ winston.verbose('(voice) checkQuoteAvailability return: '+ keep_going);
992
+ if(!keep_going){
993
+ //no token is available --> close conversation
994
+ return tiledeskMessage= {
995
+ //text:'\\close',
996
+ text:'/close',
997
+ senderFullname: sessionInfo.from,
998
+ type: 'text',
999
+ channel: { name: CHANNEL_NAME },
1000
+ attributes: {
1001
+ subtype: "info",
1002
+ action: 'close'+JSON.stringify({event: 'quota_exceeded'}),
1003
+ payload: {
1004
+ catchEvent: 'quota_exceeded'
1005
+ },
1006
+ timestamp: 'xxxxxx'
1007
+ }
1008
+ };
1009
+
1010
+ }
1011
+ }
1012
+
1013
+ text = await aiService.speechToText(audioFileUrl, attributes.STT_MODEL, GPT_KEY).catch((err)=>{
1014
+ winston.error('errr while transcript', err.response?.data)
1015
+ })
1016
+ tiledeskMessage= {
1017
+ text: text,
1018
+ senderFullname: sessionInfo.from,
1019
+ type: 'text',
1020
+ channel: { name: CHANNEL_NAME }
1021
+ };
1022
+ break;
1023
+ case VOICE_PROVIDER.ELEVENLABS:
1024
+ let ELEVENLABS_APIKEY = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.ELEVENLABS))?.key
1025
+ // La condizione negli input del metodo è corretta, ma può essere scritta in modo più leggibile:
1026
+ const ttsLanguage = attributes.TTS_LANGUAGE || 'en';
1027
+ text = await this.aiService.speechToTextElevenLabs(
1028
+ audioFileUrl,
1029
+ attributes.STT_MODEL,
1030
+ ttsLanguage,
1031
+ ELEVENLABS_APIKEY
1032
+ ).catch((err) => {
1033
+ winston.error('errr while creating elevenlabs audio message', err?.response?.data);
1034
+ });
1035
+ tiledeskMessage= {
1036
+ text: text,
1037
+ senderFullname: sessionInfo.from,
1038
+ type: 'text',
1039
+ channel: { name: CHANNEL_NAME }
1040
+ };
1041
+ break;
1042
+
1043
+ }
1044
+
1045
+ return tiledeskMessage
1046
+ }
1047
+
1048
+
982
1049
 
983
1050
  router.get('/addon/transcript', async (req, res) => {
984
1051
  winston.verbose("(vxml) called GET /transcript query-->" , req.query);
@@ -1116,8 +1183,8 @@ router.get('/test', async (req, res) => {
1116
1183
 
1117
1184
 
1118
1185
  let vxmlAttributes = {
1119
- TTS_VOICE_LANGUAGE: null,
1120
- TTS_VOICE_NAME: null,
1186
+ TTS_VOICE_LANGUAGE: VOICE_LANGUAGE,
1187
+ TTS_VOICE_NAME: VOICE_NAME,
1121
1188
  callSid: callSid
1122
1189
  };
1123
1190
 
@@ -1225,6 +1292,9 @@ async function startApp(settings, callback) {
1225
1292
  if(settings.OPENAI_ENDPOINT){
1226
1293
  OPENAI_ENDPOINT = settings.OPENAI_ENDPOINT
1227
1294
  }
1295
+ if(settings.ELEVENLABS_ENDPOINT){
1296
+ ELEVENLABS_ENDPOINT = settings.ELEVENLABS_ENDPOINT
1297
+ }
1228
1298
 
1229
1299
  if(settings.MAX_POLLING_TIME){
1230
1300
  MAX_POLLING_TIME = settings.MAX_POLLING_TIME;
@@ -1249,6 +1319,7 @@ async function startApp(settings, callback) {
1249
1319
  //init Services
1250
1320
  aiService = new AiService({
1251
1321
  OPENAI_ENDPOINT: OPENAI_ENDPOINT,
1322
+ ELEVENLABS_ENDPOINT: ELEVENLABS_ENDPOINT,
1252
1323
  API_URL: API_URL
1253
1324
  })
1254
1325
  integrationService = new IntegrationService({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-voice-twilio-connector",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "Tiledesk VOICE Twilio connector",
5
5
  "license": "MIT",
6
6
  "author": "Gabriele Panico",
@@ -77,8 +77,7 @@ class TiledeskTwilioTranslator {
77
77
  vxmlAttributes.TTS_MODEL = flowAttributes.TTS_MODEL? flowAttributes.TTS_MODEL : OPENAI_SETTINGS.TTS_MODEL;
78
78
  vxmlAttributes.STT_MODEL = flowAttributes.STT_MODEL? flowAttributes.STT_MODEL : OPENAI_SETTINGS.STT_MODEL;
79
79
  }
80
-
81
-
80
+
82
81
 
83
82
  }
84
83
 
@@ -103,7 +102,7 @@ class TiledeskTwilioTranslator {
103
102
 
104
103
 
105
104
  this.user = sessionInfo.user
106
- this.voiceSettings = sessionInfo.integrations.find((el => el.type === VOICE_PROVIDER.OPENAI))
105
+ this.integrations = sessionInfo.integrations
107
106
 
108
107
 
109
108
  const xml = xmlbuilder.create("Response", {});
@@ -310,8 +309,8 @@ class TiledeskTwilioTranslator {
310
309
  const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
311
310
  gather.att("action", this.BASE_URL + '/speechresult/' + xmlAttributes.callSid + queryUrl)
312
311
  .att("method", "POST")
313
- .att("language", xmlAttributes.voiceLanguage)
314
- .att('speechTimeout', "0")
312
+ .att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
313
+ .att('speechTimeout', "auto")
315
314
 
316
315
  //if(xmlAttributes && xmlAttributes.noInputTimeout){
317
316
  // gather.att("timeout", xmlAttributes.noInputTimeout/1000 ).up();
@@ -377,7 +376,7 @@ class TiledeskTwilioTranslator {
377
376
  .att("numDigits", "1" )
378
377
  .att("action", this.BASE_URL + '/menublock/' + xmlAttributes.callSid + queryUrl)
379
378
  .att("method", "POST")
380
- .att("language", xmlAttributes.voiceLanguage)
379
+ .att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
381
380
 
382
381
  const prompt = await this.promptVXML(gather, message, xmlAttributes);
383
382
 
@@ -398,7 +397,7 @@ class TiledeskTwilioTranslator {
398
397
  gather.att("timeout", xmlAttributes.noInputTimeout/1000 )
399
398
  .att("action", this.BASE_URL + '/menublock/' + xmlAttributes.callSid + queryUrl)
400
399
  .att("method", "POST")
401
- .att("language", xmlAttributes.voiceLanguage)
400
+ .att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
402
401
  const settings = await this.optionsVXML(gather, message, xmlAttributes);
403
402
 
404
403
  const prompt = this.promptVXML(gather, message, xmlAttributes);
@@ -429,7 +428,7 @@ class TiledeskTwilioTranslator {
429
428
  //rootEle.att("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
430
429
  //rootEle.att( "xsi:schemaLocation", "http://www.w3.org/2001/vxml http://www.w3.org/TR/2007/REC-voicexml21-20070619/vxml.xsd");
431
430
  //rootEle.att("version", "2.1");
432
- //rootEle.att("xml:lang", attributes.voiceLanguage);
431
+ //rootEle.att("xml:lang", attributes.TTS_VOICE_LANGUAGE);
433
432
 
434
433
  rootEle.ele("Parameter", { name: "callSid", value: "'" + attributes.callSid + "'" }).up();
435
434
  rootEle.ele("Parameter", { name: "intentName", value: "'" + attributes.intentName + "'"}).up();
@@ -535,7 +534,7 @@ class TiledeskTwilioTranslator {
535
534
  if (command.type === "message") {
536
535
  //case type: TEXT
537
536
  if(command.message.type === 'text'){
538
- if(that.voiceProvider === VOICE_PROVIDER.OPENAI){
537
+ if(that.voiceProvider !== VOICE_PROVIDER.TWILIO){
539
538
  let voiceMessageUrl = await that.generateTTS(command.message.text, attributes)
540
539
  rootEle.ele('Play', {}, voiceMessageUrl )
541
540
  }else{
@@ -623,11 +622,24 @@ class TiledeskTwilioTranslator {
623
622
  }
624
623
 
625
624
  async generateTTS(text, attributes){
626
- let GPT_KEY = this.voiceSettings?.key
627
625
 
628
- let audioData = await this.aiService.textToSpeech(text, attributes.TTS_VOICE_NAME, attributes.TTS_MODEL, GPT_KEY).catch((err)=>{
629
- console.log('errr while creating audio message', err.response?.data)
630
- })
626
+ 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;
640
+
641
+ }
642
+
631
643
  let fileUrl = await this.uploadService.upload(attributes.callSid, audioData, this.user).catch((err)=>{
632
644
  console.log('errr while uploading audioData', err.response)
633
645
  })
@@ -10,7 +10,8 @@ const TYPE_MESSAGE = require('./constants').TYPE_MESSAGE
10
10
  const MESSAGE_TYPE_MINE = require('./constants').MESSAGE_TYPE_MINE
11
11
  const MESSAGE_TYPE_OTHERS = require('./constants').MESSAGE_TYPE_OTHERS
12
12
  const CHANNEL_NAME = require('./constants').CHANNEL_NAME
13
-
13
+ const VOICE_PROVIDER = require('./constants').VOICE_PROVIDER;
14
+ const OPENAI_SETTINGS = require('./constants').OPENAI_SETTINGS;
14
15
 
15
16
  const winston = require("../winston");
16
17
 
@@ -89,6 +90,56 @@ class VoiceChannel {
89
90
  //if index is not present: set to default (0)
90
91
  await this.redis_client.set('tiledesk:vxml:'+callId + ':delayIndex', 0, {'EX': 86400});
91
92
  }
93
+
94
+
95
+ async saveSettingsForCallId(attributes, callId){
96
+
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
+ }
120
+
121
+ const index = await this.redis_client.get('tiledesk:vxml:'+callId + ':attributes');
122
+ winston.debug('saveSettingsForCallId: attributes found -->'+index)
123
+ if(index){
124
+ //set index to default (0)
125
+ await this.redis_client.set('tiledesk:vxml:'+callId + ':attributes', JSON.stringify(flowAttributes), {'EX': 86400});
126
+ return;
127
+ }
128
+ //if index is not present: set to default (0)
129
+ await this.redis_client.set('tiledesk:vxml:'+callId + ':attributes', JSON.stringify(flowAttributes), {'EX': 86400});
130
+
131
+ }
132
+
133
+
134
+ async getSettingsForCallId(callId){
135
+ const attributes = await this.redis_client.get('tiledesk:vxml:'+callId + ':attributes');
136
+ if(attributes){
137
+ return JSON.parse(attributes)
138
+ }
139
+ return {};
140
+ }
141
+
142
+
92
143
 
93
144
 
94
145
 
@@ -38,10 +38,10 @@ module.exports = {
38
38
  SPEECH_FORM: 'speech_form',
39
39
 
40
40
  },
41
- BASE_POOLING_DELAY: 250,
42
- MAX_POLLING_TIME: 30000,
43
- VOICE_NAME: 'Polly.Bianca-Neural',
44
- VOICE_LANGUAGE: 'it-IT',
41
+ BASE_POOLING_DELAY: 500,
42
+ MAX_POLLING_TIME: 50000,
43
+ VOICE_NAME: 'Polly.Danielle',
44
+ VOICE_LANGUAGE: 'en-US',
45
45
  CALL_STATUS: {
46
46
  INITIATED: 'initiated',
47
47
  RINGING: 'ringing',
@@ -51,7 +51,8 @@ module.exports = {
51
51
  },
52
52
  VOICE_PROVIDER: {
53
53
  OPENAI: 'openai',
54
- TWILIO: 'twilio'
54
+ TWILIO: 'twilio',
55
+ ELEVENLABS: 'elevenlabs'
55
56
  },
56
57
  OPENAI_SETTINGS:{
57
58
  TTS_VOICE_NAME: 'alloy',
@@ -39,8 +39,9 @@ class FileUtils {
39
39
  responseType: 'arraybuffer',
40
40
  method: 'GET'
41
41
  }).then((resbody) => {
42
- console.log('okkkkkkk')
43
- resolve(resbody.data);
42
+ const buffer = Buffer.from(resbody.data, 'binary');
43
+ resolve(buffer);
44
+ //resolve(resbody.data);
44
45
  }).catch((err) => {
45
46
  reject(err);
46
47
  })
@@ -15,11 +15,16 @@ class AiService {
15
15
  if (!config.OPENAI_ENDPOINT) {
16
16
  throw new Error("[AiService] config.OPENAI_ENDPOINT is mandatory");
17
17
  }
18
+ if(!config.ELEVENLABS_ENDPOINT){
19
+ throw new Error("[AiService] config.ELEVENLABS_ENDPOINT is mandatory");
20
+ }
18
21
  if (!config.API_URL) {
19
22
  throw new Error("[AiService] config.API_URL is mandatory");
20
23
  }
24
+
21
25
 
22
26
  this.OPENAI_ENDPOINT = config.OPENAI_ENDPOINT;
27
+ this.ELEVENLABS_ENDPOINT = config.ELEVENLABS_ENDPOINT;
23
28
  this.API_URL = config.API_URL;
24
29
 
25
30
  }
@@ -28,15 +33,20 @@ class AiService {
28
33
 
29
34
  winston.debug("[AiService] speechToText url: "+ fileUrl);
30
35
  let file = await fileUtils.downloadFromUrl(fileUrl).catch((err) => {
31
- winston.error("[AiService] err: ", err)
36
+ winston.error("[AiService] err while downloadFromUrl: ", err)
32
37
  return null; // fallback per evitare undefined
33
38
  })
34
39
 
40
+ if (!file) {
41
+ winston.error('file non esisteeeeeeee')
42
+ return;
43
+ }
35
44
 
36
45
  return new Promise((resolve, reject) => {
37
-
46
+
47
+
38
48
  const formData = new FormData();
39
- formData.append('file', file, { filename: 'audiofile', contentType: 'audio/mpeg' });
49
+ formData.append('file', file, { filename: 'audiofile.wav', contentType: 'audio/wav' });
40
50
  formData.append('model', model);
41
51
 
42
52
  axios({
@@ -91,10 +101,87 @@ class AiService {
91
101
 
92
102
  }
93
103
 
104
+
105
+
106
+ async speechToTextElevenLabs(fileUrl, model, language, API_KEY) {
107
+
108
+ winston.debug("[AiService] ELEVEN Labs speechToText url: "+ fileUrl);
109
+ let file = await fileUtils.downloadFromUrl(fileUrl).catch((err) => {
110
+ winston.error("[AiService] err: ", err)
111
+ return null; // fallback per evitare undefined
112
+ })
113
+
114
+ if (!file) {
115
+ winston.debug('[AiService] ELEVEN Labs speechToText file NOT EXIST: . . . return')
116
+ return;
117
+ }
118
+
119
+ return new Promise((resolve, reject) => {
120
+
121
+
122
+ const formData = new FormData();
123
+ formData.append('file', file, { filename: 'audiofile.wav', contentType: 'audio/wav' });
124
+ formData.append('model_id', "scribe_v1");
125
+ formData.append('language_code', language)
126
+
127
+ axios({
128
+ url: this.ELEVENLABS_ENDPOINT + "/v1/speech-to-text",
129
+ headers: {
130
+ ...formData.getHeaders(),
131
+ "xi-api-key": API_KEY
132
+ },
133
+ data: formData,
134
+ method: 'POST'
135
+ }).then((resbody) => {
136
+ console.log('dataaaaaa', resbody)
137
+ resolve(resbody.data.text);
138
+ }).catch((err) => {
139
+ console.log('errrrrrr', err?.response)
140
+ reject(err);
141
+ })
142
+
143
+ })
144
+ }
145
+
146
+ async textToSpeechElevenLabs(text, voice_id, model, API_KEY){
147
+
148
+
149
+ const data = {
150
+ model_id: model,
151
+ text: text,
152
+ output_format: "mp3_44100_128",
153
+ };
154
+
155
+
156
+ winston.debug('[AiService] ELEVEN Labs textToSpeech config:', data)
157
+
158
+ return new Promise((resolve, reject) => {
159
+ axios({
160
+ url: this.ELEVENLABS_ENDPOINT + "/v1/text-to-speech/"+ voice_id,
161
+ headers: {
162
+ "Content-Type": "application/json",
163
+ "xi-api-key": API_KEY
164
+ },
165
+ responseType: 'arraybuffer',
166
+ data: data,
167
+ method: "POST",
168
+ }).then( async (response) => {
169
+ resolve(response?.data)
170
+ })
171
+ .catch((err) => {
172
+ winston.error("[AiService] ELEVEN Labs textToSpeech error: ", err);
173
+ reject(err)
174
+ });
175
+ });
176
+
177
+ }
178
+
179
+
180
+
94
181
  async checkQuoteAvailability(projectId, token) {
95
182
 
96
183
  winston.debug("[AiService] checkQuoteAvailability for project: "+ projectId);
97
-
184
+
98
185
  return new Promise((resolve, reject) => {
99
186
 
100
187
  axios({
@@ -105,12 +192,13 @@ class AiService {
105
192
  },
106
193
  method: 'GET'
107
194
  }).then((resbody) => {
108
- if (resbody && resbody.isAvailable === true) {
195
+ if (resbody && resbody.data?.isAvailable === true) {
109
196
  resolve(true)
110
197
  } else {
111
198
  resolve(false)
112
199
  }
113
200
  }).catch((err) => {
201
+ winston.error("[AiService] checkQuoteAvailability error: ", err.response?.data);
114
202
  reject(err);
115
203
  })
116
204
 
@@ -24,7 +24,7 @@ class IntegrationService {
24
24
 
25
25
  async getKeyFromIntegrations(id_project, integration_name, token){
26
26
 
27
- winston.debug('[IntegrationService] getKeyFromIntegrations id_project:', id_project)
27
+ winston.debug('[IntegrationService] getKeyFromIntegrations id_project:'+ id_project + ' ' + integration_name)
28
28
 
29
29
  return await axios({
30
30
  url: this.API_URL + "/"+ id_project + "/integration/name/" + integration_name,
@@ -35,10 +35,10 @@ class IntegrationService {
35
35
  data: {},
36
36
  method: "GET",
37
37
  }).then( async (response) => {
38
- if (!response.data || response.data?.value) {
38
+ if (!response.data || !response.data?.value) {
39
39
  return null;
40
40
  }
41
-
41
+
42
42
  return response.data?.value?.apikey
43
43
  })
44
44
  .catch((err) => {
@@ -69,7 +69,6 @@ class IntegrationService {
69
69
  return response.data?.gptkey
70
70
  })
71
71
  .catch((err) => {
72
- winston.error("[IntegrationService] getKeyFromKbSettings error: ", err.response?.data);
73
72
  return null;
74
73
  });
75
74
 
@@ -47,7 +47,8 @@ class UploadService {
47
47
 
48
48
  //const formData = new FormData();
49
49
  //formData.append('file', file, { filename: 'audiofile_'+user._id+'_'+id+'.mp3', contentType: 'audio/mpeg' });
50
-
50
+ user.token = 'JWT eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2NWM1ZjExNjlmYWYyZDA0Y2Q3ZGE1MjciLCJlbWFpbCI6ImdhYnJpZWxlQHRpbGVkZXNrLmNvbSIsImZpcnN0bmFtZSI6IkdhYnJpZWxlIiwibGFzdG5hbWUiOiJQYW5pY28iLCJlbWFpbHZlcmlmaWVkIjp0cnVlLCJpYXQiOjE3NDgyNTY2MTUsImF1ZCI6Imh0dHBzOi8vdGlsZWRlc2suY29tIiwiaXNzIjoiaHR0cHM6Ly90aWxlZGVzay5jb20iLCJzdWIiOiJ1c2VyIiwianRpIjoiNWUyZDhhYmUtYzQ0YS00MjJiLWE3MjUtYWYwMjcxNDgyZTczIn0.AcT1tNbE3AcfctJXfOsfUbytRNUQlhBqPUctxzXMjehZOS2ORJThWaPqPxrvqTTIyeOU2l6eoTw8_tqfRJGlp6X4m9KLio87axGl1z3WYBgh8bSMIkAw2zSIUuJmpjBuT8EZdjXZClXRUAliAvAoFRgCmhWJ1tODVvBynLiSb37sB_zscqWH5L5eF1vdt6HHizEO4HbGABQS00I2hEPn99ssC9Y3W4_UhDcitZG80ACwS_Bpl6uk8OxAFybZ1DHHkBS1AK-lCO2P2JJCFRyM33mcvTgb9B6pADETzgJT2qfgOU4-1Pm0l55Mij1LS-h7QTj95DTFQMM7DD6elP0WcA'
51
+
51
52
  axios({
52
53
  url: this.API_URL + "/files/users",
53
54
  headers: {
@@ -69,7 +70,12 @@ class UploadService {
69
70
  }).catch((err) => {
70
71
  console.log('err', err)
71
72
  reject(err);
72
- })
73
+ }).finally(() => {
74
+ // Sempre eseguito
75
+ if (fs.existsSync(tempFilePath)) {
76
+ fs.unlinkSync(tempFilePath);
77
+ }
78
+ });
73
79
 
74
80
  })
75
81
  }