@webex/internal-plugin-voicea 2.23.0
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/README.md +90 -0
- package/dist/constants.js +40 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/utils.js +19 -0
- package/dist/utils.js.map +1 -0
- package/dist/voicea.js +376 -0
- package/dist/voicea.js.map +1 -0
- package/dist/voicea.types.js +8 -0
- package/dist/voicea.types.js.map +1 -0
- package/package.json +23 -0
- package/src/constants.ts +31 -0
- package/src/index.js +6 -0
- package/src/utils.ts +8 -0
- package/src/voicea.ts +306 -0
- package/src/voicea.types.ts +81 -0
- package/test/unit/spec/utils.js +12 -0
- package/test/unit/spec/voicea.js +580 -0
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
import MockWebex from '@webex/test-helper-mock-webex';
|
|
2
|
+
import MockWebSocket from '@webex/test-helper-mock-web-socket';
|
|
3
|
+
import {assert} from '@webex/test-helper-chai';
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import Mercury from '@webex/internal-plugin-mercury';
|
|
6
|
+
import LLMChannel from '@webex/internal-plugin-llm';
|
|
7
|
+
import {EVENT_TRIGGERS} from '@webex/internal-plugin-voicea/src/constants';
|
|
8
|
+
import VoiceaService from '@webex/internal-plugin-voicea';
|
|
9
|
+
|
|
10
|
+
describe('plugin-voicea', () => {
|
|
11
|
+
const locusUrl = 'locusUrl';
|
|
12
|
+
const datachannelUrl = 'datachannelUrl';
|
|
13
|
+
|
|
14
|
+
describe('voicea', () => {
|
|
15
|
+
let webex, voiceaService;
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
webex = new MockWebex({
|
|
20
|
+
children: {
|
|
21
|
+
mercury: Mercury,
|
|
22
|
+
llm: LLMChannel,
|
|
23
|
+
voicea: VoiceaService
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
voiceaService = webex.internal.voicea;
|
|
28
|
+
voiceaService.connect = sinon.stub().resolves(true);
|
|
29
|
+
voiceaService.request = sinon.stub().resolves({
|
|
30
|
+
headers: {},
|
|
31
|
+
body: ''
|
|
32
|
+
});
|
|
33
|
+
voiceaService.register = sinon.stub().resolves({
|
|
34
|
+
body: {
|
|
35
|
+
binding: 'binding',
|
|
36
|
+
webSocketUrl: 'url'
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('#sendAnnouncement', () => {
|
|
42
|
+
beforeEach(async () => {
|
|
43
|
+
await voiceaService.registerAndConnect(locusUrl, datachannelUrl);
|
|
44
|
+
const mockWebSocket = new MockWebSocket();
|
|
45
|
+
|
|
46
|
+
voiceaService.socket = mockWebSocket;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("sends announcement if voicea hasn't joined", () => {
|
|
50
|
+
voiceaService.sendAnnouncement();
|
|
51
|
+
assert.calledOnceWithExactly(voiceaService.socket.send, {
|
|
52
|
+
id: '1',
|
|
53
|
+
type: 'publishRequest',
|
|
54
|
+
recipients: {route: undefined},
|
|
55
|
+
headers: {},
|
|
56
|
+
data: {
|
|
57
|
+
clientPayload: {
|
|
58
|
+
version: 'v2',
|
|
59
|
+
},
|
|
60
|
+
eventType: 'relay.event',
|
|
61
|
+
relayType: 'client.annc',
|
|
62
|
+
},
|
|
63
|
+
trackingId: sinon.match.string
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('#processAnnouncementMessage', () => {
|
|
69
|
+
it('works on non-empty payload', async () => {
|
|
70
|
+
const voiceaPayload = {
|
|
71
|
+
translation: {
|
|
72
|
+
allowed_languages: ['af', 'am'],
|
|
73
|
+
max_languages: 5,
|
|
74
|
+
},
|
|
75
|
+
ASR: {
|
|
76
|
+
spoken_languages: ['en']
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
version: 'v2'
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const spy = sinon.spy();
|
|
83
|
+
|
|
84
|
+
voiceaService.on(EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT, spy);
|
|
85
|
+
voiceaService.processAnnouncementMessage(voiceaPayload);
|
|
86
|
+
assert.calledOnceWithExactly(spy, {
|
|
87
|
+
captionLanguages: ['af', 'am'],
|
|
88
|
+
spokenLanguages: ['en'],
|
|
89
|
+
maxLanguages: 5
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('works on non-empty payload', async () => {
|
|
94
|
+
const spy = sinon.spy();
|
|
95
|
+
|
|
96
|
+
voiceaService.on(EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT, spy);
|
|
97
|
+
await voiceaService.processAnnouncementMessage({});
|
|
98
|
+
assert.calledOnceWithExactly(spy, {
|
|
99
|
+
captionLanguages: [],
|
|
100
|
+
spokenLanguages: [],
|
|
101
|
+
maxLanguages: 0
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('#requestLanguage', () => {
|
|
107
|
+
beforeEach(async () => {
|
|
108
|
+
await voiceaService.registerAndConnect(locusUrl, datachannelUrl);
|
|
109
|
+
const mockWebSocket = new MockWebSocket();
|
|
110
|
+
|
|
111
|
+
voiceaService.socket = mockWebSocket;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('requests caption language', () => {
|
|
115
|
+
voiceaService.requestLanguage('en');
|
|
116
|
+
|
|
117
|
+
assert.calledOnceWithExactly(voiceaService.socket.send, {
|
|
118
|
+
id: '1',
|
|
119
|
+
type: 'publishRequest',
|
|
120
|
+
recipients: {route: undefined},
|
|
121
|
+
headers: {to: undefined},
|
|
122
|
+
data: {
|
|
123
|
+
clientPayload: {
|
|
124
|
+
translationLanguage: 'en',
|
|
125
|
+
id: sinon.match.string
|
|
126
|
+
},
|
|
127
|
+
eventType: 'relay.event',
|
|
128
|
+
relayType: 'voicea.transl.req'
|
|
129
|
+
},
|
|
130
|
+
trackingId: sinon.match.string
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('#setSpokenLanguage', () => {
|
|
136
|
+
beforeEach(async () => {
|
|
137
|
+
await voiceaService.registerAndConnect(locusUrl, datachannelUrl);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('sets spoken language', async () => {
|
|
141
|
+
const languageCode = 'en';
|
|
142
|
+
const triggerSpy = sinon.spy();
|
|
143
|
+
|
|
144
|
+
voiceaService.on(EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE, triggerSpy);
|
|
145
|
+
await voiceaService.setSpokenLanguage(languageCode);
|
|
146
|
+
|
|
147
|
+
assert.calledOnceWithExactly(triggerSpy, {languageCode});
|
|
148
|
+
|
|
149
|
+
sinon.assert.calledWith(voiceaService.request, sinon.match({
|
|
150
|
+
method: 'PUT',
|
|
151
|
+
url: `${locusUrl}/controls/`,
|
|
152
|
+
body: {languageCode}
|
|
153
|
+
}));
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('#turnOnCaptions', () => {
|
|
158
|
+
beforeEach(async () => {
|
|
159
|
+
await voiceaService.registerAndConnect(locusUrl, datachannelUrl);
|
|
160
|
+
|
|
161
|
+
const mockWebSocket = new MockWebSocket();
|
|
162
|
+
|
|
163
|
+
voiceaService.socket = mockWebSocket;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('turns on captions', async () => {
|
|
167
|
+
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
168
|
+
|
|
169
|
+
const triggerSpy = sinon.spy();
|
|
170
|
+
|
|
171
|
+
voiceaService.on(EVENT_TRIGGERS.CAPTIONS_TURNED_ON, triggerSpy);
|
|
172
|
+
await voiceaService.turnOnCaptions();
|
|
173
|
+
sinon.assert.calledWith(voiceaService.request, sinon.match({
|
|
174
|
+
method: 'PUT',
|
|
175
|
+
url: `${locusUrl}/controls/`,
|
|
176
|
+
body: {transcribe: {caption: true}}
|
|
177
|
+
}));
|
|
178
|
+
|
|
179
|
+
assert.calledOnceWithExactly(triggerSpy, undefined);
|
|
180
|
+
assert.calledOnce(announcementSpy);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('doesn\'t call API on captions', async () => {
|
|
184
|
+
await voiceaService.turnOnCaptions();
|
|
185
|
+
|
|
186
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
187
|
+
voiceaService._emit('event:relay.event', {headers: {from: 'ws'}, voiceaPayload: {}, data: {relayType: 'voicea.annc'}});
|
|
188
|
+
|
|
189
|
+
const response = await voiceaService.turnOnCaptions();
|
|
190
|
+
|
|
191
|
+
assert.equal(response, undefined);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
describe('#toggleTranscribing', () => {
|
|
196
|
+
beforeEach(async () => {
|
|
197
|
+
await voiceaService.registerAndConnect(locusUrl, datachannelUrl);
|
|
198
|
+
|
|
199
|
+
const mockWebSocket = new MockWebSocket();
|
|
200
|
+
|
|
201
|
+
voiceaService.socket = mockWebSocket;
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('turns on transcribing with CC enabled', async () => {
|
|
205
|
+
// Turn on captions
|
|
206
|
+
await voiceaService.turnOnCaptions();
|
|
207
|
+
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
208
|
+
|
|
209
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
210
|
+
voiceaService._emit('event:relay.event', {headers: {from: 'ws'}, voiceaPayload: {}, data: {relayType: 'voicea.annc'}});
|
|
211
|
+
|
|
212
|
+
const triggerSpy = sinon.spy();
|
|
213
|
+
|
|
214
|
+
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_ON, triggerSpy);
|
|
215
|
+
await voiceaService.toggleTranscribing(true);
|
|
216
|
+
sinon.assert.calledWith(voiceaService.request, sinon.match({
|
|
217
|
+
method: 'PUT',
|
|
218
|
+
url: `${locusUrl}/controls/`,
|
|
219
|
+
body: {transcribe: {transcribing: true}}
|
|
220
|
+
}));
|
|
221
|
+
|
|
222
|
+
assert.calledOnce(triggerSpy);
|
|
223
|
+
assert.notCalled(announcementSpy);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('turns on transcribing with CC disabled', async () => {
|
|
227
|
+
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
228
|
+
|
|
229
|
+
const triggerSpy = sinon.spy();
|
|
230
|
+
|
|
231
|
+
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_ON, triggerSpy);
|
|
232
|
+
await voiceaService.toggleTranscribing(true);
|
|
233
|
+
sinon.assert.calledWith(voiceaService.request, sinon.match({
|
|
234
|
+
method: 'PUT',
|
|
235
|
+
url: `${locusUrl}/controls/`,
|
|
236
|
+
body: {transcribe: {transcribing: true}}
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
assert.calledOnce(triggerSpy);
|
|
240
|
+
assert.calledOnce(announcementSpy);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('turns off transcribing', async () => {
|
|
244
|
+
await voiceaService.toggleTranscribing(true);
|
|
245
|
+
|
|
246
|
+
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
247
|
+
|
|
248
|
+
const triggerSpy = sinon.spy();
|
|
249
|
+
|
|
250
|
+
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_OFF, triggerSpy);
|
|
251
|
+
await voiceaService.toggleTranscribing(false);
|
|
252
|
+
sinon.assert.calledWith(voiceaService.request, sinon.match({
|
|
253
|
+
method: 'PUT',
|
|
254
|
+
url: `${locusUrl}/controls/`,
|
|
255
|
+
body: {transcribe: {transcribing: true}}
|
|
256
|
+
}));
|
|
257
|
+
|
|
258
|
+
assert.calledOnce(triggerSpy);
|
|
259
|
+
assert.notCalled(announcementSpy);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('doesn\'t call API on same value', async () => {
|
|
263
|
+
await voiceaService.toggleTranscribing(true);
|
|
264
|
+
const triggerSpy = sinon.spy();
|
|
265
|
+
const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
voiceaService.on(EVENT_TRIGGERS.TRANSCRIBING_OFF, triggerSpy);
|
|
269
|
+
|
|
270
|
+
await voiceaService.toggleTranscribing(true);
|
|
271
|
+
|
|
272
|
+
assert.notCalled(triggerSpy);
|
|
273
|
+
assert.notCalled(announcementSpy);
|
|
274
|
+
|
|
275
|
+
sinon.assert.calledTwice(voiceaService.request);
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('#processCaptionLanguageResponse', () => {
|
|
280
|
+
it('responds to process caption language', async () => {
|
|
281
|
+
const triggerSpy = sinon.spy();
|
|
282
|
+
const functionSpy = sinon.spy(voiceaService, 'processCaptionLanguageResponse');
|
|
283
|
+
|
|
284
|
+
voiceaService.on(EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE, triggerSpy);
|
|
285
|
+
|
|
286
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
287
|
+
voiceaService._emit('event:relay.event', {
|
|
288
|
+
headers: {from: 'ws'},
|
|
289
|
+
voiceaPayload: {
|
|
290
|
+
statusCode: 200
|
|
291
|
+
},
|
|
292
|
+
data: {relayType: 'voicea.transl.rsp'}
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
assert.calledOnceWithExactly(triggerSpy, {statusCode: 200});
|
|
296
|
+
assert.calledOnce(functionSpy);
|
|
297
|
+
});
|
|
298
|
+
it('responds to process caption language for a failed response', async () => {
|
|
299
|
+
const triggerSpy = sinon.spy();
|
|
300
|
+
const functionSpy = sinon.spy(voiceaService, 'processCaptionLanguageResponse');
|
|
301
|
+
|
|
302
|
+
voiceaService.on(EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE, triggerSpy);
|
|
303
|
+
|
|
304
|
+
const payload = {
|
|
305
|
+
errorCode: 300,
|
|
306
|
+
message: 'error text'
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
310
|
+
voiceaService._emit('event:relay.event', {
|
|
311
|
+
headers: {from: 'ws'},
|
|
312
|
+
voiceaPayload: payload,
|
|
313
|
+
data: {relayType: 'voicea.transl.rsp'}
|
|
314
|
+
});
|
|
315
|
+
assert.calledOnce(functionSpy);
|
|
316
|
+
assert.calledOnceWithExactly(triggerSpy, {statusCode: 300, errorMessage: 'error text'});
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('#processTranscription', () => {
|
|
321
|
+
let triggerSpy, functionSpy;
|
|
322
|
+
|
|
323
|
+
beforeEach(() => {
|
|
324
|
+
triggerSpy = sinon.spy();
|
|
325
|
+
functionSpy = sinon.spy(voiceaService, 'processTranscription');
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('processes interim transcription', async () => {
|
|
329
|
+
voiceaService.on(EVENT_TRIGGERS.NEW_CAPTION, triggerSpy);
|
|
330
|
+
|
|
331
|
+
const voiceaPayload = {
|
|
332
|
+
audio_received_millis: 0,
|
|
333
|
+
command_response: '',
|
|
334
|
+
csis: [3556942592],
|
|
335
|
+
data: 'Hello.',
|
|
336
|
+
id: '38093ff5-f6a8-581c-9e59-035ec027994b',
|
|
337
|
+
meeting: '61d4e269-8419-42ab-9e56-3917974cda01',
|
|
338
|
+
transcript_id: '3ec73890-bffb-f28b-e77f-99dc13caea7e',
|
|
339
|
+
ts: 1611653204.3147924,
|
|
340
|
+
type: 'transcript_interim_results',
|
|
341
|
+
|
|
342
|
+
transcripts: [
|
|
343
|
+
{
|
|
344
|
+
text: 'Hello.',
|
|
345
|
+
csis: [
|
|
346
|
+
3556942592
|
|
347
|
+
],
|
|
348
|
+
transcript_language_code: 'en',
|
|
349
|
+
translations: {
|
|
350
|
+
fr: 'Bonjour.'
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
text: 'This is Webex',
|
|
355
|
+
csis: [
|
|
356
|
+
3556942593
|
|
357
|
+
],
|
|
358
|
+
transcript_language_code: 'en',
|
|
359
|
+
translations: {
|
|
360
|
+
fr: "C'est Webex"
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
368
|
+
await voiceaService._emit('event:relay.event', {
|
|
369
|
+
headers: {from: 'ws'},
|
|
370
|
+
voiceaPayload,
|
|
371
|
+
data: {relayType: 'voicea.transcription'}
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
assert.calledOnceWithExactly(functionSpy, voiceaPayload);
|
|
375
|
+
assert.calledOnceWithExactly(triggerSpy, {
|
|
376
|
+
isFinal: false, transcriptId: '3ec73890-bffb-f28b-e77f-99dc13caea7e', translations: undefined, transcript: {text: 'Hello.', csis: [3556942592]}
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('processes final transcription', async () => {
|
|
381
|
+
voiceaService.on(EVENT_TRIGGERS.NEW_CAPTION, triggerSpy);
|
|
382
|
+
|
|
383
|
+
const voiceaPayload = {
|
|
384
|
+
audio_received_millis: 0,
|
|
385
|
+
command_response: '',
|
|
386
|
+
csis: [3556942592],
|
|
387
|
+
data: 'Hello. This is Webex',
|
|
388
|
+
id: '38093ff5-f6a8-581c-9e59-035ec027994b',
|
|
389
|
+
meeting: '61d4e269-8419-42ab-9e56-3917974cda01',
|
|
390
|
+
transcript_id: '3ec73890-bffb-f28b-e77f-99dc13caea7e',
|
|
391
|
+
ts: 1611653204.3147924,
|
|
392
|
+
type: 'transcript_final_result',
|
|
393
|
+
transcript: {
|
|
394
|
+
alignments: [
|
|
395
|
+
{
|
|
396
|
+
end_millis: 12474,
|
|
397
|
+
start_millis: 12204,
|
|
398
|
+
word: 'Hello?'
|
|
399
|
+
}
|
|
400
|
+
],
|
|
401
|
+
csis: [
|
|
402
|
+
3556942592
|
|
403
|
+
],
|
|
404
|
+
end_millis: 13044,
|
|
405
|
+
last_packet_timestamp_ms: 1611653206784,
|
|
406
|
+
start_millis: 12204,
|
|
407
|
+
text: 'Hello?',
|
|
408
|
+
transcript_language_code: 'en'
|
|
409
|
+
},
|
|
410
|
+
transcripts: [
|
|
411
|
+
{
|
|
412
|
+
start_millis: 12204,
|
|
413
|
+
end_millis: 13044,
|
|
414
|
+
text: 'Hello.',
|
|
415
|
+
csis: [
|
|
416
|
+
3556942592
|
|
417
|
+
],
|
|
418
|
+
transcript_language_code: 'en',
|
|
419
|
+
translations: {
|
|
420
|
+
fr: 'Bonjour.'
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
start_millis: 12204,
|
|
425
|
+
end_millis: 13044,
|
|
426
|
+
text: 'This is Webex',
|
|
427
|
+
csis: [
|
|
428
|
+
3556942593
|
|
429
|
+
],
|
|
430
|
+
transcript_language_code: 'en',
|
|
431
|
+
translations: {
|
|
432
|
+
fr: "C'est Webex"
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
]
|
|
436
|
+
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
440
|
+
await voiceaService._emit('event:relay.event', {
|
|
441
|
+
headers: {from: 'ws'},
|
|
442
|
+
voiceaPayload,
|
|
443
|
+
data: {relayType: 'voicea.transcription'}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
assert.calledOnceWithExactly(functionSpy, voiceaPayload);
|
|
447
|
+
assert.calledOnceWithExactly(triggerSpy, {
|
|
448
|
+
isFinal: true, transcriptId: '3ec73890-bffb-f28b-e77f-99dc13caea7e', translations: undefined, timestamp: '0:01', transcript: {text: 'Hello. This is Webex', csis: [3556942592]}
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
it('processes a eva wake up', async () => {
|
|
453
|
+
voiceaService.on(EVENT_TRIGGERS.EVA_COMMAND, triggerSpy);
|
|
454
|
+
|
|
455
|
+
const voiceaPayload = {
|
|
456
|
+
audio_received_millis: 1616137504810,
|
|
457
|
+
command_response: '',
|
|
458
|
+
id: '31fb2f81-fb55-4257-32a0-f421ef8ba4b0',
|
|
459
|
+
meeting: 'fd5bd0fc-06fb-4fd1-982b-554c4368f101',
|
|
460
|
+
trigger: {
|
|
461
|
+
detected_at: '2021-03-19T07:05:04.810669662Z',
|
|
462
|
+
ews_confidence: 0.99497044086456299,
|
|
463
|
+
ews_keyphrase: 'OkayWebEx',
|
|
464
|
+
model_version: 'WebEx',
|
|
465
|
+
offset_seconds: 2336.5900000000001,
|
|
466
|
+
recording_file_name: 'OkayWebEx_fd5bd0fc-06fb-4fd1-982b-554c4368f101_47900f3f-8579-25eb-3f6a-74d81a3c66a4_2335.8900000000003_2336.79.raw',
|
|
467
|
+
type: 'live-hotword'
|
|
468
|
+
},
|
|
469
|
+
ts: 1616137504.8107769,
|
|
470
|
+
type: 'eva_wake'
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
474
|
+
await voiceaService._emit('event:relay.event', {
|
|
475
|
+
headers: {from: 'ws'},
|
|
476
|
+
voiceaPayload,
|
|
477
|
+
data: {relayType: 'voicea.transcription'}
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
assert.calledOnceWithExactly(functionSpy, voiceaPayload);
|
|
481
|
+
assert.calledOnceWithExactly(triggerSpy, {
|
|
482
|
+
isListening: true
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('processes a eva thanks', async () => {
|
|
487
|
+
voiceaService.on(EVENT_TRIGGERS.EVA_COMMAND, triggerSpy);
|
|
488
|
+
|
|
489
|
+
const voiceaPayload = {
|
|
490
|
+
audio_received_millis: 0,
|
|
491
|
+
command_response: 'OK! Decision created.',
|
|
492
|
+
id: '9bc51440-1a22-7c81-6add-4b6ff7b59f7c',
|
|
493
|
+
intent: 'decision',
|
|
494
|
+
meeting: 'fd5bd0fc-06fb-4fd1-982b-554c4368f101',
|
|
495
|
+
ts: 1616135828.2552843,
|
|
496
|
+
type: 'eva_thanks'
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
500
|
+
await voiceaService._emit('event:relay.event', {
|
|
501
|
+
headers: {from: 'ws'},
|
|
502
|
+
voiceaPayload,
|
|
503
|
+
data: {relayType: 'voicea.transcription'}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
assert.calledOnceWithExactly(functionSpy, voiceaPayload);
|
|
507
|
+
assert.calledOnceWithExactly(triggerSpy, {
|
|
508
|
+
isListening: false, text: 'OK! Decision created.'
|
|
509
|
+
});
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it('processes a eva cancel', async () => {
|
|
513
|
+
voiceaService.on(EVENT_TRIGGERS.EVA_COMMAND, triggerSpy);
|
|
514
|
+
|
|
515
|
+
const voiceaPayload = {
|
|
516
|
+
audio_received_millis: 0,
|
|
517
|
+
command_response: '',
|
|
518
|
+
id: '9bc51440-1a22-7c81-6add-4b6ff7b59f7c',
|
|
519
|
+
intent: 'decision',
|
|
520
|
+
meeting: 'fd5bd0fc-06fb-4fd1-982b-554c4368f101',
|
|
521
|
+
ts: 1616135828.2552843,
|
|
522
|
+
type: 'eva_cancel'
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
526
|
+
await voiceaService._emit('event:relay.event', {
|
|
527
|
+
headers: {from: 'ws'},
|
|
528
|
+
voiceaPayload,
|
|
529
|
+
data: {relayType: 'voicea.transcription'}
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
assert.calledOnceWithExactly(functionSpy, voiceaPayload);
|
|
533
|
+
assert.calledOnceWithExactly(triggerSpy, {
|
|
534
|
+
isListening: false
|
|
535
|
+
});
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('processes a highlight', async () => {
|
|
539
|
+
voiceaService.on(EVENT_TRIGGERS.HIGHLIGHT_CREATED, triggerSpy);
|
|
540
|
+
|
|
541
|
+
const voiceaPayload = {
|
|
542
|
+
audio_received_millis: 0,
|
|
543
|
+
command_response: '',
|
|
544
|
+
highlight: {
|
|
545
|
+
created_by_email: '',
|
|
546
|
+
csis: [3932881920],
|
|
547
|
+
end_millis: 660160,
|
|
548
|
+
highlight_id: '219af4b1-1579-5106-53ab-f621094a0c5a',
|
|
549
|
+
highlight_label: 'Decision',
|
|
550
|
+
highlight_source: 'voice-command',
|
|
551
|
+
start_millis: 652756,
|
|
552
|
+
transcript: 'Create a decision to move ahead with the last proposal.',
|
|
553
|
+
trigger_info: {type: 'live-hotword'}
|
|
554
|
+
},
|
|
555
|
+
id: 'e6df0262-6289-db2e-581a-d44bb41b1c9c',
|
|
556
|
+
meeting: 'fd5bd0fc-06fb-4fd1-982b-554c4368f101',
|
|
557
|
+
ts: 1616135858.5349569,
|
|
558
|
+
type: 'highlight_created'
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
562
|
+
await voiceaService._emit('event:relay.event', {
|
|
563
|
+
headers: {from: 'ws'},
|
|
564
|
+
voiceaPayload,
|
|
565
|
+
data: {relayType: 'voicea.transcription'}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
assert.calledOnceWithExactly(functionSpy, voiceaPayload);
|
|
569
|
+
assert.calledOnceWithExactly(triggerSpy, {
|
|
570
|
+
csis: [3932881920],
|
|
571
|
+
highlightId: '219af4b1-1579-5106-53ab-f621094a0c5a',
|
|
572
|
+
text: 'Create a decision to move ahead with the last proposal.',
|
|
573
|
+
highlightLabel: 'Decision',
|
|
574
|
+
highlightSource: 'voice-command',
|
|
575
|
+
timestamp: '0:07'
|
|
576
|
+
});
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
});
|
|
580
|
+
});
|