@webex/plugin-meetings 3.8.0-next.6 → 3.8.0-next.60

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.
Files changed (156) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/webex-errors.js +12 -2
  4. package/dist/common/errors/webex-errors.js.map +1 -1
  5. package/dist/config.js +1 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.js +16 -121
  8. package/dist/constants.js.map +1 -1
  9. package/dist/controls-options-manager/enums.js +2 -0
  10. package/dist/controls-options-manager/enums.js.map +1 -1
  11. package/dist/controls-options-manager/types.js.map +1 -1
  12. package/dist/controls-options-manager/util.js +52 -0
  13. package/dist/controls-options-manager/util.js.map +1 -1
  14. package/dist/interpretation/index.js +1 -1
  15. package/dist/interpretation/siLanguage.js +1 -1
  16. package/dist/locus-info/controlsUtils.js +28 -10
  17. package/dist/locus-info/controlsUtils.js.map +1 -1
  18. package/dist/locus-info/index.js +20 -1
  19. package/dist/locus-info/index.js.map +1 -1
  20. package/dist/locus-info/selfUtils.js +405 -418
  21. package/dist/locus-info/selfUtils.js.map +1 -1
  22. package/dist/media/index.js +8 -16
  23. package/dist/media/index.js.map +1 -1
  24. package/dist/meeting/in-meeting-actions.js +13 -1
  25. package/dist/meeting/in-meeting-actions.js.map +1 -1
  26. package/dist/meeting/index.js +548 -288
  27. package/dist/meeting/index.js.map +1 -1
  28. package/dist/meeting/locusMediaRequest.js +0 -17
  29. package/dist/meeting/locusMediaRequest.js.map +1 -1
  30. package/dist/meeting/muteState.js +0 -2
  31. package/dist/meeting/muteState.js.map +1 -1
  32. package/dist/meeting/request.js +30 -0
  33. package/dist/meeting/request.js.map +1 -1
  34. package/dist/meeting/request.type.js.map +1 -1
  35. package/dist/meeting/util.js +13 -2
  36. package/dist/meeting/util.js.map +1 -1
  37. package/dist/meeting-info/meeting-info-v2.js +359 -60
  38. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  39. package/dist/meetings/index.js +91 -1
  40. package/dist/meetings/index.js.map +1 -1
  41. package/dist/meetings/util.js +14 -0
  42. package/dist/meetings/util.js.map +1 -1
  43. package/dist/member/index.js +10 -0
  44. package/dist/member/index.js.map +1 -1
  45. package/dist/member/util.js +3 -0
  46. package/dist/member/util.js.map +1 -1
  47. package/dist/members/index.js +23 -0
  48. package/dist/members/index.js.map +1 -1
  49. package/dist/members/request.js +21 -0
  50. package/dist/members/request.js.map +1 -1
  51. package/dist/members/util.js +15 -0
  52. package/dist/members/util.js.map +1 -1
  53. package/dist/metrics/constants.js +9 -0
  54. package/dist/metrics/constants.js.map +1 -1
  55. package/dist/reachability/clusterReachability.js +63 -27
  56. package/dist/reachability/clusterReachability.js.map +1 -1
  57. package/dist/reachability/index.js +112 -47
  58. package/dist/reachability/index.js.map +1 -1
  59. package/dist/reachability/reachability.types.js +14 -0
  60. package/dist/reachability/reachability.types.js.map +1 -1
  61. package/dist/reachability/request.js +19 -3
  62. package/dist/reachability/request.js.map +1 -1
  63. package/dist/reconnection-manager/index.js +2 -2
  64. package/dist/reconnection-manager/index.js.map +1 -1
  65. package/dist/recording-controller/util.js +5 -5
  66. package/dist/recording-controller/util.js.map +1 -1
  67. package/dist/roap/index.js.map +1 -1
  68. package/dist/roap/turnDiscovery.js +45 -27
  69. package/dist/roap/turnDiscovery.js.map +1 -1
  70. package/dist/roap/types.js +17 -0
  71. package/dist/roap/types.js.map +1 -0
  72. package/dist/types/common/errors/webex-errors.d.ts +7 -1
  73. package/dist/types/config.d.ts +1 -0
  74. package/dist/types/constants.d.ts +11 -85
  75. package/dist/types/controls-options-manager/enums.d.ts +3 -1
  76. package/dist/types/controls-options-manager/types.d.ts +7 -1
  77. package/dist/types/locus-info/index.d.ts +1 -0
  78. package/dist/types/locus-info/selfUtils.d.ts +247 -1
  79. package/dist/types/meeting/in-meeting-actions.d.ts +12 -0
  80. package/dist/types/meeting/index.d.ts +54 -1
  81. package/dist/types/meeting/muteState.d.ts +0 -1
  82. package/dist/types/meeting/request.d.ts +12 -1
  83. package/dist/types/meeting/request.type.d.ts +6 -0
  84. package/dist/types/meeting/util.d.ts +3 -1
  85. package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
  86. package/dist/types/meetings/index.d.ts +38 -0
  87. package/dist/types/member/index.d.ts +1 -0
  88. package/dist/types/members/index.d.ts +8 -0
  89. package/dist/types/members/request.d.ts +19 -0
  90. package/dist/types/members/util.d.ts +13 -0
  91. package/dist/types/metrics/constants.d.ts +9 -0
  92. package/dist/types/reachability/clusterReachability.d.ts +15 -7
  93. package/dist/types/reachability/index.d.ts +10 -1
  94. package/dist/types/reachability/reachability.types.d.ts +5 -0
  95. package/dist/types/roap/index.d.ts +3 -2
  96. package/dist/types/roap/turnDiscovery.d.ts +5 -17
  97. package/dist/types/roap/types.d.ts +16 -0
  98. package/dist/webinar/index.js +1 -1
  99. package/package.json +23 -23
  100. package/src/common/errors/webex-errors.ts +8 -1
  101. package/src/config.ts +1 -0
  102. package/src/constants.ts +18 -90
  103. package/src/controls-options-manager/enums.ts +2 -0
  104. package/src/controls-options-manager/types.ts +11 -1
  105. package/src/controls-options-manager/util.ts +62 -0
  106. package/src/locus-info/controlsUtils.ts +44 -14
  107. package/src/locus-info/index.ts +23 -1
  108. package/src/locus-info/selfUtils.ts +451 -447
  109. package/src/media/index.ts +11 -21
  110. package/src/meeting/in-meeting-actions.ts +24 -0
  111. package/src/meeting/index.ts +364 -92
  112. package/src/meeting/locusMediaRequest.ts +0 -18
  113. package/src/meeting/muteState.ts +0 -2
  114. package/src/meeting/request.ts +36 -1
  115. package/src/meeting/request.type.ts +7 -0
  116. package/src/meeting/util.ts +11 -2
  117. package/src/meeting-info/meeting-info-v2.ts +247 -6
  118. package/src/meetings/index.ts +107 -1
  119. package/src/meetings/util.ts +18 -0
  120. package/src/member/index.ts +11 -0
  121. package/src/member/util.ts +3 -0
  122. package/src/members/index.ts +25 -0
  123. package/src/members/request.ts +26 -0
  124. package/src/members/util.ts +16 -0
  125. package/src/metrics/constants.ts +9 -0
  126. package/src/reachability/clusterReachability.ts +73 -26
  127. package/src/reachability/index.ts +70 -1
  128. package/src/reachability/reachability.types.ts +6 -0
  129. package/src/reachability/request.ts +7 -0
  130. package/src/reconnection-manager/index.ts +2 -2
  131. package/src/recording-controller/util.ts +17 -13
  132. package/src/roap/index.ts +3 -7
  133. package/src/roap/turnDiscovery.ts +34 -39
  134. package/src/roap/types.ts +23 -0
  135. package/test/unit/spec/controls-options-manager/util.js +120 -0
  136. package/test/unit/spec/locus-info/controlsUtils.js +103 -9
  137. package/test/unit/spec/locus-info/index.js +28 -0
  138. package/test/unit/spec/media/index.ts +36 -16
  139. package/test/unit/spec/meeting/in-meeting-actions.ts +15 -4
  140. package/test/unit/spec/meeting/index.js +518 -34
  141. package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
  142. package/test/unit/spec/meeting/muteState.js +0 -2
  143. package/test/unit/spec/meeting/request.js +32 -1
  144. package/test/unit/spec/meeting/utils.js +119 -18
  145. package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
  146. package/test/unit/spec/meetings/index.js +120 -2
  147. package/test/unit/spec/member/index.js +7 -0
  148. package/test/unit/spec/member/util.js +24 -0
  149. package/test/unit/spec/members/index.js +103 -26
  150. package/test/unit/spec/members/request.js +45 -22
  151. package/test/unit/spec/members/utils.js +33 -0
  152. package/test/unit/spec/reachability/clusterReachability.ts +88 -56
  153. package/test/unit/spec/reachability/index.ts +101 -0
  154. package/test/unit/spec/reachability/request.js +47 -2
  155. package/test/unit/spec/reconnection-manager/index.js +4 -4
  156. package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -221,14 +221,6 @@ export class LocusMediaRequest extends WebexPlugin {
221
221
  localMedias.roapMessage = request.roapMessage;
222
222
  localMedias.reachability = request.reachability;
223
223
  body.clientMediaPreferences = request.clientMediaPreferences;
224
-
225
- // @ts-ignore
226
- this.webex.internal.newMetrics.submitClientEvent({
227
- name: 'client.locus.media.request',
228
- options: {
229
- meetingId: this.config.meetingId,
230
- },
231
- });
232
224
  break;
233
225
  }
234
226
 
@@ -269,16 +261,6 @@ export class LocusMediaRequest extends WebexPlugin {
269
261
  this.confluenceState = 'created';
270
262
  }
271
263
 
272
- if (request.type === 'RoapMessage') {
273
- // @ts-ignore
274
- this.webex.internal.newMetrics.submitClientEvent({
275
- name: 'client.locus.media.response',
276
- options: {
277
- meetingId: this.config.meetingId,
278
- },
279
- });
280
- }
281
-
282
264
  return result;
283
265
  })
284
266
  .catch((e) => {
@@ -147,7 +147,6 @@ export class MuteState {
147
147
  * @public
148
148
  * @memberof MuteState
149
149
  * @param {Object} [meeting] the meeting object
150
- * @param {Boolean} [mute] true for muting, false for unmuting request
151
150
  * @returns {void}
152
151
  */
153
152
  public handleLocalStreamMuteStateChange(meeting?: any) {
@@ -350,7 +349,6 @@ export class MuteState {
350
349
  * @param {Meeting} meeting
351
350
  * @returns {void}
352
351
  */
353
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
354
352
  private applyUnmuteAllowedToStream(meeting: any) {
355
353
  if (this.type === AUDIO) {
356
354
  meeting.mediaProperties.audioStream?.setUnmuteAllowed(this.state.server.unmuteAllowed);
@@ -27,7 +27,12 @@ import {
27
27
  _SLIDES_,
28
28
  ANNOTATION,
29
29
  } from '../constants';
30
- import {SendReactionOptions, BrbOptions, ToggleReactionsOptions} from './request.type';
30
+ import {
31
+ SendReactionOptions,
32
+ BrbOptions,
33
+ ToggleReactionsOptions,
34
+ PostMeetingDataConsentOptions,
35
+ } from './request.type';
31
36
  import MeetingUtil from './util';
32
37
  import {AnnotationInfo} from '../annotation/annotation.types';
33
38
  import {ClientMediaPreferences} from '../reachability/reachability.types';
@@ -934,4 +939,34 @@ export default class MeetingRequest extends StatelessWebexPlugin {
934
939
  },
935
940
  });
936
941
  }
942
+
943
+ /**
944
+ * Sends a request to set post meeting data consent.
945
+ *
946
+ * @param {Object} options - The options for post meeting data consent request.
947
+ * @param {boolean} options.consent - Whether accepted or declined.
948
+ * @param {string} options.locusUrl - The URL of the locus.
949
+ * @param {string} options.deviceUrl - The URL of the device.
950
+ * @param {string} options.selfId - The ID of the participant.
951
+ * @returns {Promise}
952
+ */
953
+ setPostMeetingDataConsent({
954
+ postMeetingDataConsent,
955
+ locusUrl,
956
+ deviceUrl,
957
+ selfId,
958
+ }: PostMeetingDataConsentOptions) {
959
+ const uri = `${locusUrl}/${PARTICIPANT}/${selfId}/${CONTROLS}`;
960
+
961
+ return this.locusDeltaRequest({
962
+ method: HTTP_VERBS.PATCH,
963
+ uri,
964
+ body: {
965
+ consent: {
966
+ postMeetingDataConsent,
967
+ deviceUrl,
968
+ },
969
+ },
970
+ });
971
+ }
937
972
  }
@@ -18,3 +18,10 @@ export type BrbOptions = {
18
18
  deviceUrl: string;
19
19
  selfId: string;
20
20
  };
21
+
22
+ export type PostMeetingDataConsentOptions = {
23
+ postMeetingDataConsent: boolean;
24
+ locusUrl: string;
25
+ deviceUrl: string;
26
+ selfId: string;
27
+ };
@@ -176,11 +176,12 @@ const MeetingUtil = {
176
176
  deviceCapabilities: options.deviceCapabilities,
177
177
  liveAnnotationSupported: options.liveAnnotationSupported,
178
178
  clientMediaPreferences,
179
+ alias: options.alias,
179
180
  })
180
181
  .then((res) => {
181
182
  const parsed = MeetingUtil.parseLocusJoin(res);
182
183
  meeting.setLocus(parsed);
183
-
184
+ meeting.isoLocalClientMeetingJoinTime = res?.headers?.date; // read from header if exist, else fall back to system clock : https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-555657
184
185
  webex.internal.newMetrics.submitClientEvent({
185
186
  name: 'client.locus.join.response',
186
187
  payload: {
@@ -572,9 +573,17 @@ const MeetingUtil = {
572
573
  canUserRenameSelfAndObserved: (displayHints) =>
573
574
  displayHints.includes(DISPLAY_HINTS.CAN_RENAME_SELF_AND_OBSERVED),
574
575
 
576
+ requiresPostMeetingDataConsentPrompt: (displayHints) =>
577
+ displayHints.includes(DISPLAY_HINTS.SHOW_POST_MEETING_DATA_CONSENT_PROMPT),
578
+
575
579
  canUserRenameOthers: (displayHints) => displayHints.includes(DISPLAY_HINTS.CAN_RENAME_OTHERS),
576
580
 
577
- canShareWhiteBoard: (displayHints) => displayHints.includes(DISPLAY_HINTS.SHARE_WHITEBOARD),
581
+ // Default empty value for policies if we get an undefined value (ie permissionToken is not available)
582
+ canShareWhiteBoard: (displayHints, policies = {}) =>
583
+ displayHints.includes(DISPLAY_HINTS.SHARE_WHITEBOARD) &&
584
+ !!policies[SELF_POLICY.SUPPORT_WHITEBOARD],
585
+
586
+ canMoveToLobby: (displayHints) => displayHints.includes(DISPLAY_HINTS.MOVE_TO_LOBBY),
578
587
 
579
588
  /**
580
589
  * Adds the current locus sequence information to a request body
@@ -16,6 +16,9 @@ const CAPTCHA_ERROR_DEFAULT_MESSAGE =
16
16
  'Captcha required. Call fetchMeetingInfo() with captchaInfo argument';
17
17
  const ADHOC_MEETING_DEFAULT_ERROR =
18
18
  'Failed starting the adhoc meeting, Please contact support team ';
19
+ const MEETING_IS_IN_PROGRESS_MESSAGE = 'Meeting is in progress';
20
+ const STATIC_MEETING_LINK_ALREADY_EXISTS_MESSAGE = 'Static meeting link already exists';
21
+ const FETCH_STATIC_MEETING_LINK = 'Meeting link does not exists for conversation';
19
22
  const CAPTCHA_ERROR_REQUIRES_PASSWORD_CODES = [423005, 423006];
20
23
  const CAPTCHA_ERROR_REQUIRES_REGISTRATION_ID_CODES = [423007];
21
24
 
@@ -193,6 +196,77 @@ export class MeetingInfoV2JoinForbiddenError extends Error {
193
196
  }
194
197
  }
195
198
 
199
+ /**
200
+ * Error fetching static link for a conversation when it does not exist
201
+ */
202
+ export class MeetingInfoV2StaticLinkDoesNotExistError extends Error {
203
+ sdkMessage: any;
204
+ wbxAppApiCode: any;
205
+ body: any;
206
+ /**
207
+ *
208
+ * @constructor
209
+ * @param {Number} [wbxAppApiErrorCode]
210
+ * @param {String} [message]
211
+ */
212
+ constructor(wbxAppApiErrorCode?: number, message: string = FETCH_STATIC_MEETING_LINK) {
213
+ super(`${message}, code=${wbxAppApiErrorCode}`);
214
+ this.name = 'MeetingInfoV2StaticLinkDoesNotExistError';
215
+ this.sdkMessage = message;
216
+ this.stack = new Error().stack;
217
+ this.wbxAppApiCode = wbxAppApiErrorCode;
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Error enabling/disabling static meeting link
223
+ */
224
+ export class MeetingInfoV2MeetingIsInProgressError extends Error {
225
+ sdkMessage: any;
226
+ wbxAppApiCode: any;
227
+ body: any;
228
+ /**
229
+ *
230
+ * @constructor
231
+ * @param {Number} [wbxAppApiErrorCode]
232
+ * @param {String} [message]
233
+ * @param {Boolean} [enable]
234
+ */
235
+ constructor(
236
+ wbxAppApiErrorCode?: number,
237
+ message = MEETING_IS_IN_PROGRESS_MESSAGE,
238
+ enable = false
239
+ ) {
240
+ super(`${message}, code=${wbxAppApiErrorCode}, enable=${enable}`);
241
+ this.name = 'MeetingInfoV2MeetingIsInProgressError';
242
+ this.sdkMessage = message;
243
+ this.stack = new Error().stack;
244
+ this.wbxAppApiCode = wbxAppApiErrorCode;
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Error enabling/disabling static meeting link
250
+ */
251
+ export class MeetingInfoV2StaticMeetingLinkAlreadyExists extends Error {
252
+ sdkMessage: any;
253
+ wbxAppApiCode: any;
254
+ body: any;
255
+ /**
256
+ *
257
+ * @constructor
258
+ * @param {Number} [wbxAppApiErrorCode]
259
+ * @param {String} [message]
260
+ */
261
+ constructor(wbxAppApiErrorCode?: number, message = STATIC_MEETING_LINK_ALREADY_EXISTS_MESSAGE) {
262
+ super(`${message}, code=${wbxAppApiErrorCode}`);
263
+ this.name = 'MeetingInfoV2StaticMeetingLinkAlreadyExists';
264
+ this.sdkMessage = message;
265
+ this.stack = new Error().stack;
266
+ this.wbxAppApiCode = wbxAppApiErrorCode;
267
+ }
268
+ }
269
+
196
270
  /**
197
271
  * @class MeetingInfo
198
272
  */
@@ -293,17 +367,20 @@ export default class MeetingInfoV2 {
293
367
  };
294
368
 
295
369
  /**
296
- * Creates adhoc space meetings for a space by fetching the conversation infomation
370
+ * helper function to either create an adhoc space meeting or enable static meeting link
297
371
  * @param {String} conversationUrl conversationUrl to start adhoc meeting on
298
372
  * @param {String} installedOrgID org ID of user's machine
373
+ * @param {Boolean} enableStaticMeetingLink whether or not to enable static meeting link
299
374
  * @returns {Promise} returns a meeting info object
300
375
  * @public
301
376
  * @memberof MeetingInfo
302
377
  */
303
- async createAdhocSpaceMeeting(conversationUrl: string, installedOrgID?: string) {
304
- if (!this.webex.meetings.preferredWebexSite) {
305
- throw Error('No preferred webex site found');
306
- }
378
+ async createAdhocSpaceMeetingOrEnableStaticMeetingLink(
379
+ conversationUrl: string,
380
+ installedOrgID?: string,
381
+ // setting this to true enables static meeting link
382
+ enableStaticMeetingLink = false
383
+ ) {
307
384
  const getInvitees = (particpants = []) => {
308
385
  const invitees = [];
309
386
 
@@ -329,6 +406,7 @@ export default class MeetingInfoV2 {
329
406
  kroUrl: conversation.kmsResourceObjectUrl,
330
407
  invitees: getInvitees(conversation.participants?.items),
331
408
  installedOrgID,
409
+ schedule: enableStaticMeetingLink,
332
410
  };
333
411
 
334
412
  if (installedOrgID) {
@@ -344,7 +422,23 @@ export default class MeetingInfoV2 {
344
422
  uri,
345
423
  body,
346
424
  });
347
- })
425
+ });
426
+ }
427
+
428
+ /**
429
+ * Creates adhoc space meetings for a space by fetching the conversation infomation
430
+ * @param {String} conversationUrl conversationUrl to start adhoc meeting on
431
+ * @param {String} installedOrgID org ID of user's machine
432
+ * @returns {Promise} returns a meeting info object
433
+ * @public
434
+ * @memberof MeetingInfo
435
+ */
436
+ async createAdhocSpaceMeeting(conversationUrl: string, installedOrgID?: string) {
437
+ if (!this.webex.meetings.preferredWebexSite) {
438
+ throw Error('No preferred webex site found');
439
+ }
440
+
441
+ return this.createAdhocSpaceMeetingOrEnableStaticMeetingLink(conversationUrl, installedOrgID)
348
442
  .then((requestResult) => {
349
443
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADHOC_MEETING_SUCCESS);
350
444
 
@@ -363,6 +457,153 @@ export default class MeetingInfoV2 {
363
457
  });
364
458
  }
365
459
 
460
+ /**
461
+ * Fetches details for static meeting link
462
+ * @param {String} conversationUrl conversationUrl that's required to find static meeting link if it exists
463
+ * @returns {Promise} returns a Promise
464
+ * @public
465
+ * @memberof MeetingInfo
466
+ */
467
+ async fetchStaticMeetingLink(conversationUrl: string) {
468
+ if (!this.webex.meetings.preferredWebexSite) {
469
+ throw Error('No preferred webex site found');
470
+ }
471
+
472
+ const body = {
473
+ spaceUrl: conversationUrl,
474
+ };
475
+
476
+ const uri = this.webex.meetings.preferredWebexSite
477
+ ? `https://${this.webex.meetings.preferredWebexSite}/wbxappapi/v2/meetings/spaceInstant/query`
478
+ : '';
479
+
480
+ return this.webex
481
+ .request({
482
+ method: HTTP_VERBS.POST,
483
+ uri,
484
+ body,
485
+ })
486
+ .then((requestResult) => {
487
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.FETCH_STATIC_MEETING_LINK_SUCCESS);
488
+
489
+ return requestResult;
490
+ })
491
+ .catch((err) => {
492
+ if (err?.statusCode === 403) {
493
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_LINK_DOES_NOT_EXIST_ERROR, {
494
+ reason: err.message,
495
+ stack: err.stack,
496
+ });
497
+
498
+ throw new MeetingInfoV2StaticLinkDoesNotExistError(err.body?.code, err.body?.message);
499
+ }
500
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.FETCH_STATIC_MEETING_LINK_FAILURE, {
501
+ reason: err.message,
502
+ stack: err.stack,
503
+ });
504
+
505
+ throw err;
506
+ });
507
+ }
508
+
509
+ /**
510
+ * Enables static meeting link
511
+ * @param {String} conversationUrl conversationUrl that's required to enable static meeting link
512
+ * @returns {Promise} returns a Promise
513
+ * @public
514
+ * @memberof MeetingInfo
515
+ */
516
+ async enableStaticMeetingLink(conversationUrl: string) {
517
+ if (!this.webex.meetings.preferredWebexSite) {
518
+ throw Error('No preferred webex site found');
519
+ }
520
+
521
+ return this.createAdhocSpaceMeetingOrEnableStaticMeetingLink(conversationUrl, undefined, true)
522
+ .then((requestResult) => {
523
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ENABLE_STATIC_METTING_LINK_SUCCESS);
524
+
525
+ return requestResult;
526
+ })
527
+ .catch((err) => {
528
+ if (err?.statusCode === 403) {
529
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_IS_IN_PROGRESS_ERROR, {
530
+ reason: err.message,
531
+ stack: err.stack,
532
+ });
533
+
534
+ throw new MeetingInfoV2MeetingIsInProgressError(err.body?.code, err.body?.message, true);
535
+ }
536
+
537
+ if (err?.statusCode === 409) {
538
+ Metrics.sendBehavioralMetric(
539
+ BEHAVIORAL_METRICS.STATIC_MEETING_LINK_ALREADY_EXISTS_ERROR,
540
+ {
541
+ reason: err.message,
542
+ stack: err.stack,
543
+ }
544
+ );
545
+
546
+ throw new MeetingInfoV2StaticMeetingLinkAlreadyExists(err.body?.code, err.body?.message);
547
+ }
548
+
549
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ENABLE_STATIC_METTING_LINK_FAILURE, {
550
+ reason: err.message,
551
+ stack: err.stack,
552
+ });
553
+
554
+ throw err;
555
+ });
556
+ }
557
+
558
+ /**
559
+ * Disables static meeting link for given conversation url
560
+ * @param {String} conversationUrl conversationUrl that's required to disable static meeting link if it exists
561
+ * @returns {Promise} returns a Promise
562
+ * @public
563
+ * @memberof MeetingInfo
564
+ */
565
+ async disableStaticMeetingLink(conversationUrl: string) {
566
+ if (!this.webex.meetings.preferredWebexSite) {
567
+ throw Error('No preferred webex site found');
568
+ }
569
+
570
+ const body = {
571
+ spaceUrl: conversationUrl,
572
+ };
573
+
574
+ const uri = this.webex.meetings.preferredWebexSite
575
+ ? `https://${this.webex.meetings.preferredWebexSite}/wbxappapi/v2/meetings/spaceInstant/deletePersistentMeeting`
576
+ : '';
577
+
578
+ return this.webex
579
+ .request({
580
+ method: HTTP_VERBS.POST,
581
+ uri,
582
+ body,
583
+ })
584
+ .then((requestResult) => {
585
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.DISABLE_STATIC_MEETING_LINK_SUCCESS);
586
+
587
+ return requestResult;
588
+ })
589
+ .catch((err) => {
590
+ if (err?.statusCode === 403) {
591
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.MEETING_IS_IN_PROGRESS_ERROR, {
592
+ reason: err.message,
593
+ stack: err.stack,
594
+ });
595
+
596
+ throw new MeetingInfoV2MeetingIsInProgressError(err.body?.code, err.body?.message);
597
+ }
598
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.DISABLE_STATIC_MEETING_LINK_FAILURE, {
599
+ reason: err.message,
600
+ stack: err.stack,
601
+ });
602
+
603
+ throw err;
604
+ });
605
+ }
606
+
366
607
  /**
367
608
  * Fetches meeting info from the server
368
609
  * @param {String} destination one of many different types of destinations to look up info for
@@ -807,6 +807,26 @@ export default class Meetings extends WebexPlugin {
807
807
  }
808
808
  }
809
809
 
810
+ /**
811
+ * API to toggle usage of audio main DTX, needs to be called before webex.meetings.register()
812
+ *
813
+ * @param {Boolean} newValue
814
+ * @private
815
+ * @memberof Meetings
816
+ * @returns {undefined}
817
+ */
818
+ private _toggleDisableAudioMainDtx(newValue: boolean) {
819
+ if (typeof newValue !== 'boolean') {
820
+ return;
821
+ }
822
+
823
+ // @ts-ignore
824
+ if (this.config.experimental.disableAudioMainDtx !== newValue) {
825
+ // @ts-ignore
826
+ this.config.experimental.disableAudioMainDtx = newValue;
827
+ }
828
+ }
829
+
810
830
  /**
811
831
  * Executes a registration step and updates the registration status.
812
832
  * @param {Function} step - The registration step to execute.
@@ -854,7 +874,7 @@ export default class Meetings extends WebexPlugin {
854
874
  this.executeRegistrationStep(
855
875
  () =>
856
876
  this.startReachability('registration').catch((error) => {
857
- LoggerProxy.logger.error(`Meetings:index#register --> GDM error, ${error.message}`);
877
+ LoggerProxy.logger.warn(`Meetings:index#register --> startReachability failed:`, error);
858
878
  }),
859
879
  'startReachability'
860
880
  ),
@@ -935,6 +955,21 @@ export default class Meetings extends WebexPlugin {
935
955
  .disconnect()
936
956
  // @ts-ignore
937
957
  .then(() => this.webex.internal.device.unregister())
958
+ .catch((error) => {
959
+ // If error status code is 404, continue the chain
960
+ if (error.statusCode === 404) {
961
+ LoggerProxy.logger.info(
962
+ 'Meetings:index#unregister --> 404 error during device unregister, proceeding normally'
963
+ );
964
+
965
+ return; // returning undefined allows the chain to continue
966
+ }
967
+ // For any other status code, break the chain by rethrowing
968
+ LoggerProxy.logger.error(
969
+ `Meetings:index#unregister --> Failed to unregister device: ${error.message}`
970
+ );
971
+ throw error; // rethrow to break the promise chain
972
+ })
938
973
  .then(() => {
939
974
  Trigger.trigger(
940
975
  this,
@@ -1217,6 +1252,29 @@ export default class Meetings extends WebexPlugin {
1217
1252
  );
1218
1253
  }
1219
1254
 
1255
+ /**
1256
+ * Fetch static meeting link for given conversation url.
1257
+ *
1258
+ * @param {string} conversationUrl - url for conversation
1259
+ * @returns {Promise}
1260
+ * @public
1261
+ * @memberof Meetings
1262
+ */
1263
+ public fetchStaticMeetingLink(conversationUrl: string): Promise<any> {
1264
+ return (
1265
+ this.meetingInfo
1266
+ .fetchStaticMeetingLink(conversationUrl)
1267
+ // Catch a failure to fetch static meeting link.
1268
+ .catch((error) => {
1269
+ LoggerProxy.logger.error(
1270
+ `Meetings:index#fetchStaticMeetingLink --> ERROR, unable to fetch persistent meeting link: ${error.message}`
1271
+ );
1272
+
1273
+ return Promise.reject(error);
1274
+ })
1275
+ );
1276
+ }
1277
+
1220
1278
  /**
1221
1279
  * Create a meeting or return an existing meeting.
1222
1280
  *
@@ -1369,6 +1427,54 @@ export default class Meetings extends WebexPlugin {
1369
1427
  );
1370
1428
  }
1371
1429
 
1430
+ /**
1431
+ * Enable static meeting links for given conversation url.
1432
+ *
1433
+ *
1434
+ * @param {string} conversationUrl - url for conversation
1435
+ * @returns {Promise}
1436
+ * @public
1437
+ * @memberof Meetings
1438
+ */
1439
+ public enableStaticMeetingLink(conversationUrl: string): Promise<any> {
1440
+ return (
1441
+ this.meetingInfo
1442
+ .enableStaticMeetingLink(conversationUrl)
1443
+ // Catch a failure to enable static meeting link.
1444
+ .catch((error) => {
1445
+ LoggerProxy.logger.error(
1446
+ `Meetings:index#enableStaticMeetingLink --> ERROR, unable to enable static meeting link: ${error.message}`
1447
+ );
1448
+
1449
+ return Promise.reject(error);
1450
+ })
1451
+ );
1452
+ }
1453
+
1454
+ /**
1455
+ * Disable static meeting links for given conversation url.
1456
+ *
1457
+ *
1458
+ * @param {string} conversationUrl - url for conversation
1459
+ * @returns {Promise}
1460
+ * @public
1461
+ * @memberof Meetings
1462
+ */
1463
+ public disableStaticMeetingLink(conversationUrl: string): Promise<any> {
1464
+ return (
1465
+ this.meetingInfo
1466
+ .disableStaticMeetingLink(conversationUrl)
1467
+ // Catch a failure to disable static meeting link.
1468
+ .catch((error) => {
1469
+ LoggerProxy.logger.error(
1470
+ `Meetings:index#disableStaticMeetingLink --> ERROR, unable to disable static meeting link: ${error.message}`
1471
+ );
1472
+
1473
+ return Promise.reject(error);
1474
+ })
1475
+ );
1476
+ }
1477
+
1372
1478
  /**
1373
1479
  * Create meeting
1374
1480
  *
@@ -99,6 +99,24 @@ MeetingsUtil.getMediaServer = (sdp) => {
99
99
  return mediaServer;
100
100
  };
101
101
 
102
+ MeetingsUtil.getMediaServerIp = (sdp) => {
103
+ let mediaServerIp;
104
+
105
+ // Attempt to collect the media server from the roap message.
106
+ try {
107
+ mediaServerIp = sdp
108
+ .split('\r\n')
109
+ .find((line) => line.startsWith('o='))
110
+ .match(/o=\S+ \d+ \d+ IN IP4 ([\d.]+)/)?.[1]
111
+ .toLowerCase()
112
+ .trim();
113
+ } catch {
114
+ mediaServerIp = undefined;
115
+ }
116
+
117
+ return mediaServerIp;
118
+ };
119
+
102
120
  MeetingsUtil.checkForCorrelationId = (deviceUrl, locus) => {
103
121
  let devices = [];
104
122
 
@@ -23,6 +23,7 @@ export default class Member {
23
23
  isInMeeting: any;
24
24
  isModerator: any;
25
25
  isModeratorAssignmentProhibited: any;
26
+ isPresenterAssignmentProhibited: any;
26
27
  isMutable: any;
27
28
  isNotAdmitted: any;
28
29
  isRecording: any;
@@ -257,6 +258,14 @@ export default class Member {
257
258
  */
258
259
  this.isModeratorAssignmentProhibited = null;
259
260
 
261
+ /**
262
+ * @instance
263
+ * @type {Boolean}
264
+ * @public
265
+ * @memberof Member
266
+ */
267
+ this.isPresenterAssignmentProhibited = null;
268
+
260
269
  /**
261
270
  * @instance
262
271
  * @type {IExternalRoles}
@@ -309,6 +318,8 @@ export default class Member {
309
318
  this.isModerator = MemberUtil.isModerator(participant);
310
319
  this.isModeratorAssignmentProhibited =
311
320
  MemberUtil.isModeratorAssignmentProhibited(participant);
321
+ this.isPresenterAssignmentProhibited =
322
+ MemberUtil.isPresenterAssignmentProhibited(participant);
312
323
  this.processStatus(participant);
313
324
  this.processRoles(participant as ParticipantWithRoles);
314
325
  // must be done last
@@ -127,6 +127,9 @@ MemberUtil.isDevice = (participant: any) => participant && participant.type ===
127
127
  MemberUtil.isModeratorAssignmentProhibited = (participant) =>
128
128
  participant && participant.moderatorAssignmentNotAllowed;
129
129
 
130
+ MemberUtil.isPresenterAssignmentProhibited = (participant) =>
131
+ participant && participant.presenterAssignmentNotAllowed;
132
+
130
133
  /**
131
134
  * checks to see if the participant id is the same as the passed id
132
135
  * there are multiple ids that can be used
@@ -886,6 +886,31 @@ export default class Members extends StatelessWebexPlugin {
886
886
  });
887
887
  }
888
888
 
889
+ /**
890
+ * Moves a meeting member into the lobby.
891
+ * @param {String} memberId -- The ID of the member to move.
892
+ * @returns {Promise} -- Resolves with the lobby‐move response.
893
+ * @public
894
+ * @memberof Members
895
+ */
896
+ public moveToLobby(memberId: string) {
897
+ if (!this.locusUrl) {
898
+ return Promise.reject(
899
+ new ParameterError(
900
+ 'The associated locus url for this meetings members object must be defined.'
901
+ )
902
+ );
903
+ }
904
+ if (!memberId) {
905
+ return Promise.reject(
906
+ new ParameterError('The member id must be defined to move the member to lobby.')
907
+ );
908
+ }
909
+ const body = MembersUtil.getMoveMemberToLobbyRequestBody(memberId);
910
+
911
+ return this.membersRequest.moveToLobbyMember({locusUrl: this.locusUrl, memberId}, body);
912
+ }
913
+
889
914
  /**
890
915
  * Raise or lower the hand of a member in a meeting
891
916
  * @param {String} memberId