@tiledesk/tiledesk-voice-twilio-connector 0.2.0-rc3 → 0.2.0-rc7
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/README.md +494 -23
- package/package.json +3 -2
- package/src/app.js +14 -5
- package/src/controllers/VoiceController.js +33 -28
- package/src/middlewares/httpLogger.js +25 -13
- package/src/models/KeyValueStore.js +5 -5
- package/src/routes/manageApp.js +7 -7
- package/src/services/AiService.js +8 -8
- package/src/services/AiService.sdk.js +13 -13
- package/src/services/IntegrationService.js +2 -2
- package/src/services/MessageService.js +12 -6
- package/src/services/SessionService.js +5 -5
- package/src/services/SpeechService.js +3 -3
- package/src/services/TwilioService.js +9 -2
- package/src/services/UploadService.js +1 -1
- package/src/services/channels/TiledeskChannel.js +54 -55
- package/src/services/channels/VoiceChannel.js +4 -4
- package/src/services/clients/TiledeskSubscriptionClient.js +2 -2
- package/src/services/translators/TiledeskTwilioTranslator.js +20 -15
- package/src/controllers/VoiceController.original.js +0 -811
- package/src/services/translators/TiledeskTwilioTranslator.original.js +0 -614
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { twiml } = require('twilio');
|
|
1
2
|
const logger = require('../utils/logger');
|
|
2
3
|
|
|
3
4
|
/**
|
|
@@ -44,7 +45,7 @@ class TwilioService {
|
|
|
44
45
|
method: 'POST'
|
|
45
46
|
});
|
|
46
47
|
|
|
47
|
-
logger.debug(`[TwilioService] Call ${callSid} redirected to ${url}`);
|
|
48
|
+
// logger.debug(`[TwilioService] Call ${callSid} redirected to ${url}`);
|
|
48
49
|
return true;
|
|
49
50
|
} catch (error) {
|
|
50
51
|
logger.error(`[TwilioService] Error redirecting call ${callSid}:`, error);
|
|
@@ -77,7 +78,7 @@ class TwilioService {
|
|
|
77
78
|
const flowAttrs = tiledeskMessage.attributes?.flowAttributes;
|
|
78
79
|
const callSid = flowAttrs?.CallSid;
|
|
79
80
|
|
|
80
|
-
if (!callSid || lastCallSidVerb[callSid] !== '
|
|
81
|
+
if (!callSid || lastCallSidVerb[callSid] !== 'play_loop') {
|
|
81
82
|
return false;
|
|
82
83
|
}
|
|
83
84
|
|
|
@@ -92,6 +93,12 @@ class TwilioService {
|
|
|
92
93
|
|
|
93
94
|
return this.redirectCall(callSid, redirectUrl, settings);
|
|
94
95
|
}
|
|
96
|
+
|
|
97
|
+
generateRedirectTwiML(callSid, intentName) {
|
|
98
|
+
const response = new twiml.VoiceResponse();
|
|
99
|
+
response.redirect({ method: 'POST' }, this.buildNextBlockUrl(callSid, intentName));
|
|
100
|
+
return response.toString();
|
|
101
|
+
}
|
|
95
102
|
|
|
96
103
|
/**
|
|
97
104
|
* Get call information.
|
|
@@ -26,7 +26,7 @@ class UploadService {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async upload(id, file, user) {
|
|
29
|
-
|
|
29
|
+
// logger.debug(`[UploadService] upload for id ${id} and user ${user._id}`);
|
|
30
30
|
|
|
31
31
|
const tempFilePath = path.join(os.tmpdir(), `speech_${user._id}_${id}.wav`);
|
|
32
32
|
|
|
@@ -4,14 +4,14 @@ const jwt = require("jsonwebtoken");
|
|
|
4
4
|
const utils = require('../../utils/utils-message.js');
|
|
5
5
|
const { TYPE_MESSAGE, CHANNEL_NAME } = require('../../utils/constants');
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const logger = require("../../utils/logger");
|
|
8
8
|
const voiceEventEmitter = require('../voiceEventEmitter');
|
|
9
9
|
|
|
10
10
|
class TiledeskChannel {
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
constructor(config) {
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
if (!config) {
|
|
16
16
|
throw new Error("[TiledeskChannel] config is mandatory");
|
|
17
17
|
}
|
|
@@ -21,18 +21,18 @@ class TiledeskChannel {
|
|
|
21
21
|
if (!config.redis_client) {
|
|
22
22
|
throw new Error("[TiledeskChannel] config.redis_client is mandatory");
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
this.log = config.log || false;
|
|
26
26
|
this.API_URL = config.API_URL;
|
|
27
27
|
this.redis_client = config.redis_client;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
|
|
31
31
|
async signIn(user_id, settings) {
|
|
32
32
|
// ani = calling phone number
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// logger.debug('[TiledeskChannel] sigIn settings', settings)
|
|
35
|
+
|
|
36
36
|
let payload = {
|
|
37
37
|
_id: CHANNEL_NAME + '-' + user_id,
|
|
38
38
|
firstname: user_id,
|
|
@@ -71,8 +71,8 @@ class TiledeskChannel {
|
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
async generateConversation(ani, callId, project_id){
|
|
74
|
+
|
|
75
|
+
async generateConversation(ani, callId, project_id) {
|
|
76
76
|
return "support-group-" + project_id + "-" + ani + "-" + CHANNEL_NAME + "-" + callId;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -93,10 +93,10 @@ class TiledeskChannel {
|
|
|
93
93
|
let request_id;
|
|
94
94
|
if (response.data.requests[0]) {
|
|
95
95
|
request_id = response.data.requests[0].request_id;
|
|
96
|
-
|
|
96
|
+
// logger.debug("[TiledeskChannel] use already opened conversation: ", request_id);
|
|
97
97
|
} else {
|
|
98
98
|
request_id = new_request_id;
|
|
99
|
-
|
|
99
|
+
// logger.debug("[TiledeskChannel] use new conversation: ", request_id);
|
|
100
100
|
}
|
|
101
101
|
return request_id;
|
|
102
102
|
} catch (err) {
|
|
@@ -104,7 +104,7 @@ class TiledeskChannel {
|
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
async getDepartments(token, project_id) {
|
|
109
109
|
|
|
110
110
|
try {
|
|
@@ -112,11 +112,11 @@ class TiledeskChannel {
|
|
|
112
112
|
url: this.API_URL + "/" + project_id + "/departments/allstatus",
|
|
113
113
|
headers: {
|
|
114
114
|
'Content-Type': 'application/json',
|
|
115
|
-
'Authorization': token
|
|
115
|
+
'Authorization': token
|
|
116
116
|
},
|
|
117
117
|
method: 'GET'
|
|
118
118
|
});
|
|
119
|
-
|
|
119
|
+
// logger.debug("[TiledeskChannel] get departments response.data: ", response.data);
|
|
120
120
|
return response.data;
|
|
121
121
|
} catch (err) {
|
|
122
122
|
winston.error("[TiledeskChannel] get departments error");
|
|
@@ -136,7 +136,7 @@ class TiledeskChannel {
|
|
|
136
136
|
data: tiledeskMessage,
|
|
137
137
|
method: 'POST'
|
|
138
138
|
});
|
|
139
|
-
|
|
139
|
+
// logger.debug("[TiledeskChannel] send message response: ", response.data);
|
|
140
140
|
return response.data;
|
|
141
141
|
} catch (err) {
|
|
142
142
|
winston.error("[TiledeskChannel] send message: ", err.response?.data);
|
|
@@ -144,65 +144,64 @@ class TiledeskChannel {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
}
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
|
|
148
|
+
|
|
149
149
|
/** ADD MESSAGE TO REDIS QUEUE **/
|
|
150
|
-
async addMessageToQueue(message){
|
|
151
|
-
|
|
150
|
+
async addMessageToQueue(message) {
|
|
151
|
+
|
|
152
152
|
/*SKIP INFO MESSAGES*/
|
|
153
|
-
if(utils.messageType(TYPE_MESSAGE.INFO, message)){
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
if (utils.messageType(TYPE_MESSAGE.INFO, message)) {
|
|
154
|
+
logger.debug("> SKIPPING INFO message");
|
|
155
|
+
// logger.debug("> SKIPPING INFO message: " + JSON.stringify(message) );
|
|
156
|
+
return false;
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
/*SKIP CURRENT USER MESSAGES*/
|
|
159
160
|
if (message.sender.indexOf(CHANNEL_NAME) > -1) {
|
|
160
|
-
|
|
161
|
+
logger.debug("> SKIPPING ECHO message");
|
|
162
|
+
// logger.debug("> SKIPPING ECHO message: " + JSON.stringify(message) );
|
|
161
163
|
return false;
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
// logger.debug("> SAVE message TO QUEUE: " + JSON.stringify(message) );
|
|
165
167
|
const conversation_id = message.recipient;
|
|
166
168
|
const queueKey = `tiledesk:queue:${conversation_id}`;
|
|
167
|
-
|
|
169
|
+
|
|
168
170
|
// Use pipeline for atomic push + expire (single round-trip)
|
|
169
171
|
await this.redis_client
|
|
170
172
|
.multi()
|
|
171
173
|
.rPush(queueKey, JSON.stringify(message))
|
|
172
174
|
.expire(queueKey, 86400)
|
|
173
175
|
.exec();
|
|
174
|
-
|
|
175
|
-
// Emit event for real-time subscribers
|
|
176
|
-
voiceEventEmitter.emit(`tiledesk:conversation:${conversation_id}`, message);
|
|
177
|
-
|
|
176
|
+
|
|
178
177
|
return true;
|
|
179
178
|
}
|
|
180
179
|
|
|
181
|
-
|
|
180
|
+
|
|
182
181
|
/** GET MESSAGES FROM REDIS QUEUE LIST **/
|
|
183
|
-
async getMessagesFromQueue(conversation_id){
|
|
182
|
+
async getMessagesFromQueue(conversation_id) {
|
|
184
183
|
const queueKey = `tiledesk:queue:${conversation_id}`;
|
|
185
184
|
const queue = await this.redis_client.lRange(queueKey, 0, -1);
|
|
186
|
-
|
|
185
|
+
|
|
187
186
|
if (!queue || queue.length === 0) {
|
|
188
187
|
return [];
|
|
189
188
|
}
|
|
190
|
-
|
|
189
|
+
|
|
191
190
|
return queue.map(item => JSON.parse(item));
|
|
192
191
|
}
|
|
193
192
|
|
|
194
193
|
/** PUBLISH MESSAGE TO REDIS TOPIC **/
|
|
195
|
-
async publishMessageToTopic(message){
|
|
196
|
-
|
|
194
|
+
async publishMessageToTopic(message) {
|
|
195
|
+
|
|
197
196
|
/*SKIP INFO MESSAGES*/
|
|
198
|
-
if(utils.messageType(TYPE_MESSAGE.INFO, message)){
|
|
199
|
-
|
|
197
|
+
if (utils.messageType(TYPE_MESSAGE.INFO, message)) {
|
|
198
|
+
// logger.debug("> SKIPPING INFO message");
|
|
200
199
|
return;
|
|
201
200
|
}
|
|
202
201
|
|
|
203
202
|
/*SKIP CURRENT USER MESSAGES*/
|
|
204
203
|
if (message.sender.indexOf("vxml") > -1) {
|
|
205
|
-
|
|
204
|
+
// logger.debug("> SKIPPING ECHO message");
|
|
206
205
|
return;
|
|
207
206
|
}
|
|
208
207
|
|
|
@@ -210,7 +209,7 @@ class TiledeskChannel {
|
|
|
210
209
|
}
|
|
211
210
|
|
|
212
211
|
/** SUBSCRIBE TO REDIS TOPIC */
|
|
213
|
-
async subscribeToTopic(conversation_id){
|
|
212
|
+
async subscribeToTopic(conversation_id) {
|
|
214
213
|
const topic = `tiledesk:conversation:${conversation_id}`;
|
|
215
214
|
// console.log("subscribeToTopic: " + topic);
|
|
216
215
|
|
|
@@ -220,40 +219,40 @@ class TiledeskChannel {
|
|
|
220
219
|
});
|
|
221
220
|
});
|
|
222
221
|
}
|
|
223
|
-
|
|
222
|
+
|
|
224
223
|
/** REMOVE MESSAGE FROM REDIS QUEUE LIST (removes first message - FIFO) **/
|
|
225
|
-
async removeMessageFromQueue(conversation_id, message_id){
|
|
224
|
+
async removeMessageFromQueue(conversation_id, message_id) {
|
|
226
225
|
const queueKey = `tiledesk:queue:${conversation_id}`;
|
|
227
226
|
// Use lPop for FIFO queue - removes and returns first element
|
|
228
227
|
await this.redis_client.lPop(queueKey);
|
|
229
228
|
}
|
|
230
|
-
|
|
229
|
+
|
|
231
230
|
/** CLEAR QUEUE FROM REDIS **/
|
|
232
|
-
async clearQueue(conversation_id){
|
|
233
|
-
if(conversation_id){
|
|
231
|
+
async clearQueue(conversation_id) {
|
|
232
|
+
if (conversation_id) {
|
|
234
233
|
await this.redis_client.del(`tiledesk:queue:${conversation_id}`);
|
|
235
234
|
}
|
|
236
235
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
async generateWaitTdMessage(ani, delayTime){
|
|
242
|
-
|
|
243
|
-
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
async generateWaitTdMessage(ani, delayTime) {
|
|
241
|
+
|
|
242
|
+
|
|
244
243
|
return {
|
|
245
244
|
text: '',
|
|
246
245
|
senderFullname: ani,
|
|
247
246
|
attributes: {
|
|
248
|
-
commands:[
|
|
249
|
-
{ type: 'wait', time: delayTime}
|
|
247
|
+
commands: [
|
|
248
|
+
{ type: 'wait', time: delayTime }
|
|
250
249
|
]
|
|
251
250
|
},
|
|
252
251
|
channel: { name: CHANNEL_NAME }
|
|
253
252
|
}
|
|
254
253
|
}
|
|
255
|
-
|
|
256
|
-
|
|
254
|
+
|
|
255
|
+
|
|
257
256
|
async fixToken(token) {
|
|
258
257
|
let index = token.lastIndexOf("JWT ");
|
|
259
258
|
if (index != -1) {
|
|
@@ -263,7 +262,7 @@ class TiledeskChannel {
|
|
|
263
262
|
return "JWT " + token;
|
|
264
263
|
}
|
|
265
264
|
}
|
|
266
|
-
|
|
265
|
+
|
|
267
266
|
}
|
|
268
267
|
|
|
269
268
|
module.exports = { TiledeskChannel };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
require('dotenv').config();
|
|
2
|
-
const
|
|
2
|
+
const logger = require("../../utils/logger");
|
|
3
3
|
|
|
4
4
|
const voiceEventEmitter = require('../voiceEventEmitter');
|
|
5
5
|
|
|
@@ -45,7 +45,7 @@ class VoiceChannel {
|
|
|
45
45
|
listenToVoiceEvents(){
|
|
46
46
|
|
|
47
47
|
voiceEventEmitter.on('saveSettings', async (data) => {
|
|
48
|
-
|
|
48
|
+
// logger.debug('[VoiceChannel] listenToVoiceEvents: saveSettings event received -->', data)
|
|
49
49
|
if(data){
|
|
50
50
|
await this.saveSettingsForCallId(data, data.callSid);
|
|
51
51
|
}
|
|
@@ -77,7 +77,7 @@ class VoiceChannel {
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
async saveSettingsForCallId(attributes, callId){
|
|
80
|
-
|
|
80
|
+
// logger.debug('[VoiceChannel] saveSettingsForCallId: attributes -->', attributes)
|
|
81
81
|
// Always save/overwrite settings
|
|
82
82
|
await this.redis_client.set('tiledesk:voice:'+callId + ':attributes', JSON.stringify(attributes), {'EX': 86400});
|
|
83
83
|
}
|
|
@@ -85,7 +85,7 @@ class VoiceChannel {
|
|
|
85
85
|
|
|
86
86
|
async getSettingsForCallId(callId){
|
|
87
87
|
const attributes = await this.redis_client.get('tiledesk:voice:'+callId + ':attributes');
|
|
88
|
-
|
|
88
|
+
// logger.debug('[VoiceChannel] getSettingsForCallId: attributes found -->', attributes, callId)
|
|
89
89
|
if(attributes){
|
|
90
90
|
return JSON.parse(attributes)
|
|
91
91
|
}
|
|
@@ -46,7 +46,7 @@ class TiledeskSubscriptionClient {
|
|
|
46
46
|
data: subscription_info,
|
|
47
47
|
method: 'POST'
|
|
48
48
|
});
|
|
49
|
-
|
|
49
|
+
// logger.debug("[TiledeskSubscriptionClient] Subscribed");
|
|
50
50
|
return response.data;
|
|
51
51
|
} catch (err) {
|
|
52
52
|
throw err;
|
|
@@ -64,7 +64,7 @@ class TiledeskSubscriptionClient {
|
|
|
64
64
|
},
|
|
65
65
|
method: 'DELETE'
|
|
66
66
|
});
|
|
67
|
-
|
|
67
|
+
// logger.debug("[TiledeskSubscriptionClient] Unsubscribed");
|
|
68
68
|
return response.data;
|
|
69
69
|
} catch (err) {
|
|
70
70
|
throw err;
|
|
@@ -32,7 +32,7 @@ class TiledeskTwilioTranslator {
|
|
|
32
32
|
if (!config.BASE_URL) {
|
|
33
33
|
throw new Error('[TiledeskTwilioTranslator] config.BASE_URL is mandatory');
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
this.BASE_URL = config.BASE_URL;
|
|
37
37
|
this.aiService = config.aiService;
|
|
38
38
|
this.uploadService = config.uploadService;
|
|
@@ -129,7 +129,7 @@ class TiledeskTwilioTranslator {
|
|
|
129
129
|
// Apply provider-specific defaults
|
|
130
130
|
this._applyProviderDefaults(vxmlAttributes, flowAttrs);
|
|
131
131
|
|
|
132
|
-
logger.debug('[TiledeskTwilioTranslator] Processed vxmlAttributes:', vxmlAttributes);
|
|
132
|
+
// logger.debug('[TiledeskTwilioTranslator] Processed vxmlAttributes:', vxmlAttributes);
|
|
133
133
|
voiceEventEmitter.emit('saveSettings', vxmlAttributes);
|
|
134
134
|
|
|
135
135
|
return vxmlAttributes;
|
|
@@ -218,13 +218,15 @@ class TiledeskTwilioTranslator {
|
|
|
218
218
|
|
|
219
219
|
// Handle no input/no match scenarios
|
|
220
220
|
if (noInputNoMatch?.queryNoInput) {
|
|
221
|
-
response.redirect({ method: 'POST' },
|
|
221
|
+
response.redirect({ method: 'POST' },
|
|
222
222
|
`${this.BASE_URL}/handle/${attrs.callSid}/no_input?${noInputNoMatch.queryNoInput}`);
|
|
223
223
|
}
|
|
224
224
|
} else {
|
|
225
225
|
// No barge-in: simple play prompt
|
|
226
226
|
await this._addPromptElements(response, msg, attrs, sessionInfo, false);
|
|
227
|
-
response.
|
|
227
|
+
if (!response.toString().trim().includes('<Play loop="300">')) {
|
|
228
|
+
response.redirect({ method: 'POST' }, `${this.BASE_URL}/nextblock/${attrs.callSid}`);
|
|
229
|
+
}
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
return response.toString();
|
|
@@ -253,7 +255,7 @@ class TiledeskTwilioTranslator {
|
|
|
253
255
|
await this._addPromptElements(gather, msg, attrs, sessionInfo);
|
|
254
256
|
|
|
255
257
|
if (noInputNoMatch?.queryNoInput) {
|
|
256
|
-
response.redirect({ method: 'POST' },
|
|
258
|
+
response.redirect({ method: 'POST' },
|
|
257
259
|
`${this.BASE_URL}/handle/${attrs.callSid}/no_input?${noInputNoMatch.queryNoInput}`);
|
|
258
260
|
}
|
|
259
261
|
} else {
|
|
@@ -302,7 +304,7 @@ class TiledeskTwilioTranslator {
|
|
|
302
304
|
await this._addPromptElements(gather, msg, attrs, sessionInfo);
|
|
303
305
|
|
|
304
306
|
if (noInputNoMatch?.queryNoInput) {
|
|
305
|
-
response.redirect({},
|
|
307
|
+
response.redirect({},
|
|
306
308
|
`${this.BASE_URL}/handle/${attrs.callSid}/no_input?${noInputNoMatch.queryNoInput}`);
|
|
307
309
|
}
|
|
308
310
|
|
|
@@ -329,7 +331,7 @@ class TiledeskTwilioTranslator {
|
|
|
329
331
|
await this._addPromptElements(gather, msg, attrs, sessionInfo);
|
|
330
332
|
|
|
331
333
|
if (noInputNoMatch?.queryNoInput) {
|
|
332
|
-
response.redirect({ method: 'POST' },
|
|
334
|
+
response.redirect({ method: 'POST' },
|
|
333
335
|
`${this.BASE_URL}/handle/${attrs.callSid}/no_input?${noInputNoMatch.queryNoInput}`);
|
|
334
336
|
}
|
|
335
337
|
|
|
@@ -388,10 +390,10 @@ class TiledeskTwilioTranslator {
|
|
|
388
390
|
|
|
389
391
|
async _addMessageElement(element, command, attrs, sessionInfo, bargeIn = false) {
|
|
390
392
|
const { message } = command;
|
|
391
|
-
|
|
393
|
+
|
|
392
394
|
if (message.type === 'text') {
|
|
393
395
|
const text = utils.markdownToTwilioSpeech(message.text);
|
|
394
|
-
|
|
396
|
+
|
|
395
397
|
if (this.voiceProvider !== VOICE_PROVIDER.TWILIO) {
|
|
396
398
|
const audioUrl = await this._generateTTS(text, attrs, sessionInfo);
|
|
397
399
|
if (audioUrl) {
|
|
@@ -411,12 +413,15 @@ class TiledeskTwilioTranslator {
|
|
|
411
413
|
}
|
|
412
414
|
} else if (message.type === 'frame' && message.metadata?.src) {
|
|
413
415
|
|
|
414
|
-
const playOptions = { loop:
|
|
416
|
+
const playOptions = { loop: 1 };
|
|
415
417
|
if (bargeIn) {
|
|
416
418
|
playOptions.bargeIn = true;
|
|
419
|
+
this.lastCallSidVerb[attrs.callSid] = 'play';
|
|
420
|
+
} else {
|
|
421
|
+
playOptions.loop = 300; // Long audio (if the audio last 1 second, it will play for 5 minutes)
|
|
422
|
+
this.lastCallSidVerb[attrs.callSid] = 'play_loop';
|
|
417
423
|
}
|
|
418
424
|
element.play(playOptions, message.metadata.src);
|
|
419
|
-
this.lastCallSidVerb[attrs.callSid] = 'play';
|
|
420
425
|
}
|
|
421
426
|
}
|
|
422
427
|
|
|
@@ -425,13 +430,13 @@ class TiledeskTwilioTranslator {
|
|
|
425
430
|
async _generateTTS(text, attrs, sessionInfo) {
|
|
426
431
|
try {
|
|
427
432
|
const audioData = await this._callTTSProvider(text, attrs, sessionInfo);
|
|
428
|
-
|
|
433
|
+
|
|
429
434
|
if (!audioData) {
|
|
430
435
|
throw new SttError('TTS_FAILED', 'TTS returned no audio data');
|
|
431
436
|
}
|
|
432
437
|
|
|
433
438
|
const fileUrl = await this.uploadService.upload(attrs.callSid, audioData, sessionInfo.user);
|
|
434
|
-
logger.debug('[TiledeskTwilioTranslator] TTS audio URL:', fileUrl);
|
|
439
|
+
// logger.debug('[TiledeskTwilioTranslator] TTS audio URL:', fileUrl);
|
|
435
440
|
return fileUrl;
|
|
436
441
|
} catch (error) {
|
|
437
442
|
logger.error('[TiledeskTwilioTranslator] TTS generation error:', error);
|
|
@@ -479,7 +484,7 @@ class TiledeskTwilioTranslator {
|
|
|
479
484
|
}
|
|
480
485
|
|
|
481
486
|
_getButtonsFromMessage(msg) {
|
|
482
|
-
const command = msg.attributes?.commands?.find(cmd =>
|
|
487
|
+
const command = msg.attributes?.commands?.find(cmd =>
|
|
483
488
|
cmd.type === 'message' && cmd.message?.attributes?.attachment?.buttons
|
|
484
489
|
);
|
|
485
490
|
const buttons = command?.message?.attributes?.attachment?.buttons || [];
|
|
@@ -502,7 +507,7 @@ class TiledeskTwilioTranslator {
|
|
|
502
507
|
|
|
503
508
|
// Legacy method for backwards compatibility
|
|
504
509
|
toTiledesk(vxmlMessage) {
|
|
505
|
-
logger.debug('[TiledeskTwilioTranslator] toTiledesk:', vxmlMessage);
|
|
510
|
+
// logger.debug('[TiledeskTwilioTranslator] toTiledesk:', vxmlMessage);
|
|
506
511
|
}
|
|
507
512
|
}
|
|
508
513
|
|