@webex/contact-center 3.12.0-task-refactor.11 → 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 +297 -83
- package/dist/cc.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 +6 -0
- package/dist/services/task/Task.js.map +1 -1
- package/dist/services/task/TaskManager.js +155 -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/types.js +59 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +76 -2
- 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 +75 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/cc.ts +389 -111
- 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 +13 -0
- package/src/services/task/TaskManager.ts +171 -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/types.ts +80 -0
- package/src/types.ts +4 -1
- package/test/unit/spec/cc.ts +293 -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 +2325 -150
- package/test/unit/spec/services/task/dialer.ts +372 -96
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/src/cc.ts
CHANGED
|
@@ -56,7 +56,13 @@ import {
|
|
|
56
56
|
import {ConnectionLostDetails} from './services/core/websocket/types';
|
|
57
57
|
import TaskManager from './services/task/TaskManager';
|
|
58
58
|
import WebCallingService from './services/WebCallingService';
|
|
59
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
ITask,
|
|
61
|
+
TASK_EVENTS,
|
|
62
|
+
TaskResponse,
|
|
63
|
+
DialerPayload,
|
|
64
|
+
PreviewContactPayload,
|
|
65
|
+
} from './services/task/types';
|
|
60
66
|
import MetricsManager from './metrics/MetricsManager';
|
|
61
67
|
import {METRIC_EVENT_NAMES} from './metrics/constants';
|
|
62
68
|
import {Failure} from './services/core/GlobalTypes';
|
|
@@ -121,6 +127,7 @@ import type {
|
|
|
121
127
|
* - `task:established` - Task/call has been connected
|
|
122
128
|
* - `task:ended` - Task/call has ended
|
|
123
129
|
* - `task:error` - An error occurred during task handling
|
|
130
|
+
* - `task:campaignPreviewReservation` - Campaign preview contact offered to agent
|
|
124
131
|
*
|
|
125
132
|
* @public
|
|
126
133
|
*
|
|
@@ -364,7 +371,6 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
364
371
|
webex: this.$webex,
|
|
365
372
|
connectionConfig: this.getConnectionConfig(),
|
|
366
373
|
});
|
|
367
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
368
374
|
|
|
369
375
|
this.webCallingService = new WebCallingService(this.$webex);
|
|
370
376
|
this.apiAIAssistant = new ApiAIAssistant(this.$webex);
|
|
@@ -427,6 +433,20 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
427
433
|
this.trigger(TASK_EVENTS.TASK_MERGED, task);
|
|
428
434
|
};
|
|
429
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Handles campaign preview reservation events when a contact is offered to the agent
|
|
438
|
+
* @private
|
|
439
|
+
* @param {ITask} task The campaign reservation task
|
|
440
|
+
*/
|
|
441
|
+
private handleCampaignPreviewReservation = (task: ITask) => {
|
|
442
|
+
// @ts-ignore
|
|
443
|
+
this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
private handleRTDWebsocketMessage = (event: string) => {
|
|
447
|
+
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
448
|
+
};
|
|
449
|
+
|
|
430
450
|
/**
|
|
431
451
|
* Sets up event listeners for incoming tasks and task hydration
|
|
432
452
|
* Subscribes to task events from the task manager
|
|
@@ -437,6 +457,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
437
457
|
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
438
458
|
this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
439
459
|
this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
460
|
+
this.taskManager.on(
|
|
461
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
462
|
+
this.handleCampaignPreviewReservation
|
|
463
|
+
);
|
|
440
464
|
}
|
|
441
465
|
|
|
442
466
|
/**
|
|
@@ -476,7 +500,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
476
500
|
* ```
|
|
477
501
|
*/
|
|
478
502
|
public async register(): Promise<Profile> {
|
|
479
|
-
LoggerProxy.
|
|
503
|
+
LoggerProxy.log('Starting CC SDK registration', {
|
|
480
504
|
module: CC_FILE,
|
|
481
505
|
method: METHODS.REGISTER,
|
|
482
506
|
});
|
|
@@ -486,6 +510,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
486
510
|
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
|
|
487
511
|
]);
|
|
488
512
|
this.setupEventListeners();
|
|
513
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
489
514
|
|
|
490
515
|
const resp = await this.connectWebsocket();
|
|
491
516
|
// Ensure 'dn' is always populated from 'defaultDn'
|
|
@@ -566,6 +591,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
566
591
|
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
567
592
|
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
568
593
|
this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
594
|
+
this.taskManager.off(
|
|
595
|
+
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
596
|
+
this.handleCampaignPreviewReservation
|
|
597
|
+
);
|
|
569
598
|
this.taskManager.unregisterIncomingCallEvent();
|
|
570
599
|
|
|
571
600
|
this.services.webSocketManager.off('message', this.handleWebsocketMessage);
|
|
@@ -596,6 +625,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
596
625
|
this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
|
|
597
626
|
}
|
|
598
627
|
|
|
628
|
+
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
629
|
+
this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
|
|
630
|
+
}
|
|
631
|
+
|
|
599
632
|
// Clear any cached agent configuration
|
|
600
633
|
this.agentConfig = null;
|
|
601
634
|
|
|
@@ -716,107 +749,110 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
716
749
|
* @private
|
|
717
750
|
*/
|
|
718
751
|
private async connectWebsocket() {
|
|
719
|
-
LoggerProxy.
|
|
752
|
+
LoggerProxy.log('Connecting to websocket', {
|
|
720
753
|
module: CC_FILE,
|
|
721
754
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
722
755
|
});
|
|
756
|
+
|
|
723
757
|
try {
|
|
724
|
-
|
|
725
|
-
.
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
})
|
|
729
|
-
.then(async (data: WelcomeEvent) => {
|
|
730
|
-
const agentId = data.agentId;
|
|
731
|
-
const orgId = this.$webex.credentials.getOrgId();
|
|
732
|
-
this.agentConfig = await this.services.config.getAgentConfig(orgId, agentId);
|
|
733
|
-
LoggerProxy.log(`Agent config is fetched successfully`, {
|
|
734
|
-
module: CC_FILE,
|
|
735
|
-
method: METHODS.CONNECT_WEBSOCKET,
|
|
736
|
-
});
|
|
758
|
+
const data = (await this.services.webSocketManager.initWebSocket({
|
|
759
|
+
body: this.getConnectionConfig(),
|
|
760
|
+
resource: SUBSCRIBE_API,
|
|
761
|
+
})) as WelcomeEvent;
|
|
737
762
|
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
763
|
+
const agentId = data.agentId;
|
|
764
|
+
const orgId = this.$webex.credentials.getOrgId();
|
|
765
|
+
this.agentConfig = await this.services.config.getAgentConfig(orgId, agentId);
|
|
766
|
+
|
|
767
|
+
LoggerProxy.log(`Agent config is fetched successfully`, {
|
|
768
|
+
module: CC_FILE,
|
|
769
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
const configFlags: ConfigFlags = {
|
|
773
|
+
isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
|
|
774
|
+
isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
|
|
775
|
+
webRtcEnabled: this.agentConfig.webRtcEnabled,
|
|
776
|
+
autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
|
|
777
|
+
aiFeature: this.agentConfig.aiFeature,
|
|
778
|
+
};
|
|
779
|
+
this.taskManager.setConfigFlags(configFlags);
|
|
780
|
+
// TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
|
|
781
|
+
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
782
|
+
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
783
|
+
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
784
|
+
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
788
|
+
* Extend this condition when additional AI RTD features are introduced.
|
|
789
|
+
*/
|
|
790
|
+
if (
|
|
791
|
+
this.agentConfig.aiFeature?.realtimeTranscripts?.enable ||
|
|
792
|
+
this.agentConfig.aiFeature?.suggestedResponses?.enable
|
|
793
|
+
) {
|
|
794
|
+
LoggerProxy.info('Connecting to RTD websocket', {
|
|
795
|
+
module: CC_FILE,
|
|
796
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
this.services.rtdWebSocketManager
|
|
800
|
+
.initWebSocket({
|
|
801
|
+
body: this.getConnectionConfig(),
|
|
802
|
+
resource: RTD_SUBSCRIBE_API,
|
|
803
|
+
})
|
|
804
|
+
.then(() => {
|
|
805
|
+
LoggerProxy.log('RTD websocket connected successfully', {
|
|
806
|
+
module: CC_FILE,
|
|
807
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
808
|
+
});
|
|
809
|
+
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
810
|
+
})
|
|
811
|
+
.catch((error) => {
|
|
812
|
+
LoggerProxy.error(`Error connecting to RTD websocket ${error}`, {
|
|
760
813
|
module: CC_FILE,
|
|
761
814
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
762
815
|
});
|
|
816
|
+
});
|
|
817
|
+
}
|
|
763
818
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
) {
|
|
788
|
-
this.$webex.internal.mercury
|
|
789
|
-
.connect()
|
|
790
|
-
.then(() => {
|
|
791
|
-
LoggerProxy.log('Authentication: webex.internal.mercury.connect successful', {
|
|
792
|
-
module: CC_FILE,
|
|
793
|
-
method: METHODS.CONNECT_WEBSOCKET,
|
|
794
|
-
});
|
|
795
|
-
})
|
|
796
|
-
.catch((error) => {
|
|
797
|
-
LoggerProxy.error(`Error occurred during mercury.connect() ${error}`, {
|
|
798
|
-
module: CC_FILE,
|
|
799
|
-
method: METHODS.CONNECT_WEBSOCKET,
|
|
800
|
-
});
|
|
801
|
-
});
|
|
802
|
-
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
803
|
-
LoggerProxy.info(
|
|
804
|
-
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
805
|
-
{
|
|
806
|
-
module: CC_FILE,
|
|
807
|
-
method: METHODS.CONNECT_WEBSOCKET,
|
|
808
|
-
}
|
|
809
|
-
);
|
|
810
|
-
}
|
|
811
|
-
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
812
|
-
await this.silentRelogin();
|
|
819
|
+
if (
|
|
820
|
+
this.agentConfig.webRtcEnabled &&
|
|
821
|
+
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
|
|
822
|
+
!this.isWebRTCRegistrationDisabled()
|
|
823
|
+
) {
|
|
824
|
+
try {
|
|
825
|
+
await this.$webex.internal.mercury.connect();
|
|
826
|
+
LoggerProxy.log('Authentication: webex.internal.mercury.connect successful', {
|
|
827
|
+
module: CC_FILE,
|
|
828
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
829
|
+
});
|
|
830
|
+
} catch (error) {
|
|
831
|
+
LoggerProxy.error(`Error occurred during mercury.connect() ${error}`, {
|
|
832
|
+
module: CC_FILE,
|
|
833
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
837
|
+
LoggerProxy.info(
|
|
838
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
839
|
+
{
|
|
840
|
+
module: CC_FILE,
|
|
841
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
813
842
|
}
|
|
843
|
+
);
|
|
844
|
+
}
|
|
814
845
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
846
|
+
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
847
|
+
await this.silentRelogin();
|
|
848
|
+
} else {
|
|
849
|
+
LoggerProxy.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
|
|
850
|
+
module: CC_FILE,
|
|
851
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
819
852
|
});
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return this.agentConfig;
|
|
820
856
|
} catch (error) {
|
|
821
857
|
LoggerProxy.error(`Error during register: ${error}`, {
|
|
822
858
|
module: CC_FILE,
|
|
@@ -856,22 +892,37 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
856
892
|
* ```
|
|
857
893
|
*/
|
|
858
894
|
public async stationLogin(data: AgentLogin): Promise<StationLoginResponse> {
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
method: METHODS.STATION_LOGIN,
|
|
862
|
-
});
|
|
895
|
+
const loggerContext = {module: CC_FILE, method: METHODS.STATION_LOGIN};
|
|
896
|
+
|
|
863
897
|
try {
|
|
898
|
+
LoggerProxy.log(
|
|
899
|
+
`Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`,
|
|
900
|
+
loggerContext
|
|
901
|
+
);
|
|
864
902
|
this.metricsManager.timeEvent([
|
|
865
903
|
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
866
904
|
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
867
905
|
]);
|
|
868
906
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
907
|
+
const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
|
|
908
|
+
if (data.loginOption === LoginOption.AGENT_DN) {
|
|
909
|
+
LoggerProxy.log(
|
|
910
|
+
`Validating dial number | dialPlanEnabled: ${!!this.agentConfig
|
|
911
|
+
?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
912
|
+
loggerContext
|
|
913
|
+
);
|
|
873
914
|
|
|
874
|
-
|
|
915
|
+
if (!isValidDialNumber(data.dialNumber, dialPlanEntries)) {
|
|
916
|
+
LoggerProxy.log(
|
|
917
|
+
`Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
918
|
+
loggerContext
|
|
919
|
+
);
|
|
920
|
+
const error = new Error('INVALID_DIAL_NUMBER');
|
|
921
|
+
// @ts-ignore - adding custom key to the error object
|
|
922
|
+
error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
|
|
923
|
+
|
|
924
|
+
throw error;
|
|
925
|
+
}
|
|
875
926
|
}
|
|
876
927
|
|
|
877
928
|
const loginResponse = await this.services.agent.stationLogin({
|
|
@@ -1269,10 +1320,6 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1269
1320
|
}
|
|
1270
1321
|
};
|
|
1271
1322
|
|
|
1272
|
-
private handleRTDWebsocketMessage = (event: string) => {
|
|
1273
|
-
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
1274
|
-
};
|
|
1275
|
-
|
|
1276
1323
|
/**
|
|
1277
1324
|
* Checks whether Mobius/WebRTC registration should be skipped by config
|
|
1278
1325
|
* @returns {boolean} True when browser WebRTC registration is disabled
|
|
@@ -1328,13 +1375,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1328
1375
|
private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
|
|
1329
1376
|
if (msg.isConnectionLost) {
|
|
1330
1377
|
// TODO: Emit an event saying connection is lost
|
|
1331
|
-
LoggerProxy.
|
|
1378
|
+
LoggerProxy.log('event=handleConnectionLost | Connection lost', {
|
|
1332
1379
|
module: CC_FILE,
|
|
1333
1380
|
method: METHODS.HANDLE_CONNECTION_LOST,
|
|
1334
1381
|
});
|
|
1335
1382
|
} else if (msg.isSocketReconnected) {
|
|
1336
1383
|
// TODO: Emit an event saying connection is re-estabilished
|
|
1337
|
-
LoggerProxy.
|
|
1384
|
+
LoggerProxy.log(
|
|
1338
1385
|
'event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin',
|
|
1339
1386
|
{module: CC_FILE, method: METHODS.HANDLE_CONNECTION_LOST}
|
|
1340
1387
|
);
|
|
@@ -1349,7 +1396,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1349
1396
|
* @private
|
|
1350
1397
|
*/
|
|
1351
1398
|
private async silentRelogin(): Promise<void> {
|
|
1352
|
-
LoggerProxy.
|
|
1399
|
+
LoggerProxy.log('Starting silent relogin process', {
|
|
1353
1400
|
module: CC_FILE,
|
|
1354
1401
|
method: METHODS.SILENT_RELOGIN,
|
|
1355
1402
|
});
|
|
@@ -1371,7 +1418,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1371
1418
|
await this.handleDeviceType(deviceType as LoginOption, dn);
|
|
1372
1419
|
|
|
1373
1420
|
if (lastStateChangeReason === 'agent-wss-disconnect') {
|
|
1374
|
-
LoggerProxy.
|
|
1421
|
+
LoggerProxy.log(
|
|
1375
1422
|
'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
|
|
1376
1423
|
{module: CC_FILE, method: METHODS.SILENT_RELOGIN}
|
|
1377
1424
|
);
|
|
@@ -1400,8 +1447,6 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1400
1447
|
}
|
|
1401
1448
|
this.agentConfig.lastStateAuxCodeId = auxCodeId;
|
|
1402
1449
|
this.agentConfig.isAgentLoggedIn = true;
|
|
1403
|
-
// TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
|
|
1404
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
1405
1450
|
|
|
1406
1451
|
LoggerProxy.log(
|
|
1407
1452
|
`Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
|
|
@@ -1644,6 +1689,239 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1644
1689
|
}
|
|
1645
1690
|
}
|
|
1646
1691
|
|
|
1692
|
+
/**
|
|
1693
|
+
* Accepts a campaign preview contact, initiating the outbound call.
|
|
1694
|
+
*
|
|
1695
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
1696
|
+
* `AgentOfferCampaignReservation` event. The agent can then accept the preview contact
|
|
1697
|
+
* to initiate the outbound call.
|
|
1698
|
+
*
|
|
1699
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
1700
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
1701
|
+
* @throws {Error} If the operation fails (network error, customer unavailable, etc.)
|
|
1702
|
+
*
|
|
1703
|
+
* @example
|
|
1704
|
+
* ```typescript
|
|
1705
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
1706
|
+
* const { interactionId } = task.data;
|
|
1707
|
+
* // campaignId is the campaign name (e.g. "MyCampaign"), not a UUID
|
|
1708
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
1709
|
+
*
|
|
1710
|
+
* const result = await webex.cc.acceptPreviewContact({ interactionId, campaignId });
|
|
1711
|
+
* });
|
|
1712
|
+
* ```
|
|
1713
|
+
*/
|
|
1714
|
+
public async acceptPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
1715
|
+
LoggerProxy.info('Accepting campaign preview contact', {
|
|
1716
|
+
module: CC_FILE,
|
|
1717
|
+
method: METHODS.ACCEPT_PREVIEW_CONTACT,
|
|
1718
|
+
});
|
|
1719
|
+
try {
|
|
1720
|
+
this.metricsManager.timeEvent([
|
|
1721
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
|
|
1722
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
1723
|
+
]);
|
|
1724
|
+
|
|
1725
|
+
const result = await this.services.dialer.acceptPreviewContact({data: payload});
|
|
1726
|
+
|
|
1727
|
+
this.metricsManager.trackEvent(
|
|
1728
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
|
|
1729
|
+
{
|
|
1730
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
1731
|
+
interactionId: payload.interactionId,
|
|
1732
|
+
campaignId: payload.campaignId,
|
|
1733
|
+
},
|
|
1734
|
+
['behavioral', 'business', 'operational']
|
|
1735
|
+
);
|
|
1736
|
+
|
|
1737
|
+
LoggerProxy.log('Campaign preview contact accepted successfully', {
|
|
1738
|
+
module: CC_FILE,
|
|
1739
|
+
method: METHODS.ACCEPT_PREVIEW_CONTACT,
|
|
1740
|
+
trackingId: result.trackingId,
|
|
1741
|
+
interactionId: payload.interactionId,
|
|
1742
|
+
});
|
|
1743
|
+
|
|
1744
|
+
return result;
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
const failure = error.details as Failure;
|
|
1747
|
+
this.metricsManager.trackEvent(
|
|
1748
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
1749
|
+
{
|
|
1750
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
1751
|
+
interactionId: payload.interactionId,
|
|
1752
|
+
campaignId: payload.campaignId,
|
|
1753
|
+
},
|
|
1754
|
+
['behavioral', 'business', 'operational']
|
|
1755
|
+
);
|
|
1756
|
+
const {error: detailedError} = getErrorDetails(
|
|
1757
|
+
error,
|
|
1758
|
+
METHODS.ACCEPT_PREVIEW_CONTACT,
|
|
1759
|
+
CC_FILE
|
|
1760
|
+
);
|
|
1761
|
+
throw detailedError;
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
/**
|
|
1766
|
+
* Skips a campaign preview contact, requesting the next contact from the campaign.
|
|
1767
|
+
*
|
|
1768
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
1769
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
|
|
1770
|
+
* preview contact to move to the next contact in the campaign.
|
|
1771
|
+
*
|
|
1772
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
1773
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
1774
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
1775
|
+
*
|
|
1776
|
+
* @example
|
|
1777
|
+
* ```typescript
|
|
1778
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
1779
|
+
* const { interactionId } = task.data;
|
|
1780
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
1781
|
+
*
|
|
1782
|
+
* const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
|
|
1783
|
+
* });
|
|
1784
|
+
* ```
|
|
1785
|
+
*/
|
|
1786
|
+
public async skipPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
1787
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
1788
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
|
|
1789
|
+
LoggerProxy.warn('Skip action is disabled for this campaign preview contact', {
|
|
1790
|
+
module: CC_FILE,
|
|
1791
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
1792
|
+
interactionId: payload.interactionId,
|
|
1793
|
+
});
|
|
1794
|
+
throw new Error('Skip action is disabled for this campaign preview contact');
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
LoggerProxy.info('Skipping campaign preview contact', {
|
|
1798
|
+
module: CC_FILE,
|
|
1799
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
1800
|
+
});
|
|
1801
|
+
try {
|
|
1802
|
+
this.metricsManager.timeEvent([
|
|
1803
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
|
|
1804
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
1805
|
+
]);
|
|
1806
|
+
|
|
1807
|
+
const result = await this.services.dialer.skipPreviewContact({data: payload});
|
|
1808
|
+
|
|
1809
|
+
this.metricsManager.trackEvent(
|
|
1810
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
|
|
1811
|
+
{
|
|
1812
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
1813
|
+
interactionId: payload.interactionId,
|
|
1814
|
+
campaignId: payload.campaignId,
|
|
1815
|
+
},
|
|
1816
|
+
['behavioral', 'business', 'operational']
|
|
1817
|
+
);
|
|
1818
|
+
|
|
1819
|
+
LoggerProxy.log('Campaign preview contact skipped successfully', {
|
|
1820
|
+
module: CC_FILE,
|
|
1821
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
1822
|
+
trackingId: result.trackingId,
|
|
1823
|
+
interactionId: payload.interactionId,
|
|
1824
|
+
});
|
|
1825
|
+
|
|
1826
|
+
return result;
|
|
1827
|
+
} catch (error) {
|
|
1828
|
+
const failure = error.details as Failure;
|
|
1829
|
+
this.metricsManager.trackEvent(
|
|
1830
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
1831
|
+
{
|
|
1832
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
1833
|
+
interactionId: payload.interactionId,
|
|
1834
|
+
campaignId: payload.campaignId,
|
|
1835
|
+
},
|
|
1836
|
+
['behavioral', 'business', 'operational']
|
|
1837
|
+
);
|
|
1838
|
+
const {error: detailedError} = getErrorDetails(error, METHODS.SKIP_PREVIEW_CONTACT, CC_FILE);
|
|
1839
|
+
throw detailedError;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
/**
|
|
1844
|
+
* Removes a campaign preview contact from the campaign list entirely.
|
|
1845
|
+
*
|
|
1846
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
1847
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
|
|
1848
|
+
* remove the preview contact to permanently take it out of the campaign contact list.
|
|
1849
|
+
*
|
|
1850
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
1851
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
1852
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
1853
|
+
*
|
|
1854
|
+
* @example
|
|
1855
|
+
* ```typescript
|
|
1856
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
1857
|
+
* const { interactionId } = task.data;
|
|
1858
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
1859
|
+
*
|
|
1860
|
+
* const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
|
|
1861
|
+
* });
|
|
1862
|
+
* ```
|
|
1863
|
+
*/
|
|
1864
|
+
public async removePreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
1865
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
1866
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
|
|
1867
|
+
LoggerProxy.warn('Remove action is disabled for this campaign preview contact', {
|
|
1868
|
+
module: CC_FILE,
|
|
1869
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
1870
|
+
interactionId: payload.interactionId,
|
|
1871
|
+
});
|
|
1872
|
+
throw new Error('Remove action is disabled for this campaign preview contact');
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
LoggerProxy.info('Removing campaign preview contact', {
|
|
1876
|
+
module: CC_FILE,
|
|
1877
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
1878
|
+
});
|
|
1879
|
+
try {
|
|
1880
|
+
this.metricsManager.timeEvent([
|
|
1881
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
|
|
1882
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
1883
|
+
]);
|
|
1884
|
+
|
|
1885
|
+
const result = await this.services.dialer.removePreviewContact({data: payload});
|
|
1886
|
+
|
|
1887
|
+
this.metricsManager.trackEvent(
|
|
1888
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
|
|
1889
|
+
{
|
|
1890
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
1891
|
+
interactionId: payload.interactionId,
|
|
1892
|
+
campaignId: payload.campaignId,
|
|
1893
|
+
},
|
|
1894
|
+
['behavioral', 'business', 'operational']
|
|
1895
|
+
);
|
|
1896
|
+
|
|
1897
|
+
LoggerProxy.log('Campaign preview contact removed successfully', {
|
|
1898
|
+
module: CC_FILE,
|
|
1899
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
1900
|
+
trackingId: result.trackingId,
|
|
1901
|
+
interactionId: payload.interactionId,
|
|
1902
|
+
});
|
|
1903
|
+
|
|
1904
|
+
return result;
|
|
1905
|
+
} catch (error) {
|
|
1906
|
+
const failure = error.details as Failure;
|
|
1907
|
+
this.metricsManager.trackEvent(
|
|
1908
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
1909
|
+
{
|
|
1910
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
1911
|
+
interactionId: payload.interactionId,
|
|
1912
|
+
campaignId: payload.campaignId,
|
|
1913
|
+
},
|
|
1914
|
+
['behavioral', 'business', 'operational']
|
|
1915
|
+
);
|
|
1916
|
+
const {error: detailedError} = getErrorDetails(
|
|
1917
|
+
error,
|
|
1918
|
+
METHODS.REMOVE_PREVIEW_CONTACT,
|
|
1919
|
+
CC_FILE
|
|
1920
|
+
);
|
|
1921
|
+
throw detailedError;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1647
1925
|
/**
|
|
1648
1926
|
* Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
|
|
1649
1927
|
*
|
package/src/constants.ts
CHANGED
|
@@ -61,6 +61,9 @@ export const METHODS = {
|
|
|
61
61
|
TOGGLE_MUTE: 'toggleMute',
|
|
62
62
|
COMPLETE_TRANSFER: 'completeTransfer',
|
|
63
63
|
GET_OUTDIAL_ANI_ENTRIES: 'getOutdialAniEntries',
|
|
64
|
+
ACCEPT_PREVIEW_CONTACT: 'acceptPreviewContact',
|
|
65
|
+
SKIP_PREVIEW_CONTACT: 'skipPreviewContact',
|
|
66
|
+
REMOVE_PREVIEW_CONTACT: 'removePreviewContact',
|
|
64
67
|
GET_BASE_URL: 'getBaseUrl',
|
|
65
68
|
SEND_EVENT: 'sendEvent',
|
|
66
69
|
GET_SUGGESTED_RESPONSE: 'getSuggestedResponse',
|