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