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.
- package/dist/botium-connector-voip-cjs.js +77 -148
- package/dist/botium-connector-voip-cjs.js.map +1 -1
- package/dist/botium-connector-voip-es.js +75 -147
- package/dist/botium-connector-voip-es.js.map +1 -1
- package/index.js +3 -1
- package/package.json +3 -2
- package/src/confidence-threshold-logichook.js +13 -0
- package/src/connector.js +34 -25
|
@@ -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$
|
|
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$
|
|
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$
|
|
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$
|
|
99
|
-
debug$
|
|
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"]({
|
|
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$
|
|
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$
|
|
132
|
-
debug$
|
|
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$
|
|
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
|
-
}
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
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
|
|
409
|
-
|
|
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$
|
|
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$
|
|
506
|
-
debug$
|
|
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 = {
|
|
528
|
-
|
|
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
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
'
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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;
|