botium-connector-voip 0.0.15 → 0.0.16

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.
@@ -5,18 +5,20 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var ws = require('ws');
6
6
  var lodash = require('lodash');
7
7
  var axios = require('axios');
8
- var debug$3 = require('debug');
8
+ var debug$4 = require('debug');
9
9
  var path = require('path');
10
+ var musicMetadata = require('music-metadata');
10
11
 
11
12
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
13
 
13
14
  var ws__default = /*#__PURE__*/_interopDefaultLegacy(ws);
14
15
  var lodash__default = /*#__PURE__*/_interopDefaultLegacy(lodash);
15
16
  var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
16
- var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug$3);
17
+ var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug$4);
17
18
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
19
+ var musicMetadata__default = /*#__PURE__*/_interopDefaultLegacy(musicMetadata);
18
20
 
19
- const debug$2 = debug__default["default"]('botium-connector-voip');
21
+ const debug$3 = debug__default["default"]('botium-connector-voip');
20
22
  const Capabilities = {
21
23
  VOIP_STT_URL_STREAM: 'VOIP_STT_URL_STREAM',
22
24
  VOIP_STT_PARAMS_STREAM: 'VOIP_STT_PARAMS_STREAM',
@@ -78,7 +80,6 @@ const Defaults = {
78
80
  VOIP_SILENCE_DURATION_TIMEOUT_START_ENABLE: false,
79
81
  VOIP_STT_CONFIDENCE_THRESHOLD: 0.5
80
82
  };
81
-
82
83
  class BotiumConnectorVoip {
83
84
  constructor({
84
85
  queueBotSays,
@@ -93,11 +94,9 @@ class BotiumConnectorVoip {
93
94
  this.sentencesFinal = 0;
94
95
  this.sentenceBuilding = false;
95
96
  }
96
-
97
97
  async Validate() {
98
- debug$2('Validate called');
99
- debug$2(this.caps.VOIP_STT_MESSAGE_HANDLING);
100
-
98
+ debug$3('Validate called');
99
+ debug$3(this.caps.VOIP_STT_MESSAGE_HANDLING);
101
100
  if (this.caps.VOIP_TTS_URL) {
102
101
  this.axiosTtsParams = {
103
102
  url: this.caps.VOIP_TTS_URL,
@@ -106,16 +105,15 @@ class BotiumConnectorVoip {
106
105
  timeout: this.caps.VOIP_TTS_TIMEOUT,
107
106
  headers: this._getHeaders(Capabilities.VOIP_TTS_HEADERS)
108
107
  };
109
-
110
108
  try {
111
109
  const {
112
110
  data
113
- } = await axios__default["default"]({ ...this.axiosTtsParams,
111
+ } = await axios__default["default"]({
112
+ ...this.axiosTtsParams,
114
113
  url: this._getAxiosUrl(this.caps.VOIP_TTS_URL, '/api/status')
115
114
  });
116
-
117
115
  if (data && data.status === 'OK') {
118
- debug$2(`Checking TTS Status response: ${this._getAxiosShortenedOutput(data)}`);
116
+ debug$3(`Checking TTS Status response: ${this._getAxiosShortenedOutput(data)}`);
119
117
  } else {
120
118
  throw new Error(`Checking TTS Status failed, response is: ${this._getAxiosShortenedOutput(data)}`);
121
119
  }
@@ -123,28 +121,23 @@ class BotiumConnectorVoip {
123
121
  throw new Error(`Checking TTS Status failed - ${this._getAxiosErrOutput(err)}`);
124
122
  }
125
123
  }
126
-
127
124
  this.caps = Object.assign({}, Defaults, this.caps);
128
125
  }
129
-
130
126
  async Start() {
131
- debug$2('Start called');
132
- debug$2(this.caps[Capabilities.VOIP_TTS_URL]);
127
+ debug$3('Start called');
128
+ debug$3(this.caps[Capabilities.VOIP_TTS_URL]);
133
129
  this.stopCalled = false;
134
130
  this.fullRecord = '';
135
131
  this.end = false;
136
132
  this.connected = false;
137
133
  this.convoStep = null;
138
-
139
134
  const sendBotMsg = botMsg => {
140
135
  setTimeout(() => this.queueBotSays(botMsg), 0);
141
136
  };
142
-
143
137
  const joinBotMsg = (botMsgs, joinLastPrevMsg) => {
144
138
  const botMsg = {};
145
- botMsg.messageText = botMsgs.map(m => m.messageText).join(this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING_DELIMITER]);
139
+ botMsg.messageText = botMsgs.map(m => m.messageText).join(this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING_DELIMITER] || '');
146
140
  botMsg.sourceData = botMsgs.map(m => m.sourceData);
147
-
148
141
  if (lodash__default["default"].isNil(joinLastPrevMsg)) {
149
142
  botMsg.sourceData[0].silenceDuration = botMsgs[0].sourceData.data.start;
150
143
  botMsg.sourceData[0].voiceDuration = botMsgs[botMsgs.length - 1].sourceData.data.end - botMsgs[0].sourceData.data.start;
@@ -152,23 +145,17 @@ class BotiumConnectorVoip {
152
145
  botMsg.sourceData[0].silenceDuration = botMsgs[0].sourceData.data.start - joinLastPrevMsg.sourceData.data.end;
153
146
  botMsg.sourceData[0].voiceDuration = botMsgs[botMsgs.length - 1].sourceData.data.end - botMsgs[0].sourceData.data.start;
154
147
  }
155
-
156
148
  return botMsg;
157
149
  };
158
-
159
150
  const splitBotMsgs = botMsgs => {
160
151
  const splitSentences = s => s.match(new RegExp(`[^${this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING_PUNCTUATION]}]+[${this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING_PUNCTUATION]}]+`, 'g'));
161
-
162
152
  const botMsgsFinal = [];
163
-
164
153
  for (const botMsg of botMsgs) {
165
154
  const sentences = splitSentences(botMsg.messageText);
166
-
167
155
  if (lodash__default["default"].isNil(sentences)) {
168
156
  botMsgsFinal.push(botMsg);
169
157
  } else {
170
158
  let botMsgCount = 0;
171
-
172
159
  for (const sentence of sentences) {
173
160
  botMsgsFinal.push({
174
161
  messageText: sentence,
@@ -178,16 +165,12 @@ class BotiumConnectorVoip {
178
165
  }
179
166
  }
180
167
  }
181
-
182
168
  return botMsgsFinal;
183
169
  };
184
-
185
170
  let data = null;
186
171
  let headers = null;
187
-
188
172
  const connect = async retryIndex => {
189
173
  retryIndex = retryIndex || 0;
190
-
191
174
  try {
192
175
  const res = await axios__default["default"]({
193
176
  method: 'post',
@@ -196,29 +179,25 @@ class BotiumConnectorVoip {
196
179
  },
197
180
  url: new URL(path__default["default"].join(`${this.caps[Capabilities.VOIP_WORKER_URL].replace('wss', 'https').replace('ws', 'http')}`, 'initCall')).toString()
198
181
  });
199
-
200
182
  if (res) {
201
183
  data = res.data;
202
184
  headers = res.headers;
203
185
  }
204
186
  } catch (err) {
205
- debug$2(`Retry ${retryIndex} / ${this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES]}: Connecting to VOIP Worker failed: ${err.message || 'Not reachable'}`);
206
-
187
+ debug$3(`Retry ${retryIndex} / ${this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES]}: Connecting to VOIP Worker failed: ${err.message || 'Not reachable'}`);
207
188
  if (retryIndex === this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES]) {
208
189
  throw new Error(`Connecting to VOIP Worker failed: ${err.message || 'Not reachable'}`);
209
- } // Retry after the defined timeout
210
-
211
-
212
- await new Promise(resolve => setTimeout(resolve, this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_TIMEOUT])); // Retry the connection
190
+ }
191
+ // Retry after the defined timeout
192
+ await new Promise(resolve => setTimeout(resolve, this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_TIMEOUT]));
213
193
 
194
+ // Retry the connection
214
195
  await connect(retryIndex + 1);
215
196
  }
216
197
  };
217
-
218
198
  await connect();
219
199
  return new Promise((resolve, reject) => {
220
200
  const wsEndpoint = `${this.caps[Capabilities.VOIP_WORKER_URL]}/ws/${data.port}`;
221
-
222
201
  const connect = retryIndex => {
223
202
  retryIndex = retryIndex || 0;
224
203
  return new Promise((resolve, reject) => {
@@ -231,7 +210,6 @@ class BotiumConnectorVoip {
231
210
  } else {
232
211
  this.ws = new ws__default["default"](wsEndpoint);
233
212
  }
234
-
235
213
  this.ws.on('open', () => {
236
214
  resolve();
237
215
  });
@@ -239,12 +217,10 @@ class BotiumConnectorVoip {
239
217
  if (retryIndex === this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_MAXRETRIES]) {
240
218
  reject(new Error(`Websocket connection failed to ${wsEndpoint}`));
241
219
  }
242
-
243
220
  setTimeout(() => connect(retryIndex + 1).then(resolve).catch(reject), this.caps[Capabilities.VOIP_WEBSOCKET_CONNECT_TIMEOUT]);
244
221
  });
245
222
  });
246
223
  };
247
-
248
224
  connect().then(() => {
249
225
  if (!lodash__default["default"].isArray(this.caps[Capabilities.VOIP_ICE_STUN_SERVERS])) {
250
226
  if (this.caps[Capabilities.VOIP_ICE_STUN_SERVERS] === '') {
@@ -253,7 +229,6 @@ class BotiumConnectorVoip {
253
229
  this.caps[Capabilities.VOIP_ICE_STUN_SERVERS] = this.caps[Capabilities.VOIP_ICE_STUN_SERVERS].split(',');
254
230
  }
255
231
  }
256
-
257
232
  this.eventEmitter.on('CONVO_STEP_NEXT', (container, convoStep) => {
258
233
  this.convoStep = convoStep;
259
234
  });
@@ -263,7 +238,7 @@ class BotiumConnectorVoip {
263
238
  this.firstSttInfoReceived = false;
264
239
  this.silenceTimeout = null;
265
240
  this.wsOpened = true;
266
- debug$2(`Websocket connection to ${wsEndpoint} opened.`);
241
+ debug$3(`Websocket connection to ${wsEndpoint} opened.`);
267
242
  const request = {
268
243
  METHOD: 'initCall',
269
244
  SIP_CALLER_AUTO: this.caps[Capabilities.VOIP_SIP_POOL_CALLER_ENABLE],
@@ -294,11 +269,10 @@ class BotiumConnectorVoip {
294
269
  tts_body: this.caps[Capabilities.VOIP_TTS_BODY] || null
295
270
  }
296
271
  };
297
- debug$2(JSON.stringify(request, null, 2));
272
+ debug$3(JSON.stringify(request, null, 2));
298
273
  this.ws.send(JSON.stringify(request));
299
274
  this.ws.on('error', err => {
300
- debug$2(err);
301
-
275
+ debug$3(err);
302
276
  if (!this.wsOpened) {
303
277
  this.end = true;
304
278
  reject(new Error(`${this.sessionId} - Websocket connection to ${wsEndpoint} error: ${err.message || err}`));
@@ -306,30 +280,25 @@ class BotiumConnectorVoip {
306
280
  });
307
281
  this.ws.on('message', async data => {
308
282
  const parsedData = JSON.parse(data);
309
-
310
283
  const parsedDataLog = lodash__default["default"].cloneDeep(parsedData);
284
+ parsedDataLog.fullRecord = '<full_record_buffer>';
311
285
 
312
- parsedDataLog.fullRecord = '<full_record_buffer>'; // debug(JSON.stringify(parsedDataLog, null, 2))
286
+ // debug(JSON.stringify(parsedDataLog, null, 2))
313
287
 
314
288
  if (parsedData && parsedData.type === 'callinfo' && parsedData.status === 'initialized') {
315
289
  this.sessionId = parsedData.voipConfig.sessionId;
316
290
  }
317
-
318
291
  if (parsedData && parsedData.type === 'callinfo' && parsedData.status === 'unauthorized') {
319
292
  reject(new Error('Error: Cannot open a call: SIP Authorization failed'));
320
293
  }
321
-
322
294
  if (parsedData && parsedData.type === 'callinfo' && parsedData.status === 'forbidden' && parsedData.event === 'onCallRegState') {
323
295
  reject(new Error('Error: Sip Registration failed'));
324
296
  }
325
-
326
297
  if (parsedData && parsedData.type === 'callinfo' && parsedData.status === 'connected') {
327
298
  resolve();
328
299
  }
329
-
330
300
  if (parsedData && parsedData.type === 'callinfo' && parsedData.status === 'disconnected') {
331
301
  const apiKey = this._extractApiKey(this._getBody(Capabilities.VOIP_STT_BODY));
332
-
333
302
  if (parsedData.connectDuration && parsedData.connectDuration > 0) {
334
303
  this.eventEmitter.emit('CONSUMPTION_METADATA', this, {
335
304
  type: lodash__default["default"].isNil(apiKey) ? 'INBUILT' : 'THIRD_PARTY',
@@ -340,13 +309,11 @@ class BotiumConnectorVoip {
340
309
  });
341
310
  }
342
311
  }
343
-
344
312
  if (parsedData && parsedData.type === 'error') {
345
313
  this.end = true;
346
314
  reject(new Error(`Error: ${parsedData.message}`));
347
315
  sendBotMsg(new Error(`Error: ${parsedData.message}`));
348
316
  }
349
-
350
317
  if (parsedData && parsedData.type === 'silence') {
351
318
  if (lodash__default["default"].isNil(this._getIgnoreSilenceDurationAsserterLogicHook(this.convoStep))) {
352
319
  if (lodash__default["default"].isNil(this._getJoinLogicHook(this.convoStep)) && parsedData.data.silence.length > 0) {
@@ -355,7 +322,6 @@ class BotiumConnectorVoip {
355
322
  }
356
323
  }
357
324
  }
358
-
359
325
  if (parsedData && parsedData.type === 'fullRecord') {
360
326
  this.eventEmitter.emit('MESSAGE_ATTACHMENT', this.container, {
361
327
  name: 'full_record.wav',
@@ -364,36 +330,28 @@ class BotiumConnectorVoip {
364
330
  });
365
331
  this.end = true;
366
332
  }
367
-
368
333
  if (parsedData && parsedData.data && parsedData.data.final === false) {
369
334
  if (this.silenceTimeout) {
370
335
  clearTimeout(this.silenceTimeout);
371
336
  }
372
-
373
337
  if (lodash__default["default"].isNil(this._getIgnoreSilenceDurationAsserterLogicHook(this.convoStep))) {
374
338
  if (!this.firstSttInfoReceived && lodash__default["default"].isNil(this._getJoinLogicHook(this.convoStep)) && this.caps[Capabilities.VOIP_SILENCE_DURATION_TIMEOUT_START_ENABLE] && parsedData.data.start && parsedData.data.start * 1000 > this.caps[Capabilities.VOIP_SILENCE_DURATION_TIMEOUT_START]) {
375
339
  this.end = true;
376
340
  sendBotMsg(new Error(`Silence Duration of ${parsedData.data.start}s exceeded Initial Silence Duration Timeout of ${this.caps[Capabilities.VOIP_SILENCE_DURATION_TIMEOUT_START] / 1000}s`));
377
341
  }
378
342
  }
379
-
380
343
  this.firstSttInfoReceived = true;
381
-
382
344
  if (this.prevData) {
383
345
  if (!lodash__default["default"].isNil(parsedData.data.start)) {
384
346
  const silenceDuration = parsedData.data.start - this.prevData.data.end;
385
-
386
347
  const joinLogicHook = this._getJoinLogicHook(this.convoStep);
387
-
388
348
  const isJoinMethod = this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING] === 'JOIN';
389
349
  let matched = false;
390
-
391
350
  if ((!lodash__default["default"].isNil(joinLogicHook) && isJoinMethod && silenceDuration > parseInt(joinLogicHook.args[0])) / 1000) {
392
351
  matched = true;
393
352
  } else if (lodash__default["default"].isNil(joinLogicHook) && isJoinMethod && silenceDuration > parseInt(this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING_TIMEOUT]) / 1000) {
394
353
  matched = true;
395
354
  }
396
-
397
355
  if (matched && this.botMsgs.length > 0) {
398
356
  sendBotMsg(joinBotMsg(this.botMsgs, this.joinLastPrevMsg));
399
357
  this.firstMsg = false;
@@ -403,16 +361,14 @@ class BotiumConnectorVoip {
403
361
  }
404
362
  }
405
363
  }
406
-
407
364
  if (parsedData && parsedData.data && parsedData.data.type === 'stt' && parsedData.data.final) {
408
- const successfulConfidenceScore = this._getConfidenceScore(parsedData) >= this.caps[Capabilities.VOIP_STT_CONFIDENCE_THRESHOLD];
409
- debug$2(`Message: ${parsedData.data.message} / Confidence Score: ${this._getConfidenceScore(parsedData)} (Threshold: ${this.caps[Capabilities.VOIP_STT_CONFIDENCE_THRESHOLD]})`);
410
-
365
+ const confidenceThreshold = this._getConfidenceScoreLogicHook(this.convoStep) && this._getConfidenceScoreLogicHook(this.convoStep).args[0] || this.caps[Capabilities.VOIP_STT_CONFIDENCE_THRESHOLD];
366
+ const successfulConfidenceScore = this._getConfidenceScore(parsedData) >= confidenceThreshold;
367
+ debug$3(`Message: ${parsedData.data.message} / Confidence Score: ${this._getConfidenceScore(parsedData)} (Threshold: ${confidenceThreshold})`);
411
368
  if (this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING] === 'ORIGINAL' && lodash__default["default"].isNil(this._getJoinLogicHook(this.convoStep))) {
412
369
  let botMsg = {
413
370
  messageText: parsedData.data.message
414
371
  };
415
-
416
372
  if (this.firstMsg) {
417
373
  const sourceData = parsedData;
418
374
  sourceData.silenceDuration = parsedData.data.start;
@@ -429,22 +385,17 @@ class BotiumConnectorVoip {
429
385
  sourceData
430
386
  });
431
387
  }
432
-
433
388
  this.prevData = parsedData;
434
-
435
389
  if (successfulConfidenceScore) {
436
390
  this.botMsgs.push(botMsg);
437
391
  this.botMsgs.forEach(botMsg => sendBotMsg(botMsg));
438
392
  }
439
-
440
393
  this.botMsgs = [];
441
394
  }
442
-
443
395
  if (this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING] === 'SPLIT' || this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING] === 'EXPAND') {
444
396
  let botMsg = {
445
397
  messageText: parsedData.data.message
446
398
  };
447
-
448
399
  if (this.firstMsg) {
449
400
  const sourceData = parsedData;
450
401
  sourceData.silenceDuration = parsedData.data.start;
@@ -461,30 +412,25 @@ class BotiumConnectorVoip {
461
412
  sourceData
462
413
  });
463
414
  }
464
-
465
415
  this.prevData = parsedData;
466
-
467
416
  if (successfulConfidenceScore) {
468
417
  this.botMsgs.push(botMsg);
469
418
  const botMsgsExpanded = splitBotMsgs(this.botMsgs);
470
419
  botMsgsExpanded.forEach(botMsg => sendBotMsg(botMsg));
471
420
  }
472
-
473
421
  this.botMsgs = [];
474
422
  }
475
-
476
423
  if (this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING] === 'JOIN' || this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING] === 'CONCAT' || !lodash__default["default"].isNil(this._getJoinLogicHook(this.convoStep))) {
477
424
  const botMsg = {
478
425
  messageText: parsedData.data.message,
479
426
  sourceData: parsedData
480
427
  };
481
428
  this.prevData = parsedData;
482
-
483
429
  if (successfulConfidenceScore) {
484
430
  this.botMsgs.push(botMsg);
485
431
  this.silenceTimeout = setTimeout(() => {
486
432
  if (this.botMsgs.length > 0) {
487
- debug$2(this._getJoinLogicHook(this.convoStep) && parseInt(this._getJoinLogicHook(this.convoStep).args[0]) || this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING_TIMEOUT]);
433
+ debug$3('Silence Duration Timeout (PSST):', this._getJoinLogicHook(this.convoStep) && parseInt(this._getJoinLogicHook(this.convoStep).args[0]) || this.caps[Capabilities.VOIP_STT_MESSAGE_HANDLING_TIMEOUT], 'ms');
488
434
  sendBotMsg(joinBotMsg(this.botMsgs, this.joinLastPrevMsg));
489
435
  this.firstMsg = false;
490
436
  this.joinLastPrevMsg = this.botMsgs[this.botMsgs.length - 1];
@@ -500,19 +446,15 @@ class BotiumConnectorVoip {
500
446
  });
501
447
  });
502
448
  }
503
-
504
449
  async UserSays(msg) {
505
- debug$2('UserSays called');
506
- debug$2(msg);
507
-
450
+ debug$3('UserSays called');
451
+ debug$3(msg);
508
452
  if (!msg.attachments) {
509
453
  msg.attachments = [];
510
454
  }
511
-
512
455
  return new Promise((resolve, reject) => {
513
456
  setTimeout(async () => {
514
457
  let duration = 0;
515
-
516
458
  if (msg && msg.buttons && msg.buttons.length > 0) {
517
459
  const request = JSON.stringify({
518
460
  METHOD: 'sendDtmf',
@@ -522,10 +464,11 @@ class BotiumConnectorVoip {
522
464
  this.ws.send(request);
523
465
  } else if (msg && msg.messageText) {
524
466
  if (!this.axiosTtsParams) reject(new Error('TTS not configured, only audio input supported'));
525
-
526
467
  if (this.axiosTtsParams) {
527
- const ttsRequest = { ...this.axiosTtsParams,
528
- params: { ...(this.axiosTtsParams.params || {}),
468
+ const ttsRequest = {
469
+ ...this.axiosTtsParams,
470
+ params: {
471
+ ...(this.axiosTtsParams.params || {}),
529
472
  text: msg.messageText
530
473
  },
531
474
  data: this._getBody(Capabilities.VOIP_TTS_BODY),
@@ -533,16 +476,13 @@ class BotiumConnectorVoip {
533
476
  };
534
477
  msg.sourceData = ttsRequest;
535
478
  let ttsResponse = null;
536
-
537
479
  try {
538
480
  ttsResponse = await axios__default["default"](ttsRequest);
539
481
  } catch (err) {
540
482
  reject(new Error(`TTS "${msg.messageText}" failed - ${this._getAxiosErrOutput(err)}`));
541
483
  }
542
-
543
484
  if (msg && msg.messageText && msg.messageText.length > 0) {
544
485
  const apiKey = this._extractApiKey(this._getBody(Capabilities.VOIP_TTS_BODY));
545
-
546
486
  this.eventEmitter.emit('CONSUMPTION_METADATA', this.container, {
547
487
  type: lodash__default["default"].isNil(apiKey) ? 'INBUILT' : 'THIRD_PARTY',
548
488
  metricName: 'consumption.e2e.voip.tts.characters',
@@ -550,7 +490,6 @@ class BotiumConnectorVoip {
550
490
  apiKey
551
491
  });
552
492
  }
553
-
554
493
  if (Buffer.isBuffer(ttsResponse.data)) {
555
494
  duration = ttsResponse.headers['content-duration'];
556
495
  const request = JSON.stringify({
@@ -570,7 +509,6 @@ class BotiumConnectorVoip {
570
509
  }
571
510
  }
572
511
  }
573
-
574
512
  if (msg && msg.media && msg.media.length > 0 && msg.media[0].buffer) {
575
513
  const request = JSON.stringify({
576
514
  METHOD: 'sendAudio',
@@ -583,34 +521,24 @@ class BotiumConnectorVoip {
583
521
  mimeType: msg.media[0].mimeType,
584
522
  base64: msg.media[0].buffer.toString('base64')
585
523
  });
586
- const {
587
- data
588
- } = await axios__default["default"]({
589
- method: 'post',
590
- data: msg.media[0].buffer,
591
- headers: { ...this._getHeaders(Capabilities.VOIP_TTS_HEADERS),
592
- 'Content-Type': 'audio/wave'
593
- },
594
- url: this._getAxiosUrl(this.caps.VOIP_TTS_URL, '/api/audio/info'),
595
- maxContentLength: Infinity,
596
- maxBodyLength: Infinity
597
- });
598
-
599
- if (data && data.duration) {
600
- duration = parseInt(data.duration);
601
- } else {
602
- reject(new Error('Getting audio duration from Speech Api failed'));
524
+ try {
525
+ const metadata = await musicMetadata__default["default"].parseBuffer(msg.media[0].buffer);
526
+ if (metadata && metadata.format && metadata.format.duration) {
527
+ debug$3('Audio duration of user audio:', metadata.format.duration, 'seconds');
528
+ duration = Math.round(metadata.format.duration);
529
+ } else {
530
+ reject(new Error('Could not determine audio duration from metadata'));
531
+ }
532
+ } catch (err) {
533
+ reject(new Error(`Getting audio duration failed: ${err.message}`));
603
534
  }
604
535
  }
605
-
606
536
  setTimeout(resolve, duration * 1000);
607
537
  }, 0);
608
538
  });
609
539
  }
610
-
611
540
  async Stop() {
612
- debug$2(`${this.sessionId} - Stop called`);
613
-
541
+ debug$3(`${this.sessionId} - Stop called`);
614
542
  if (this.ws && this.ws.readyState !== ws__default["default"].CLOSED) {
615
543
  const request = JSON.stringify({
616
544
  METHOD: 'stopCall',
@@ -638,35 +566,27 @@ class BotiumConnectorVoip {
638
566
  this.firstSttInfoReceived = false;
639
567
  }
640
568
  }
641
-
642
569
  _getParams(capParams) {
643
570
  if (this.caps[capParams]) {
644
571
  if (lodash__default["default"].isString(this.caps[capParams])) return JSON.parse(this.caps[capParams]);else return this.caps[capParams];
645
572
  }
646
-
647
573
  return {};
648
574
  }
649
-
650
575
  _getBody(capBody) {
651
576
  if (this.caps[capBody]) {
652
577
  if (lodash__default["default"].isString(this.caps[capBody])) return JSON.parse(this.caps[capBody]);else return this.caps[capBody];
653
578
  }
654
-
655
579
  return null;
656
580
  }
657
-
658
581
  _getHeaders(capHeaders) {
659
582
  if (this.caps[capHeaders]) {
660
583
  if (lodash__default["default"].isString(this.caps[capHeaders])) return JSON.parse(this.caps[capHeaders]);else return this.caps[capHeaders];
661
584
  }
662
-
663
585
  return {};
664
586
  }
665
-
666
587
  _getAxiosUrl(baseUrl, extUrl) {
667
588
  return baseUrl.substr(0, baseUrl.indexOf('/', 8)) + extUrl;
668
589
  }
669
-
670
590
  _getAxiosShortenedOutput(data) {
671
591
  if (data) {
672
592
  if (lodash__default["default"].isBuffer(data)) {
@@ -674,7 +594,6 @@ class BotiumConnectorVoip {
674
594
  data = data.toString();
675
595
  } catch (err) {}
676
596
  }
677
-
678
597
  return lodash__default["default"].truncate(lodash__default["default"].isString(data) ? data : JSON.stringify(data), {
679
598
  length: 200
680
599
  });
@@ -682,7 +601,6 @@ class BotiumConnectorVoip {
682
601
  return '';
683
602
  }
684
603
  }
685
-
686
604
  _getAxiosErrOutput(err) {
687
605
  if (err && err.response) {
688
606
  return `Status: ${err.response.status} / Response: ${this._getAxiosShortenedOutput(err.response.data)}`;
@@ -690,66 +608,71 @@ class BotiumConnectorVoip {
690
608
  return err.message;
691
609
  }
692
610
  }
693
-
694
611
  _extractApiKey(body) {
695
612
  return lodash__default["default"].get(body, 'polly.credentials.accessKeyId') || lodash__default["default"].get(body, 'google.credentials.client_email') || lodash__default["default"].get(body, 'ibm.credentials.apikey') || lodash__default["default"].get(body, 'awstranscribe.credentials.accessKeyId') || lodash__default["default"].get(body, 'azure.credentials.subscriptionKey') || null;
696
613
  }
697
-
698
614
  _getJoinLogicHook(convoStep) {
699
615
  if (lodash__default["default"].isNil(convoStep)) return null;
700
616
  if (lodash__default["default"].isNil(convoStep.logicHooks)) return null;
701
617
  return convoStep && convoStep.logicHooks && convoStep.logicHooks.find(lh => lh.name === 'VOIP_JOIN_SILENCE_DURATION');
702
618
  }
703
-
704
619
  _getIgnoreSilenceDurationAsserterLogicHook(convoStep) {
705
620
  if (lodash__default["default"].isNil(convoStep)) return null;
706
621
  if (lodash__default["default"].isNil(convoStep.logicHooks)) return null;
707
622
  return convoStep && convoStep.logicHooks && convoStep.logicHooks.find(lh => lh.name === 'VOIP_IGNORE_SILENCE_DURATION');
708
623
  }
709
-
624
+ _getConfidenceScoreLogicHook(convoStep) {
625
+ if (lodash__default["default"].isNil(convoStep)) return null;
626
+ if (lodash__default["default"].isNil(convoStep.logicHooks)) return null;
627
+ return convoStep && convoStep.logicHooks && convoStep.logicHooks.find(lh => lh.name === 'VOIP_CONFIDENCE_THRESHOLD');
628
+ }
710
629
  _getConfidenceScore(parsedData) {
711
630
  // Azure Speech Service
712
631
  if (parsedData?.data?.source?.debug?.privJson) {
713
632
  const privJson = JSON.parse(parsedData?.data?.source?.debug?.privJson);
714
-
715
633
  if (privJson && privJson.NBest && privJson.NBest.length > 0) {
716
634
  return Math.max(...privJson.NBest.map(n => n.Confidence));
717
635
  }
718
636
  }
719
-
720
637
  return null;
721
638
  }
722
-
723
639
  }
724
-
725
640
  var connector = BotiumConnectorVoip;
726
641
 
727
- const debug$1 = debug__default["default"]('botium-voip-logichook-ignore-silence-duration');
642
+ const debug$2 = debug__default["default"]('botium-voip-logichook-ignore-silence-duration');
728
643
  var ignoreSdLogichook = class IgnoreSilenceDurationLogicHook {
729
644
  constructor(context, caps = {}, args = {}) {
730
645
  this.context = context;
731
646
  this.caps = caps;
732
647
  this.globalArgs = args;
733
648
  }
734
-
735
649
  async onBotPrepare(props) {
736
- debug$1('Silence duration assertion ignored by logic hook');
650
+ debug$2('Silence duration assertion ignored by logic hook');
737
651
  }
738
-
739
652
  };
740
653
 
741
- const debug = debug__default["default"]('botium-voip-logichook-join-silence-duration');
654
+ const debug$1 = debug__default["default"]('botium-voip-logichook-join-silence-duration');
742
655
  var joinLogichook = class JoinLogicHook {
743
656
  constructor(context, caps = {}, args = {}) {
744
657
  this.context = context;
745
658
  this.caps = caps;
746
659
  this.globalArgs = args;
747
660
  }
748
-
749
661
  async onBotPrepare(props) {
750
- debug('Joined bot messages because of JOIN logic hook');
662
+ debug$1('Joined bot messages because of JOIN logic hook');
751
663
  }
664
+ };
752
665
 
666
+ const debug = debug__default["default"]('botium-voip-logichook-confidence-threshold');
667
+ var confidenceThresholdLogichook = class ConfidenceThresholdLogicHook {
668
+ constructor(context, caps = {}, args = {}) {
669
+ this.context = context;
670
+ this.caps = caps;
671
+ this.globalArgs = args;
672
+ }
673
+ async onBotPrepare(props) {
674
+ debug('Updated confidence threshold because of ConfidenceThreshold LogicHook');
675
+ }
753
676
  };
754
677
 
755
678
  var botiumConnectorVoip = {
@@ -776,7 +699,8 @@ var botiumConnectorVoip = {
776
699
  },
777
700
  PluginLogicHooks: {
778
701
  VOIP_IGNORE_SILENCE_DURATION: ignoreSdLogichook,
779
- VOIP_JOIN_SILENCE_DURATION: joinLogichook
702
+ VOIP_JOIN_SILENCE_DURATION: joinLogichook,
703
+ VOIP_CONFIDENCE_THRESHOLD: confidenceThresholdLogichook
780
704
  }
781
705
  };
782
706
  var botiumConnectorVoip_1 = botiumConnectorVoip.PluginVersion;