@webex/internal-plugin-voicea 3.0.0-beta.17 → 3.0.0-beta.170

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/constants.js CHANGED
@@ -4,7 +4,7 @@ var _Object$defineProperty = require("@babel/runtime-corejs2/core-js/object/defi
4
4
  _Object$defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.VOICEA_RELAY_TYPES = exports.VOICEA = exports.TRANSCRIPTION_TYPE = exports.EVENT_TRIGGERS = void 0;
7
+ exports.VOICEA_RELAY_TYPES = exports.VOICEA = exports.TURN_ON_CAPTION_STATUS = exports.TRANSCRIPTION_TYPE = exports.EVENT_TRIGGERS = exports.ANNOUNCE_STATUS = void 0;
8
8
  var EVENT_TRIGGERS = {
9
9
  VOICEA_ANNOUNCEMENT: 'voicea:announcement',
10
10
  CAPTION_LANGUAGE_UPDATE: 'voicea:captionLanguageUpdate',
@@ -37,4 +37,16 @@ var TRANSCRIPTION_TYPE = {
37
37
  exports.TRANSCRIPTION_TYPE = TRANSCRIPTION_TYPE;
38
38
  var VOICEA = 'voicea';
39
39
  exports.VOICEA = VOICEA;
40
+ var ANNOUNCE_STATUS = {
41
+ IDLE: 'idle',
42
+ JOINED: 'joined',
43
+ JOINING: 'joining'
44
+ };
45
+ exports.ANNOUNCE_STATUS = ANNOUNCE_STATUS;
46
+ var TURN_ON_CAPTION_STATUS = {
47
+ IDLE: 'idle',
48
+ ENABLED: 'enabled',
49
+ SENDING: 'sending'
50
+ };
51
+ exports.TURN_ON_CAPTION_STATUS = TURN_ON_CAPTION_STATUS;
40
52
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["EVENT_TRIGGERS","VOICEA_ANNOUNCEMENT","CAPTION_LANGUAGE_UPDATE","SPOKEN_LANGUAGE_UPDATE","CAPTIONS_TURNED_ON","TRANSCRIBING_ON","TRANSCRIBING_OFF","NEW_CAPTION","EVA_COMMAND","HIGHLIGHT_CREATED","VOICEA_RELAY_TYPES","ANNOUNCEMENT","CLIENT_ANNOUNCEMENT","TRANSLATION_REQUEST","TRANSLATION_RESPONSE","TRANSCRIPTION","TRANSCRIPTION_TYPE","UNKNOWN","EVA_WAKE","EVA_THANKS","EVA_CANCEL","TRANSCRIPT_INTERIM_RESULTS","TRANSCRIPT_FINAL_RESULT","VOICEA"],"sources":["constants.ts"],"sourcesContent":["export const EVENT_TRIGGERS = {\n VOICEA_ANNOUNCEMENT: 'voicea:announcement',\n CAPTION_LANGUAGE_UPDATE: 'voicea:captionLanguageUpdate',\n SPOKEN_LANGUAGE_UPDATE: 'voicea:spokenLanguageUpdate',\n CAPTIONS_TURNED_ON: 'voicea:captionOn',\n TRANSCRIBING_ON: 'voicea:transcribingOn',\n TRANSCRIBING_OFF: 'voicea:transcribingOff',\n\n NEW_CAPTION: 'voicea:newCaption',\n EVA_COMMAND: 'voicea:wxa',\n HIGHLIGHT_CREATED: 'voicea:highlightCreated',\n};\n\nexport const VOICEA_RELAY_TYPES = {\n ANNOUNCEMENT: 'voicea.annc',\n CLIENT_ANNOUNCEMENT: 'client.annc',\n TRANSLATION_REQUEST: 'voicea.transl.req',\n TRANSLATION_RESPONSE: 'voicea.transl.rsp',\n TRANSCRIPTION: 'voicea.transcription',\n};\n\nexport const TRANSCRIPTION_TYPE = {\n UNKNOWN: 'unknown',\n EVA_WAKE: 'eva_wake',\n EVA_THANKS: 'eva_thanks',\n EVA_CANCEL: 'eva_cancel',\n HIGHLIGHT_CREATED: 'highlight_created',\n TRANSCRIPT_INTERIM_RESULTS: 'transcript_interim_results',\n TRANSCRIPT_FINAL_RESULT: 'transcript_final_result',\n};\n\nexport const VOICEA = 'voicea';\n"],"mappings":";;;;;;;AAAO,IAAMA,cAAc,GAAG;EAC5BC,mBAAmB,EAAE,qBAAqB;EAC1CC,uBAAuB,EAAE,8BAA8B;EACvDC,sBAAsB,EAAE,6BAA6B;EACrDC,kBAAkB,EAAE,kBAAkB;EACtCC,eAAe,EAAE,uBAAuB;EACxCC,gBAAgB,EAAE,wBAAwB;EAE1CC,WAAW,EAAE,mBAAmB;EAChCC,WAAW,EAAE,YAAY;EACzBC,iBAAiB,EAAE;AACrB,CAAC;AAAC;AAEK,IAAMC,kBAAkB,GAAG;EAChCC,YAAY,EAAE,aAAa;EAC3BC,mBAAmB,EAAE,aAAa;EAClCC,mBAAmB,EAAE,mBAAmB;EACxCC,oBAAoB,EAAE,mBAAmB;EACzCC,aAAa,EAAE;AACjB,CAAC;AAAC;AAEK,IAAMC,kBAAkB,GAAG;EAChCC,OAAO,EAAE,SAAS;EAClBC,QAAQ,EAAE,UAAU;EACpBC,UAAU,EAAE,YAAY;EACxBC,UAAU,EAAE,YAAY;EACxBX,iBAAiB,EAAE,mBAAmB;EACtCY,0BAA0B,EAAE,4BAA4B;EACxDC,uBAAuB,EAAE;AAC3B,CAAC;AAAC;AAEK,IAAMC,MAAM,GAAG,QAAQ;AAAC"}
1
+ {"version":3,"names":["EVENT_TRIGGERS","VOICEA_ANNOUNCEMENT","CAPTION_LANGUAGE_UPDATE","SPOKEN_LANGUAGE_UPDATE","CAPTIONS_TURNED_ON","TRANSCRIBING_ON","TRANSCRIBING_OFF","NEW_CAPTION","EVA_COMMAND","HIGHLIGHT_CREATED","VOICEA_RELAY_TYPES","ANNOUNCEMENT","CLIENT_ANNOUNCEMENT","TRANSLATION_REQUEST","TRANSLATION_RESPONSE","TRANSCRIPTION","TRANSCRIPTION_TYPE","UNKNOWN","EVA_WAKE","EVA_THANKS","EVA_CANCEL","TRANSCRIPT_INTERIM_RESULTS","TRANSCRIPT_FINAL_RESULT","VOICEA","ANNOUNCE_STATUS","IDLE","JOINED","JOINING","TURN_ON_CAPTION_STATUS","ENABLED","SENDING"],"sources":["constants.ts"],"sourcesContent":["export const EVENT_TRIGGERS = {\n VOICEA_ANNOUNCEMENT: 'voicea:announcement',\n CAPTION_LANGUAGE_UPDATE: 'voicea:captionLanguageUpdate',\n SPOKEN_LANGUAGE_UPDATE: 'voicea:spokenLanguageUpdate',\n CAPTIONS_TURNED_ON: 'voicea:captionOn',\n TRANSCRIBING_ON: 'voicea:transcribingOn',\n TRANSCRIBING_OFF: 'voicea:transcribingOff',\n\n NEW_CAPTION: 'voicea:newCaption',\n EVA_COMMAND: 'voicea:wxa',\n HIGHLIGHT_CREATED: 'voicea:highlightCreated',\n};\n\nexport const VOICEA_RELAY_TYPES = {\n ANNOUNCEMENT: 'voicea.annc',\n CLIENT_ANNOUNCEMENT: 'client.annc',\n TRANSLATION_REQUEST: 'voicea.transl.req',\n TRANSLATION_RESPONSE: 'voicea.transl.rsp',\n TRANSCRIPTION: 'voicea.transcription',\n};\n\nexport const TRANSCRIPTION_TYPE = {\n UNKNOWN: 'unknown',\n EVA_WAKE: 'eva_wake',\n EVA_THANKS: 'eva_thanks',\n EVA_CANCEL: 'eva_cancel',\n HIGHLIGHT_CREATED: 'highlight_created',\n TRANSCRIPT_INTERIM_RESULTS: 'transcript_interim_results',\n TRANSCRIPT_FINAL_RESULT: 'transcript_final_result',\n};\n\nexport const VOICEA = 'voicea';\n\nexport const ANNOUNCE_STATUS = {\n IDLE: 'idle',\n JOINED: 'joined',\n JOINING: 'joining',\n};\n\nexport const TURN_ON_CAPTION_STATUS = {\n IDLE: 'idle',\n ENABLED: 'enabled',\n SENDING: 'sending',\n};\n"],"mappings":";;;;;;;AAAO,IAAMA,cAAc,GAAG;EAC5BC,mBAAmB,EAAE,qBAAqB;EAC1CC,uBAAuB,EAAE,8BAA8B;EACvDC,sBAAsB,EAAE,6BAA6B;EACrDC,kBAAkB,EAAE,kBAAkB;EACtCC,eAAe,EAAE,uBAAuB;EACxCC,gBAAgB,EAAE,wBAAwB;EAE1CC,WAAW,EAAE,mBAAmB;EAChCC,WAAW,EAAE,YAAY;EACzBC,iBAAiB,EAAE;AACrB,CAAC;AAAC;AAEK,IAAMC,kBAAkB,GAAG;EAChCC,YAAY,EAAE,aAAa;EAC3BC,mBAAmB,EAAE,aAAa;EAClCC,mBAAmB,EAAE,mBAAmB;EACxCC,oBAAoB,EAAE,mBAAmB;EACzCC,aAAa,EAAE;AACjB,CAAC;AAAC;AAEK,IAAMC,kBAAkB,GAAG;EAChCC,OAAO,EAAE,SAAS;EAClBC,QAAQ,EAAE,UAAU;EACpBC,UAAU,EAAE,YAAY;EACxBC,UAAU,EAAE,YAAY;EACxBX,iBAAiB,EAAE,mBAAmB;EACtCY,0BAA0B,EAAE,4BAA4B;EACxDC,uBAAuB,EAAE;AAC3B,CAAC;AAAC;AAEK,IAAMC,MAAM,GAAG,QAAQ;AAAC;AAExB,IAAMC,eAAe,GAAG;EAC7BC,IAAI,EAAE,MAAM;EACZC,MAAM,EAAE,QAAQ;EAChBC,OAAO,EAAE;AACX,CAAC;AAAC;AAEK,IAAMC,sBAAsB,GAAG;EACpCH,IAAI,EAAE,MAAM;EACZI,OAAO,EAAE,SAAS;EAClBC,OAAO,EAAE;AACX,CAAC;AAAC"}
package/dist/voicea.js CHANGED
@@ -44,16 +44,17 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
44
44
  _this = _super.call.apply(_super, [this].concat(args));
45
45
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "namespace", _constants.VOICEA);
46
46
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "seqNum", void 0);
47
- (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "hasVoiceaJoined", void 0);
48
47
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "areCaptionsEnabled", void 0);
49
48
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "hasSubscribedToEvents", false);
50
49
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "vmcDeviceId", void 0);
50
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "announceStatus", void 0);
51
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "captionStatus", void 0);
51
52
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "eventProcessor", function (e) {
52
53
  _this.seqNum = e.sequenceNumber + 1;
53
54
  switch (e.data.relayType) {
54
55
  case _constants.VOICEA_RELAY_TYPES.ANNOUNCEMENT:
55
56
  _this.vmcDeviceId = e.headers.from;
56
- _this.hasVoiceaJoined = true;
57
+ _this.announceStatus = _constants.ANNOUNCE_STATUS.JOINED;
57
58
  _this.processAnnouncementMessage(e.data.voiceaPayload);
58
59
  break;
59
60
  case _constants.VOICEA_RELAY_TYPES.TRANSLATION_RESPONSE:
@@ -159,12 +160,14 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
159
160
  }, _constants.EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT, voiceaLanguageOptions);
160
161
  });
161
162
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "sendAnnouncement", function () {
162
- if (_this.hasVoiceaJoined || !_this.webex.internal.llm.isConnected()) return;
163
+ _this.announceStatus = _constants.ANNOUNCE_STATUS.JOINING;
163
164
  _this.listenToEvents();
165
+ // @ts-ignore
164
166
  _this.webex.internal.llm.socket.send({
165
167
  id: "".concat(_this.seqNum),
166
168
  type: 'publishRequest',
167
169
  recipients: {
170
+ // @ts-ignore
168
171
  route: _this.webex.internal.llm.getBinding()
169
172
  },
170
173
  headers: {},
@@ -180,27 +183,34 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
180
183
  _this.seqNum += 1;
181
184
  });
182
185
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "setSpokenLanguage", function (languageCode) {
183
- return _this.request({
184
- method: 'PUT',
185
- url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
186
- body: {
187
- languageCode: languageCode
188
- }
189
- }).then(function () {
190
- _pluginMeetings.TriggerProxy.trigger((0, _assertThisInitialized2.default)(_this), {
191
- file: 'voicea',
192
- function: 'setSpokenLanguage'
193
- }, _constants.EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE, {
194
- languageCode: languageCode
195
- });
196
- });
186
+ return (
187
+ // @ts-ignore
188
+ _this.request({
189
+ method: 'PUT',
190
+ // @ts-ignore
191
+ url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
192
+ body: {
193
+ languageCode: languageCode
194
+ }
195
+ }).then(function () {
196
+ _pluginMeetings.TriggerProxy.trigger((0, _assertThisInitialized2.default)(_this), {
197
+ file: 'voicea',
198
+ function: 'setSpokenLanguage'
199
+ }, _constants.EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE, {
200
+ languageCode: languageCode
201
+ });
202
+ })
203
+ );
197
204
  });
198
205
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "requestLanguage", function (languageCode) {
206
+ // @ts-ignore
199
207
  if (!_this.webex.internal.llm.isConnected()) return;
208
+ // @ts-ignore
200
209
  _this.webex.internal.llm.socket.send({
201
210
  id: "".concat(_this.seqNum),
202
211
  type: 'publishRequest',
203
212
  recipients: {
213
+ // @ts-ignore
204
214
  route: _this.webex.internal.llm.getBinding()
205
215
  },
206
216
  headers: {
@@ -218,33 +228,64 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
218
228
  });
219
229
  _this.seqNum += 1;
220
230
  });
231
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "requestTurnOnCaptions", function () {
232
+ _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.SENDING;
233
+ // @ts-ignore
234
+ // eslint-disable-next-line newline-before-return
235
+ return _this.request({
236
+ method: 'PUT',
237
+ // @ts-ignore
238
+ url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
239
+ body: {
240
+ transcribe: {
241
+ caption: true
242
+ }
243
+ }
244
+ }).then(function () {
245
+ _pluginMeetings.TriggerProxy.trigger((0, _assertThisInitialized2.default)(_this), {
246
+ file: 'voicea',
247
+ function: 'turnOnCaptions'
248
+ }, _constants.EVENT_TRIGGERS.CAPTIONS_TURNED_ON);
249
+ _this.areCaptionsEnabled = true;
250
+ _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.ENABLED;
251
+ _this.announce();
252
+ }).catch(function () {
253
+ _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.IDLE;
254
+ throw new Error('turn on captions fail');
255
+ });
256
+ });
257
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "isAnnounceProcessing", function () {
258
+ return [_constants.ANNOUNCE_STATUS.JOINING, _constants.ANNOUNCE_STATUS.JOINED].includes(_this.announceStatus);
259
+ });
260
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "announce", function () {
261
+ if (_this.isAnnounceProcessing()) return;
262
+ // @ts-ignore
263
+ if (!_this.webex.internal.llm.isConnected()) {
264
+ throw new Error('voicea can not announce before llm connected');
265
+ }
266
+ _this.sendAnnouncement();
267
+ });
268
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "isCaptionProcessing", function () {
269
+ return [_constants.TURN_ON_CAPTION_STATUS.SENDING, _constants.TURN_ON_CAPTION_STATUS.ENABLED].includes(_this.captionStatus);
270
+ });
221
271
  (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "turnOnCaptions", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
222
272
  return _regenerator.default.wrap(function _callee$(_context) {
223
273
  while (1) switch (_context.prev = _context.next) {
224
274
  case 0:
225
- if (!(_this.hasVoiceaJoined && _this.areCaptionsEnabled)) {
275
+ if (!_this.isCaptionProcessing()) {
226
276
  _context.next = 2;
227
277
  break;
228
278
  }
229
279
  return _context.abrupt("return", undefined);
230
280
  case 2:
231
- return _context.abrupt("return", _this.request({
232
- method: 'PUT',
233
- url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
234
- body: {
235
- transcribe: {
236
- caption: true
237
- }
238
- }
239
- }).then(function () {
240
- _pluginMeetings.TriggerProxy.trigger((0, _assertThisInitialized2.default)(_this), {
241
- file: 'voicea',
242
- function: 'turnOnCaptions'
243
- }, _constants.EVENT_TRIGGERS.CAPTIONS_TURNED_ON);
244
- _this.areCaptionsEnabled = true;
245
- _this.sendAnnouncement();
246
- }));
247
- case 3:
281
+ if (_this.webex.internal.llm.isConnected()) {
282
+ _context.next = 4;
283
+ break;
284
+ }
285
+ throw new Error('can not turn on captions before llm connected');
286
+ case 4:
287
+ return _context.abrupt("return", _this.requestTurnOnCaptions());
288
+ case 5:
248
289
  case "end":
249
290
  return _context.stop();
250
291
  }
@@ -257,6 +298,7 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
257
298
  case 0:
258
299
  return _context2.abrupt("return", _this.request({
259
300
  method: 'PUT',
301
+ // @ts-ignore
260
302
  url: "".concat(_this.webex.internal.llm.getLocusUrl(), "/controls/"),
261
303
  body: {
262
304
  transcribe: {
@@ -264,7 +306,7 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
264
306
  }
265
307
  }
266
308
  }).then(function () {
267
- if (activate && !_this.areCaptionsEnabled && !_this.hasVoiceaJoined) _this.turnOnCaptions();
309
+ if (activate && !_this.areCaptionsEnabled) _this.turnOnCaptions();
268
310
  }));
269
311
  case 1:
270
312
  case "end":
@@ -276,10 +318,17 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
276
318
  return _ref2.apply(this, arguments);
277
319
  };
278
320
  }());
321
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "getCaptionStatus", function () {
322
+ return _this.captionStatus;
323
+ });
324
+ (0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "getAnnounceStatus", function () {
325
+ return _this.announceStatus;
326
+ });
279
327
  _this.seqNum = 1;
280
- _this.hasVoiceaJoined = false;
281
328
  _this.areCaptionsEnabled = false;
282
329
  _this.vmcDeviceId = undefined;
330
+ _this.announceStatus = _constants.ANNOUNCE_STATUS.IDLE;
331
+ _this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.IDLE;
283
332
  return _this;
284
333
  }
285
334
 
@@ -302,18 +351,26 @@ var VoiceaChannel = /*#__PURE__*/function (_WebexPlugin) {
302
351
  */
303
352
  function listenToEvents() {
304
353
  if (!this.hasSubscribedToEvents) {
354
+ // @ts-ignore
305
355
  this.webex.internal.llm.on('event:relay.event', this.eventProcessor);
306
356
  this.hasSubscribedToEvents = true;
307
357
  }
308
358
  }
359
+
360
+ /**
361
+ * Listen to websocket messages
362
+ * @returns {void}
363
+ */
309
364
  }, {
310
365
  key: "deregisterEvents",
311
366
  value: function deregisterEvents() {
312
- this.hasVoiceaJoined = false;
313
367
  this.areCaptionsEnabled = false;
314
368
  this.vmcDeviceId = undefined;
369
+ // @ts-ignore
315
370
  this.webex.internal.llm.off('event:relay.event', this.eventProcessor);
316
371
  this.hasSubscribedToEvents = false;
372
+ this.announceStatus = _constants.ANNOUNCE_STATUS.IDLE;
373
+ this.captionStatus = _constants.TURN_ON_CAPTION_STATUS.IDLE;
317
374
  }
318
375
  }]);
319
376
  return VoiceaChannel;
@@ -1 +1 @@
1
- {"version":3,"names":["VoiceaChannel","args","VOICEA","e","seqNum","sequenceNumber","data","relayType","VOICEA_RELAY_TYPES","ANNOUNCEMENT","vmcDeviceId","headers","from","hasVoiceaJoined","processAnnouncementMessage","voiceaPayload","TRANSLATION_RESPONSE","processCaptionLanguageResponse","TRANSCRIPTION","processTranscription","type","TRANSCRIPTION_TYPE","TRANSCRIPT_INTERIM_RESULTS","Trigger","trigger","file","function","EVENT_TRIGGERS","NEW_CAPTION","isFinal","transcriptId","transcript_id","transcripts","TRANSCRIPT_FINAL_RESULT","transcript","csis","text","transcriptLanguageCode","transcript_language_code","timestamp","millisToMinutesAndSeconds","end_millis","HIGHLIGHT_CREATED","highlight","highlightId","highlight_id","highlightLabel","highlight_label","highlightSource","highlight_source","EVA_THANKS","EVA_COMMAND","isListening","command_response","EVA_WAKE","EVA_CANCEL","statusCode","CAPTION_LANGUAGE_UPDATE","errorCode","errorMessage","message","voiceaLanguageOptions","captionLanguages","translation","allowed_languages","maxLanguages","max_languages","spokenLanguages","ASR","spoken_languages","VOICEA_ANNOUNCEMENT","webex","internal","llm","isConnected","listenToEvents","socket","send","id","recipients","route","getBinding","clientPayload","version","eventType","CLIENT_ANNOUNCEMENT","trackingId","config","trackingIdPrefix","uuid","v4","toString","languageCode","request","method","url","getLocusUrl","body","then","SPOKEN_LANGUAGE_UPDATE","to","translationLanguage","TRANSLATION_REQUEST","areCaptionsEnabled","undefined","transcribe","caption","CAPTIONS_TURNED_ON","sendAnnouncement","activate","transcribing","turnOnCaptions","hasSubscribedToEvents","on","eventProcessor","off","WebexPlugin"],"sources":["voicea.ts"],"sourcesContent":["import uuid from 'uuid';\nimport {TriggerProxy as Trigger} from '@webex/plugin-meetings';\nimport {WebexPlugin, config} from '@webex/webex-core';\n\nimport {EVENT_TRIGGERS, VOICEA_RELAY_TYPES, TRANSCRIPTION_TYPE, VOICEA} from './constants';\n// eslint-disable-next-line no-unused-vars\nimport {\n AnnouncementPayload,\n CaptionLanguageResponse,\n TranscriptionResponse,\n IVoiceaChannel,\n} from './voicea.types';\nimport {millisToMinutesAndSeconds} from './utils';\n\n/**\n * @description VoiceaChannel to hold single instance of LLM\n * @export\n * @class VoiceaChannel\n */\nexport class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {\n namespace = VOICEA;\n\n private seqNum: number;\n\n private hasVoiceaJoined: boolean;\n\n private areCaptionsEnabled: boolean;\n\n private hasSubscribedToEvents = false;\n\n private vmcDeviceId?: string;\n\n /**\n * @param {Object} e\n * @returns {undefined}\n */\n\n private eventProcessor = (e) => {\n this.seqNum = e.sequenceNumber + 1;\n switch (e.data.relayType) {\n case VOICEA_RELAY_TYPES.ANNOUNCEMENT:\n this.vmcDeviceId = e.headers.from;\n this.hasVoiceaJoined = true;\n this.processAnnouncementMessage(e.data.voiceaPayload);\n break;\n case VOICEA_RELAY_TYPES.TRANSLATION_RESPONSE:\n this.processCaptionLanguageResponse(e.data.voiceaPayload);\n break;\n case VOICEA_RELAY_TYPES.TRANSCRIPTION:\n this.processTranscription(e.data.voiceaPayload);\n break;\n default:\n break;\n }\n };\n\n /**\n * Listen to websocket messages\n * @returns {undefined}\n */\n private listenToEvents() {\n if (!this.hasSubscribedToEvents) {\n this.webex.internal.llm.on('event:relay.event', this.eventProcessor);\n this.hasSubscribedToEvents = true;\n }\n }\n\n public deregisterEvents() {\n this.hasVoiceaJoined = false;\n this.areCaptionsEnabled = false;\n this.vmcDeviceId = undefined;\n this.webex.internal.llm.off('event:relay.event', this.eventProcessor);\n this.hasSubscribedToEvents = false;\n }\n\n /**\n * Initializes Voicea plugin\n * @param {any} args\n */\n constructor(...args) {\n super(...args);\n this.seqNum = 1;\n this.hasVoiceaJoined = false;\n this.areCaptionsEnabled = false;\n this.vmcDeviceId = undefined;\n }\n\n /**\n * Process Transcript and send alert\n * @param {TranscriptionResponse} voiceaPayload\n * @returns {void}\n */\n private processTranscription = (voiceaPayload: TranscriptionResponse): void => {\n switch (voiceaPayload.type) {\n case TRANSCRIPTION_TYPE.TRANSCRIPT_INTERIM_RESULTS:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.NEW_CAPTION,\n {\n isFinal: false,\n transcriptId: voiceaPayload.transcript_id,\n transcripts: voiceaPayload.transcripts,\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.TRANSCRIPT_FINAL_RESULT:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.NEW_CAPTION,\n {\n isFinal: true,\n transcriptId: voiceaPayload.transcript_id,\n transcript: {\n csis: voiceaPayload.csis,\n text: voiceaPayload.transcript.text,\n transcriptLanguageCode: voiceaPayload.transcript.transcript_language_code,\n },\n timestamp: millisToMinutesAndSeconds(voiceaPayload.transcript.end_millis),\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.HIGHLIGHT_CREATED:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.HIGHLIGHT_CREATED,\n {\n csis: voiceaPayload.highlight.csis,\n highlightId: voiceaPayload.highlight.highlight_id,\n text: voiceaPayload.highlight.transcript,\n highlightLabel: voiceaPayload.highlight.highlight_label,\n highlightSource: voiceaPayload.highlight.highlight_source,\n timestamp: millisToMinutesAndSeconds(voiceaPayload.highlight.end_millis),\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.EVA_THANKS:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.EVA_COMMAND,\n {\n isListening: false,\n text: voiceaPayload.command_response,\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.EVA_WAKE:\n case TRANSCRIPTION_TYPE.EVA_CANCEL:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.EVA_COMMAND,\n {\n isListening: voiceaPayload.type === TRANSCRIPTION_TYPE.EVA_WAKE,\n }\n );\n break;\n\n default:\n break;\n }\n };\n\n /**\n * Processes Caption Language Response\n * @param {CaptionLanguageResponse} voiceaPayload\n * @returns {void}\n */\n private processCaptionLanguageResponse = (voiceaPayload: CaptionLanguageResponse): void => {\n if (voiceaPayload.statusCode === 200) {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processCaptionLanguageResponse',\n },\n EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,\n {statusCode: 200}\n );\n } else {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processCaptionLanguageResponse',\n },\n EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,\n {statusCode: voiceaPayload.errorCode, errorMessage: voiceaPayload.message}\n );\n }\n };\n\n /**\n * processes voicea announcement response and triggers event\n * @param {Object} voiceaPayload\n * @returns {void}\n */\n private processAnnouncementMessage = (voiceaPayload: AnnouncementPayload): void => {\n const voiceaLanguageOptions = {\n captionLanguages: voiceaPayload?.translation?.allowed_languages ?? [],\n maxLanguages: voiceaPayload?.translation?.max_languages ?? 0,\n spokenLanguages: voiceaPayload?.ASR?.spoken_languages ?? [],\n };\n\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processAnnouncementMessage',\n },\n EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT,\n voiceaLanguageOptions\n );\n };\n\n /**\n * Sends Announcement to add voicea to the meeting\n * @returns {void}\n */\n private sendAnnouncement = (): void => {\n if (this.hasVoiceaJoined || !this.webex.internal.llm.isConnected()) return;\n\n this.listenToEvents();\n\n this.webex.internal.llm.socket.send({\n id: `${this.seqNum}`,\n type: 'publishRequest',\n recipients: {\n route: this.webex.internal.llm.getBinding(),\n },\n headers: {},\n data: {\n clientPayload: {\n version: 'v2',\n },\n eventType: 'relay.event',\n relayType: VOICEA_RELAY_TYPES.CLIENT_ANNOUNCEMENT,\n },\n trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,\n });\n this.seqNum += 1;\n };\n\n /**\n * Set Spoken Language for the meeting\n * @param {string} languageCode\n * @returns {Promise}\n */\n public setSpokenLanguage = (languageCode: string): Promise<void> =>\n this.request({\n method: 'PUT',\n url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,\n body: {\n languageCode,\n },\n }).then(() => {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'setSpokenLanguage',\n },\n EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE,\n {languageCode}\n );\n });\n\n /**\n * Request Language translation\n * @param {string} languageCode\n * @returns {void}\n */\n public requestLanguage = (languageCode: string): void => {\n if (!this.webex.internal.llm.isConnected()) return;\n this.webex.internal.llm.socket.send({\n id: `${this.seqNum}`,\n type: 'publishRequest',\n recipients: {\n route: this.webex.internal.llm.getBinding(),\n },\n headers: {\n to: this.vmcDeviceId,\n },\n data: {\n clientPayload: {\n translationLanguage: languageCode,\n id: uuid.v4(),\n },\n eventType: 'relay.event',\n relayType: VOICEA_RELAY_TYPES.TRANSLATION_REQUEST,\n },\n trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,\n });\n this.seqNum += 1;\n };\n\n /**\n * Turn on Captions\n * @returns {Promise}\n */\n public turnOnCaptions = async (): undefined | Promise<void> => {\n if (this.hasVoiceaJoined && this.areCaptionsEnabled) return undefined;\n\n return this.request({\n method: 'PUT',\n url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,\n body: {\n transcribe: {caption: true},\n },\n }).then(() => {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'turnOnCaptions',\n },\n EVENT_TRIGGERS.CAPTIONS_TURNED_ON\n );\n this.areCaptionsEnabled = true;\n this.sendAnnouncement();\n });\n };\n\n /**\n * Toggle transcribing for highlights\n * @param {bool} activate if true transcribing is turned on\n * @returns {Promise}\n */\n public toggleTranscribing = async (activate: boolean): undefined | Promise<void> => {\n return this.request({\n method: 'PUT',\n url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,\n body: {\n transcribe: {transcribing: activate},\n },\n }).then(() => {\n if (activate && !this.areCaptionsEnabled && !this.hasVoiceaJoined) this.turnOnCaptions();\n });\n };\n}\n\nexport default VoiceaChannel;\n"],"mappings":"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"}
1
+ {"version":3,"names":["VoiceaChannel","args","VOICEA","e","seqNum","sequenceNumber","data","relayType","VOICEA_RELAY_TYPES","ANNOUNCEMENT","vmcDeviceId","headers","from","announceStatus","ANNOUNCE_STATUS","JOINED","processAnnouncementMessage","voiceaPayload","TRANSLATION_RESPONSE","processCaptionLanguageResponse","TRANSCRIPTION","processTranscription","type","TRANSCRIPTION_TYPE","TRANSCRIPT_INTERIM_RESULTS","Trigger","trigger","file","function","EVENT_TRIGGERS","NEW_CAPTION","isFinal","transcriptId","transcript_id","transcripts","TRANSCRIPT_FINAL_RESULT","transcript","csis","text","transcriptLanguageCode","transcript_language_code","timestamp","millisToMinutesAndSeconds","end_millis","HIGHLIGHT_CREATED","highlight","highlightId","highlight_id","highlightLabel","highlight_label","highlightSource","highlight_source","EVA_THANKS","EVA_COMMAND","isListening","command_response","EVA_WAKE","EVA_CANCEL","statusCode","CAPTION_LANGUAGE_UPDATE","errorCode","errorMessage","message","voiceaLanguageOptions","captionLanguages","translation","allowed_languages","maxLanguages","max_languages","spokenLanguages","ASR","spoken_languages","VOICEA_ANNOUNCEMENT","JOINING","listenToEvents","webex","internal","llm","socket","send","id","recipients","route","getBinding","clientPayload","version","eventType","CLIENT_ANNOUNCEMENT","trackingId","config","trackingIdPrefix","uuid","v4","toString","languageCode","request","method","url","getLocusUrl","body","then","SPOKEN_LANGUAGE_UPDATE","isConnected","to","translationLanguage","TRANSLATION_REQUEST","captionStatus","TURN_ON_CAPTION_STATUS","SENDING","transcribe","caption","CAPTIONS_TURNED_ON","areCaptionsEnabled","ENABLED","announce","catch","IDLE","Error","includes","isAnnounceProcessing","sendAnnouncement","isCaptionProcessing","undefined","requestTurnOnCaptions","activate","transcribing","turnOnCaptions","hasSubscribedToEvents","on","eventProcessor","off","WebexPlugin"],"sources":["voicea.ts"],"sourcesContent":["import uuid from 'uuid';\nimport {TriggerProxy as Trigger} from '@webex/plugin-meetings';\nimport {WebexPlugin, config} from '@webex/webex-core';\n\nimport {\n EVENT_TRIGGERS,\n VOICEA_RELAY_TYPES,\n TRANSCRIPTION_TYPE,\n VOICEA,\n ANNOUNCE_STATUS,\n TURN_ON_CAPTION_STATUS,\n} from './constants';\n// eslint-disable-next-line no-unused-vars\nimport {\n AnnouncementPayload,\n CaptionLanguageResponse,\n TranscriptionResponse,\n IVoiceaChannel,\n} from './voicea.types';\nimport {millisToMinutesAndSeconds} from './utils';\n\n/**\n * @description VoiceaChannel to hold single instance of LLM\n * @export\n * @class VoiceaChannel\n */\nexport class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {\n namespace = VOICEA;\n\n private seqNum: number;\n\n private areCaptionsEnabled: boolean;\n\n private hasSubscribedToEvents = false;\n\n private vmcDeviceId?: string;\n\n private announceStatus: string;\n\n private captionStatus: string;\n\n /**\n * @param {Object} e\n * @returns {undefined}\n */\n\n private eventProcessor = (e) => {\n this.seqNum = e.sequenceNumber + 1;\n switch (e.data.relayType) {\n case VOICEA_RELAY_TYPES.ANNOUNCEMENT:\n this.vmcDeviceId = e.headers.from;\n this.announceStatus = ANNOUNCE_STATUS.JOINED;\n this.processAnnouncementMessage(e.data.voiceaPayload);\n break;\n case VOICEA_RELAY_TYPES.TRANSLATION_RESPONSE:\n this.processCaptionLanguageResponse(e.data.voiceaPayload);\n break;\n case VOICEA_RELAY_TYPES.TRANSCRIPTION:\n this.processTranscription(e.data.voiceaPayload);\n break;\n default:\n break;\n }\n };\n\n /**\n * Listen to websocket messages\n * @returns {undefined}\n */\n private listenToEvents() {\n if (!this.hasSubscribedToEvents) {\n // @ts-ignore\n this.webex.internal.llm.on('event:relay.event', this.eventProcessor);\n this.hasSubscribedToEvents = true;\n }\n }\n\n /**\n * Listen to websocket messages\n * @returns {void}\n */\n public deregisterEvents() {\n this.areCaptionsEnabled = false;\n this.vmcDeviceId = undefined;\n // @ts-ignore\n this.webex.internal.llm.off('event:relay.event', this.eventProcessor);\n this.hasSubscribedToEvents = false;\n this.announceStatus = ANNOUNCE_STATUS.IDLE;\n this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;\n }\n\n /**\n * Initializes Voicea plugin\n * @param {any} args\n */\n constructor(...args) {\n super(...args);\n this.seqNum = 1;\n this.areCaptionsEnabled = false;\n this.vmcDeviceId = undefined;\n this.announceStatus = ANNOUNCE_STATUS.IDLE;\n this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;\n }\n\n /**\n * Process Transcript and send alert\n * @param {TranscriptionResponse} voiceaPayload\n * @returns {void}\n */\n private processTranscription = (voiceaPayload: TranscriptionResponse): void => {\n switch (voiceaPayload.type) {\n case TRANSCRIPTION_TYPE.TRANSCRIPT_INTERIM_RESULTS:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.NEW_CAPTION,\n {\n isFinal: false,\n transcriptId: voiceaPayload.transcript_id,\n transcripts: voiceaPayload.transcripts,\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.TRANSCRIPT_FINAL_RESULT:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.NEW_CAPTION,\n {\n isFinal: true,\n transcriptId: voiceaPayload.transcript_id,\n transcript: {\n csis: voiceaPayload.csis,\n text: voiceaPayload.transcript.text,\n transcriptLanguageCode: voiceaPayload.transcript.transcript_language_code,\n },\n timestamp: millisToMinutesAndSeconds(voiceaPayload.transcript.end_millis),\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.HIGHLIGHT_CREATED:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.HIGHLIGHT_CREATED,\n {\n csis: voiceaPayload.highlight.csis,\n highlightId: voiceaPayload.highlight.highlight_id,\n text: voiceaPayload.highlight.transcript,\n highlightLabel: voiceaPayload.highlight.highlight_label,\n highlightSource: voiceaPayload.highlight.highlight_source,\n timestamp: millisToMinutesAndSeconds(voiceaPayload.highlight.end_millis),\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.EVA_THANKS:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.EVA_COMMAND,\n {\n isListening: false,\n text: voiceaPayload.command_response,\n }\n );\n break;\n\n case TRANSCRIPTION_TYPE.EVA_WAKE:\n case TRANSCRIPTION_TYPE.EVA_CANCEL:\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processTranscription',\n },\n EVENT_TRIGGERS.EVA_COMMAND,\n {\n isListening: voiceaPayload.type === TRANSCRIPTION_TYPE.EVA_WAKE,\n }\n );\n break;\n\n default:\n break;\n }\n };\n\n /**\n * Processes Caption Language Response\n * @param {CaptionLanguageResponse} voiceaPayload\n * @returns {void}\n */\n private processCaptionLanguageResponse = (voiceaPayload: CaptionLanguageResponse): void => {\n if (voiceaPayload.statusCode === 200) {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processCaptionLanguageResponse',\n },\n EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,\n {statusCode: 200}\n );\n } else {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processCaptionLanguageResponse',\n },\n EVENT_TRIGGERS.CAPTION_LANGUAGE_UPDATE,\n {statusCode: voiceaPayload.errorCode, errorMessage: voiceaPayload.message}\n );\n }\n };\n\n /**\n * processes voicea announcement response and triggers event\n * @param {Object} voiceaPayload\n * @returns {void}\n */\n private processAnnouncementMessage = (voiceaPayload: AnnouncementPayload): void => {\n const voiceaLanguageOptions = {\n captionLanguages: voiceaPayload?.translation?.allowed_languages ?? [],\n maxLanguages: voiceaPayload?.translation?.max_languages ?? 0,\n spokenLanguages: voiceaPayload?.ASR?.spoken_languages ?? [],\n };\n\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'processAnnouncementMessage',\n },\n EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT,\n voiceaLanguageOptions\n );\n };\n\n /**\n * Sends Announcement to add voicea to the meeting\n * @returns {void}\n */\n private sendAnnouncement = (): void => {\n this.announceStatus = ANNOUNCE_STATUS.JOINING;\n this.listenToEvents();\n // @ts-ignore\n this.webex.internal.llm.socket.send({\n id: `${this.seqNum}`,\n type: 'publishRequest',\n recipients: {\n // @ts-ignore\n route: this.webex.internal.llm.getBinding(),\n },\n headers: {},\n data: {\n clientPayload: {\n version: 'v2',\n },\n eventType: 'relay.event',\n relayType: VOICEA_RELAY_TYPES.CLIENT_ANNOUNCEMENT,\n },\n trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,\n });\n this.seqNum += 1;\n };\n\n /**\n * Set Spoken Language for the meeting\n * @param {string} languageCode\n * @returns {Promise}\n */\n public setSpokenLanguage = (languageCode: string): Promise<void> =>\n // @ts-ignore\n this.request({\n method: 'PUT',\n // @ts-ignore\n url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,\n body: {\n languageCode,\n },\n }).then(() => {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'setSpokenLanguage',\n },\n EVENT_TRIGGERS.SPOKEN_LANGUAGE_UPDATE,\n {languageCode}\n );\n });\n\n /**\n * Request Language translation\n * @param {string} languageCode\n * @returns {void}\n */\n public requestLanguage = (languageCode: string): void => {\n // @ts-ignore\n if (!this.webex.internal.llm.isConnected()) return;\n // @ts-ignore\n this.webex.internal.llm.socket.send({\n id: `${this.seqNum}`,\n type: 'publishRequest',\n recipients: {\n // @ts-ignore\n route: this.webex.internal.llm.getBinding(),\n },\n headers: {\n to: this.vmcDeviceId,\n },\n data: {\n clientPayload: {\n translationLanguage: languageCode,\n id: uuid.v4(),\n },\n eventType: 'relay.event',\n relayType: VOICEA_RELAY_TYPES.TRANSLATION_REQUEST,\n },\n trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,\n });\n this.seqNum += 1;\n };\n\n /**\n * request turn on Captions\n * @returns {Promise}\n */\n private requestTurnOnCaptions = (): undefined | Promise<void> => {\n this.captionStatus = TURN_ON_CAPTION_STATUS.SENDING;\n // @ts-ignore\n // eslint-disable-next-line newline-before-return\n return this.request({\n method: 'PUT',\n // @ts-ignore\n url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,\n body: {\n transcribe: {caption: true},\n },\n })\n .then(() => {\n Trigger.trigger(\n this,\n {\n file: 'voicea',\n function: 'turnOnCaptions',\n },\n EVENT_TRIGGERS.CAPTIONS_TURNED_ON\n );\n this.areCaptionsEnabled = true;\n this.captionStatus = TURN_ON_CAPTION_STATUS.ENABLED;\n this.announce();\n })\n .catch(() => {\n this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;\n throw new Error('turn on captions fail');\n });\n };\n\n /**\n * is announce processing\n * @returns {boolean}\n */\n private isAnnounceProcessing = () =>\n [ANNOUNCE_STATUS.JOINING, ANNOUNCE_STATUS.JOINED].includes(this.announceStatus);\n\n /**\n * announce to voicea data chanel\n * @returns {void}\n */\n public announce = () => {\n if (this.isAnnounceProcessing()) return;\n // @ts-ignore\n if (!this.webex.internal.llm.isConnected()) {\n throw new Error('voicea can not announce before llm connected');\n }\n this.sendAnnouncement();\n };\n\n /**\n * is turn on caption processing\n * @returns {boolean}\n */\n private isCaptionProcessing = () =>\n [TURN_ON_CAPTION_STATUS.SENDING, TURN_ON_CAPTION_STATUS.ENABLED].includes(this.captionStatus);\n\n /**\n * Turn on Captions\n * @returns {Promise}\n */\n public turnOnCaptions = async (): undefined | Promise<void> => {\n if (this.isCaptionProcessing()) return undefined;\n // @ts-ignore\n if (!this.webex.internal.llm.isConnected()) {\n throw new Error('can not turn on captions before llm connected');\n }\n\n return this.requestTurnOnCaptions();\n };\n\n /**\n * Toggle transcribing for highlights\n * @param {bool} activate if true transcribing is turned on\n * @returns {Promise}\n */\n public toggleTranscribing = async (activate: boolean): undefined | Promise<void> => {\n // @ts-ignore\n return this.request({\n method: 'PUT',\n // @ts-ignore\n url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,\n body: {\n transcribe: {transcribing: activate},\n },\n }).then(() => {\n if (activate && !this.areCaptionsEnabled) this.turnOnCaptions();\n });\n };\n\n /**\n * get caption status\n * @returns {string}\n */\n public getCaptionStatus = () => this.captionStatus;\n\n /**\n * get announce status\n * @returns {string}\n */\n public getAnnounceStatus = () => this.announceStatus;\n}\n\nexport default VoiceaChannel;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"names":[],"sources":["voicea.types.ts"],"sourcesContent":["/* eslint-disable camelcase */\n/* eslint-disable no-undef */\n\n/**\n * Type for payload\n */\ninterface AnnouncementPayload {\n translation: {\n max_languages: number;\n allowed_languages: string[];\n };\n\n ASR: {\n spoken_languages: string[];\n };\n}\n\n// eslint-disable-next-line no-undef\ntype TRANSCRIPTION_TYPES =\n | 'unknown'\n | 'eva_wake'\n | 'eva_thanks'\n | 'eva_cancel'\n | 'highlight_created'\n | 'transcript_interim_results'\n | 'transcript_final_result';\n\n/**\n * Class for an Transcription Object\n */\ninterface Transcription {\n start_millis: number;\n end_millis: number;\n text: string;\n transcript_language_code: string;\n translations: {[x: string]: string};\n csis: number[];\n last_packet_timestamp_ms: number;\n}\n\n/**\n * Highlights\n */\ninterface Highlight {\n highlight_id: string;\n transcript: string;\n highlight_label: string;\n highlight_source: string;\n start_millis: number;\n end_millis: number;\n csis: number[];\n}\n/**\n * Type for Transcription message\n */\ninterface TranscriptionResponse {\n type: TRANSCRIPTION_TYPES;\n transcript_id: string;\n translations?: {[x: string]: string};\n transcripts?: Transcription[];\n transcript?: Transcription;\n highlight?: Highlight;\n csis: number[];\n data: string;\n command_response: string;\n}\n/**\n * Type for CaptionLanguageResponse\n */\ninterface CaptionLanguageResponse {\n requestId: string;\n statusCode: number;\n errorCode: number;\n message: string;\n}\n\ninterface IVoiceaChannel {\n setSpokenLanguage: (languageCode: string) => Promise<void>;\n requestLanguage: (languageCode: string) => void;\n turnOnCaptions: () => undefined | Promise<void>;\n toggleTranscribing: (activate: boolean) => undefined | Promise<void>;\n deregisterEvents: () => undefined | Promise<void>;\n}\n\nexport {\n AnnouncementPayload,\n CaptionLanguageResponse,\n TranscriptionResponse,\n Transcription,\n Highlight,\n IVoiceaChannel,\n};\n"],"mappings":""}
1
+ {"version":3,"names":[],"sources":["voicea.types.ts"],"sourcesContent":["/* eslint-disable camelcase */\n/* eslint-disable no-undef */\n\n/**\n * Type for payload\n */\ninterface AnnouncementPayload {\n translation: {\n max_languages: number;\n allowed_languages: string[];\n };\n\n ASR: {\n spoken_languages: string[];\n };\n}\n\n// eslint-disable-next-line no-undef\ntype TRANSCRIPTION_TYPES =\n | 'unknown'\n | 'eva_wake'\n | 'eva_thanks'\n | 'eva_cancel'\n | 'highlight_created'\n | 'transcript_interim_results'\n | 'transcript_final_result';\n\n/**\n * Class for an Transcription Object\n */\ninterface Transcription {\n start_millis: number;\n end_millis: number;\n text: string;\n transcript_language_code: string;\n translations: {[x: string]: string};\n csis: number[];\n last_packet_timestamp_ms: number;\n}\n\n/**\n * Highlights\n */\ninterface Highlight {\n highlight_id: string;\n transcript: string;\n highlight_label: string;\n highlight_source: string;\n start_millis: number;\n end_millis: number;\n csis: number[];\n}\n/**\n * Type for Transcription message\n */\ninterface TranscriptionResponse {\n type: TRANSCRIPTION_TYPES;\n transcript_id: string;\n translations?: {[x: string]: string};\n transcripts?: Transcription[];\n transcript?: Transcription;\n highlight?: Highlight;\n csis: number[];\n data: string;\n command_response: string;\n}\n/**\n * Type for CaptionLanguageResponse\n */\ninterface CaptionLanguageResponse {\n requestId: string;\n statusCode: number;\n errorCode: number;\n message: string;\n}\n\ninterface IVoiceaChannel {\n setSpokenLanguage: (languageCode: string) => Promise<void>;\n requestLanguage: (languageCode: string) => void;\n turnOnCaptions: () => undefined | Promise<void>;\n toggleTranscribing: (activate: boolean) => undefined | Promise<void>;\n deregisterEvents: () => void;\n}\n\nexport type {\n AnnouncementPayload,\n CaptionLanguageResponse,\n TranscriptionResponse,\n Transcription,\n Highlight,\n IVoiceaChannel,\n};\n"],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/internal-plugin-voicea",
3
- "version": "3.0.0-beta.17",
3
+ "version": "3.0.0-beta.170",
4
4
  "description": "",
5
5
  "license": "Cisco EULA (https://www.cisco.com/c/en/us/products/end-user-license-agreement.html)",
6
6
  "main": "dist/index.js",
@@ -14,11 +14,11 @@
14
14
  "node": ">=16"
15
15
  },
16
16
  "dependencies": {
17
- "@webex/internal-plugin-llm": "3.0.0-beta.17",
18
- "@webex/internal-plugin-mercury": "3.0.0-beta.17",
19
- "@webex/internal-plugin-voicea": "3.0.0-beta.17",
20
- "@webex/plugin-meetings": "3.0.0-beta.17",
21
- "@webex/webex-core": "3.0.0-beta.17",
17
+ "@webex/internal-plugin-llm": "3.0.0-beta.170",
18
+ "@webex/internal-plugin-mercury": "3.0.0-beta.170",
19
+ "@webex/internal-plugin-voicea": "3.0.0-beta.170",
20
+ "@webex/plugin-meetings": "3.0.0-beta.170",
21
+ "@webex/webex-core": "3.0.0-beta.170",
22
22
  "uuid": "^3.3.2"
23
23
  },
24
24
  "browserify": {
@@ -28,9 +28,9 @@
28
28
  ]
29
29
  },
30
30
  "devDependencies": {
31
- "@webex/test-helper-chai": "3.0.0-beta.17",
32
- "@webex/test-helper-mock-web-socket": "3.0.0-beta.17",
33
- "@webex/test-helper-mock-webex": "3.0.0-beta.17",
31
+ "@webex/test-helper-chai": "3.0.0-beta.170",
32
+ "@webex/test-helper-mock-web-socket": "3.0.0-beta.170",
33
+ "@webex/test-helper-mock-webex": "3.0.0-beta.170",
34
34
  "sinon": "^9.2.4"
35
35
  }
36
36
  }
package/src/constants.ts CHANGED
@@ -30,3 +30,15 @@ export const TRANSCRIPTION_TYPE = {
30
30
  };
31
31
 
32
32
  export const VOICEA = 'voicea';
33
+
34
+ export const ANNOUNCE_STATUS = {
35
+ IDLE: 'idle',
36
+ JOINED: 'joined',
37
+ JOINING: 'joining',
38
+ };
39
+
40
+ export const TURN_ON_CAPTION_STATUS = {
41
+ IDLE: 'idle',
42
+ ENABLED: 'enabled',
43
+ SENDING: 'sending',
44
+ };
package/src/voicea.ts CHANGED
@@ -2,7 +2,14 @@ import uuid from 'uuid';
2
2
  import {TriggerProxy as Trigger} from '@webex/plugin-meetings';
3
3
  import {WebexPlugin, config} from '@webex/webex-core';
4
4
 
5
- import {EVENT_TRIGGERS, VOICEA_RELAY_TYPES, TRANSCRIPTION_TYPE, VOICEA} from './constants';
5
+ import {
6
+ EVENT_TRIGGERS,
7
+ VOICEA_RELAY_TYPES,
8
+ TRANSCRIPTION_TYPE,
9
+ VOICEA,
10
+ ANNOUNCE_STATUS,
11
+ TURN_ON_CAPTION_STATUS,
12
+ } from './constants';
6
13
  // eslint-disable-next-line no-unused-vars
7
14
  import {
8
15
  AnnouncementPayload,
@@ -22,14 +29,16 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
22
29
 
23
30
  private seqNum: number;
24
31
 
25
- private hasVoiceaJoined: boolean;
26
-
27
32
  private areCaptionsEnabled: boolean;
28
33
 
29
34
  private hasSubscribedToEvents = false;
30
35
 
31
36
  private vmcDeviceId?: string;
32
37
 
38
+ private announceStatus: string;
39
+
40
+ private captionStatus: string;
41
+
33
42
  /**
34
43
  * @param {Object} e
35
44
  * @returns {undefined}
@@ -40,7 +49,7 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
40
49
  switch (e.data.relayType) {
41
50
  case VOICEA_RELAY_TYPES.ANNOUNCEMENT:
42
51
  this.vmcDeviceId = e.headers.from;
43
- this.hasVoiceaJoined = true;
52
+ this.announceStatus = ANNOUNCE_STATUS.JOINED;
44
53
  this.processAnnouncementMessage(e.data.voiceaPayload);
45
54
  break;
46
55
  case VOICEA_RELAY_TYPES.TRANSLATION_RESPONSE:
@@ -60,17 +69,24 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
60
69
  */
61
70
  private listenToEvents() {
62
71
  if (!this.hasSubscribedToEvents) {
72
+ // @ts-ignore
63
73
  this.webex.internal.llm.on('event:relay.event', this.eventProcessor);
64
74
  this.hasSubscribedToEvents = true;
65
75
  }
66
76
  }
67
77
 
78
+ /**
79
+ * Listen to websocket messages
80
+ * @returns {void}
81
+ */
68
82
  public deregisterEvents() {
69
- this.hasVoiceaJoined = false;
70
83
  this.areCaptionsEnabled = false;
71
84
  this.vmcDeviceId = undefined;
85
+ // @ts-ignore
72
86
  this.webex.internal.llm.off('event:relay.event', this.eventProcessor);
73
87
  this.hasSubscribedToEvents = false;
88
+ this.announceStatus = ANNOUNCE_STATUS.IDLE;
89
+ this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
74
90
  }
75
91
 
76
92
  /**
@@ -80,9 +96,10 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
80
96
  constructor(...args) {
81
97
  super(...args);
82
98
  this.seqNum = 1;
83
- this.hasVoiceaJoined = false;
84
99
  this.areCaptionsEnabled = false;
85
100
  this.vmcDeviceId = undefined;
101
+ this.announceStatus = ANNOUNCE_STATUS.IDLE;
102
+ this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
86
103
  }
87
104
 
88
105
  /**
@@ -240,14 +257,14 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
240
257
  * @returns {void}
241
258
  */
242
259
  private sendAnnouncement = (): void => {
243
- if (this.hasVoiceaJoined || !this.webex.internal.llm.isConnected()) return;
244
-
260
+ this.announceStatus = ANNOUNCE_STATUS.JOINING;
245
261
  this.listenToEvents();
246
-
262
+ // @ts-ignore
247
263
  this.webex.internal.llm.socket.send({
248
264
  id: `${this.seqNum}`,
249
265
  type: 'publishRequest',
250
266
  recipients: {
267
+ // @ts-ignore
251
268
  route: this.webex.internal.llm.getBinding(),
252
269
  },
253
270
  headers: {},
@@ -269,8 +286,10 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
269
286
  * @returns {Promise}
270
287
  */
271
288
  public setSpokenLanguage = (languageCode: string): Promise<void> =>
289
+ // @ts-ignore
272
290
  this.request({
273
291
  method: 'PUT',
292
+ // @ts-ignore
274
293
  url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
275
294
  body: {
276
295
  languageCode,
@@ -293,11 +312,14 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
293
312
  * @returns {void}
294
313
  */
295
314
  public requestLanguage = (languageCode: string): void => {
315
+ // @ts-ignore
296
316
  if (!this.webex.internal.llm.isConnected()) return;
317
+ // @ts-ignore
297
318
  this.webex.internal.llm.socket.send({
298
319
  id: `${this.seqNum}`,
299
320
  type: 'publishRequest',
300
321
  recipients: {
322
+ // @ts-ignore
301
323
  route: this.webex.internal.llm.getBinding(),
302
324
  },
303
325
  headers: {
@@ -317,30 +339,79 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
317
339
  };
318
340
 
319
341
  /**
320
- * Turn on Captions
342
+ * request turn on Captions
321
343
  * @returns {Promise}
322
344
  */
323
- public turnOnCaptions = async (): undefined | Promise<void> => {
324
- if (this.hasVoiceaJoined && this.areCaptionsEnabled) return undefined;
325
-
345
+ private requestTurnOnCaptions = (): undefined | Promise<void> => {
346
+ this.captionStatus = TURN_ON_CAPTION_STATUS.SENDING;
347
+ // @ts-ignore
348
+ // eslint-disable-next-line newline-before-return
326
349
  return this.request({
327
350
  method: 'PUT',
351
+ // @ts-ignore
328
352
  url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
329
353
  body: {
330
354
  transcribe: {caption: true},
331
355
  },
332
- }).then(() => {
333
- Trigger.trigger(
334
- this,
335
- {
336
- file: 'voicea',
337
- function: 'turnOnCaptions',
338
- },
339
- EVENT_TRIGGERS.CAPTIONS_TURNED_ON
340
- );
341
- this.areCaptionsEnabled = true;
342
- this.sendAnnouncement();
343
- });
356
+ })
357
+ .then(() => {
358
+ Trigger.trigger(
359
+ this,
360
+ {
361
+ file: 'voicea',
362
+ function: 'turnOnCaptions',
363
+ },
364
+ EVENT_TRIGGERS.CAPTIONS_TURNED_ON
365
+ );
366
+ this.areCaptionsEnabled = true;
367
+ this.captionStatus = TURN_ON_CAPTION_STATUS.ENABLED;
368
+ this.announce();
369
+ })
370
+ .catch(() => {
371
+ this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
372
+ throw new Error('turn on captions fail');
373
+ });
374
+ };
375
+
376
+ /**
377
+ * is announce processing
378
+ * @returns {boolean}
379
+ */
380
+ private isAnnounceProcessing = () =>
381
+ [ANNOUNCE_STATUS.JOINING, ANNOUNCE_STATUS.JOINED].includes(this.announceStatus);
382
+
383
+ /**
384
+ * announce to voicea data chanel
385
+ * @returns {void}
386
+ */
387
+ public announce = () => {
388
+ if (this.isAnnounceProcessing()) return;
389
+ // @ts-ignore
390
+ if (!this.webex.internal.llm.isConnected()) {
391
+ throw new Error('voicea can not announce before llm connected');
392
+ }
393
+ this.sendAnnouncement();
394
+ };
395
+
396
+ /**
397
+ * is turn on caption processing
398
+ * @returns {boolean}
399
+ */
400
+ private isCaptionProcessing = () =>
401
+ [TURN_ON_CAPTION_STATUS.SENDING, TURN_ON_CAPTION_STATUS.ENABLED].includes(this.captionStatus);
402
+
403
+ /**
404
+ * Turn on Captions
405
+ * @returns {Promise}
406
+ */
407
+ public turnOnCaptions = async (): undefined | Promise<void> => {
408
+ if (this.isCaptionProcessing()) return undefined;
409
+ // @ts-ignore
410
+ if (!this.webex.internal.llm.isConnected()) {
411
+ throw new Error('can not turn on captions before llm connected');
412
+ }
413
+
414
+ return this.requestTurnOnCaptions();
344
415
  };
345
416
 
346
417
  /**
@@ -349,16 +420,30 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
349
420
  * @returns {Promise}
350
421
  */
351
422
  public toggleTranscribing = async (activate: boolean): undefined | Promise<void> => {
423
+ // @ts-ignore
352
424
  return this.request({
353
425
  method: 'PUT',
426
+ // @ts-ignore
354
427
  url: `${this.webex.internal.llm.getLocusUrl()}/controls/`,
355
428
  body: {
356
429
  transcribe: {transcribing: activate},
357
430
  },
358
431
  }).then(() => {
359
- if (activate && !this.areCaptionsEnabled && !this.hasVoiceaJoined) this.turnOnCaptions();
432
+ if (activate && !this.areCaptionsEnabled) this.turnOnCaptions();
360
433
  });
361
434
  };
435
+
436
+ /**
437
+ * get caption status
438
+ * @returns {string}
439
+ */
440
+ public getCaptionStatus = () => this.captionStatus;
441
+
442
+ /**
443
+ * get announce status
444
+ * @returns {string}
445
+ */
446
+ public getAnnounceStatus = () => this.announceStatus;
362
447
  }
363
448
 
364
449
  export default VoiceaChannel;
@@ -79,10 +79,10 @@ interface IVoiceaChannel {
79
79
  requestLanguage: (languageCode: string) => void;
80
80
  turnOnCaptions: () => undefined | Promise<void>;
81
81
  toggleTranscribing: (activate: boolean) => undefined | Promise<void>;
82
- deregisterEvents: () => undefined | Promise<void>;
82
+ deregisterEvents: () => void;
83
83
  }
84
84
 
85
- export {
85
+ export type {
86
86
  AnnouncementPayload,
87
87
  CaptionLanguageResponse,
88
88
  TranscriptionResponse,
@@ -1,6 +1,7 @@
1
+ import 'jsdom-global/register';
1
2
  import MockWebex from '@webex/test-helper-mock-webex';
2
3
  import MockWebSocket from '@webex/test-helper-mock-web-socket';
3
- import {assert} from '@webex/test-helper-chai';
4
+ import {assert, expect} from '@webex/test-helper-chai';
4
5
  import sinon from 'sinon';
5
6
  import Mercury from '@webex/internal-plugin-mercury';
6
7
  import LLMChannel from '@webex/internal-plugin-llm';
@@ -41,17 +42,26 @@ describe('plugin-voicea', () => {
41
42
  });
42
43
  });
43
44
 
45
+ describe("#constructor", () => {
46
+ it('should init status', () => {
47
+ assert.equal(voiceaService.announceStatus, 'idle');
48
+ assert.equal(voiceaService.captionStatus, 'idle');
49
+ });
50
+ });
51
+
44
52
  describe('#sendAnnouncement', () => {
45
53
  beforeEach(async () => {
46
54
  const mockWebSocket = new MockWebSocket();
47
55
 
48
56
  voiceaService.webex.internal.llm.socket = mockWebSocket;
57
+ voiceaService.announceStatus = "idle";
49
58
  });
50
59
 
51
60
  it("sends announcement if voicea hasn't joined", () => {
52
61
  const spy = sinon.spy(voiceaService, 'listenToEvents');
53
62
 
54
63
  voiceaService.sendAnnouncement();
64
+ assert.equal(voiceaService.announceStatus, 'joining');
55
65
  assert.calledOnce(spy);
56
66
 
57
67
  assert.calledOnceWithExactly(voiceaService.webex.internal.llm.socket.send, {
@@ -98,14 +108,14 @@ describe('plugin-voicea', () => {
98
108
  data: {relayType: 'voicea.annc', voiceaPayload: {}},
99
109
  });
100
110
 
101
- assert.equal(voiceaService.hasVoiceaJoined, true);
102
111
  assert.equal(voiceaService.areCaptionsEnabled, true);
103
112
  assert.equal(voiceaService.vmcDeviceId, 'ws');
104
113
 
105
114
  voiceaService.deregisterEvents();
106
- assert.equal(voiceaService.hasVoiceaJoined, false);
107
115
  assert.equal(voiceaService.areCaptionsEnabled, false);
108
116
  assert.equal(voiceaService.vmcDeviceId, undefined);
117
+ assert.equal(voiceaService.announceStatus, 'idle');
118
+ assert.equal(voiceaService.captionStatus, 'idle');
109
119
  });
110
120
  });
111
121
  describe('#processAnnouncementMessage', () => {
@@ -198,22 +208,28 @@ describe('plugin-voicea', () => {
198
208
  });
199
209
  });
200
210
 
201
- describe('#turnOnCaptions', () => {
211
+ describe('#requestTurnOnCaptions', () => {
202
212
  beforeEach(async () => {
203
213
  const mockWebSocket = new MockWebSocket();
204
214
 
205
215
  voiceaService.webex.internal.llm.socket = mockWebSocket;
216
+ voiceaService.captionStatus = 'idle';
206
217
  });
207
218
 
219
+ afterEach( () => {
220
+ voiceaService.captionStatus = 'idle';
221
+ })
222
+
208
223
  it('turns on captions', async () => {
209
- const announcementSpy = sinon.spy(voiceaService, 'sendAnnouncement');
224
+ const announcementSpy = sinon.spy(voiceaService, 'announce');
210
225
 
211
226
  const triggerSpy = sinon.spy();
212
227
 
213
228
  voiceaService.on(EVENT_TRIGGERS.CAPTIONS_TURNED_ON, triggerSpy);
214
229
  voiceaService.listenToEvents();
215
230
 
216
- await voiceaService.turnOnCaptions();
231
+ await voiceaService.requestTurnOnCaptions();
232
+ assert.equal(voiceaService.captionStatus, 'enabled');
217
233
  sinon.assert.calledWith(
218
234
  voiceaService.request,
219
235
  sinon.match({
@@ -228,18 +244,119 @@ describe('plugin-voicea', () => {
228
244
  assert.calledOnce(announcementSpy);
229
245
  });
230
246
 
231
- it("doesn't call API on captions", async () => {
232
- await voiceaService.turnOnCaptions();
247
+ it("should handle request fail", async () => {
248
+ voiceaService.captionStatus = 'sending';
249
+ voiceaService.request = sinon.stub().rejects();
250
+
251
+ try {
252
+ await voiceaService.requestTurnOnCaptions();
253
+ } catch (error) {
254
+ expect(error.message).to.include('turn on captions fail');
255
+ return;
256
+ }
257
+ assert.equal(voiceaService.captionStatus, 'idle');
258
+ });
259
+ });
233
260
 
234
- // eslint-disable-next-line no-underscore-dangle
235
- voiceaService.webex.internal.llm._emit('event:relay.event', {
236
- headers: {from: 'ws'},
237
- data: {relayType: 'voicea.annc', voiceaPayload: {}},
261
+ describe("#isAnnounceProcessing", () => {
262
+ afterEach(() => {
263
+ voiceaService.announceStatus = 'idle';
264
+ });
265
+
266
+ ['joining', 'joined'].forEach((status) => {
267
+ it(`should return true when status is ${status}`, () => {
268
+ voiceaService.announceStatus = status;
269
+ assert.equal(voiceaService.isAnnounceProcessing(), true);
270
+ });
271
+ });
272
+
273
+ it('should return false when status is not processing status', () => {
274
+ voiceaService.announceStatus = 'idle';
275
+ assert.equal(voiceaService.isAnnounceProcessing(), false);
276
+ });
277
+ });
278
+
279
+ describe("#announce", () => {
280
+ let isAnnounceProcessing, sendAnnouncement;
281
+ beforeEach(() => {
282
+ voiceaService.webex.internal.llm.isConnected.returns(true);
283
+ sendAnnouncement = sinon.stub(voiceaService, 'sendAnnouncement');
284
+ isAnnounceProcessing = sinon.stub(voiceaService, 'isAnnounceProcessing').returns(false)
285
+ });
286
+
287
+ afterEach(() => {
288
+ voiceaService.webex.internal.llm.isConnected.returns(true);
289
+ isAnnounceProcessing.restore();
290
+ sendAnnouncement.restore();
291
+ });
292
+
293
+ it('announce to llm data channel', ()=> {
294
+ voiceaService.announce();
295
+ assert.calledOnce(sendAnnouncement);
296
+ });
297
+
298
+ it('announce to llm data channel before llm connected', ()=> {
299
+ voiceaService.webex.internal.llm.isConnected.returns(false);
300
+ assert.throws(() => voiceaService.announce(), "voicea can not announce before llm connected");
301
+ assert.notCalled(sendAnnouncement);
302
+ });
303
+
304
+ it('should not announce duplicate', () => {
305
+ isAnnounceProcessing.returns(true);
306
+ voiceaService.announce();
307
+ assert.notCalled(sendAnnouncement);
308
+ })
309
+ });
310
+
311
+ describe("#isCaptionProcessing", () => {
312
+ afterEach(() => {
313
+ voiceaService.captionStatus = 'idle';
314
+ });
315
+
316
+ ['sending', 'enabled'].forEach((status) => {
317
+ it(`should return true when status is ${status}`, () => {
318
+ voiceaService.captionStatus = status;
319
+ assert.equal(voiceaService.isCaptionProcessing(), true);
238
320
  });
321
+ });
239
322
 
240
- const response = await voiceaService.turnOnCaptions();
323
+ it('should return false when status is not processing status', () => {
324
+ voiceaService.captionStatus = 'idle';
325
+ assert.equal(voiceaService.isCaptionProcessing(), false);
326
+ });
327
+ });
241
328
 
242
- assert.equal(response, undefined);
329
+ describe('#turnOnCaptions', () => {
330
+ let requestTurnOnCaptions, isCaptionProcessing;
331
+ beforeEach(() => {
332
+ requestTurnOnCaptions = sinon.stub(voiceaService, 'requestTurnOnCaptions');
333
+ isCaptionProcessing = sinon.stub(voiceaService, 'isCaptionProcessing').returns(false);
334
+ voiceaService.webex.internal.llm.isConnected.returns(true);
335
+ });
336
+
337
+ afterEach(() => {
338
+ requestTurnOnCaptions.restore();
339
+ isCaptionProcessing.restore();
340
+ voiceaService.webex.internal.llm.isConnected.returns(true);
341
+ });
342
+
343
+ it('call request turn on captions', () => {
344
+ isCaptionProcessing.returns(false);
345
+ voiceaService.turnOnCaptions();
346
+ assert.calledOnce(requestTurnOnCaptions);
347
+ });
348
+
349
+ it("turns on captions before llm connected", () => {
350
+ isCaptionProcessing.returns(false);
351
+ voiceaService.webex.internal.llm.isConnected.returns(true);
352
+ // assert.throws(() => voiceaService.turnOnCaptions(), "can not turn on captions before llm connected");
353
+ assert.notCalled(requestTurnOnCaptions);
354
+ });
355
+
356
+ it('should not turn on duplicate when processing', () => {
357
+ isCaptionProcessing.returns(true);
358
+ voiceaService.turnOnCaptions();
359
+ assert.notCalled(voiceaService.requestTurnOnCaptions);
243
360
  });
244
361
  });
245
362
 
@@ -613,5 +730,19 @@ describe('plugin-voicea', () => {
613
730
  });
614
731
  });
615
732
  });
733
+
734
+ describe("#getCaptionStatus", () => {
735
+ it('works correctly', () => {
736
+ voiceaService.captionStatus = "enabled"
737
+ assert.equal(voiceaService.getCaptionStatus(), "enabled");
738
+ });
739
+ });
740
+
741
+ describe("#getAnnounceStatus", () => {
742
+ it('works correctly', () => {
743
+ voiceaService.announceStatus = "joined"
744
+ assert.equal(voiceaService.getAnnounceStatus(), "joined");
745
+ });
746
+ });
616
747
  });
617
748
  });