@webex/contact-center 3.12.0-task-refactor.10 → 3.12.0-task-refactor.12
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/cc.js +345 -82
- package/dist/cc.js.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +39 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +8 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +3 -0
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +10 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +1 -0
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +57 -6
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +92 -17
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +24 -3
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/index.js +1 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +16 -4
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +158 -6
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +39 -11
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +129 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +30 -3
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +4 -0
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/constants.js +3 -0
- package/dist/services/task/state-machine/constants.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +23 -0
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/types.js.map +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/services/task/types.js +69 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +93 -2
- package/dist/types/config.d.ts +6 -0
- package/dist/types/constants.d.ts +3 -0
- package/dist/types/metrics/constants.d.ts +6 -0
- package/dist/types/services/config/types.d.ts +22 -1
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +20 -2
- package/dist/types/services/core/aqm-reqs.d.ts +49 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +1 -0
- package/dist/types/services/task/Task.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +13 -0
- package/dist/types/services/task/constants.d.ts +6 -0
- package/dist/types/services/task/dialer.d.ts +45 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +36 -8
- package/dist/types/services/task/state-machine/constants.d.ts +3 -0
- package/dist/types/services/task/state-machine/guards.d.ts +2 -0
- package/dist/types/services/task/state-machine/types.d.ts +11 -0
- package/dist/types/services/task/types.d.ts +85 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +452 -102
- package/src/config.ts +6 -0
- package/src/constants.ts +3 -0
- package/src/metrics/behavioral-events.ts +42 -0
- package/src/metrics/constants.ts +9 -1
- package/src/services/ApiAiAssistant.ts +3 -0
- package/src/services/config/Util.ts +1 -1
- package/src/services/config/types.ts +12 -1
- package/src/services/constants.ts +1 -0
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +68 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +23 -2
- package/src/services/index.ts +1 -0
- package/src/services/task/Task.ts +22 -4
- package/src/services/task/TaskManager.ts +175 -7
- package/src/services/task/TaskUtils.ts +33 -1
- package/src/services/task/constants.ts +6 -0
- package/src/services/task/dialer.ts +136 -1
- package/src/services/task/state-machine/TaskStateMachine.ts +37 -5
- package/src/services/task/state-machine/actions.ts +4 -0
- package/src/services/task/state-machine/constants.ts +3 -0
- package/src/services/task/state-machine/guards.ts +30 -0
- package/src/services/task/state-machine/types.ts +14 -1
- package/src/services/task/state-machine/uiControlsComputer.ts +1 -1
- package/src/services/task/types.ts +91 -0
- package/src/types.ts +6 -1
- package/test/unit/spec/cc.ts +432 -26
- package/test/unit/spec/services/config/index.ts +30 -2
- package/test/unit/spec/services/core/Utils.ts +367 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +90 -1
- package/test/unit/spec/services/core/websocket/connection-service.ts +4 -5
- package/test/unit/spec/services/task/TaskManager.ts +2334 -145
- package/test/unit/spec/services/task/dialer.ts +372 -96
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +47 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/dist/cc.js
CHANGED
|
@@ -85,6 +85,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
85
85
|
* - `task:established` - Task/call has been connected
|
|
86
86
|
* - `task:ended` - Task/call has ended
|
|
87
87
|
* - `task:error` - An error occurred during task handling
|
|
88
|
+
* - `task:campaignPreviewReservation` - Campaign preview contact offered to agent
|
|
88
89
|
*
|
|
89
90
|
* @public
|
|
90
91
|
*
|
|
@@ -300,6 +301,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
300
301
|
this.$webex.once(_constants.READY, () => {
|
|
301
302
|
// @ts-ignore
|
|
302
303
|
this.$config = this.config;
|
|
304
|
+
this.validatePluginConfig();
|
|
303
305
|
|
|
304
306
|
/**
|
|
305
307
|
* This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
|
|
@@ -311,7 +313,6 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
311
313
|
webex: this.$webex,
|
|
312
314
|
connectionConfig: this.getConnectionConfig()
|
|
313
315
|
});
|
|
314
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
315
316
|
this.webCallingService = new _WebCallingService.default(this.$webex);
|
|
316
317
|
this.apiAIAssistant = new _ApiAiAssistant.ApiAIAssistant(this.$webex);
|
|
317
318
|
this.metricsManager = _MetricsManager.default.getInstance({
|
|
@@ -349,6 +350,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
349
350
|
this.trigger(_types4.TASK_EVENTS.TASK_HYDRATE, task);
|
|
350
351
|
};
|
|
351
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Handles multi-login hydrate events for SDK instances without Mobius registration
|
|
355
|
+
* @private
|
|
356
|
+
* @param {ITask} task The task object associated with the multi-login hydrate
|
|
357
|
+
*/
|
|
358
|
+
handleTaskMultiLoginHydrate = task => {
|
|
359
|
+
// @ts-ignore
|
|
360
|
+
this.trigger(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
361
|
+
};
|
|
362
|
+
|
|
352
363
|
/**
|
|
353
364
|
* Handles task merged events when tasks are combined eg: EPDN merge/transfer
|
|
354
365
|
* @private
|
|
@@ -359,6 +370,19 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
359
370
|
this.trigger(_types4.TASK_EVENTS.TASK_MERGED, task);
|
|
360
371
|
};
|
|
361
372
|
|
|
373
|
+
/**
|
|
374
|
+
* Handles campaign preview reservation events when a contact is offered to the agent
|
|
375
|
+
* @private
|
|
376
|
+
* @param {ITask} task The campaign reservation task
|
|
377
|
+
*/
|
|
378
|
+
handleCampaignPreviewReservation = task => {
|
|
379
|
+
// @ts-ignore
|
|
380
|
+
this.trigger(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
381
|
+
};
|
|
382
|
+
handleRTDWebsocketMessage = event => {
|
|
383
|
+
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
384
|
+
};
|
|
385
|
+
|
|
362
386
|
/**
|
|
363
387
|
* Sets up event listeners for incoming tasks and task hydration
|
|
364
388
|
* Subscribes to task events from the task manager
|
|
@@ -367,7 +391,9 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
367
391
|
incomingTaskListener() {
|
|
368
392
|
this.taskManager.on(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
369
393
|
this.taskManager.on(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
394
|
+
this.taskManager.on(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
370
395
|
this.taskManager.on(_types4.TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
396
|
+
this.taskManager.on(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
|
|
371
397
|
}
|
|
372
398
|
|
|
373
399
|
/**
|
|
@@ -407,13 +433,14 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
407
433
|
* ```
|
|
408
434
|
*/
|
|
409
435
|
async register() {
|
|
410
|
-
_loggerProxy.default.
|
|
436
|
+
_loggerProxy.default.log('Starting CC SDK registration', {
|
|
411
437
|
module: _constants.CC_FILE,
|
|
412
438
|
method: _constants.METHODS.REGISTER
|
|
413
439
|
});
|
|
414
440
|
try {
|
|
415
441
|
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_SUCCESS, _constants4.METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED]);
|
|
416
442
|
this.setupEventListeners();
|
|
443
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
417
444
|
const resp = await this.connectWebsocket();
|
|
418
445
|
// Ensure 'dn' is always populated from 'defaultDn'
|
|
419
446
|
resp.dn = resp.defaultDn;
|
|
@@ -476,6 +503,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
476
503
|
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS, _constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_FAIL]);
|
|
477
504
|
this.taskManager.off(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
478
505
|
this.taskManager.off(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
506
|
+
this.taskManager.off(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
507
|
+
this.taskManager.off(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
|
|
479
508
|
this.taskManager.unregisterIncomingCallEvent();
|
|
480
509
|
this.services.webSocketManager.off('message', this.handleWebsocketMessage);
|
|
481
510
|
this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
|
|
@@ -499,6 +528,9 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
499
528
|
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
500
529
|
this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
|
|
501
530
|
}
|
|
531
|
+
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
532
|
+
this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
|
|
533
|
+
}
|
|
502
534
|
|
|
503
535
|
// Clear any cached agent configuration
|
|
504
536
|
this.agentConfig = null;
|
|
@@ -600,80 +632,89 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
600
632
|
* @private
|
|
601
633
|
*/
|
|
602
634
|
async connectWebsocket() {
|
|
603
|
-
_loggerProxy.default.
|
|
635
|
+
_loggerProxy.default.log('Connecting to websocket', {
|
|
604
636
|
module: _constants.CC_FILE,
|
|
605
637
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
606
638
|
});
|
|
607
639
|
try {
|
|
608
|
-
|
|
640
|
+
const data = await this.services.webSocketManager.initWebSocket({
|
|
609
641
|
body: this.getConnectionConfig(),
|
|
610
642
|
resource: _constants3.SUBSCRIBE_API
|
|
611
|
-
})
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
643
|
+
});
|
|
644
|
+
const agentId = data.agentId;
|
|
645
|
+
const orgId = this.$webex.credentials.getOrgId();
|
|
646
|
+
this.agentConfig = await this.services.config.getAgentConfig(orgId, agentId);
|
|
647
|
+
_loggerProxy.default.log(`Agent config is fetched successfully`, {
|
|
648
|
+
module: _constants.CC_FILE,
|
|
649
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
650
|
+
});
|
|
651
|
+
const configFlags = {
|
|
652
|
+
isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
|
|
653
|
+
isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
|
|
654
|
+
webRtcEnabled: this.agentConfig.webRtcEnabled,
|
|
655
|
+
autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
|
|
656
|
+
aiFeature: this.agentConfig.aiFeature
|
|
657
|
+
};
|
|
658
|
+
this.taskManager.setConfigFlags(configFlags);
|
|
659
|
+
// TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
|
|
660
|
+
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
661
|
+
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
662
|
+
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
663
|
+
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
667
|
+
* Extend this condition when additional AI RTD features are introduced.
|
|
668
|
+
*/
|
|
669
|
+
if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable || this.agentConfig.aiFeature?.suggestedResponses?.enable) {
|
|
670
|
+
_loggerProxy.default.info('Connecting to RTD websocket', {
|
|
616
671
|
module: _constants.CC_FILE,
|
|
617
672
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
618
673
|
});
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
aiFeature: this.agentConfig.aiFeature
|
|
625
|
-
};
|
|
626
|
-
this.taskManager.setConfigFlags(configFlags);
|
|
627
|
-
// TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
|
|
628
|
-
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
629
|
-
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
630
|
-
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
631
|
-
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
632
|
-
/**
|
|
633
|
-
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
634
|
-
* Extend this condition when additional AI RTD features are introduced.
|
|
635
|
-
*/
|
|
636
|
-
if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable || this.agentConfig.aiFeature?.suggestedResponses?.enable) {
|
|
637
|
-
_loggerProxy.default.info('Connecting to RTD websocket', {
|
|
674
|
+
this.services.rtdWebSocketManager.initWebSocket({
|
|
675
|
+
body: this.getConnectionConfig(),
|
|
676
|
+
resource: _constants3.RTD_SUBSCRIBE_API
|
|
677
|
+
}).then(() => {
|
|
678
|
+
_loggerProxy.default.log('RTD websocket connected successfully', {
|
|
638
679
|
module: _constants.CC_FILE,
|
|
639
680
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
640
681
|
});
|
|
641
|
-
this.services.rtdWebSocketManager.
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
module: _constants.CC_FILE,
|
|
647
|
-
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
648
|
-
});
|
|
649
|
-
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
650
|
-
}).catch(error => {
|
|
651
|
-
_loggerProxy.default.error(`Error connecting to RTD websocket ${error}`, {
|
|
652
|
-
module: _constants.CC_FILE,
|
|
653
|
-
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
654
|
-
});
|
|
682
|
+
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
683
|
+
}).catch(error => {
|
|
684
|
+
_loggerProxy.default.error(`Error connecting to RTD websocket ${error}`, {
|
|
685
|
+
module: _constants.CC_FILE,
|
|
686
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
655
687
|
});
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER) && !this.isWebRTCRegistrationDisabled()) {
|
|
691
|
+
try {
|
|
692
|
+
await this.$webex.internal.mercury.connect();
|
|
693
|
+
_loggerProxy.default.log('Authentication: webex.internal.mercury.connect successful', {
|
|
694
|
+
module: _constants.CC_FILE,
|
|
695
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
696
|
+
});
|
|
697
|
+
} catch (error) {
|
|
698
|
+
_loggerProxy.default.error(`Error occurred during mercury.connect() ${error}`, {
|
|
699
|
+
module: _constants.CC_FILE,
|
|
700
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
668
701
|
});
|
|
669
702
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
703
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
704
|
+
_loggerProxy.default.info('Skipping Mobius registration because disableWebRTCRegistration is enabled', {
|
|
705
|
+
module: _constants.CC_FILE,
|
|
706
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
710
|
+
await this.silentRelogin();
|
|
711
|
+
} else {
|
|
712
|
+
_loggerProxy.default.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
|
|
713
|
+
module: _constants.CC_FILE,
|
|
714
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
return this.agentConfig;
|
|
677
718
|
} catch (error) {
|
|
678
719
|
_loggerProxy.default.error(`Error during register: ${error}`, {
|
|
679
720
|
module: _constants.CC_FILE,
|
|
@@ -712,21 +753,27 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
712
753
|
* ```
|
|
713
754
|
*/
|
|
714
755
|
async stationLogin(data) {
|
|
715
|
-
|
|
756
|
+
const loggerContext = {
|
|
716
757
|
module: _constants.CC_FILE,
|
|
717
758
|
method: _constants.METHODS.STATION_LOGIN
|
|
718
|
-
}
|
|
759
|
+
};
|
|
719
760
|
try {
|
|
761
|
+
_loggerProxy.default.log(`Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`, loggerContext);
|
|
720
762
|
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, _constants4.METRIC_EVENT_NAMES.STATION_LOGIN_FAILED]);
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
data: {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
763
|
+
const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
|
|
764
|
+
if (data.loginOption === _types.LoginOption.AGENT_DN) {
|
|
765
|
+
_loggerProxy.default.log(`Validating dial number | dialPlanEnabled: ${!!this.agentConfig?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`, loggerContext);
|
|
766
|
+
if (!(0, _Utils.isValidDialNumber)(data.dialNumber, dialPlanEntries)) {
|
|
767
|
+
_loggerProxy.default.log(`Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`, loggerContext);
|
|
768
|
+
const error = new Error('INVALID_DIAL_NUMBER');
|
|
769
|
+
// @ts-ignore - adding custom key to the error object
|
|
770
|
+
error.details = {
|
|
771
|
+
data: {
|
|
772
|
+
reason: 'INVALID_DIAL_NUMBER'
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
throw error;
|
|
776
|
+
}
|
|
730
777
|
}
|
|
731
778
|
const loginResponse = await this.services.agent.stationLogin({
|
|
732
779
|
data: {
|
|
@@ -742,8 +789,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
742
789
|
auxCodeId: _constants.EMPTY_STRING
|
|
743
790
|
}
|
|
744
791
|
});
|
|
745
|
-
if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER) {
|
|
792
|
+
if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER && !this.isWebRTCRegistrationDisabled()) {
|
|
746
793
|
await this.webCallingService.registerWebCallingLine();
|
|
794
|
+
} else if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER && this.isWebRTCRegistrationDisabled()) {
|
|
795
|
+
_loggerProxy.default.info('Skipping web calling line registration because disableWebRTCRegistration is enabled', {
|
|
796
|
+
module: _constants.CC_FILE,
|
|
797
|
+
method: _constants.METHODS.STATION_LOGIN
|
|
798
|
+
});
|
|
747
799
|
}
|
|
748
800
|
const resp = await loginResponse;
|
|
749
801
|
const {
|
|
@@ -1066,9 +1118,25 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1066
1118
|
break;
|
|
1067
1119
|
}
|
|
1068
1120
|
};
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1121
|
+
|
|
1122
|
+
/**
|
|
1123
|
+
* Checks whether Mobius/WebRTC registration should be skipped by config
|
|
1124
|
+
* @returns {boolean} True when browser WebRTC registration is disabled
|
|
1125
|
+
* @private
|
|
1126
|
+
*/
|
|
1127
|
+
isWebRTCRegistrationDisabled() {
|
|
1128
|
+
return this.$config?.disableWebRTCRegistration === true;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Validates contact-center plugin configuration before service initialization
|
|
1133
|
+
* @private
|
|
1134
|
+
*/
|
|
1135
|
+
validatePluginConfig() {
|
|
1136
|
+
if (this.$config?.disableWebRTCRegistration === true && this.$config?.allowMultiLogin === false) {
|
|
1137
|
+
throw new Error('Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.');
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1072
1140
|
|
|
1073
1141
|
/**
|
|
1074
1142
|
* Initializes event listeners for the Contact Center service
|
|
@@ -1101,13 +1169,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1101
1169
|
async handleConnectionLost(msg) {
|
|
1102
1170
|
if (msg.isConnectionLost) {
|
|
1103
1171
|
// TODO: Emit an event saying connection is lost
|
|
1104
|
-
_loggerProxy.default.
|
|
1172
|
+
_loggerProxy.default.log('event=handleConnectionLost | Connection lost', {
|
|
1105
1173
|
module: _constants.CC_FILE,
|
|
1106
1174
|
method: _constants.METHODS.HANDLE_CONNECTION_LOST
|
|
1107
1175
|
});
|
|
1108
1176
|
} else if (msg.isSocketReconnected) {
|
|
1109
1177
|
// TODO: Emit an event saying connection is re-estabilished
|
|
1110
|
-
_loggerProxy.default.
|
|
1178
|
+
_loggerProxy.default.log('event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin', {
|
|
1111
1179
|
module: _constants.CC_FILE,
|
|
1112
1180
|
method: _constants.METHODS.HANDLE_CONNECTION_LOST
|
|
1113
1181
|
});
|
|
@@ -1122,7 +1190,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1122
1190
|
* @private
|
|
1123
1191
|
*/
|
|
1124
1192
|
async silentRelogin() {
|
|
1125
|
-
_loggerProxy.default.
|
|
1193
|
+
_loggerProxy.default.log('Starting silent relogin process', {
|
|
1126
1194
|
module: _constants.CC_FILE,
|
|
1127
1195
|
method: _constants.METHODS.SILENT_RELOGIN
|
|
1128
1196
|
});
|
|
@@ -1144,7 +1212,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1144
1212
|
this.agentConfig.currentTeamId = reLoginResponse.data.teamId;
|
|
1145
1213
|
await this.handleDeviceType(deviceType, dn);
|
|
1146
1214
|
if (lastStateChangeReason === 'agent-wss-disconnect') {
|
|
1147
|
-
_loggerProxy.default.
|
|
1215
|
+
_loggerProxy.default.log('event=requestAutoStateChange | Requesting state change to available on socket reconnect', {
|
|
1148
1216
|
module: _constants.CC_FILE,
|
|
1149
1217
|
method: _constants.METHODS.SILENT_RELOGIN
|
|
1150
1218
|
});
|
|
@@ -1168,8 +1236,6 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1168
1236
|
}
|
|
1169
1237
|
this.agentConfig.lastStateAuxCodeId = auxCodeId;
|
|
1170
1238
|
this.agentConfig.isAgentLoggedIn = true;
|
|
1171
|
-
// TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
|
|
1172
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
1173
1239
|
_loggerProxy.default.log(`Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`, {
|
|
1174
1240
|
module: _constants.CC_FILE,
|
|
1175
1241
|
method: _constants.METHODS.SILENT_RELOGIN,
|
|
@@ -1204,6 +1270,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1204
1270
|
this.agentConfig.deviceType = deviceType;
|
|
1205
1271
|
switch (deviceType) {
|
|
1206
1272
|
case _types.LoginOption.BROWSER:
|
|
1273
|
+
if (this.isWebRTCRegistrationDisabled()) {
|
|
1274
|
+
_loggerProxy.default.info('Skipping web calling line registration because disableWebRTCRegistration is enabled', {
|
|
1275
|
+
module: _constants.CC_FILE,
|
|
1276
|
+
method: _constants.METHODS.HANDLE_DEVICE_TYPE
|
|
1277
|
+
});
|
|
1278
|
+
break;
|
|
1279
|
+
}
|
|
1207
1280
|
try {
|
|
1208
1281
|
await this.webCallingService.registerWebCallingLine();
|
|
1209
1282
|
} catch (error) {
|
|
@@ -1385,6 +1458,196 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1385
1458
|
}
|
|
1386
1459
|
}
|
|
1387
1460
|
|
|
1461
|
+
/**
|
|
1462
|
+
* Accepts a campaign preview contact, initiating the outbound call.
|
|
1463
|
+
*
|
|
1464
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
1465
|
+
* `AgentOfferCampaignReservation` event. The agent can then accept the preview contact
|
|
1466
|
+
* to initiate the outbound call.
|
|
1467
|
+
*
|
|
1468
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
1469
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
1470
|
+
* @throws {Error} If the operation fails (network error, customer unavailable, etc.)
|
|
1471
|
+
*
|
|
1472
|
+
* @example
|
|
1473
|
+
* ```typescript
|
|
1474
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
1475
|
+
* const { interactionId } = task.data;
|
|
1476
|
+
* // campaignId is the campaign name (e.g. "MyCampaign"), not a UUID
|
|
1477
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
1478
|
+
*
|
|
1479
|
+
* const result = await webex.cc.acceptPreviewContact({ interactionId, campaignId });
|
|
1480
|
+
* });
|
|
1481
|
+
* ```
|
|
1482
|
+
*/
|
|
1483
|
+
async acceptPreviewContact(payload) {
|
|
1484
|
+
_loggerProxy.default.info('Accepting campaign preview contact', {
|
|
1485
|
+
module: _constants.CC_FILE,
|
|
1486
|
+
method: _constants.METHODS.ACCEPT_PREVIEW_CONTACT
|
|
1487
|
+
});
|
|
1488
|
+
try {
|
|
1489
|
+
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED]);
|
|
1490
|
+
const result = await this.services.dialer.acceptPreviewContact({
|
|
1491
|
+
data: payload
|
|
1492
|
+
});
|
|
1493
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS, {
|
|
1494
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
|
|
1495
|
+
interactionId: payload.interactionId,
|
|
1496
|
+
campaignId: payload.campaignId
|
|
1497
|
+
}, ['behavioral', 'business', 'operational']);
|
|
1498
|
+
_loggerProxy.default.log('Campaign preview contact accepted successfully', {
|
|
1499
|
+
module: _constants.CC_FILE,
|
|
1500
|
+
method: _constants.METHODS.ACCEPT_PREVIEW_CONTACT,
|
|
1501
|
+
trackingId: result.trackingId,
|
|
1502
|
+
interactionId: payload.interactionId
|
|
1503
|
+
});
|
|
1504
|
+
return result;
|
|
1505
|
+
} catch (error) {
|
|
1506
|
+
const failure = error.details;
|
|
1507
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED, {
|
|
1508
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
1509
|
+
interactionId: payload.interactionId,
|
|
1510
|
+
campaignId: payload.campaignId
|
|
1511
|
+
}, ['behavioral', 'business', 'operational']);
|
|
1512
|
+
const {
|
|
1513
|
+
error: detailedError
|
|
1514
|
+
} = (0, _Utils.getErrorDetails)(error, _constants.METHODS.ACCEPT_PREVIEW_CONTACT, _constants.CC_FILE);
|
|
1515
|
+
throw detailedError;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* Skips a campaign preview contact, requesting the next contact from the campaign.
|
|
1521
|
+
*
|
|
1522
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
1523
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
|
|
1524
|
+
* preview contact to move to the next contact in the campaign.
|
|
1525
|
+
*
|
|
1526
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
1527
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
1528
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
1529
|
+
*
|
|
1530
|
+
* @example
|
|
1531
|
+
* ```typescript
|
|
1532
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
1533
|
+
* const { interactionId } = task.data;
|
|
1534
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
1535
|
+
*
|
|
1536
|
+
* const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
|
|
1537
|
+
* });
|
|
1538
|
+
* ```
|
|
1539
|
+
*/
|
|
1540
|
+
async skipPreviewContact(payload) {
|
|
1541
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
1542
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
|
|
1543
|
+
_loggerProxy.default.warn('Skip action is disabled for this campaign preview contact', {
|
|
1544
|
+
module: _constants.CC_FILE,
|
|
1545
|
+
method: _constants.METHODS.SKIP_PREVIEW_CONTACT,
|
|
1546
|
+
interactionId: payload.interactionId
|
|
1547
|
+
});
|
|
1548
|
+
throw new Error('Skip action is disabled for this campaign preview contact');
|
|
1549
|
+
}
|
|
1550
|
+
_loggerProxy.default.info('Skipping campaign preview contact', {
|
|
1551
|
+
module: _constants.CC_FILE,
|
|
1552
|
+
method: _constants.METHODS.SKIP_PREVIEW_CONTACT
|
|
1553
|
+
});
|
|
1554
|
+
try {
|
|
1555
|
+
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED]);
|
|
1556
|
+
const result = await this.services.dialer.skipPreviewContact({
|
|
1557
|
+
data: payload
|
|
1558
|
+
});
|
|
1559
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS, {
|
|
1560
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
|
|
1561
|
+
interactionId: payload.interactionId,
|
|
1562
|
+
campaignId: payload.campaignId
|
|
1563
|
+
}, ['behavioral', 'business', 'operational']);
|
|
1564
|
+
_loggerProxy.default.log('Campaign preview contact skipped successfully', {
|
|
1565
|
+
module: _constants.CC_FILE,
|
|
1566
|
+
method: _constants.METHODS.SKIP_PREVIEW_CONTACT,
|
|
1567
|
+
trackingId: result.trackingId,
|
|
1568
|
+
interactionId: payload.interactionId
|
|
1569
|
+
});
|
|
1570
|
+
return result;
|
|
1571
|
+
} catch (error) {
|
|
1572
|
+
const failure = error.details;
|
|
1573
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED, {
|
|
1574
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
1575
|
+
interactionId: payload.interactionId,
|
|
1576
|
+
campaignId: payload.campaignId
|
|
1577
|
+
}, ['behavioral', 'business', 'operational']);
|
|
1578
|
+
const {
|
|
1579
|
+
error: detailedError
|
|
1580
|
+
} = (0, _Utils.getErrorDetails)(error, _constants.METHODS.SKIP_PREVIEW_CONTACT, _constants.CC_FILE);
|
|
1581
|
+
throw detailedError;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
/**
|
|
1586
|
+
* Removes a campaign preview contact from the campaign list entirely.
|
|
1587
|
+
*
|
|
1588
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
1589
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
|
|
1590
|
+
* remove the preview contact to permanently take it out of the campaign contact list.
|
|
1591
|
+
*
|
|
1592
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
1593
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
1594
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
1595
|
+
*
|
|
1596
|
+
* @example
|
|
1597
|
+
* ```typescript
|
|
1598
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
1599
|
+
* const { interactionId } = task.data;
|
|
1600
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
1601
|
+
*
|
|
1602
|
+
* const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
|
|
1603
|
+
* });
|
|
1604
|
+
* ```
|
|
1605
|
+
*/
|
|
1606
|
+
async removePreviewContact(payload) {
|
|
1607
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
1608
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
|
|
1609
|
+
_loggerProxy.default.warn('Remove action is disabled for this campaign preview contact', {
|
|
1610
|
+
module: _constants.CC_FILE,
|
|
1611
|
+
method: _constants.METHODS.REMOVE_PREVIEW_CONTACT,
|
|
1612
|
+
interactionId: payload.interactionId
|
|
1613
|
+
});
|
|
1614
|
+
throw new Error('Remove action is disabled for this campaign preview contact');
|
|
1615
|
+
}
|
|
1616
|
+
_loggerProxy.default.info('Removing campaign preview contact', {
|
|
1617
|
+
module: _constants.CC_FILE,
|
|
1618
|
+
method: _constants.METHODS.REMOVE_PREVIEW_CONTACT
|
|
1619
|
+
});
|
|
1620
|
+
try {
|
|
1621
|
+
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED]);
|
|
1622
|
+
const result = await this.services.dialer.removePreviewContact({
|
|
1623
|
+
data: payload
|
|
1624
|
+
});
|
|
1625
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS, {
|
|
1626
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
|
|
1627
|
+
interactionId: payload.interactionId,
|
|
1628
|
+
campaignId: payload.campaignId
|
|
1629
|
+
}, ['behavioral', 'business', 'operational']);
|
|
1630
|
+
_loggerProxy.default.log('Campaign preview contact removed successfully', {
|
|
1631
|
+
module: _constants.CC_FILE,
|
|
1632
|
+
method: _constants.METHODS.REMOVE_PREVIEW_CONTACT,
|
|
1633
|
+
trackingId: result.trackingId,
|
|
1634
|
+
interactionId: payload.interactionId
|
|
1635
|
+
});
|
|
1636
|
+
return result;
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
const failure = error.details;
|
|
1639
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED, {
|
|
1640
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
1641
|
+
interactionId: payload.interactionId,
|
|
1642
|
+
campaignId: payload.campaignId
|
|
1643
|
+
}, ['behavioral', 'business', 'operational']);
|
|
1644
|
+
const {
|
|
1645
|
+
error: detailedError
|
|
1646
|
+
} = (0, _Utils.getErrorDetails)(error, _constants.METHODS.REMOVE_PREVIEW_CONTACT, _constants.CC_FILE);
|
|
1647
|
+
throw detailedError;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1388
1651
|
/**
|
|
1389
1652
|
* Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
|
|
1390
1653
|
*
|