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
- API_KEY: this.caps[Capabilities.VOIP_USE_GLOBAL_VOIP_WORKER] ? process.env.BOTIUM_VOIP_WORKER_APIKEY : this.caps[Capabilities.VOIP_WORKER_APIKEY]
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
- if ('set-cookie' in headers) {
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: msg.buttons[0].payload,
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 digits = dtmfMatch[1];
784
- debug$3(`Sending DTMF from messageText: ${digits}`);
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,