botium-connector-voip 0.0.15 → 0.0.17

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