@tiledesk/tiledesk-voice-twilio-connector 0.1.26 → 0.1.28
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 +85 -82
- package/package.json +2 -1
- package/tiledesk/TiledeskTwilioTranslator.js +20 -9
- package/tiledesk/VoiceChannel.js +23 -4
- package/tiledesk/utils.js +10 -1
package/index.js
CHANGED
|
@@ -157,32 +157,48 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
157
157
|
}
|
|
158
158
|
let end2 = new Date().getTime();
|
|
159
159
|
|
|
160
|
-
//
|
|
161
|
-
let conversation_id = await
|
|
160
|
+
// Parallelizza generateConversation e recupero chiavi di integrazione
|
|
161
|
+
let [conversation_id, openaiKeyResult, elevenLabsKey] = await Promise.all([
|
|
162
|
+
//let conversation_id = await tdChannel.getConversation(ani, callId, user.token);
|
|
163
|
+
tdChannel.generateConversation(from, callSid, user.token),
|
|
164
|
+
|
|
165
|
+
// Recupero chiave OpenAI (con fallback sequenziale)
|
|
166
|
+
(async () => {
|
|
167
|
+
try {
|
|
168
|
+
let key = await integrationService.getKeyFromIntegrations(project_id, 'openai', settings.token)
|
|
169
|
+
if (!key) {
|
|
170
|
+
winston.debug("(voice) - Key not found in Integrations. Searching in kb settings...");
|
|
171
|
+
key = await integrationService.getKeyFromKbSettings(project_id, settings.token);
|
|
172
|
+
}
|
|
173
|
+
if (!key) {
|
|
174
|
+
winston.debug("(voice) - Retrieve public gptkey")
|
|
175
|
+
key = GPT_KEY;
|
|
176
|
+
return { key, publicKey: true };
|
|
177
|
+
}
|
|
178
|
+
return { key, publicKey: false };
|
|
179
|
+
} catch (error) {
|
|
180
|
+
winston.error('(voice) - Error retrieving OpenAI key:', error);
|
|
181
|
+
return { key: GPT_KEY, publicKey: true };
|
|
182
|
+
}
|
|
183
|
+
})(),
|
|
184
|
+
|
|
185
|
+
// Recupero chiave ElevenLabs in parallelo
|
|
186
|
+
integrationService.getKeyFromIntegrations(project_id, 'elevenlabs', settings.token).catch((error) => {
|
|
187
|
+
winston.error('(voice) - Error retrieving ElevenLabs key:', error);
|
|
188
|
+
return null;
|
|
189
|
+
})
|
|
190
|
+
]);
|
|
191
|
+
|
|
162
192
|
winston.debug("(voice) conversation returned:"+ conversation_id);
|
|
163
193
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
if (!key) {
|
|
173
|
-
winston.debug("(voice) - Retrieve public gptkey")
|
|
174
|
-
key = GPT_KEY;
|
|
175
|
-
publicKey = true;
|
|
176
|
-
}
|
|
177
|
-
integrations.push({type: 'openai', key: key, publicKey: publicKey})
|
|
178
|
-
|
|
179
|
-
let eleven_labs = await integrationService.getKeyFromIntegrations(project_id, 'elevenlabs', settings.token)
|
|
180
|
-
if (eleven_labs) {
|
|
181
|
-
winston.debug("(voice) - Key found in Integrations: "+ eleven_labs);
|
|
182
|
-
integrations.push({type: 'elevenlabs', key: eleven_labs, publicKey: false})
|
|
183
|
-
}
|
|
184
|
-
} catch (error) {
|
|
185
|
-
winston.error('(voice) - Error retrieving integrations keys:', error);
|
|
194
|
+
// Costruisci array integrations
|
|
195
|
+
let integrations = [];
|
|
196
|
+
if (openaiKeyResult && openaiKeyResult.key) {
|
|
197
|
+
integrations.push({type: 'openai', key: openaiKeyResult.key, publicKey: openaiKeyResult.publicKey})
|
|
198
|
+
}
|
|
199
|
+
if (elevenLabsKey) {
|
|
200
|
+
winston.debug("(voice) - Key found in Integrations: "+ elevenLabsKey);
|
|
201
|
+
integrations.push({type: 'elevenlabs', key: elevenLabsKey, publicKey: false})
|
|
186
202
|
}
|
|
187
203
|
|
|
188
204
|
//save data to redis
|
|
@@ -195,16 +211,8 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
195
211
|
conversation_id: conversation_id,
|
|
196
212
|
integrations: integrations
|
|
197
213
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
//for (const [key, value] of Object.entries(redis_data)){
|
|
202
|
-
// await redis_client.hSet('tiledesk:voice:'+callId, key, JSON.stringify(value))
|
|
203
|
-
//}
|
|
204
|
-
//await redis_client.expire('tiledesk:voice:'+callId, 86400)
|
|
205
|
-
|
|
206
|
-
await redis_client.set('tiledesk:voice:'+callSid+':session', JSON.stringify(session_data), {'EX': 86400});
|
|
207
|
-
|
|
214
|
+
voiceChannel.setSessionForCallId(callSid, session_data)
|
|
215
|
+
|
|
208
216
|
let tiledeskMessage= {
|
|
209
217
|
text:'/start',
|
|
210
218
|
senderFullname: from,
|
|
@@ -240,12 +248,16 @@ router.post('/webhook/:id_project', async (req, res) => {
|
|
|
240
248
|
let messageToVXML = await tdTranslator.toVXML(message, callSid, vxmlAttributes, session_data)
|
|
241
249
|
winston.debug('(voice) /webhook/:id_project messageVXML-->'+ messageToVXML)
|
|
242
250
|
|
|
243
|
-
let
|
|
244
|
-
winston.info(`Time to respond to /webhook/${project_id}: ${(
|
|
251
|
+
let end_call1 = new Date().getTime();
|
|
252
|
+
winston.info(`Time to respond to /webhook/${project_id} before response: ${(end_call1-start_call)}[ms]`)
|
|
245
253
|
|
|
254
|
+
|
|
246
255
|
// Render the response as XML in reply to the webhook request
|
|
247
256
|
res.set('Content-Type', 'text/xml');
|
|
248
257
|
res.status(200).send(messageToVXML);
|
|
258
|
+
|
|
259
|
+
let end_call2 = new Date().getTime();
|
|
260
|
+
winston.info(`Time to respond to /webhook/${project_id}: ${(end_call2-start_call)}[ms]`)
|
|
249
261
|
});
|
|
250
262
|
|
|
251
263
|
|
|
@@ -262,8 +274,7 @@ router.post('/nextblock_old/:callSid/', async(req, res) => {
|
|
|
262
274
|
let project_id, conversation_id, user;
|
|
263
275
|
let from, to;
|
|
264
276
|
|
|
265
|
-
let redis_data = await
|
|
266
|
-
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
277
|
+
let redis_data = await voiceChannel.getSessionForCallId(callSid)
|
|
267
278
|
if (!redis_data) {
|
|
268
279
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
269
280
|
}
|
|
@@ -345,16 +356,13 @@ router.post('/nextblock/:callSid/', async(req, res) => {
|
|
|
345
356
|
let confidence = req.body.Confidence
|
|
346
357
|
let callSid = req.params.callSid;
|
|
347
358
|
|
|
348
|
-
let sessionInfo;
|
|
349
359
|
let project_id, conversation_id, user;
|
|
350
360
|
let from, to;
|
|
351
361
|
|
|
352
|
-
let
|
|
353
|
-
|
|
354
|
-
if (!redis_data) {
|
|
362
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
363
|
+
if (!sessionInfo) {
|
|
355
364
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
356
365
|
}
|
|
357
|
-
sessionInfo = JSON.parse(redis_data)
|
|
358
366
|
project_id = sessionInfo.project_id;
|
|
359
367
|
from = sessionInfo.from;
|
|
360
368
|
to = sessionInfo.to;
|
|
@@ -465,7 +473,7 @@ async function getMessage(callSid, ani, project_id, conversation_id){
|
|
|
465
473
|
if (queue && queue.length > 0) {
|
|
466
474
|
//CASE: queue has at least one message to reproduce --> get message from tiledesk queue and reset delayTime
|
|
467
475
|
message = queue[0]
|
|
468
|
-
winston.
|
|
476
|
+
winston.verbose('[getMessage] QUEUE --> '+ queue[0].text)
|
|
469
477
|
|
|
470
478
|
// remove message from queue and reset delayIndex
|
|
471
479
|
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
@@ -486,7 +494,7 @@ async function getMessage(callSid, ani, project_id, conversation_id){
|
|
|
486
494
|
}
|
|
487
495
|
|
|
488
496
|
message = queue[0]
|
|
489
|
-
winston.
|
|
497
|
+
winston.verbose(`[getMessage] Message received from subscription: ${message.text}`);
|
|
490
498
|
|
|
491
499
|
// remove message from queue and reset delayIndex
|
|
492
500
|
await tdChannel.removeMessageFromQueue(conversation_id, message._id)
|
|
@@ -528,16 +536,13 @@ router.post('/speechresult/:callSid', async (req, res) => {
|
|
|
528
536
|
let confidence = req.body.Confidence
|
|
529
537
|
let callSid = req.params.callSid;
|
|
530
538
|
|
|
531
|
-
let sessionInfo;
|
|
532
539
|
let project_id, conversation_id, user;
|
|
533
540
|
let from, to;
|
|
534
541
|
|
|
535
|
-
let
|
|
536
|
-
|
|
537
|
-
if (!redis_data) {
|
|
542
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
543
|
+
if (!sessionInfo) {
|
|
538
544
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
539
545
|
}
|
|
540
|
-
sessionInfo = JSON.parse(redis_data)
|
|
541
546
|
project_id = sessionInfo.project_id;
|
|
542
547
|
from = sessionInfo.from;
|
|
543
548
|
to = sessionInfo.to;
|
|
@@ -623,15 +628,13 @@ router.post('/record/action/:callSid/',async (req, res) => {
|
|
|
623
628
|
let start_call = new Date();
|
|
624
629
|
|
|
625
630
|
let callSid = req.body.CallSid;
|
|
626
|
-
let sessionInfo;
|
|
627
631
|
let project_id, conversation_id, user;
|
|
628
632
|
let from, to;
|
|
629
633
|
|
|
630
|
-
let
|
|
631
|
-
if (!
|
|
634
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
635
|
+
if (!sessionInfo) {
|
|
632
636
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
633
637
|
}
|
|
634
|
-
sessionInfo = JSON.parse(redis_data)
|
|
635
638
|
project_id = sessionInfo.project_id;
|
|
636
639
|
from = sessionInfo.from;
|
|
637
640
|
to = sessionInfo.to;
|
|
@@ -692,16 +695,13 @@ router.post('/record/callback/:callSid/',async (req, res) => {
|
|
|
692
695
|
let button_action = req.query.button_action ? '#' + req.query.button_action : '';
|
|
693
696
|
let previousIntentName = req.query.intentName || '';
|
|
694
697
|
|
|
695
|
-
|
|
696
|
-
let sessionInfo;
|
|
697
698
|
let project_id, conversation_id, user;
|
|
698
699
|
let from, to;
|
|
699
700
|
|
|
700
|
-
let
|
|
701
|
-
if (!
|
|
701
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
702
|
+
if (!sessionInfo) {
|
|
702
703
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
703
704
|
}
|
|
704
|
-
sessionInfo = JSON.parse(redis_data)
|
|
705
705
|
project_id = sessionInfo.project_id;
|
|
706
706
|
from = sessionInfo.from;
|
|
707
707
|
to = sessionInfo.to;
|
|
@@ -799,16 +799,13 @@ router.post('/menublock/:callSid', async (req, res) => {
|
|
|
799
799
|
winston.debug("(voice) button menu: ", button);
|
|
800
800
|
winston.debug("(voice) message_text menu: "+ message_text);
|
|
801
801
|
|
|
802
|
-
|
|
803
|
-
let sessionInfo;
|
|
804
802
|
let project_id, conversation_id, user;
|
|
805
803
|
let from, to;
|
|
806
804
|
|
|
807
|
-
let
|
|
808
|
-
if (!
|
|
805
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
806
|
+
if (!sessionInfo) {
|
|
809
807
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
810
808
|
}
|
|
811
|
-
sessionInfo = JSON.parse(redis_data)
|
|
812
809
|
project_id = sessionInfo.project_id;
|
|
813
810
|
from = sessionInfo.from;
|
|
814
811
|
to = sessionInfo.to;
|
|
@@ -878,15 +875,13 @@ router.post('/handle/:callSid/:event', async (req, res) => {
|
|
|
878
875
|
let button_action = '#' + req.query.button_action;
|
|
879
876
|
let previousIntentName = req.query.intentName;
|
|
880
877
|
|
|
881
|
-
let sessionInfo;
|
|
882
878
|
let project_id, conversation_id, user;
|
|
883
879
|
let from, to;
|
|
884
880
|
|
|
885
|
-
let
|
|
886
|
-
if (!
|
|
881
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
882
|
+
if (!sessionInfo) {
|
|
887
883
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
888
884
|
}
|
|
889
|
-
sessionInfo = JSON.parse(redis_data)
|
|
890
885
|
project_id = sessionInfo.project_id;
|
|
891
886
|
from = sessionInfo.from;
|
|
892
887
|
to = sessionInfo.to;
|
|
@@ -962,16 +957,13 @@ router.post('/event/:callSid/:event', async(req, res)=> {
|
|
|
962
957
|
let currentIntentName = req.query.intentName;
|
|
963
958
|
let currentIntentTimestamp = req.query.previousIntentTimestamp;
|
|
964
959
|
|
|
965
|
-
|
|
966
|
-
let sessionInfo;
|
|
967
960
|
let project_id, conversation_id, user;
|
|
968
961
|
let from, to;
|
|
969
962
|
|
|
970
|
-
let
|
|
971
|
-
if (!
|
|
963
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
964
|
+
if (!sessionInfo) {
|
|
972
965
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
973
966
|
}
|
|
974
|
-
sessionInfo = JSON.parse(redis_data)
|
|
975
967
|
project_id = sessionInfo.project_id;
|
|
976
968
|
from = sessionInfo.from;
|
|
977
969
|
to = sessionInfo.to;
|
|
@@ -1062,15 +1054,13 @@ router.post('/twilio/status',async (req, res) => {
|
|
|
1062
1054
|
clearTimeout(messageTimeout)
|
|
1063
1055
|
}
|
|
1064
1056
|
|
|
1065
|
-
let sessionInfo;
|
|
1066
1057
|
let project_id, conversation_id, user;
|
|
1067
1058
|
let from, to;
|
|
1068
1059
|
|
|
1069
|
-
let
|
|
1070
|
-
if (!
|
|
1060
|
+
let sessionInfo = await voiceChannel.getSessionForCallId(callSid)
|
|
1061
|
+
if (!sessionInfo) {
|
|
1071
1062
|
return res.status(500).send({ success: "false", message: "Can't retrive data for callSid ->" + callSid });
|
|
1072
1063
|
}
|
|
1073
|
-
sessionInfo = JSON.parse(redis_data)
|
|
1074
1064
|
project_id = sessionInfo.project_id;
|
|
1075
1065
|
from = sessionInfo.from;
|
|
1076
1066
|
to = sessionInfo.to;
|
|
@@ -1476,12 +1466,22 @@ async function startApp(settings, callback) {
|
|
|
1476
1466
|
if(settings.OPENAI_ENDPOINT){
|
|
1477
1467
|
OPENAI_ENDPOINT = settings.OPENAI_ENDPOINT
|
|
1478
1468
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1469
|
+
|
|
1470
|
+
// Read ELEVENLABS_ENDPOINT from process.env (not from settings)
|
|
1471
|
+
if(process.env.ELEVENLABS_ENDPOINT){
|
|
1472
|
+
ELEVENLABS_ENDPOINT = process.env.ELEVENLABS_ENDPOINT
|
|
1473
|
+
} else {
|
|
1474
|
+
ELEVENLABS_ENDPOINT = "https://api.elevenlabs.io" // default
|
|
1481
1475
|
}
|
|
1482
1476
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1477
|
+
// Read MAX_POLLING_TIME from process.env (not from settings)
|
|
1478
|
+
if(process.env.MAX_POLLING_TIME){
|
|
1479
|
+
MAX_POLLING_TIME = process.env.MAX_POLLING_TIME/1000; //convert in seconds
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
// Read BASE_POOLING_DELAY from process.env (not from settings)
|
|
1483
|
+
if(process.env.BASE_POOLING_DELAY){
|
|
1484
|
+
BASE_POOLING_DELAY = process.env.BASE_POOLING_DELAY;
|
|
1485
1485
|
}
|
|
1486
1486
|
|
|
1487
1487
|
if (settings.REDIS_HOST && settings.REDIS_PORT) {
|
|
@@ -1493,9 +1493,12 @@ async function startApp(settings, callback) {
|
|
|
1493
1493
|
winston.error("(voice) Missing redis parameters --> REDIS_HOST and REDIS_PORT");
|
|
1494
1494
|
}
|
|
1495
1495
|
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
|
|
1496
1499
|
//init VOICE CHANNEL
|
|
1497
1500
|
voiceChannel = new VoiceChannel({
|
|
1498
|
-
BASE_POOLING_DELAY:
|
|
1501
|
+
BASE_POOLING_DELAY: BASE_POOLING_DELAY,
|
|
1499
1502
|
redis_client: redis_client,
|
|
1500
1503
|
})
|
|
1501
1504
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiledesk/tiledesk-voice-twilio-connector",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.28",
|
|
4
4
|
"description": "Tiledesk VOICE Twilio connector",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Gabriele Panico",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"redis": "^4.6.13",
|
|
37
37
|
"redis-lock": "^1.0.0",
|
|
38
38
|
"redlock": "^4.2.0",
|
|
39
|
+
"remove-markdown": "^0.6.2",
|
|
39
40
|
"twilio": "^5.2.1",
|
|
40
41
|
"uuid": "^8.3.2",
|
|
41
42
|
"winston": "^3.3.3",
|
|
@@ -3,7 +3,8 @@ const winston = require("../winston");
|
|
|
3
3
|
const xmlbuilder = require("xmlbuilder");
|
|
4
4
|
const querystring = require("querystring");
|
|
5
5
|
|
|
6
|
-
const utils = require("./utils
|
|
6
|
+
const utils = require("./utils.js");
|
|
7
|
+
const utils_message = require("./utils-message.js");
|
|
7
8
|
const MENU_CHOICE = require("./constants.js").MENU_CHOICE;
|
|
8
9
|
const WAIT_MESSAGE = require("./constants.js").WAIT_MESSAGE;
|
|
9
10
|
const TEXT_MESSAGE = require("./constants.js").TEXT_MESSAGE;
|
|
@@ -123,9 +124,9 @@ class TiledeskTwilioTranslator {
|
|
|
123
124
|
|
|
124
125
|
|
|
125
126
|
//MANAGE CLOSE info message
|
|
126
|
-
const isInfoSupport =
|
|
127
|
+
const isInfoSupport = utils_message.messageType(TYPE_MESSAGE.INFO_SUPPORT, msg)
|
|
127
128
|
winston.debug("[TiledeskVXMLTranslator] isInfoSupport:"+ isInfoSupport);
|
|
128
|
-
if(isInfoSupport &&
|
|
129
|
+
if(isInfoSupport && utils_message.infoMessageType(msg) === INFO_MESSAGE_TYPE.CHAT_CLOSED){
|
|
129
130
|
const hangUp = this.hangupCall(xml)
|
|
130
131
|
return hangUp;
|
|
131
132
|
}
|
|
@@ -305,9 +306,19 @@ class TiledeskTwilioTranslator {
|
|
|
305
306
|
|
|
306
307
|
async playPromptVXMLConverter(rootEle, message, xmlAttributes){
|
|
307
308
|
|
|
309
|
+
const gather = rootEle.ele("Gather", { input: "speech"})
|
|
310
|
+
|
|
311
|
+
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + '&previousIntentTimestamp='+Date.now();
|
|
312
|
+
gather.att("action", this.BASE_URL + '/nextBlock/' + xmlAttributes.callSid + queryUrl)
|
|
313
|
+
// gather.att("action", this.BASE_URL + '/speechresult/' + xmlAttributes.callSid + queryUrl)
|
|
314
|
+
.att("method", "POST")
|
|
315
|
+
.att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
|
|
316
|
+
.att('speechTimeout', "auto")
|
|
317
|
+
.att("enhanced", "true") // enable enhanced recognition
|
|
318
|
+
|
|
308
319
|
const prompt = await this.promptVXML(rootEle, message, xmlAttributes);
|
|
309
320
|
|
|
310
|
-
|
|
321
|
+
/** fallback se non parla --> redirige alla nextblock */
|
|
311
322
|
rootEle.ele("Redirect", {method: "POST"}, this.BASE_URL + '/nextblock/' + xmlAttributes.callSid + queryUrl).up()
|
|
312
323
|
//prompt.ele("submit", { fetchhint: "safe", expr: "proxyBaseUrl +'/nextblock/' + session.connection.calltoken", method: "post", namelist: "usertext session intentName previousIntentTimestamp" });
|
|
313
324
|
|
|
@@ -319,7 +330,6 @@ class TiledeskTwilioTranslator {
|
|
|
319
330
|
async speechFormVXMLConverter(rootEle, message, xmlAttributes) {
|
|
320
331
|
|
|
321
332
|
if(this.voiceProvider === VOICE_PROVIDER.TWILIO){
|
|
322
|
-
|
|
323
333
|
const gather = rootEle.ele("Gather", { input: "speech"})
|
|
324
334
|
|
|
325
335
|
const queryUrl = '?intentName='+ querystring.encode(xmlAttributes.intentName) + "&previousIntentTimestamp="+Date.now();
|
|
@@ -328,6 +338,7 @@ class TiledeskTwilioTranslator {
|
|
|
328
338
|
.att("method", "POST")
|
|
329
339
|
.att("language", xmlAttributes.TTS_VOICE_LANGUAGE)
|
|
330
340
|
.att('speechTimeout', "auto")
|
|
341
|
+
.att("enhanced", "true") // enable enhanced recognition
|
|
331
342
|
|
|
332
343
|
//if(xmlAttributes && xmlAttributes.noInputTimeout){
|
|
333
344
|
// gather.att("timeout", Math.round(xmlAttributes.noInputTimeout/1000) ).up();
|
|
@@ -552,19 +563,19 @@ class TiledeskTwilioTranslator {
|
|
|
552
563
|
if (command.type === "message") {
|
|
553
564
|
//case type: TEXT
|
|
554
565
|
if(command.message.type === 'text'){
|
|
566
|
+
let text = utils.markdownToTwilioSpeech(command.message.text);
|
|
555
567
|
if(that.voiceProvider !== VOICE_PROVIDER.TWILIO){
|
|
556
|
-
let voiceMessageUrl = await that.generateTTS(
|
|
568
|
+
let voiceMessageUrl = await that.generateTTS(text, attributes)
|
|
557
569
|
rootEle.ele('Play', {}, voiceMessageUrl )
|
|
558
570
|
}else{
|
|
559
|
-
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE },
|
|
571
|
+
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE }, text);
|
|
560
572
|
}
|
|
561
573
|
|
|
562
|
-
|
|
563
574
|
}
|
|
564
575
|
//case type: FRAME
|
|
565
576
|
if(command.message.type === 'frame' && command.message.metadata.src !== ""){
|
|
566
577
|
if(command.message.text != ''){
|
|
567
|
-
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE },
|
|
578
|
+
rootEle.ele("Say", { voice: attributes.TTS_VOICE_NAME, language: attributes.TTS_VOICE_LANGUAGE }, text);
|
|
568
579
|
}
|
|
569
580
|
rootEle.ele('Play', {}, command.message.metadata.src )
|
|
570
581
|
}
|
package/tiledesk/VoiceChannel.js
CHANGED
|
@@ -75,11 +75,11 @@ class VoiceChannel {
|
|
|
75
75
|
//increment
|
|
76
76
|
const delayIndex = (+index) +1
|
|
77
77
|
//save new index to redis
|
|
78
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', delayIndex, 'EX'
|
|
78
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', delayIndex, {'EX': 86400});
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
81
81
|
//if index is not present: set to default (0)
|
|
82
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, 'EX'
|
|
82
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':delayIndex', 0, {'EX': 86400});
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/** RESET INDEX INTO REDIS DATA FOR CURRENT CALLID **/
|
|
@@ -106,11 +106,11 @@ class VoiceChannel {
|
|
|
106
106
|
winston.debug('[VoiceChannel] saveSettingsForCallId: attributes found -->'+index)
|
|
107
107
|
if(index){
|
|
108
108
|
//set index to default (0)
|
|
109
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), 'EX'
|
|
109
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), {'EX': 86400});
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
//if index is not present: set to default (0)
|
|
113
|
-
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), 'EX'
|
|
113
|
+
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), {'EX': 86400});
|
|
114
114
|
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -125,6 +125,25 @@ class VoiceChannel {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
|
|
128
|
+
async setSessionForCallId(callSid, session) {
|
|
129
|
+
await this.redis_client.set(`tiledesk:voice:${callSid}:session`, JSON.stringify(session), { 'EX': 86400 });
|
|
130
|
+
//for (const [key, value] of Object.entries(redis_data)){
|
|
131
|
+
// await redis_client.hSet('tiledesk:voice:'+callId, key, JSON.stringify(value))
|
|
132
|
+
//}
|
|
133
|
+
//await redis_client.expire('tiledesk:voice:'+callId, 86400)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async getSessionForCallId(callSid) {
|
|
137
|
+
const sessionData = await this.redis_client.get(`tiledesk:voice:${callSid}:session`);
|
|
138
|
+
//let redis_data = await redis_client.hGetAll('tiledesk:voice:'+callId);
|
|
139
|
+
if (sessionData) {
|
|
140
|
+
return JSON.parse(sessionData);
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
128
147
|
async deleteCallKeys(callSid) {
|
|
129
148
|
const pattern = `tiledesk:voice:${callSid}:*`;
|
|
130
149
|
let cursor = 0;
|
package/tiledesk/utils.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const NON_SPEECH_TOKENS = require('./constants').NON_SPEECH_TOKENS
|
|
2
|
+
const removeMarkdown = require('remove-markdown');
|
|
2
3
|
|
|
3
4
|
function getNumber(phoneNumber){
|
|
4
5
|
if(phoneNumber.startsWith('+')){
|
|
@@ -31,5 +32,13 @@ function normalizeSTT(text) {
|
|
|
31
32
|
return cleaned;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
function markdownToTwilioSpeech(text) {
|
|
36
|
+
return removeMarkdown(text)
|
|
37
|
+
.replace(/:/g, ': ')
|
|
38
|
+
.replace(/\n+/g, '. ')
|
|
39
|
+
.replace(/\s+/g, ' ')
|
|
40
|
+
.trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
|
|
35
|
-
module.exports = {getNumber, buildQueryString, normalizeSTT}
|
|
44
|
+
module.exports = {getNumber, buildQueryString, normalizeSTT, markdownToTwilioSpeech}
|