botium-connector-voip 0.0.22 → 0.0.24
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.
|
@@ -33,6 +33,12 @@ const _info = (event, data) => {
|
|
|
33
33
|
}).filter(([, v]) => v != null && v !== '').map(([k, v]) => `${k}=${typeof v === 'string' && v.length > 80 ? `"${v.substring(0, 77)}..."` : JSON.stringify(v)}`);
|
|
34
34
|
console.info(`[botium-connector-voip] ${parts.join(' ')}`);
|
|
35
35
|
};
|
|
36
|
+
|
|
37
|
+
/** DTMF for RTP/PJSIP: 0–9, *, #, A–D. Strips spaces and other separators so the worker gets one compact string. */
|
|
38
|
+
const sanitizeDtmfDigits = raw => {
|
|
39
|
+
if (raw == null) return '';
|
|
40
|
+
return String(raw).replace(/[^0-9*#ABCDabcd]/g, '').toUpperCase();
|
|
41
|
+
};
|
|
36
42
|
const Capabilities = {
|
|
37
43
|
VOIP_STT_URL_STREAM: 'VOIP_STT_URL_STREAM',
|
|
38
44
|
VOIP_STT_PARAMS_STREAM: 'VOIP_STT_PARAMS_STREAM',
|
|
@@ -230,21 +236,31 @@ class BotiumConnectorVoip {
|
|
|
230
236
|
let httpInitRetries = 0;
|
|
231
237
|
const connectHttp = async retryIndex => {
|
|
232
238
|
retryIndex = retryIndex || 0;
|
|
239
|
+
let initCallUrl = '';
|
|
233
240
|
try {
|
|
234
241
|
const workerUrl = this.caps[Capabilities.VOIP_USE_GLOBAL_VOIP_WORKER] ? process.env.BOTIUM_VOIP_WORKER_URL : this.caps[Capabilities.VOIP_WORKER_URL].replace('wss', 'https').replace('ws', 'http');
|
|
235
242
|
const baseUrl = workerUrl.endsWith('/') ? workerUrl.slice(0, -1) : workerUrl;
|
|
243
|
+
initCallUrl = `${baseUrl}/initCall`;
|
|
244
|
+
const postPayload = {
|
|
245
|
+
API_KEY: this.caps[Capabilities.VOIP_USE_GLOBAL_VOIP_WORKER] ? process.env.BOTIUM_VOIP_WORKER_APIKEY : this.caps[Capabilities.VOIP_WORKER_APIKEY]
|
|
246
|
+
};
|
|
247
|
+
const payloadForLog = {
|
|
248
|
+
...postPayload,
|
|
249
|
+
API_KEY: postPayload.API_KEY != null && postPayload.API_KEY !== '' ? '[REDACTED]' : postPayload.API_KEY
|
|
250
|
+
};
|
|
251
|
+
debug$3(`HTTP initCall: POST ${initCallUrl} payload=${JSON.stringify(payloadForLog)}`);
|
|
236
252
|
const res = await axios__default["default"]({
|
|
237
253
|
method: 'post',
|
|
238
|
-
data:
|
|
239
|
-
|
|
240
|
-
},
|
|
241
|
-
url: `${baseUrl}/initCall`
|
|
254
|
+
data: postPayload,
|
|
255
|
+
url: initCallUrl
|
|
242
256
|
});
|
|
243
257
|
if (res) {
|
|
244
258
|
data = res.data;
|
|
245
259
|
headers = res.headers;
|
|
260
|
+
debug$3(`HTTP initCall: response status=${res.status} data=${this._getAxiosShortenedOutput(res.data)}`);
|
|
246
261
|
}
|
|
247
262
|
} catch (err) {
|
|
263
|
+
debug$3(`HTTP initCall: failed ${initCallUrl} — ${this._getAxiosErrOutput(err)}`);
|
|
248
264
|
debug$3(`Retry ${retryIndex} / ${this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES]}: Connecting to VOIP Worker failed: ${err.message || 'Not reachable'}`);
|
|
249
265
|
if (retryIndex === this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES]) {
|
|
250
266
|
throw new Error(`Connecting to VOIP Worker failed: ${err.message || 'Not reachable'}`);
|
|
@@ -269,7 +285,15 @@ class BotiumConnectorVoip {
|
|
|
269
285
|
const connectWs = retryIndex => {
|
|
270
286
|
retryIndex = retryIndex || 0;
|
|
271
287
|
return new Promise((resolve, reject) => {
|
|
272
|
-
|
|
288
|
+
const useCookie = headers && 'set-cookie' in headers;
|
|
289
|
+
const connectPayload = useCookie ? {
|
|
290
|
+
withCookie: true,
|
|
291
|
+
cookieSize: headers['set-cookie'] && String(headers['set-cookie']).length || 0
|
|
292
|
+
} : {
|
|
293
|
+
withCookie: false
|
|
294
|
+
};
|
|
295
|
+
debug$3(`WebSocket connect: url=${wsEndpoint} attempt=${retryIndex + 1} max=${this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES] + 1} ` + `options=${JSON.stringify(connectPayload)}`);
|
|
296
|
+
if (useCookie) {
|
|
273
297
|
this.ws = new ws__default["default"](wsEndpoint, {
|
|
274
298
|
headers: {
|
|
275
299
|
Cookie: headers['set-cookie']
|
|
@@ -279,9 +303,11 @@ class BotiumConnectorVoip {
|
|
|
279
303
|
this.ws = new ws__default["default"](wsEndpoint);
|
|
280
304
|
}
|
|
281
305
|
this.ws.on('open', () => {
|
|
306
|
+
debug$3(`WebSocket connect: result=open url=${wsEndpoint} attempt=${retryIndex + 1} ` + `readyState=${this.ws && this.ws.readyState}`);
|
|
282
307
|
resolve(retryIndex);
|
|
283
308
|
});
|
|
284
|
-
this.ws.on('error',
|
|
309
|
+
this.ws.on('error', err => {
|
|
310
|
+
debug$3(`WebSocket connect: result=error url=${wsEndpoint} attempt=${retryIndex + 1} ` + `err=${err && err.message || err || 'unknown'}`);
|
|
285
311
|
if (retryIndex === this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES]) {
|
|
286
312
|
reject(new Error(`Websocket connection failed to ${wsEndpoint}`));
|
|
287
313
|
}
|
|
@@ -770,9 +796,15 @@ class BotiumConnectorVoip {
|
|
|
770
796
|
const skipTtsForMixedInput = preferVoice && hasText && hasVoiceMedia;
|
|
771
797
|
debug$3(`UserSays routing: hasText=${hasText} hasVoiceMedia=${hasVoiceMedia} preferVoice=${preferVoice} preferVoiceRaw=${JSON.stringify(preferVoiceCapRaw)} skipTtsForMixedInput=${skipTtsForMixedInput}`);
|
|
772
798
|
if (msg && msg.buttons && msg.buttons.length > 0) {
|
|
799
|
+
const digits = sanitizeDtmfDigits(msg.buttons[0].payload);
|
|
800
|
+
if (!digits) {
|
|
801
|
+
debug$3('sendDtmf skipped: no valid DTMF digits after sanitizing button payload');
|
|
802
|
+
return resolve();
|
|
803
|
+
}
|
|
804
|
+
debug$3(`Sending DTMF digits: ${digits}`);
|
|
773
805
|
const request = JSON.stringify({
|
|
774
806
|
METHOD: 'sendDtmf',
|
|
775
|
-
digits
|
|
807
|
+
digits,
|
|
776
808
|
sessionId: this.sessionId
|
|
777
809
|
});
|
|
778
810
|
this.ws.send(request);
|
|
@@ -780,8 +812,17 @@ class BotiumConnectorVoip {
|
|
|
780
812
|
// Check for DTMF tag in messageText: <DTMF>1234</DTMF>
|
|
781
813
|
const dtmfMatch = msg.messageText.match(/<DTMF>([^<]+)<\/DTMF>/i);
|
|
782
814
|
if (dtmfMatch && dtmfMatch[1]) {
|
|
783
|
-
const
|
|
784
|
-
|
|
815
|
+
const rawDigits = dtmfMatch[1];
|
|
816
|
+
const digits = sanitizeDtmfDigits(rawDigits);
|
|
817
|
+
if (!digits) {
|
|
818
|
+
debug$3(`sendDtmf skipped: no valid DTMF digits after sanitizing <DTMF> content (raw length=${String(rawDigits).length})`);
|
|
819
|
+
return resolve();
|
|
820
|
+
}
|
|
821
|
+
if (digits !== String(rawDigits).replace(/\s/g, '')) {
|
|
822
|
+
debug$3(`Sending DTMF from messageText (sanitized): "${rawDigits}" -> "${digits}"`);
|
|
823
|
+
} else {
|
|
824
|
+
debug$3(`Sending DTMF from messageText: ${digits}`);
|
|
825
|
+
}
|
|
785
826
|
const request = JSON.stringify({
|
|
786
827
|
METHOD: 'sendDtmf',
|
|
787
828
|
digits,
|