@webex/internal-plugin-voicea 2.59.2 → 2.59.3-next.1
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/.eslintrc.js +6 -6
- package/LICENSE +1 -1
- package/README.md +93 -93
- package/babel.config.js +3 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/voicea.js +43 -43
- package/dist/voicea.js.map +1 -1
- package/dist/voicea.types.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +17 -16
- package/process +1 -1
- package/src/constants.ts +32 -32
- package/src/index.ts +7 -7
- package/src/utils.ts +7 -7
- package/src/voicea.ts +378 -378
- package/src/voicea.types.ts +92 -92
- package/test/unit/spec/utils.js +12 -12
- package/test/unit/spec/voicea.js +617 -617
package/src/voicea.ts
CHANGED
|
@@ -1,378 +1,378 @@
|
|
|
1
|
-
import uuid from 'uuid';
|
|
2
|
-
import {TriggerProxy as Trigger} from '@webex/plugin-meetings';
|
|
3
|
-
import {WebexPlugin, config} from '@webex/webex-core';
|
|
4
|
-
|
|
5
|
-
import {EVENT_TRIGGERS, VOICEA_RELAY_TYPES, TRANSCRIPTION_TYPE, VOICEA} from './constants';
|
|
6
|
-
// eslint-disable-next-line no-unused-vars
|
|
7
|
-
import {
|
|
8
|
-
AnnouncementPayload,
|
|
9
|
-
CaptionLanguageResponse,
|
|
10
|
-
TranscriptionResponse,
|
|
11
|
-
IVoiceaChannel,
|
|
12
|
-
} from './voicea.types';
|
|
13
|
-
import {millisToMinutesAndSeconds} from './utils';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @description VoiceaChannel to hold single instance of LLM
|
|
17
|
-
* @export
|
|
18
|
-
* @class VoiceaChannel
|
|
19
|
-
*/
|
|
20
|
-
export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
21
|
-
namespace = VOICEA;
|
|
22
|
-
|
|
23
|
-
private seqNum: number;
|
|
24
|
-
|
|
25
|
-
private hasVoiceaJoined: boolean;
|
|
26
|
-
|
|
27
|
-
private areCaptionsEnabled: boolean;
|
|
28
|
-
|
|
29
|
-
private hasSubscribedToEvents = false;
|
|
30
|
-
|
|
31
|
-
private vmcDeviceId?: string;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @param {Object} e
|
|
35
|
-
* @returns {undefined}
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
private eventProcessor = (e) => {
|
|
39
|
-
this.seqNum = e.sequenceNumber + 1;
|
|
40
|
-
switch (e.data.relayType) {
|
|
41
|
-
case VOICEA_RELAY_TYPES.ANNOUNCEMENT:
|
|
42
|
-
this.vmcDeviceId = e.headers.from;
|
|
43
|
-
this.hasVoiceaJoined = true;
|
|
44
|
-
this.processAnnouncementMessage(e.data.voiceaPayload);
|
|
45
|
-
break;
|
|
46
|
-
case VOICEA_RELAY_TYPES.TRANSLATION_RESPONSE:
|
|
47
|
-
this.processCaptionLanguageResponse(e.data.voiceaPayload);
|
|
48
|
-
break;
|
|
49
|
-
case VOICEA_RELAY_TYPES.TRANSCRIPTION:
|
|
50
|
-
this.processTranscription(e.data.voiceaPayload);
|
|
51
|
-
break;
|
|
52
|
-
default:
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Listen to websocket messages
|
|
59
|
-
* @returns {undefined}
|
|
60
|
-
*/
|
|
61
|
-
private listenToEvents() {
|
|
62
|
-
if (!this.hasSubscribedToEvents) {
|
|
63
|
-
// @ts-ignore
|
|
64
|
-
this.webex.internal.llm.on('event:relay.event', this.eventProcessor);
|
|
65
|
-
this.hasSubscribedToEvents = true;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// @ts-ignore
|
|
70
|
-
public deregisterEvents() {
|
|
71
|
-
this.hasVoiceaJoined = false;
|
|
72
|
-
this.areCaptionsEnabled = false;
|
|
73
|
-
this.vmcDeviceId = undefined;
|
|
74
|
-
// @ts-ignore
|
|
75
|
-
this.webex.internal.llm.off('event:relay.event', this.eventProcessor);
|
|
76
|
-
this.hasSubscribedToEvents = false;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Initializes Voicea plugin
|
|
81
|
-
* @param {any} args
|
|
82
|
-
*/
|
|
83
|
-
constructor(...args) {
|
|
84
|
-
super(...args);
|
|
85
|
-
this.seqNum = 1;
|
|
86
|
-
this.hasVoiceaJoined = false;
|
|
87
|
-
this.areCaptionsEnabled = false;
|
|
88
|
-
this.vmcDeviceId = undefined;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Process Transcript and send alert
|
|
93
|
-
* @param {TranscriptionResponse} voiceaPayload
|
|
94
|
-
* @returns {void}
|
|
95
|
-
*/
|
|
96
|
-
private processTranscription = (voiceaPayload: TranscriptionResponse): void => {
|
|
97
|
-
switch (voiceaPayload.type) {
|
|
98
|
-
case TRANSCRIPTION_TYPE.TRANSCRIPT_INTERIM_RESULTS:
|
|
99
|
-
Trigger.trigger(
|
|
100
|
-
this,
|
|
101
|
-
{
|
|
102
|
-
file: 'voicea',
|
|
103
|
-
function: 'processTranscription',
|
|
104
|
-
},
|
|
105
|
-
EVENT_TRIGGERS.NEW_CAPTION,
|
|
106
|
-
{
|
|
107
|
-
isFinal: false,
|
|
108
|
-
transcriptId: voiceaPayload.transcript_id,
|
|
109
|
-
transcripts: voiceaPayload.transcripts,
|
|
110
|
-
}
|
|
111
|
-
);
|
|
112
|
-
break;
|
|
113
|
-
|
|
114
|
-
case TRANSCRIPTION_TYPE.TRANSCRIPT_FINAL_RESULT:
|
|
115
|
-
Trigger.trigger(
|
|
116
|
-
this,
|
|
117
|
-
{
|
|
118
|
-
file: 'voicea',
|
|
119
|
-
function: 'processTranscription',
|
|
120
|
-
},
|
|
121
|
-
EVENT_TRIGGERS.NEW_CAPTION,
|
|
122
|
-
{
|
|
123
|
-
isFinal: true,
|
|
124
|
-
transcriptId: voiceaPayload.transcript_id,
|
|
125
|
-
transcript: {
|
|
126
|
-
csis: voiceaPayload.csis,
|
|
127
|
-
text: voiceaPayload.transcript.text,
|
|
128
|
-
transcriptLanguageCode: voiceaPayload.transcript.transcript_language_code,
|
|
129
|
-
},
|
|
130
|
-
timestamp: millisToMinutesAndSeconds(voiceaPayload.transcript.end_millis),
|
|
131
|
-
}
|
|
132
|
-
);
|
|
133
|
-
break;
|
|
134
|
-
|
|
135
|
-
case TRANSCRIPTION_TYPE.HIGHLIGHT_CREATED:
|
|
136
|
-
Trigger.trigger(
|
|
137
|
-
this,
|
|
138
|
-
{
|
|
139
|
-
file: 'voicea',
|
|
140
|
-
function: 'processTranscription',
|
|
141
|
-
},
|
|
142
|
-
EVENT_TRIGGERS.HIGHLIGHT_CREATED,
|
|
143
|
-
{
|
|
144
|
-
csis: voiceaPayload.highlight.csis,
|
|
145
|
-
highlightId: voiceaPayload.highlight.highlight_id,
|
|
146
|
-
text: voiceaPayload.highlight.transcript,
|
|
147
|
-
highlightLabel: voiceaPayload.highlight.highlight_label,
|
|
148
|
-
highlightSource: voiceaPayload.highlight.highlight_source,
|
|
149
|
-
timestamp: millisToMinutesAndSeconds(voiceaPayload.highlight.end_millis),
|
|
150
|
-
}
|
|
151
|
-
);
|
|
152
|
-
break;
|
|
153
|
-
|
|
154
|
-
case TRANSCRIPTION_TYPE.EVA_THANKS:
|
|
155
|
-
Trigger.trigger(
|
|
156
|
-
this,
|
|
157
|
-
{
|
|
158
|
-
file: 'voicea',
|
|
159
|
-
function: 'processTranscription',
|
|
160
|
-
},
|
|
161
|
-
EVENT_TRIGGERS.EVA_COMMAND,
|
|
162
|
-
{
|
|
163
|
-
isListening: false,
|
|
164
|
-
text: voiceaPayload.command_response,
|
|
165
|
-
}
|
|
166
|
-
);
|
|
167
|
-
break;
|
|
168
|
-
|
|
169
|
-
case TRANSCRIPTION_TYPE.EVA_WAKE:
|
|
170
|
-
case TRANSCRIPTION_TYPE.EVA_CANCEL:
|
|
171
|
-
Trigger.trigger(
|
|
172
|
-
this,
|
|
173
|
-
{
|
|
174
|
-
file: 'voicea',
|
|
175
|
-
function: 'processTranscription',
|
|
176
|
-
},
|
|
177
|
-
EVENT_TRIGGERS.EVA_COMMAND,
|
|
178
|
-
{
|
|
179
|
-
isListening: voiceaPayload.type === TRANSCRIPTION_TYPE.EVA_WAKE,
|
|
180
|
-
}
|
|
181
|
-
);
|
|
182
|
-
break;
|
|
183
|
-
|
|
184
|
-
default:
|
|
185
|
-
break;
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Processes Caption Language Response
|
|
191
|
-
* @param {CaptionLanguageResponse} voiceaPayload
|
|
192
|
-
* @returns {void}
|
|
193
|
-
*/
|
|
194
|
-
private processCaptionLanguageResponse = (voiceaPayload: CaptionLanguageResponse): void => {
|
|
195
|
-
if (voiceaPayload.statusCode === 200) {
|
|
196
|
-
Trigger.trigger(
|
|
197
|
-
this,
|
|
198
|
-
{
|
|
199
|
-
file: 'voicea',
|
|
200
|
-
function: 'processCaptionLanguageResponse',
|
|
201
|
-
},
|
|
202
|
-
EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,
|
|
203
|
-
{statusCode: 200}
|
|
204
|
-
);
|
|
205
|
-
} else {
|
|
206
|
-
Trigger.trigger(
|
|
207
|
-
this,
|
|
208
|
-
{
|
|
209
|
-
file: 'voicea',
|
|
210
|
-
function: 'processCaptionLanguageResponse',
|
|
211
|
-
},
|
|
212
|
-
EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,
|
|
213
|
-
{statusCode: voiceaPayload.errorCode, errorMessage: voiceaPayload.message}
|
|
214
|
-
);
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* processes voicea announcement response and triggers event
|
|
220
|
-
* @param {Object} voiceaPayload
|
|
221
|
-
* @returns {void}
|
|
222
|
-
*/
|
|
223
|
-
private processAnnouncementMessage = (voiceaPayload: AnnouncementPayload): void => {
|
|
224
|
-
const voiceaLanguageOptions = {
|
|
225
|
-
captionLanguages: voiceaPayload?.translation?.allowed_languages ?? [],
|
|
226
|
-
maxLanguages: voiceaPayload?.translation?.max_languages ?? 0,
|
|
227
|
-
spokenLanguages: voiceaPayload?.ASR?.spoken_languages ?? [],
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
Trigger.trigger(
|
|
231
|
-
this,
|
|
232
|
-
{
|
|
233
|
-
file: 'voicea',
|
|
234
|
-
function: 'processAnnouncementMessage',
|
|
235
|
-
},
|
|
236
|
-
EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT,
|
|
237
|
-
voiceaLanguageOptions
|
|
238
|
-
);
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Sends Announcement to add voicea to the meeting
|
|
243
|
-
* @returns {void}
|
|
244
|
-
*/
|
|
245
|
-
private sendAnnouncement = (): void => {
|
|
246
|
-
// @ts-ignore
|
|
247
|
-
if (this.hasVoiceaJoined || !this.webex.internal.llm.isConnected()) return;
|
|
248
|
-
|
|
249
|
-
this.listenToEvents();
|
|
250
|
-
// @ts-ignore
|
|
251
|
-
this.webex.internal.llm.socket.send({
|
|
252
|
-
id: `${this.seqNum}`,
|
|
253
|
-
type: 'publishRequest',
|
|
254
|
-
recipients: {
|
|
255
|
-
// @ts-ignore
|
|
256
|
-
route: this.webex.internal.llm.getBinding(),
|
|
257
|
-
},
|
|
258
|
-
headers: {},
|
|
259
|
-
data: {
|
|
260
|
-
clientPayload: {
|
|
261
|
-
version: 'v2',
|
|
262
|
-
},
|
|
263
|
-
eventType: 'relay.event',
|
|
264
|
-
relayType: VOICEA_RELAY_TYPES.CLIENT_ANNOUNCEMENT,
|
|
265
|
-
},
|
|
266
|
-
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
267
|
-
});
|
|
268
|
-
this.seqNum += 1;
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Set Spoken Language for the meeting
|
|
273
|
-
* @param {string} languageCode
|
|
274
|
-
* @returns {Promise}
|
|
275
|
-
*/
|
|
276
|
-
public setSpokenLanguage = (languageCode: string): Promise<void> =>
|
|
277
|
-
// @ts-ignore
|
|
278
|
-
this.request({
|
|
279
|
-
method: 'PUT',
|
|
280
|
-
// @ts-ignore
|
|
281
|
-
url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
|
|
282
|
-
body: {
|
|
283
|
-
languageCode,
|
|
284
|
-
},
|
|
285
|
-
}).then(() => {
|
|
286
|
-
Trigger.trigger(
|
|
287
|
-
this,
|
|
288
|
-
{
|
|
289
|
-
file: 'voicea',
|
|
290
|
-
function: 'setSpokenLanguage',
|
|
291
|
-
},
|
|
292
|
-
EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE,
|
|
293
|
-
{languageCode}
|
|
294
|
-
);
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Request Language translation
|
|
299
|
-
* @param {string} languageCode
|
|
300
|
-
* @returns {void}
|
|
301
|
-
*/
|
|
302
|
-
public requestLanguage = (languageCode: string): void => {
|
|
303
|
-
// @ts-ignore
|
|
304
|
-
if (!this.webex.internal.llm.isConnected()) return;
|
|
305
|
-
// @ts-ignore
|
|
306
|
-
this.webex.internal.llm.socket.send({
|
|
307
|
-
id: `${this.seqNum}`,
|
|
308
|
-
type: 'publishRequest',
|
|
309
|
-
recipients: {
|
|
310
|
-
// @ts-ignore
|
|
311
|
-
route: this.webex.internal.llm.getBinding(),
|
|
312
|
-
},
|
|
313
|
-
headers: {
|
|
314
|
-
to: this.vmcDeviceId,
|
|
315
|
-
},
|
|
316
|
-
data: {
|
|
317
|
-
clientPayload: {
|
|
318
|
-
translationLanguage: languageCode,
|
|
319
|
-
id: uuid.v4(),
|
|
320
|
-
},
|
|
321
|
-
eventType: 'relay.event',
|
|
322
|
-
relayType: VOICEA_RELAY_TYPES.TRANSLATION_REQUEST,
|
|
323
|
-
},
|
|
324
|
-
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
325
|
-
});
|
|
326
|
-
this.seqNum += 1;
|
|
327
|
-
};
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Turn on Captions
|
|
331
|
-
* @returns {Promise}
|
|
332
|
-
*/
|
|
333
|
-
public turnOnCaptions = async (): undefined | Promise<void> => {
|
|
334
|
-
if (this.hasVoiceaJoined && this.areCaptionsEnabled) return undefined;
|
|
335
|
-
// @ts-ignore
|
|
336
|
-
// eslint-disable-next-line newline-before-return
|
|
337
|
-
return this.request({
|
|
338
|
-
method: 'PUT',
|
|
339
|
-
// @ts-ignore
|
|
340
|
-
url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
|
|
341
|
-
body: {
|
|
342
|
-
transcribe: {caption: true},
|
|
343
|
-
},
|
|
344
|
-
}).then(() => {
|
|
345
|
-
Trigger.trigger(
|
|
346
|
-
this,
|
|
347
|
-
{
|
|
348
|
-
file: 'voicea',
|
|
349
|
-
function: 'turnOnCaptions',
|
|
350
|
-
},
|
|
351
|
-
EVENT_TRIGGERS.CAPTIONS_TURNED_ON
|
|
352
|
-
);
|
|
353
|
-
this.areCaptionsEnabled = true;
|
|
354
|
-
this.sendAnnouncement();
|
|
355
|
-
});
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Toggle transcribing for highlights
|
|
360
|
-
* @param {bool} activate if true transcribing is turned on
|
|
361
|
-
* @returns {Promise}
|
|
362
|
-
*/
|
|
363
|
-
public toggleTranscribing = async (activate: boolean): undefined | Promise<void> => {
|
|
364
|
-
// @ts-ignore
|
|
365
|
-
return this.request({
|
|
366
|
-
method: 'PUT',
|
|
367
|
-
// @ts-ignore
|
|
368
|
-
url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
|
|
369
|
-
body: {
|
|
370
|
-
transcribe: {transcribing: activate},
|
|
371
|
-
},
|
|
372
|
-
}).then(() => {
|
|
373
|
-
if (activate && !this.areCaptionsEnabled && !this.hasVoiceaJoined) this.turnOnCaptions();
|
|
374
|
-
});
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
export default VoiceaChannel;
|
|
1
|
+
import uuid from 'uuid';
|
|
2
|
+
import {TriggerProxy as Trigger} from '@webex/plugin-meetings';
|
|
3
|
+
import {WebexPlugin, config} from '@webex/webex-core';
|
|
4
|
+
|
|
5
|
+
import {EVENT_TRIGGERS, VOICEA_RELAY_TYPES, TRANSCRIPTION_TYPE, VOICEA} from './constants';
|
|
6
|
+
// eslint-disable-next-line no-unused-vars
|
|
7
|
+
import {
|
|
8
|
+
AnnouncementPayload,
|
|
9
|
+
CaptionLanguageResponse,
|
|
10
|
+
TranscriptionResponse,
|
|
11
|
+
IVoiceaChannel,
|
|
12
|
+
} from './voicea.types';
|
|
13
|
+
import {millisToMinutesAndSeconds} from './utils';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @description VoiceaChannel to hold single instance of LLM
|
|
17
|
+
* @export
|
|
18
|
+
* @class VoiceaChannel
|
|
19
|
+
*/
|
|
20
|
+
export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
21
|
+
namespace = VOICEA;
|
|
22
|
+
|
|
23
|
+
private seqNum: number;
|
|
24
|
+
|
|
25
|
+
private hasVoiceaJoined: boolean;
|
|
26
|
+
|
|
27
|
+
private areCaptionsEnabled: boolean;
|
|
28
|
+
|
|
29
|
+
private hasSubscribedToEvents = false;
|
|
30
|
+
|
|
31
|
+
private vmcDeviceId?: string;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Object} e
|
|
35
|
+
* @returns {undefined}
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
private eventProcessor = (e) => {
|
|
39
|
+
this.seqNum = e.sequenceNumber + 1;
|
|
40
|
+
switch (e.data.relayType) {
|
|
41
|
+
case VOICEA_RELAY_TYPES.ANNOUNCEMENT:
|
|
42
|
+
this.vmcDeviceId = e.headers.from;
|
|
43
|
+
this.hasVoiceaJoined = true;
|
|
44
|
+
this.processAnnouncementMessage(e.data.voiceaPayload);
|
|
45
|
+
break;
|
|
46
|
+
case VOICEA_RELAY_TYPES.TRANSLATION_RESPONSE:
|
|
47
|
+
this.processCaptionLanguageResponse(e.data.voiceaPayload);
|
|
48
|
+
break;
|
|
49
|
+
case VOICEA_RELAY_TYPES.TRANSCRIPTION:
|
|
50
|
+
this.processTranscription(e.data.voiceaPayload);
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Listen to websocket messages
|
|
59
|
+
* @returns {undefined}
|
|
60
|
+
*/
|
|
61
|
+
private listenToEvents() {
|
|
62
|
+
if (!this.hasSubscribedToEvents) {
|
|
63
|
+
// @ts-ignore
|
|
64
|
+
this.webex.internal.llm.on('event:relay.event', this.eventProcessor);
|
|
65
|
+
this.hasSubscribedToEvents = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
public deregisterEvents() {
|
|
71
|
+
this.hasVoiceaJoined = false;
|
|
72
|
+
this.areCaptionsEnabled = false;
|
|
73
|
+
this.vmcDeviceId = undefined;
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
this.webex.internal.llm.off('event:relay.event', this.eventProcessor);
|
|
76
|
+
this.hasSubscribedToEvents = false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Initializes Voicea plugin
|
|
81
|
+
* @param {any} args
|
|
82
|
+
*/
|
|
83
|
+
constructor(...args) {
|
|
84
|
+
super(...args);
|
|
85
|
+
this.seqNum = 1;
|
|
86
|
+
this.hasVoiceaJoined = false;
|
|
87
|
+
this.areCaptionsEnabled = false;
|
|
88
|
+
this.vmcDeviceId = undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Process Transcript and send alert
|
|
93
|
+
* @param {TranscriptionResponse} voiceaPayload
|
|
94
|
+
* @returns {void}
|
|
95
|
+
*/
|
|
96
|
+
private processTranscription = (voiceaPayload: TranscriptionResponse): void => {
|
|
97
|
+
switch (voiceaPayload.type) {
|
|
98
|
+
case TRANSCRIPTION_TYPE.TRANSCRIPT_INTERIM_RESULTS:
|
|
99
|
+
Trigger.trigger(
|
|
100
|
+
this,
|
|
101
|
+
{
|
|
102
|
+
file: 'voicea',
|
|
103
|
+
function: 'processTranscription',
|
|
104
|
+
},
|
|
105
|
+
EVENT_TRIGGERS.NEW_CAPTION,
|
|
106
|
+
{
|
|
107
|
+
isFinal: false,
|
|
108
|
+
transcriptId: voiceaPayload.transcript_id,
|
|
109
|
+
transcripts: voiceaPayload.transcripts,
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
break;
|
|
113
|
+
|
|
114
|
+
case TRANSCRIPTION_TYPE.TRANSCRIPT_FINAL_RESULT:
|
|
115
|
+
Trigger.trigger(
|
|
116
|
+
this,
|
|
117
|
+
{
|
|
118
|
+
file: 'voicea',
|
|
119
|
+
function: 'processTranscription',
|
|
120
|
+
},
|
|
121
|
+
EVENT_TRIGGERS.NEW_CAPTION,
|
|
122
|
+
{
|
|
123
|
+
isFinal: true,
|
|
124
|
+
transcriptId: voiceaPayload.transcript_id,
|
|
125
|
+
transcript: {
|
|
126
|
+
csis: voiceaPayload.csis,
|
|
127
|
+
text: voiceaPayload.transcript.text,
|
|
128
|
+
transcriptLanguageCode: voiceaPayload.transcript.transcript_language_code,
|
|
129
|
+
},
|
|
130
|
+
timestamp: millisToMinutesAndSeconds(voiceaPayload.transcript.end_millis),
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
break;
|
|
134
|
+
|
|
135
|
+
case TRANSCRIPTION_TYPE.HIGHLIGHT_CREATED:
|
|
136
|
+
Trigger.trigger(
|
|
137
|
+
this,
|
|
138
|
+
{
|
|
139
|
+
file: 'voicea',
|
|
140
|
+
function: 'processTranscription',
|
|
141
|
+
},
|
|
142
|
+
EVENT_TRIGGERS.HIGHLIGHT_CREATED,
|
|
143
|
+
{
|
|
144
|
+
csis: voiceaPayload.highlight.csis,
|
|
145
|
+
highlightId: voiceaPayload.highlight.highlight_id,
|
|
146
|
+
text: voiceaPayload.highlight.transcript,
|
|
147
|
+
highlightLabel: voiceaPayload.highlight.highlight_label,
|
|
148
|
+
highlightSource: voiceaPayload.highlight.highlight_source,
|
|
149
|
+
timestamp: millisToMinutesAndSeconds(voiceaPayload.highlight.end_millis),
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
break;
|
|
153
|
+
|
|
154
|
+
case TRANSCRIPTION_TYPE.EVA_THANKS:
|
|
155
|
+
Trigger.trigger(
|
|
156
|
+
this,
|
|
157
|
+
{
|
|
158
|
+
file: 'voicea',
|
|
159
|
+
function: 'processTranscription',
|
|
160
|
+
},
|
|
161
|
+
EVENT_TRIGGERS.EVA_COMMAND,
|
|
162
|
+
{
|
|
163
|
+
isListening: false,
|
|
164
|
+
text: voiceaPayload.command_response,
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case TRANSCRIPTION_TYPE.EVA_WAKE:
|
|
170
|
+
case TRANSCRIPTION_TYPE.EVA_CANCEL:
|
|
171
|
+
Trigger.trigger(
|
|
172
|
+
this,
|
|
173
|
+
{
|
|
174
|
+
file: 'voicea',
|
|
175
|
+
function: 'processTranscription',
|
|
176
|
+
},
|
|
177
|
+
EVENT_TRIGGERS.EVA_COMMAND,
|
|
178
|
+
{
|
|
179
|
+
isListening: voiceaPayload.type === TRANSCRIPTION_TYPE.EVA_WAKE,
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
break;
|
|
183
|
+
|
|
184
|
+
default:
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Processes Caption Language Response
|
|
191
|
+
* @param {CaptionLanguageResponse} voiceaPayload
|
|
192
|
+
* @returns {void}
|
|
193
|
+
*/
|
|
194
|
+
private processCaptionLanguageResponse = (voiceaPayload: CaptionLanguageResponse): void => {
|
|
195
|
+
if (voiceaPayload.statusCode === 200) {
|
|
196
|
+
Trigger.trigger(
|
|
197
|
+
this,
|
|
198
|
+
{
|
|
199
|
+
file: 'voicea',
|
|
200
|
+
function: 'processCaptionLanguageResponse',
|
|
201
|
+
},
|
|
202
|
+
EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,
|
|
203
|
+
{statusCode: 200}
|
|
204
|
+
);
|
|
205
|
+
} else {
|
|
206
|
+
Trigger.trigger(
|
|
207
|
+
this,
|
|
208
|
+
{
|
|
209
|
+
file: 'voicea',
|
|
210
|
+
function: 'processCaptionLanguageResponse',
|
|
211
|
+
},
|
|
212
|
+
EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,
|
|
213
|
+
{statusCode: voiceaPayload.errorCode, errorMessage: voiceaPayload.message}
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* processes voicea announcement response and triggers event
|
|
220
|
+
* @param {Object} voiceaPayload
|
|
221
|
+
* @returns {void}
|
|
222
|
+
*/
|
|
223
|
+
private processAnnouncementMessage = (voiceaPayload: AnnouncementPayload): void => {
|
|
224
|
+
const voiceaLanguageOptions = {
|
|
225
|
+
captionLanguages: voiceaPayload?.translation?.allowed_languages ?? [],
|
|
226
|
+
maxLanguages: voiceaPayload?.translation?.max_languages ?? 0,
|
|
227
|
+
spokenLanguages: voiceaPayload?.ASR?.spoken_languages ?? [],
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
Trigger.trigger(
|
|
231
|
+
this,
|
|
232
|
+
{
|
|
233
|
+
file: 'voicea',
|
|
234
|
+
function: 'processAnnouncementMessage',
|
|
235
|
+
},
|
|
236
|
+
EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT,
|
|
237
|
+
voiceaLanguageOptions
|
|
238
|
+
);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Sends Announcement to add voicea to the meeting
|
|
243
|
+
* @returns {void}
|
|
244
|
+
*/
|
|
245
|
+
private sendAnnouncement = (): void => {
|
|
246
|
+
// @ts-ignore
|
|
247
|
+
if (this.hasVoiceaJoined || !this.webex.internal.llm.isConnected()) return;
|
|
248
|
+
|
|
249
|
+
this.listenToEvents();
|
|
250
|
+
// @ts-ignore
|
|
251
|
+
this.webex.internal.llm.socket.send({
|
|
252
|
+
id: `${this.seqNum}`,
|
|
253
|
+
type: 'publishRequest',
|
|
254
|
+
recipients: {
|
|
255
|
+
// @ts-ignore
|
|
256
|
+
route: this.webex.internal.llm.getBinding(),
|
|
257
|
+
},
|
|
258
|
+
headers: {},
|
|
259
|
+
data: {
|
|
260
|
+
clientPayload: {
|
|
261
|
+
version: 'v2',
|
|
262
|
+
},
|
|
263
|
+
eventType: 'relay.event',
|
|
264
|
+
relayType: VOICEA_RELAY_TYPES.CLIENT_ANNOUNCEMENT,
|
|
265
|
+
},
|
|
266
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
267
|
+
});
|
|
268
|
+
this.seqNum += 1;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Set Spoken Language for the meeting
|
|
273
|
+
* @param {string} languageCode
|
|
274
|
+
* @returns {Promise}
|
|
275
|
+
*/
|
|
276
|
+
public setSpokenLanguage = (languageCode: string): Promise<void> =>
|
|
277
|
+
// @ts-ignore
|
|
278
|
+
this.request({
|
|
279
|
+
method: 'PUT',
|
|
280
|
+
// @ts-ignore
|
|
281
|
+
url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
|
|
282
|
+
body: {
|
|
283
|
+
languageCode,
|
|
284
|
+
},
|
|
285
|
+
}).then(() => {
|
|
286
|
+
Trigger.trigger(
|
|
287
|
+
this,
|
|
288
|
+
{
|
|
289
|
+
file: 'voicea',
|
|
290
|
+
function: 'setSpokenLanguage',
|
|
291
|
+
},
|
|
292
|
+
EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE,
|
|
293
|
+
{languageCode}
|
|
294
|
+
);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Request Language translation
|
|
299
|
+
* @param {string} languageCode
|
|
300
|
+
* @returns {void}
|
|
301
|
+
*/
|
|
302
|
+
public requestLanguage = (languageCode: string): void => {
|
|
303
|
+
// @ts-ignore
|
|
304
|
+
if (!this.webex.internal.llm.isConnected()) return;
|
|
305
|
+
// @ts-ignore
|
|
306
|
+
this.webex.internal.llm.socket.send({
|
|
307
|
+
id: `${this.seqNum}`,
|
|
308
|
+
type: 'publishRequest',
|
|
309
|
+
recipients: {
|
|
310
|
+
// @ts-ignore
|
|
311
|
+
route: this.webex.internal.llm.getBinding(),
|
|
312
|
+
},
|
|
313
|
+
headers: {
|
|
314
|
+
to: this.vmcDeviceId,
|
|
315
|
+
},
|
|
316
|
+
data: {
|
|
317
|
+
clientPayload: {
|
|
318
|
+
translationLanguage: languageCode,
|
|
319
|
+
id: uuid.v4(),
|
|
320
|
+
},
|
|
321
|
+
eventType: 'relay.event',
|
|
322
|
+
relayType: VOICEA_RELAY_TYPES.TRANSLATION_REQUEST,
|
|
323
|
+
},
|
|
324
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
325
|
+
});
|
|
326
|
+
this.seqNum += 1;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Turn on Captions
|
|
331
|
+
* @returns {Promise}
|
|
332
|
+
*/
|
|
333
|
+
public turnOnCaptions = async (): undefined | Promise<void> => {
|
|
334
|
+
if (this.hasVoiceaJoined && this.areCaptionsEnabled) return undefined;
|
|
335
|
+
// @ts-ignore
|
|
336
|
+
// eslint-disable-next-line newline-before-return
|
|
337
|
+
return this.request({
|
|
338
|
+
method: 'PUT',
|
|
339
|
+
// @ts-ignore
|
|
340
|
+
url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
|
|
341
|
+
body: {
|
|
342
|
+
transcribe: {caption: true},
|
|
343
|
+
},
|
|
344
|
+
}).then(() => {
|
|
345
|
+
Trigger.trigger(
|
|
346
|
+
this,
|
|
347
|
+
{
|
|
348
|
+
file: 'voicea',
|
|
349
|
+
function: 'turnOnCaptions',
|
|
350
|
+
},
|
|
351
|
+
EVENT_TRIGGERS.CAPTIONS_TURNED_ON
|
|
352
|
+
);
|
|
353
|
+
this.areCaptionsEnabled = true;
|
|
354
|
+
this.sendAnnouncement();
|
|
355
|
+
});
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Toggle transcribing for highlights
|
|
360
|
+
* @param {bool} activate if true transcribing is turned on
|
|
361
|
+
* @returns {Promise}
|
|
362
|
+
*/
|
|
363
|
+
public toggleTranscribing = async (activate: boolean): undefined | Promise<void> => {
|
|
364
|
+
// @ts-ignore
|
|
365
|
+
return this.request({
|
|
366
|
+
method: 'PUT',
|
|
367
|
+
// @ts-ignore
|
|
368
|
+
url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
|
|
369
|
+
body: {
|
|
370
|
+
transcribe: {transcribing: activate},
|
|
371
|
+
},
|
|
372
|
+
}).then(() => {
|
|
373
|
+
if (activate && !this.areCaptionsEnabled && !this.hasVoiceaJoined) this.turnOnCaptions();
|
|
374
|
+
});
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export default VoiceaChannel;
|