@webex/contact-center 3.12.0-next.13 → 3.12.0-next.130
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/.sdd/manifest.json +884 -0
- package/AGENTS.md +94 -0
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +53 -0
- package/ai-docs/GETTING_STARTED.md +168 -0
- package/ai-docs/GLOSSARY.md +43 -0
- package/ai-docs/README.md +138 -0
- package/ai-docs/REVIEW_CHECKLIST.md +41 -0
- package/ai-docs/RULES.md +444 -0
- package/ai-docs/SECURITY.md +52 -0
- package/ai-docs/SERVICE_STATE.md +52 -0
- package/ai-docs/SPEC_INDEX.md +65 -0
- package/ai-docs/adr/0001-spec-source-policy.md +55 -0
- package/ai-docs/adr/README.md +8 -0
- package/ai-docs/adr/_adr-template.md +31 -0
- package/ai-docs/contact-center-spec.md +382 -0
- package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
- package/ai-docs/features/contact-owner-change-refactor.md +78 -0
- package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
- package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
- package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
- package/ai-docs/patterns/event-driven-patterns.md +485 -0
- package/ai-docs/patterns/testing-patterns.md +480 -0
- package/ai-docs/patterns/typescript-patterns.md +365 -0
- package/ai-docs/templates/README.md +102 -0
- package/ai-docs/templates/documentation/create-agents-md.md +240 -0
- package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
- package/ai-docs/templates/existing-service/bug-fix.md +254 -0
- package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
- package/ai-docs/templates/new-method/00-master.md +80 -0
- package/ai-docs/templates/new-method/01-requirements.md +232 -0
- package/ai-docs/templates/new-method/02-implementation.md +295 -0
- package/ai-docs/templates/new-method/03-tests.md +201 -0
- package/ai-docs/templates/new-method/04-validation.md +141 -0
- package/ai-docs/templates/new-service/00-master.md +109 -0
- package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
- package/ai-docs/templates/new-service/02-code-generation.md +346 -0
- package/ai-docs/templates/new-service/03-integration.md +178 -0
- package/ai-docs/templates/new-service/04-test-generation.md +205 -0
- package/ai-docs/templates/new-service/05-validation.md +145 -0
- package/dist/cc.js +971 -42
- package/dist/cc.js.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +31 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js +45 -15
- package/dist/index.js.map +1 -1
- package/dist/metrics/MetricsManager.js +16 -0
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +205 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +48 -4
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +18 -15
- package/dist/services/AddressBook.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +202 -0
- package/dist/services/AnswerCallOnWebexService.js.map +1 -0
- package/dist/services/ApiAiAssistant.js +269 -9
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/EntryPoint.js +46 -68
- package/dist/services/EntryPoint.js.map +1 -1
- package/dist/services/Queue.js +27 -22
- package/dist/services/Queue.js.map +1 -1
- package/dist/services/UserPreference.js +427 -0
- package/dist/services/UserPreference.js.map +1 -0
- package/dist/services/WebexCrossClientService.js +222 -0
- package/dist/services/WebexCrossClientService.js.map +1 -0
- package/dist/services/WxAppTelephonyMercurySync.js +108 -0
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
- package/dist/services/config/Util.js +11 -4
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +41 -7
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +51 -0
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +57 -12
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +111 -32
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/WebexRequest.js +6 -2
- package/dist/services/core/WebexRequest.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +28 -14
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/types.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +2 -1
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +754 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +49 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +1172 -521
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +211 -27
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/WebexCallingUtils.js +70 -0
- package/dist/services/task/WebexCallingUtils.js.map +1 -0
- package/dist/services/task/constants.js +12 -2
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +29 -0
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/dialer.js +78 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +78 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +982 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +572 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +161 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +379 -0
- package/dist/services/task/state-machine/guards.js.map +1 -0
- package/dist/services/task/state-machine/index.js +53 -0
- package/dist/services/task/state-machine/index.js.map +1 -0
- package/dist/services/task/state-machine/types.js +54 -0
- package/dist/services/task/state-machine/types.js.map +1 -0
- package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
- package/dist/services/task/taskDataNormalizer.js +99 -0
- package/dist/services/task/taskDataNormalizer.js.map +1 -0
- package/dist/services/task/types.js +228 -5
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1485 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +152 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js +354 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
- package/dist/services/wxAppDiagnosticLogging.js +122 -0
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
- package/dist/services/wxAppTelephonyUtils.js +19 -0
- package/dist/services/wxAppTelephonyUtils.js.map +1 -0
- package/dist/types/cc.d.ts +193 -6
- package/dist/types/config.d.ts +13 -0
- package/dist/types/constants.d.ts +29 -1
- package/dist/types/index.d.ts +35 -8
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +43 -1
- package/dist/types/services/AddressBook.d.ts +2 -1
- package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
- package/dist/types/services/ApiAiAssistant.d.ts +56 -5
- package/dist/types/services/EntryPoint.d.ts +7 -5
- package/dist/types/services/Queue.d.ts +5 -3
- package/dist/types/services/UserPreference.d.ts +118 -0
- package/dist/types/services/WebexCrossClientService.d.ts +32 -0
- package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
- package/dist/types/services/config/constants.d.ts +34 -5
- package/dist/types/services/config/types.d.ts +197 -12
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +35 -13
- package/dist/types/services/core/WebexRequest.d.ts +1 -0
- package/dist/types/services/core/types.d.ts +2 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +175 -0
- package/dist/types/services/task/TaskFactory.d.ts +13 -0
- package/dist/types/services/task/TaskUtils.d.ts +48 -2
- package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
- package/dist/types/services/task/constants.d.ts +10 -0
- package/dist/types/services/task/contact.d.ts +4 -0
- package/dist/types/services/task/dialer.d.ts +30 -0
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1442 -0
- package/dist/types/services/task/state-machine/actions.d.ts +10 -0
- package/dist/types/services/task/state-machine/constants.d.ts +107 -0
- package/dist/types/services/task/state-machine/guards.d.ts +103 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +277 -0
- package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
- package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
- package/dist/types/services/task/types.d.ts +680 -66
- package/dist/types/services/task/voice/Voice.d.ts +227 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
- package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
- package/dist/types/types.d.ts +205 -9
- package/dist/types/utils/PageCache.d.ts +20 -3
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +162 -4
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +19 -5
- package/dist/utils/PageCache.js.map +1 -1
- package/dist/webex.js +22 -2
- package/dist/webex.js.map +1 -1
- package/package.json +16 -12
- package/src/cc.ts +1289 -112
- package/src/config.ts +13 -0
- package/src/constants.ts +29 -1
- package/src/index.ts +36 -5
- package/src/metrics/MetricsManager.ts +16 -0
- package/src/metrics/ai-docs/AGENTS.md +350 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
- package/src/metrics/ai-docs/metrics-spec.md +921 -0
- package/src/metrics/behavioral-events.ts +212 -0
- package/src/metrics/constants.ts +52 -4
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +239 -0
- package/src/services/ApiAiAssistant.ts +356 -9
- package/src/services/EntryPoint.ts +59 -60
- package/src/services/Queue.ts +29 -12
- package/src/services/UserPreference.ts +509 -0
- package/src/services/WebexCrossClientService.ts +293 -0
- package/src/services/WxAppTelephonyMercurySync.ts +142 -0
- package/src/services/agent/ai-docs/AGENTS.md +240 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
- package/src/services/agent/ai-docs/agent-spec.md +505 -0
- package/src/services/ai-docs/AGENTS.md +386 -0
- package/src/services/ai-docs/services-spec.md +505 -0
- package/src/services/config/Util.ts +12 -3
- package/src/services/config/ai-docs/AGENTS.md +255 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
- package/src/services/config/ai-docs/config-spec.md +686 -0
- package/src/services/config/constants.ts +46 -6
- package/src/services/config/index.ts +65 -0
- package/src/services/config/types.ts +197 -13
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +127 -37
- package/src/services/core/WebexRequest.ts +3 -1
- package/src/services/core/ai-docs/AGENTS.md +381 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
- package/src/services/core/ai-docs/core-spec.md +787 -0
- package/src/services/core/aqm-reqs.ts +30 -15
- package/src/services/core/types.ts +2 -0
- package/src/services/core/websocket/WebSocketManager.ts +2 -0
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -1
- package/src/services/task/Task.ts +908 -0
- package/src/services/task/TaskFactory.ts +60 -0
- package/src/services/task/TaskManager.ts +1419 -608
- package/src/services/task/TaskUtils.ts +303 -25
- package/src/services/task/WebexCallingUtils.ts +136 -0
- package/src/services/task/ai-docs/AGENTS.md +457 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
- package/src/services/task/ai-docs/task-spec.md +1474 -0
- package/src/services/task/constants.ts +10 -0
- package/src/services/task/contact.ts +30 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/digital/Digital.ts +97 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +1320 -0
- package/src/services/task/state-machine/actions.ts +741 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1156 -0
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2247 -0
- package/src/services/task/state-machine/constants.ts +172 -0
- package/src/services/task/state-machine/guards.ts +442 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +258 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +819 -70
- package/src/services/task/voice/Voice.ts +1851 -0
- package/src/services/task/voice/WebRTC.ts +191 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
- package/src/services/wxAppDiagnosticLogging.ts +174 -0
- package/src/services/wxAppTelephonyUtils.ts +14 -0
- package/src/types.ts +262 -11
- package/src/utils/AGENTS.md +289 -0
- package/src/utils/PageCache.ts +38 -5
- package/src/utils/ai-docs/utils-spec.md +391 -0
- package/src/webex.js +10 -0
- package/test/unit/spec/cc.ts +2210 -119
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/metrics/MetricsManager.ts +25 -0
- package/test/unit/spec/metrics/behavioral-events.ts +60 -0
- package/test/unit/spec/services/AddressBook.ts +37 -6
- package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +311 -21
- package/test/unit/spec/services/EntryPoint.ts +87 -40
- package/test/unit/spec/services/Queue.ts +123 -12
- package/test/unit/spec/services/UserPreference.ts +401 -0
- package/test/unit/spec/services/WebCallingService.ts +7 -1
- package/test/unit/spec/services/WebexCrossClientService.ts +467 -0
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
- package/test/unit/spec/services/config/Util.ts +129 -0
- package/test/unit/spec/services/config/index.ts +97 -29
- package/test/unit/spec/services/core/Utils.ts +425 -8
- package/test/unit/spec/services/core/WebexRequest.ts +3 -1
- package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +745 -0
- package/test/unit/spec/services/task/TaskFactory.ts +99 -0
- package/test/unit/spec/services/task/TaskManager.ts +2518 -1494
- package/test/unit/spec/services/task/TaskUtils.ts +235 -0
- package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
- package/test/unit/spec/services/task/contact.ts +33 -0
- package/test/unit/spec/services/task/dialer.ts +190 -0
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +4060 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +2061 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
- package/test/unit/spec/wellnessSampleUtils.ts +288 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
package/src/cc.ts
CHANGED
|
@@ -25,6 +25,10 @@ import {
|
|
|
25
25
|
UploadLogsResponse,
|
|
26
26
|
UpdateDeviceTypeResponse,
|
|
27
27
|
GenericError,
|
|
28
|
+
ConfigFlags,
|
|
29
|
+
WellnessBreakEvent,
|
|
30
|
+
WellnessBreakNotificationAction,
|
|
31
|
+
WELLNESS_BREAK_NOTIFICATION_ACTIONS,
|
|
28
32
|
} from './types';
|
|
29
33
|
import {
|
|
30
34
|
READY,
|
|
@@ -37,6 +41,7 @@ import {
|
|
|
37
41
|
UNKNOWN_ERROR,
|
|
38
42
|
MERCURY_DISCONNECTED_SUCCESS,
|
|
39
43
|
METHODS,
|
|
44
|
+
WELLNESS_BREAK_HANDLER,
|
|
40
45
|
} from './constants';
|
|
41
46
|
import {AGENT_STATE_AVAILABLE, AGENT_STATE_AVAILABLE_ID} from './services/config/constants';
|
|
42
47
|
import {AGENT, RTD_SUBSCRIBE_API, SUBSCRIBE_API, WEB_RTC_PREFIX} from './services/constants';
|
|
@@ -44,17 +49,22 @@ import Services from './services';
|
|
|
44
49
|
import WebexRequest from './services/core/WebexRequest';
|
|
45
50
|
import LoggerProxy from './logger-proxy';
|
|
46
51
|
import {StateChange, Logout, StateChangeSuccess, AGENT_EVENTS} from './services/agent/types';
|
|
47
|
-
import {getErrorDetails, isValidDialNumber} from './services/core/Utils';
|
|
52
|
+
import {getErrorDetails, isRecord, isValidDialNumber} from './services/core/Utils';
|
|
48
53
|
import {
|
|
49
54
|
Profile,
|
|
50
55
|
WelcomeEvent,
|
|
51
56
|
CC_EVENTS,
|
|
52
57
|
OutdialAniEntriesResponse,
|
|
53
58
|
OutdialAniParams,
|
|
59
|
+
Entity,
|
|
54
60
|
} from './services/config/types';
|
|
55
61
|
import {ConnectionLostDetails} from './services/core/websocket/types';
|
|
56
62
|
import TaskManager from './services/task/TaskManager';
|
|
57
63
|
import WebCallingService from './services/WebCallingService';
|
|
64
|
+
import AnswerCallOnWebexService from './services/AnswerCallOnWebexService';
|
|
65
|
+
import WebexCrossClientService from './services/WebexCrossClientService';
|
|
66
|
+
import WxAppTelephonyMercurySync from './services/WxAppTelephonyMercurySync';
|
|
67
|
+
import {logWxAppSessionReadiness} from './services/wxAppDiagnosticLogging';
|
|
58
68
|
import {
|
|
59
69
|
ITask,
|
|
60
70
|
TASK_EVENTS,
|
|
@@ -69,6 +79,7 @@ import {EntryPoint} from './services/EntryPoint';
|
|
|
69
79
|
import {AddressBook} from './services/AddressBook';
|
|
70
80
|
import {Queue} from './services/Queue';
|
|
71
81
|
import {ApiAIAssistant} from './services/ApiAiAssistant';
|
|
82
|
+
import {UserPreference} from './services/UserPreference';
|
|
72
83
|
import type {
|
|
73
84
|
EntryPointListResponse,
|
|
74
85
|
EntryPointSearchParams,
|
|
@@ -230,6 +241,34 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
230
241
|
*/
|
|
231
242
|
private webCallingService: WebCallingService;
|
|
232
243
|
|
|
244
|
+
/**
|
|
245
|
+
* REST telephony for wxApp thick-client answer (WXCC-6026)
|
|
246
|
+
* @private
|
|
247
|
+
*/
|
|
248
|
+
private answerCallOnWebexService: AnswerCallOnWebexService;
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* usersub cross-client state for Webex App toast suppression
|
|
252
|
+
* @private
|
|
253
|
+
*/
|
|
254
|
+
private webexCrossClientService: WebexCrossClientService;
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Mercury telephony mute sync for wxApp thick-client answer
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
private wxAppTelephonyMercurySync: WxAppTelephonyMercurySync;
|
|
261
|
+
|
|
262
|
+
/** True when wxApp mute sync opened Mercury (Extension/DN-only profiles). */
|
|
263
|
+
private wxAppMercuryConnectedByCc = false;
|
|
264
|
+
|
|
265
|
+
/** True when wxApp mute sync registered the Webex device. */
|
|
266
|
+
private wxAppDeviceRegisteredByCc = false;
|
|
267
|
+
|
|
268
|
+
private static readonly WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS = 30_000;
|
|
269
|
+
private static readonly WXAPP_FALSE_PUBLISH_MAX_RETRIES = 3;
|
|
270
|
+
private wxAppFalsePublishRetryTimer?: ReturnType<typeof setTimeout>;
|
|
271
|
+
|
|
233
272
|
/**
|
|
234
273
|
* Core service managers for Contact Center operations
|
|
235
274
|
* Includes agent, connection, and configuration services
|
|
@@ -262,6 +301,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
262
301
|
*/
|
|
263
302
|
private metricsManager: MetricsManager;
|
|
264
303
|
|
|
304
|
+
/** Current login/relogin session used for agent-scoped wellness validation. */
|
|
305
|
+
private currentAgentSessionId?: string;
|
|
306
|
+
|
|
307
|
+
/** Registered-session cache for the system WellbeingBreak idle code. */
|
|
308
|
+
private wellbeingBreakIdleCode?: Entity;
|
|
309
|
+
|
|
310
|
+
/** Registration generation that owns the wellness idle-code cache and in-flight lookups. */
|
|
311
|
+
private wellnessRegistrationGeneration = 0;
|
|
312
|
+
|
|
313
|
+
private updateWellnessSession(agentSessionId?: string): void {
|
|
314
|
+
this.currentAgentSessionId = agentSessionId;
|
|
315
|
+
}
|
|
316
|
+
|
|
265
317
|
/**
|
|
266
318
|
* API instance for managing Webex Contact Center entry points
|
|
267
319
|
* Provides functionality to fetch entry points with caching support
|
|
@@ -325,7 +377,38 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
325
377
|
* });
|
|
326
378
|
* ```
|
|
327
379
|
*/
|
|
328
|
-
|
|
380
|
+
public queue: Queue;
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* API instance for managing Webex Contact Center user preferences
|
|
384
|
+
* Provides functionality to get, create, update, and delete user preferences
|
|
385
|
+
* @type {UserPreference}
|
|
386
|
+
* @public
|
|
387
|
+
* @example
|
|
388
|
+
* ```typescript
|
|
389
|
+
* const cc = webex.cc;
|
|
390
|
+
* await cc.register();
|
|
391
|
+
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
392
|
+
*
|
|
393
|
+
* // Get user preferences for current user
|
|
394
|
+
* const preferences = await cc.userPreference.getUserPreference();
|
|
395
|
+
*
|
|
396
|
+
* // Create new user preferences
|
|
397
|
+
* const newPrefs = await cc.userPreference.createUserPreference({
|
|
398
|
+
* userId: 'user123',
|
|
399
|
+
* preferences: { e911Reminder: true }
|
|
400
|
+
* });
|
|
401
|
+
*
|
|
402
|
+
* // Update user preferences
|
|
403
|
+
* const updatedPrefs = await cc.userPreference.updateUserPreference('user123', {
|
|
404
|
+
* preferences: { e911Reminder: false }
|
|
405
|
+
* });
|
|
406
|
+
*
|
|
407
|
+
* // Delete user preferences
|
|
408
|
+
* await cc.userPreference.deleteUserPreference('user123');
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
public userPreference: UserPreference;
|
|
329
412
|
|
|
330
413
|
/**
|
|
331
414
|
* API instance for AI Assistant operations such as transcript controls.
|
|
@@ -357,6 +440,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
357
440
|
this.$webex.once(READY, () => {
|
|
358
441
|
// @ts-ignore
|
|
359
442
|
this.$config = this.config;
|
|
443
|
+
this.validatePluginConfig();
|
|
360
444
|
|
|
361
445
|
/**
|
|
362
446
|
* This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
|
|
@@ -369,17 +453,25 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
369
453
|
webex: this.$webex,
|
|
370
454
|
connectionConfig: this.getConnectionConfig(),
|
|
371
455
|
});
|
|
372
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
373
456
|
|
|
374
457
|
this.webCallingService = new WebCallingService(this.$webex);
|
|
375
|
-
this.
|
|
458
|
+
this.answerCallOnWebexService = new AnswerCallOnWebexService(this.$webex);
|
|
459
|
+
this.webexCrossClientService = new WebexCrossClientService(this.$webex);
|
|
460
|
+
this.wxAppTelephonyMercurySync = new WxAppTelephonyMercurySync(this.$webex);
|
|
461
|
+
this.apiAIAssistant = new ApiAIAssistant(this.$webex, () => ({
|
|
462
|
+
isWellnessBreakEnabled: this.agentConfig?.isWellnessBreakEnabled === true,
|
|
463
|
+
agentId: this.agentConfig?.agentId,
|
|
464
|
+
agentSessionId: this.currentAgentSessionId,
|
|
465
|
+
}));
|
|
376
466
|
this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
|
|
377
467
|
this.taskManager = TaskManager.getTaskManager(
|
|
378
468
|
this.apiAIAssistant,
|
|
379
469
|
this.services.contact,
|
|
380
470
|
this.webCallingService,
|
|
381
|
-
this.services.webSocketManager
|
|
471
|
+
this.services.webSocketManager,
|
|
472
|
+
this.services.rtdWebSocketManager
|
|
382
473
|
);
|
|
474
|
+
this.taskManager.setAnswerCallOnWebexService(this.answerCallOnWebexService);
|
|
383
475
|
this.incomingTaskListener();
|
|
384
476
|
|
|
385
477
|
// Initialize API instances
|
|
@@ -387,6 +479,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
387
479
|
this.entryPoint = new EntryPoint(this.$webex);
|
|
388
480
|
this.addressBook = new AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
|
|
389
481
|
this.queue = new Queue(this.$webex);
|
|
482
|
+
this.userPreference = new UserPreference(this.$webex, () => this.agentConfig?.agentId);
|
|
390
483
|
LoggerProxy.initialize(this.$webex.logger);
|
|
391
484
|
});
|
|
392
485
|
}
|
|
@@ -411,6 +504,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
411
504
|
this.trigger(TASK_EVENTS.TASK_HYDRATE, task);
|
|
412
505
|
};
|
|
413
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Handles multi-login hydrate events for SDK instances without Mobius registration
|
|
509
|
+
* @private
|
|
510
|
+
* @param {ITask} task The task object associated with the multi-login hydrate
|
|
511
|
+
*/
|
|
512
|
+
private handleTaskMultiLoginHydrate = (task: ITask) => {
|
|
513
|
+
// @ts-ignore
|
|
514
|
+
this.trigger(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
515
|
+
};
|
|
516
|
+
|
|
414
517
|
/**
|
|
415
518
|
* Handles task merged events when tasks are combined eg: EPDN merge/transfer
|
|
416
519
|
* @private
|
|
@@ -431,8 +534,116 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
431
534
|
this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
432
535
|
};
|
|
433
536
|
|
|
434
|
-
private
|
|
435
|
-
this.
|
|
537
|
+
private trackInvalidWellnessEvent(reason: string): void {
|
|
538
|
+
this.metricsManager.trackEvent(
|
|
539
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_EVENT_INVALID,
|
|
540
|
+
{reason},
|
|
541
|
+
['operational']
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private normalizeWellnessBreakEvent(payload: Record<string, unknown>): WellnessBreakEvent | null {
|
|
546
|
+
if (this.agentConfig?.isWellnessBreakEnabled !== true) {
|
|
547
|
+
this.trackInvalidWellnessEvent('wellness_disabled');
|
|
548
|
+
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const notificationData = isRecord(payload.data) ? payload.data : null;
|
|
553
|
+
const notificationDetails =
|
|
554
|
+
notificationData && isRecord(notificationData.notifDetails)
|
|
555
|
+
? notificationData.notifDetails
|
|
556
|
+
: null;
|
|
557
|
+
const sessionData =
|
|
558
|
+
notificationData && isRecord(notificationData.data) ? notificationData.data : null;
|
|
559
|
+
const actionEvent = notificationDetails?.actionEvent;
|
|
560
|
+
|
|
561
|
+
if (
|
|
562
|
+
payload.type !== WELLNESS_BREAK_HANDLER ||
|
|
563
|
+
notificationData?.notifType !== WELLNESS_BREAK_HANDLER ||
|
|
564
|
+
typeof actionEvent !== 'string'
|
|
565
|
+
) {
|
|
566
|
+
this.trackInvalidWellnessEvent('malformed_wellness_event');
|
|
567
|
+
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (
|
|
572
|
+
!Object.values(WELLNESS_BREAK_NOTIFICATION_ACTIONS).includes(
|
|
573
|
+
actionEvent as WellnessBreakNotificationAction
|
|
574
|
+
)
|
|
575
|
+
) {
|
|
576
|
+
this.trackInvalidWellnessEvent('unknown_wellness_action');
|
|
577
|
+
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const agentId = notificationData.agentId;
|
|
582
|
+
const eventOrgId = notificationData.orgId;
|
|
583
|
+
const sessionOrgId = sessionData?.orgId;
|
|
584
|
+
const envelopeOrgId = payload.orgId;
|
|
585
|
+
const agentSessionId = sessionData?.agentSessionId;
|
|
586
|
+
const currentOrgId = this.$webex.credentials.getOrgId();
|
|
587
|
+
|
|
588
|
+
if (
|
|
589
|
+
typeof agentId !== 'string' ||
|
|
590
|
+
typeof eventOrgId !== 'string' ||
|
|
591
|
+
typeof sessionOrgId !== 'string' ||
|
|
592
|
+
typeof envelopeOrgId !== 'string' ||
|
|
593
|
+
typeof agentSessionId !== 'string' ||
|
|
594
|
+
!agentId ||
|
|
595
|
+
!eventOrgId ||
|
|
596
|
+
!sessionOrgId ||
|
|
597
|
+
!envelopeOrgId ||
|
|
598
|
+
!agentSessionId
|
|
599
|
+
) {
|
|
600
|
+
this.trackInvalidWellnessEvent('missing_wellness_identity');
|
|
601
|
+
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (
|
|
606
|
+
agentId !== this.agentConfig?.agentId ||
|
|
607
|
+
eventOrgId !== currentOrgId ||
|
|
608
|
+
sessionOrgId !== currentOrgId ||
|
|
609
|
+
envelopeOrgId !== currentOrgId
|
|
610
|
+
) {
|
|
611
|
+
this.trackInvalidWellnessEvent('mismatched_wellness_identity');
|
|
612
|
+
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const interactionValue = sessionData.interactionId ?? sessionData.InteractionId;
|
|
617
|
+
const actionText = notificationDetails.actionText;
|
|
618
|
+
const trackingId = payload.trackingId;
|
|
619
|
+
|
|
620
|
+
return {
|
|
621
|
+
agentId,
|
|
622
|
+
orgId: currentOrgId,
|
|
623
|
+
agentSessionId,
|
|
624
|
+
actionEvent: actionEvent as WellnessBreakNotificationAction,
|
|
625
|
+
...(typeof actionText === 'string' ? {actionText} : {}),
|
|
626
|
+
...(typeof interactionValue === 'string' ? {interactionId: interactionValue} : {}),
|
|
627
|
+
...(typeof trackingId === 'string' ? {trackingId} : {}),
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
private handleWellnessBreakWebsocketPayload(payload: Record<string, unknown>): boolean {
|
|
632
|
+
if (payload.type !== WELLNESS_BREAK_HANDLER) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const wellnessEvent = this.normalizeWellnessBreakEvent(payload);
|
|
637
|
+
if (wellnessEvent) {
|
|
638
|
+
// @ts-ignore - WebexPlugin emit is available at runtime but absent from its declaration.
|
|
639
|
+
this.emit(CC_EVENTS.WELLNESS_BREAK, wellnessEvent);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
private handleRTDWebsocketMessage = (event: string) => {
|
|
646
|
+
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
436
647
|
};
|
|
437
648
|
|
|
438
649
|
/**
|
|
@@ -443,6 +654,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
443
654
|
private incomingTaskListener() {
|
|
444
655
|
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
445
656
|
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
657
|
+
this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
446
658
|
this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
447
659
|
this.taskManager.on(
|
|
448
660
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
@@ -487,7 +699,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
487
699
|
* ```
|
|
488
700
|
*/
|
|
489
701
|
public async register(): Promise<Profile> {
|
|
490
|
-
|
|
702
|
+
this.wellnessRegistrationGeneration += 1;
|
|
703
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
704
|
+
LoggerProxy.log('Starting CC SDK registration', {
|
|
491
705
|
module: CC_FILE,
|
|
492
706
|
method: METHODS.REGISTER,
|
|
493
707
|
});
|
|
@@ -497,6 +711,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
497
711
|
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
|
|
498
712
|
]);
|
|
499
713
|
this.setupEventListeners();
|
|
714
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
500
715
|
|
|
501
716
|
const resp = await this.connectWebsocket();
|
|
502
717
|
// Ensure 'dn' is always populated from 'defaultDn'
|
|
@@ -568,6 +783,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
568
783
|
|
|
569
784
|
*/
|
|
570
785
|
public async deregister(): Promise<void> {
|
|
786
|
+
this.wellnessRegistrationGeneration += 1;
|
|
787
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
788
|
+
this.updateWellnessSession();
|
|
571
789
|
try {
|
|
572
790
|
this.metricsManager.timeEvent([
|
|
573
791
|
METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS,
|
|
@@ -576,6 +794,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
576
794
|
|
|
577
795
|
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
578
796
|
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
797
|
+
this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
579
798
|
this.taskManager.off(
|
|
580
799
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
581
800
|
this.handleCampaignPreviewReservation
|
|
@@ -586,6 +805,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
586
805
|
this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
|
|
587
806
|
this.services.connectionService.off('connectionLost', this.handleConnectionLost);
|
|
588
807
|
|
|
808
|
+
const {publishError: wxAppPublishError} = await this.teardownWxAppLocalState({
|
|
809
|
+
clearInitFlag: true,
|
|
810
|
+
});
|
|
811
|
+
|
|
589
812
|
if (
|
|
590
813
|
this.agentConfig.webRtcEnabled &&
|
|
591
814
|
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
@@ -606,13 +829,22 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
606
829
|
if (!this.services.webSocketManager.isSocketClosed) {
|
|
607
830
|
this.services.webSocketManager.close(false, 'Unregistering the SDK');
|
|
608
831
|
}
|
|
832
|
+
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
833
|
+
this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
|
|
834
|
+
}
|
|
609
835
|
|
|
610
836
|
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
611
837
|
this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
|
|
612
838
|
}
|
|
613
839
|
|
|
614
840
|
// Clear any cached agent configuration
|
|
841
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
615
842
|
this.agentConfig = null;
|
|
843
|
+
this.updateWellnessSession();
|
|
844
|
+
|
|
845
|
+
if (wxAppPublishError) {
|
|
846
|
+
throw wxAppPublishError;
|
|
847
|
+
}
|
|
616
848
|
|
|
617
849
|
LoggerProxy.log('Deregistered successfully', {
|
|
618
850
|
module: CC_FILE,
|
|
@@ -676,6 +908,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
676
908
|
* ```
|
|
677
909
|
*/
|
|
678
910
|
public async getBuddyAgents(data: BuddyAgents): Promise<BuddyAgentsResponse> {
|
|
911
|
+
const {action, state, mediaType = 'telephony'} = data;
|
|
912
|
+
const buddyAgentState = state ?? (action === 'Transfer' ? AGENT_STATE_AVAILABLE : undefined);
|
|
913
|
+
|
|
679
914
|
LoggerProxy.info('Fetching buddy agents', {
|
|
680
915
|
module: CC_FILE,
|
|
681
916
|
method: METHODS.GET_BUDDY_AGENTS,
|
|
@@ -686,15 +921,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
686
921
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
|
|
687
922
|
]);
|
|
688
923
|
const resp = await this.services.agent.buddyAgents({
|
|
689
|
-
data: {
|
|
924
|
+
data: {
|
|
925
|
+
agentProfileId: this.agentConfig.agentProfileID,
|
|
926
|
+
mediaType,
|
|
927
|
+
...(buddyAgentState ? {state: buddyAgentState} : {}),
|
|
928
|
+
},
|
|
690
929
|
});
|
|
691
930
|
|
|
692
931
|
this.metricsManager.trackEvent(
|
|
693
932
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS,
|
|
694
933
|
{
|
|
695
934
|
...MetricsManager.getCommonTrackingFieldForAQMResponse(resp),
|
|
696
|
-
mediaType
|
|
697
|
-
buddyAgentState
|
|
935
|
+
mediaType,
|
|
936
|
+
buddyAgentState,
|
|
698
937
|
buddyAgentCount: resp.data.agentList.length,
|
|
699
938
|
},
|
|
700
939
|
['operational']
|
|
@@ -714,8 +953,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
714
953
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
|
|
715
954
|
{
|
|
716
955
|
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failureResp),
|
|
717
|
-
mediaType
|
|
718
|
-
buddyAgentState
|
|
956
|
+
mediaType,
|
|
957
|
+
buddyAgentState,
|
|
719
958
|
},
|
|
720
959
|
['operational']
|
|
721
960
|
);
|
|
@@ -731,7 +970,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
731
970
|
* @private
|
|
732
971
|
*/
|
|
733
972
|
private async connectWebsocket() {
|
|
734
|
-
LoggerProxy.
|
|
973
|
+
LoggerProxy.log('Connecting to websocket', {
|
|
735
974
|
module: CC_FILE,
|
|
736
975
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
737
976
|
});
|
|
@@ -751,19 +990,43 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
751
990
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
752
991
|
});
|
|
753
992
|
|
|
993
|
+
const configFlags: ConfigFlags = {
|
|
994
|
+
isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
|
|
995
|
+
isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
|
|
996
|
+
webRtcEnabled: this.agentConfig.webRtcEnabled,
|
|
997
|
+
autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
|
|
998
|
+
aiFeature: this.agentConfig.aiFeature,
|
|
999
|
+
consultTransfer: {
|
|
1000
|
+
allowConsultToQueue: this.agentConfig.allowConsultToQueue,
|
|
1001
|
+
accessQueue: this.agentConfig.accessQueue,
|
|
1002
|
+
accessEntryPoint: this.agentConfig.accessEntryPoint,
|
|
1003
|
+
accessBuddyTeam: this.agentConfig.accessBuddyTeam,
|
|
1004
|
+
},
|
|
1005
|
+
enableWxBetterTogether: this.isWxBetterTogetherEnabled(),
|
|
1006
|
+
};
|
|
1007
|
+
this.taskManager.setConfigFlags(configFlags);
|
|
754
1008
|
// TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
|
|
755
1009
|
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
756
1010
|
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
757
1011
|
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
758
1012
|
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
1013
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
1014
|
+
this.updateWellnessSession();
|
|
759
1015
|
|
|
760
|
-
|
|
1016
|
+
/**
|
|
1017
|
+
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
1018
|
+
* Extend this condition when additional AI RTD features are introduced.
|
|
1019
|
+
*/
|
|
1020
|
+
if (
|
|
1021
|
+
this.agentConfig.aiFeature?.realtimeTranscripts?.enable ||
|
|
1022
|
+
this.agentConfig.aiFeature?.suggestedResponses?.enable
|
|
1023
|
+
) {
|
|
761
1024
|
LoggerProxy.info('Connecting to RTD websocket', {
|
|
762
1025
|
module: CC_FILE,
|
|
763
1026
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
764
1027
|
});
|
|
765
1028
|
|
|
766
|
-
|
|
1029
|
+
this.services.rtdWebSocketManager
|
|
767
1030
|
.initWebSocket({
|
|
768
1031
|
body: this.getConnectionConfig(),
|
|
769
1032
|
resource: RTD_SUBSCRIBE_API,
|
|
@@ -776,7 +1039,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
776
1039
|
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
777
1040
|
})
|
|
778
1041
|
.catch((error) => {
|
|
779
|
-
LoggerProxy.error(`Error
|
|
1042
|
+
LoggerProxy.error(`Error connecting to RTD websocket ${error}`, {
|
|
780
1043
|
module: CC_FILE,
|
|
781
1044
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
782
1045
|
});
|
|
@@ -785,7 +1048,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
785
1048
|
|
|
786
1049
|
if (
|
|
787
1050
|
this.agentConfig.webRtcEnabled &&
|
|
788
|
-
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
1051
|
+
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
|
|
1052
|
+
!this.isWebRTCRegistrationDisabled()
|
|
789
1053
|
) {
|
|
790
1054
|
try {
|
|
791
1055
|
await this.$webex.internal.mercury.connect();
|
|
@@ -799,14 +1063,28 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
799
1063
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
800
1064
|
});
|
|
801
1065
|
}
|
|
1066
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
1067
|
+
LoggerProxy.info(
|
|
1068
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
1069
|
+
{
|
|
1070
|
+
module: CC_FILE,
|
|
1071
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
1072
|
+
}
|
|
1073
|
+
);
|
|
802
1074
|
}
|
|
803
1075
|
|
|
804
1076
|
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
805
1077
|
await this.silentRelogin();
|
|
1078
|
+
} else {
|
|
1079
|
+
LoggerProxy.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
|
|
1080
|
+
module: CC_FILE,
|
|
1081
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
1082
|
+
});
|
|
806
1083
|
}
|
|
807
1084
|
|
|
808
1085
|
return this.agentConfig;
|
|
809
1086
|
} catch (error) {
|
|
1087
|
+
this.updateWellnessSession();
|
|
810
1088
|
LoggerProxy.error(`Error during register: ${error}`, {
|
|
811
1089
|
module: CC_FILE,
|
|
812
1090
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
@@ -845,36 +1123,48 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
845
1123
|
* ```
|
|
846
1124
|
*/
|
|
847
1125
|
public async stationLogin(data: AgentLogin): Promise<StationLoginResponse> {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
method: METHODS.STATION_LOGIN,
|
|
851
|
-
});
|
|
1126
|
+
const loggerContext = {module: CC_FILE, method: METHODS.STATION_LOGIN};
|
|
1127
|
+
|
|
852
1128
|
try {
|
|
1129
|
+
LoggerProxy.log(
|
|
1130
|
+
`Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`,
|
|
1131
|
+
loggerContext
|
|
1132
|
+
);
|
|
853
1133
|
this.metricsManager.timeEvent([
|
|
854
1134
|
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
855
1135
|
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
856
1136
|
]);
|
|
857
1137
|
|
|
858
1138
|
const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
|
|
859
|
-
if (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1139
|
+
if (data.loginOption === LoginOption.AGENT_DN) {
|
|
1140
|
+
LoggerProxy.log(
|
|
1141
|
+
`Validating dial number | dialPlanEnabled: ${!!this.agentConfig
|
|
1142
|
+
?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
1143
|
+
loggerContext
|
|
1144
|
+
);
|
|
1145
|
+
|
|
1146
|
+
if (!isValidDialNumber(data.dialNumber, dialPlanEntries)) {
|
|
1147
|
+
LoggerProxy.log(
|
|
1148
|
+
`Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
1149
|
+
loggerContext
|
|
1150
|
+
);
|
|
1151
|
+
const error = new Error('INVALID_DIAL_NUMBER');
|
|
1152
|
+
// @ts-ignore - adding custom key to the error object
|
|
1153
|
+
error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
|
|
866
1154
|
|
|
867
|
-
|
|
1155
|
+
throw error;
|
|
1156
|
+
}
|
|
868
1157
|
}
|
|
869
1158
|
|
|
1159
|
+
const deviceId = this.getDeviceId(data.loginOption, data.dialNumber);
|
|
870
1160
|
const loginResponse = await this.services.agent.stationLogin({
|
|
871
1161
|
data: {
|
|
872
|
-
|
|
873
|
-
|
|
1162
|
+
// BROWSER DN must be webrtc-{agentId} so VPOP/RTMS do not treat a bare UUID as PSTN.
|
|
1163
|
+
dialNumber: data.loginOption === LoginOption.BROWSER ? deviceId : data.dialNumber,
|
|
874
1164
|
teamId: data.teamId,
|
|
875
1165
|
deviceType: data.loginOption,
|
|
876
1166
|
isExtension: data.loginOption === LoginOption.EXTENSION,
|
|
877
|
-
deviceId
|
|
1167
|
+
deviceId,
|
|
878
1168
|
roles: [AGENT],
|
|
879
1169
|
teamName: EMPTY_STRING,
|
|
880
1170
|
siteId: EMPTY_STRING,
|
|
@@ -883,12 +1173,29 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
883
1173
|
},
|
|
884
1174
|
});
|
|
885
1175
|
|
|
886
|
-
if (
|
|
1176
|
+
if (
|
|
1177
|
+
this.agentConfig.webRtcEnabled &&
|
|
1178
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
1179
|
+
!this.isWebRTCRegistrationDisabled()
|
|
1180
|
+
) {
|
|
887
1181
|
await this.webCallingService.registerWebCallingLine();
|
|
1182
|
+
} else if (
|
|
1183
|
+
this.agentConfig.webRtcEnabled &&
|
|
1184
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
1185
|
+
this.isWebRTCRegistrationDisabled()
|
|
1186
|
+
) {
|
|
1187
|
+
LoggerProxy.info(
|
|
1188
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
1189
|
+
{
|
|
1190
|
+
module: CC_FILE,
|
|
1191
|
+
method: METHODS.STATION_LOGIN,
|
|
1192
|
+
}
|
|
1193
|
+
);
|
|
888
1194
|
}
|
|
889
1195
|
|
|
890
1196
|
const resp = await loginResponse;
|
|
891
1197
|
const {channelsMap, ...loginData} = resp.data;
|
|
1198
|
+
this.updateWellnessSession(resp.data.agentSessionId);
|
|
892
1199
|
this.agentConfig.currentTeamId = resp.data.teamId;
|
|
893
1200
|
const response = {
|
|
894
1201
|
...loginData,
|
|
@@ -902,6 +1209,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
902
1209
|
};
|
|
903
1210
|
|
|
904
1211
|
this.webCallingService.setLoginOption(data.loginOption);
|
|
1212
|
+
if (this.agentConfig) {
|
|
1213
|
+
this.agentConfig.deviceType = data.loginOption;
|
|
1214
|
+
if (
|
|
1215
|
+
data.loginOption === LoginOption.AGENT_DN ||
|
|
1216
|
+
data.loginOption === LoginOption.EXTENSION
|
|
1217
|
+
) {
|
|
1218
|
+
this.agentConfig.defaultDn = data.dialNumber;
|
|
1219
|
+
this.agentConfig.dn = data.dialNumber;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
905
1222
|
this.metricsManager.trackEvent(
|
|
906
1223
|
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
907
1224
|
{
|
|
@@ -923,6 +1240,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
923
1240
|
}
|
|
924
1241
|
);
|
|
925
1242
|
|
|
1243
|
+
await this.ensureWxAppPostStationLogin();
|
|
1244
|
+
|
|
926
1245
|
return response;
|
|
927
1246
|
} catch (error) {
|
|
928
1247
|
const failure = error.details as Failure;
|
|
@@ -991,6 +1310,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
991
1310
|
this.webCallingService.deregisterWebCallingLine();
|
|
992
1311
|
}
|
|
993
1312
|
|
|
1313
|
+
await this.teardownWxAppLocalState();
|
|
1314
|
+
this.updateWellnessSession();
|
|
1315
|
+
|
|
994
1316
|
LoggerProxy.log(`Agent station logout completed successfully`, {
|
|
995
1317
|
module: CC_FILE,
|
|
996
1318
|
method: METHODS.STATION_LOGOUT,
|
|
@@ -1127,6 +1449,77 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1127
1449
|
}
|
|
1128
1450
|
}
|
|
1129
1451
|
|
|
1452
|
+
/**
|
|
1453
|
+
* Returns the system-managed `WellbeingBreak` idle code for the current registration.
|
|
1454
|
+
* The value is fetched outside Desktop Profile filtering and cached until deregistration.
|
|
1455
|
+
* @returns The normalized system idle-code entity
|
|
1456
|
+
* @throws Structured Contact Center error when wellness is disabled or the code is unavailable
|
|
1457
|
+
* @example
|
|
1458
|
+
* const idleCode = await webex.cc.getWellbeingBreakIdleCode();
|
|
1459
|
+
* @public
|
|
1460
|
+
*/
|
|
1461
|
+
public async getWellbeingBreakIdleCode(): Promise<Entity> {
|
|
1462
|
+
const method = METHODS.GET_WELLBEING_BREAK_IDLE_CODE;
|
|
1463
|
+
try {
|
|
1464
|
+
if (!this.agentConfig?.isWellnessBreakEnabled) {
|
|
1465
|
+
const validationError = new Error('WELLNESS_BREAK_NOT_ENABLED') as GenericError;
|
|
1466
|
+
validationError.details = {
|
|
1467
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1468
|
+
orgId: this.$webex.credentials.getOrgId(),
|
|
1469
|
+
trackingId: EMPTY_STRING,
|
|
1470
|
+
data: {reason: 'WELLNESS_BREAK_NOT_ENABLED'},
|
|
1471
|
+
};
|
|
1472
|
+
throw validationError;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
if (this.wellbeingBreakIdleCode) {
|
|
1476
|
+
return this.wellbeingBreakIdleCode;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
this.metricsManager.timeEvent([
|
|
1480
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1481
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1482
|
+
]);
|
|
1483
|
+
const registrationGeneration = this.wellnessRegistrationGeneration;
|
|
1484
|
+
const orgId = this.$webex.credentials.getOrgId();
|
|
1485
|
+
const idleCode = await this.services.config.getWellbeingBreakIdleCode(orgId);
|
|
1486
|
+
|
|
1487
|
+
if (
|
|
1488
|
+
registrationGeneration !== this.wellnessRegistrationGeneration ||
|
|
1489
|
+
orgId !== this.$webex.credentials.getOrgId() ||
|
|
1490
|
+
!this.agentConfig?.isWellnessBreakEnabled
|
|
1491
|
+
) {
|
|
1492
|
+
const staleRegistrationError = new Error(
|
|
1493
|
+
'WELLNESS_BREAK_REGISTRATION_CHANGED'
|
|
1494
|
+
) as GenericError;
|
|
1495
|
+
staleRegistrationError.details = {
|
|
1496
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1497
|
+
orgId,
|
|
1498
|
+
trackingId: EMPTY_STRING,
|
|
1499
|
+
data: {reason: 'WELLNESS_BREAK_REGISTRATION_CHANGED'},
|
|
1500
|
+
};
|
|
1501
|
+
throw staleRegistrationError;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
this.wellbeingBreakIdleCode = idleCode;
|
|
1505
|
+
this.metricsManager.trackEvent(
|
|
1506
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1507
|
+
{},
|
|
1508
|
+
['operational']
|
|
1509
|
+
);
|
|
1510
|
+
|
|
1511
|
+
return idleCode;
|
|
1512
|
+
} catch (error) {
|
|
1513
|
+
this.metricsManager.trackEvent(
|
|
1514
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1515
|
+
{reason: error instanceof Error ? error.message : UNKNOWN_ERROR},
|
|
1516
|
+
['operational']
|
|
1517
|
+
);
|
|
1518
|
+
const {error: detailedError} = getErrorDetails(error, method, CC_FILE);
|
|
1519
|
+
throw detailedError;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1130
1523
|
/**
|
|
1131
1524
|
* Processes incoming websocket messages and emits corresponding events
|
|
1132
1525
|
* Handles various event types including agent state changes, login events,
|
|
@@ -1165,8 +1558,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1165
1558
|
]);
|
|
1166
1559
|
}
|
|
1167
1560
|
|
|
1561
|
+
if (this.handleWellnessBreakWebsocketPayload(eventData)) {
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1168
1565
|
switch (eventData.type) {
|
|
1169
1566
|
case CC_EVENTS.AGENT_MULTI_LOGIN:
|
|
1567
|
+
if (
|
|
1568
|
+
eventData.data?.type === 'AgentMultiLoginCloseSession' &&
|
|
1569
|
+
(!eventData.data.agentSessionId ||
|
|
1570
|
+
eventData.data.agentSessionId === this.currentAgentSessionId)
|
|
1571
|
+
) {
|
|
1572
|
+
this.updateWellnessSession();
|
|
1573
|
+
}
|
|
1170
1574
|
// @ts-ignore
|
|
1171
1575
|
this.emit(AGENT_EVENTS.AGENT_MULTI_LOGIN, eventData.data);
|
|
1172
1576
|
break;
|
|
@@ -1185,6 +1589,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1185
1589
|
switch (eventData.data.type) {
|
|
1186
1590
|
case CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS: {
|
|
1187
1591
|
const {channelsMap, ...loginData} = eventData.data;
|
|
1592
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1188
1593
|
const stationLoginData = {
|
|
1189
1594
|
...loginData,
|
|
1190
1595
|
mmProfile: {
|
|
@@ -1200,23 +1605,23 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1200
1605
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, stationLoginData);
|
|
1201
1606
|
break;
|
|
1202
1607
|
}
|
|
1203
|
-
case CC_EVENTS.AGENT_RELOGIN_SUCCESS:
|
|
1204
|
-
{
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1608
|
+
case CC_EVENTS.AGENT_RELOGIN_SUCCESS: {
|
|
1609
|
+
const {channelsMap, ...loginData} = eventData.data;
|
|
1610
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1611
|
+
const stationReLoginData = {
|
|
1612
|
+
...loginData,
|
|
1613
|
+
mmProfile: {
|
|
1614
|
+
chat: channelsMap.chat?.length,
|
|
1615
|
+
email: channelsMap.email?.length,
|
|
1616
|
+
social: channelsMap.social?.length,
|
|
1617
|
+
telephony: channelsMap.telephony?.length,
|
|
1618
|
+
},
|
|
1619
|
+
notifsTrackingId: eventData.trackingId,
|
|
1620
|
+
};
|
|
1621
|
+
// @ts-ignore
|
|
1622
|
+
this.emit(AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
|
|
1219
1623
|
break;
|
|
1624
|
+
}
|
|
1220
1625
|
case CC_EVENTS.AGENT_STATE_CHANGE_SUCCESS:
|
|
1221
1626
|
// @ts-ignore
|
|
1222
1627
|
this.emit(AGENT_EVENTS.AGENT_STATE_CHANGE_SUCCESS, eventData.data);
|
|
@@ -1230,6 +1635,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1230
1635
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_FAILED, eventData.data);
|
|
1231
1636
|
break;
|
|
1232
1637
|
case CC_EVENTS.AGENT_LOGOUT_SUCCESS:
|
|
1638
|
+
if (
|
|
1639
|
+
!eventData.data.agentSessionId ||
|
|
1640
|
+
eventData.data.agentSessionId === this.currentAgentSessionId
|
|
1641
|
+
) {
|
|
1642
|
+
this.updateWellnessSession();
|
|
1643
|
+
}
|
|
1233
1644
|
// @ts-ignore
|
|
1234
1645
|
this.emit(AGENT_EVENTS.AGENT_LOGOUT_SUCCESS, eventData.data);
|
|
1235
1646
|
break;
|
|
@@ -1247,84 +1658,680 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1247
1658
|
};
|
|
1248
1659
|
|
|
1249
1660
|
/**
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1661
|
+
* Checks whether Mobius/WebRTC registration should be skipped by config
|
|
1662
|
+
* @returns {boolean} True when browser WebRTC registration is disabled
|
|
1252
1663
|
* @private
|
|
1253
1664
|
*/
|
|
1254
|
-
private
|
|
1255
|
-
this
|
|
1665
|
+
private isWebRTCRegistrationDisabled(): boolean {
|
|
1666
|
+
return this.$config?.disableWebRTCRegistration === true;
|
|
1256
1667
|
}
|
|
1257
1668
|
|
|
1258
1669
|
/**
|
|
1259
|
-
*
|
|
1260
|
-
* @returns {SubscribeRequest} Connection configuration
|
|
1670
|
+
* Validates contact-center plugin configuration before service initialization
|
|
1261
1671
|
* @private
|
|
1262
1672
|
*/
|
|
1263
|
-
private
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1673
|
+
private validatePluginConfig(): void {
|
|
1674
|
+
if (
|
|
1675
|
+
this.$config?.disableWebRTCRegistration === true &&
|
|
1676
|
+
this.$config?.allowMultiLogin === false
|
|
1677
|
+
) {
|
|
1678
|
+
throw new Error(
|
|
1679
|
+
'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.'
|
|
1680
|
+
);
|
|
1681
|
+
}
|
|
1270
1682
|
}
|
|
1271
1683
|
|
|
1272
1684
|
/**
|
|
1273
|
-
*
|
|
1274
|
-
* @
|
|
1685
|
+
* Whether wxApp thick-client answer is enabled for this SDK instance.
|
|
1686
|
+
* @public
|
|
1687
|
+
*/
|
|
1688
|
+
public isWxBetterTogetherEnabled(): boolean {
|
|
1689
|
+
return this.$config?.enableWxBetterTogether === true;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
/**
|
|
1693
|
+
* Current station login option (Extension, Dial Number, or Browser/Desktop).
|
|
1694
|
+
* Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
|
|
1275
1695
|
* @private
|
|
1276
1696
|
*/
|
|
1277
|
-
private
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1697
|
+
private getCurrentStationLoginOption(): LoginOption | undefined {
|
|
1698
|
+
return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
/**
|
|
1702
|
+
* wxApp thick-client answer is supported for Extension and Dial Number station login only —
|
|
1703
|
+
* not Browser/Desktop (WebRTC) login.
|
|
1704
|
+
* @private
|
|
1705
|
+
*/
|
|
1706
|
+
private assertWxAppStationLoginSupportedForEnable(): void {
|
|
1707
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1708
|
+
if (loginOption !== LoginOption.EXTENSION && loginOption !== LoginOption.AGENT_DN) {
|
|
1709
|
+
throw new Error(
|
|
1710
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.'
|
|
1289
1711
|
);
|
|
1290
|
-
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
1291
|
-
await this.silentRelogin();
|
|
1292
|
-
}
|
|
1293
1712
|
}
|
|
1294
1713
|
}
|
|
1295
1714
|
|
|
1296
1715
|
/**
|
|
1297
|
-
*
|
|
1716
|
+
* Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
|
|
1298
1717
|
* @private
|
|
1299
1718
|
*/
|
|
1300
|
-
private
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1719
|
+
private resetEnableWxBetterTogetherConfig(): void {
|
|
1720
|
+
if (this.$config) {
|
|
1721
|
+
this.$config.enableWxBetterTogether = false;
|
|
1722
|
+
}
|
|
1723
|
+
this.taskManager.applyEnableWxBetterTogether(false);
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/**
|
|
1727
|
+
* Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
|
|
1728
|
+
*
|
|
1729
|
+
* **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
|
|
1730
|
+
* Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
|
|
1731
|
+
* To change the flag after the SDK is initialized, re-init the SDK with the updated config.
|
|
1732
|
+
*
|
|
1733
|
+
* **Supported public read API:** {@link isWxBetterTogetherEnabled}.
|
|
1734
|
+
* **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
|
|
1735
|
+
* `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
|
|
1736
|
+
*
|
|
1737
|
+
* **Production lifecycle does not call this method.** Station login, silent relogin, logout,
|
|
1738
|
+
* and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
|
|
1739
|
+
* (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
|
|
1740
|
+
*
|
|
1741
|
+
* This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
|
|
1742
|
+
* It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
|
|
1743
|
+
* (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
|
|
1744
|
+
*
|
|
1745
|
+
* @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
|
|
1746
|
+
* @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
|
|
1747
|
+
* @internal
|
|
1748
|
+
* @see enableWxBetterTogether (CCPluginConfig init flag)
|
|
1749
|
+
* @see ensureWxAppPostStationLogin
|
|
1750
|
+
* @see teardownWxAppLocalState
|
|
1751
|
+
*/
|
|
1752
|
+
private async setManageWebexCallingInWxcc(enabled: boolean): Promise<void> {
|
|
1753
|
+
if (enabled && !this.$webex.internal.device?.userId) {
|
|
1754
|
+
throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
if (enabled) {
|
|
1758
|
+
this.assertWxAppStationLoginSupportedForEnable();
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
const previousEnabled = this.isWxBetterTogetherEnabled();
|
|
1762
|
+
|
|
1763
|
+
if (this.$config) {
|
|
1764
|
+
this.$config.enableWxBetterTogether = enabled;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
this.taskManager.applyEnableWxBetterTogether(enabled);
|
|
1768
|
+
|
|
1769
|
+
let publishedEnable = false;
|
|
1305
1770
|
|
|
1306
1771
|
try {
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1772
|
+
if (enabled) {
|
|
1773
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1774
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1775
|
+
publishedEnable = true;
|
|
1776
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1777
|
+
} else {
|
|
1778
|
+
await this.publishAnswerOnWebexCrossClientState(false);
|
|
1779
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1780
|
+
await this.releaseWxAppMercuryResources();
|
|
1781
|
+
}
|
|
1782
|
+
} catch (error) {
|
|
1783
|
+
if (publishedEnable) {
|
|
1784
|
+
await this.revertWxAppCrossClientPublish();
|
|
1785
|
+
} else if (enabled) {
|
|
1786
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1787
|
+
await this.releaseWxAppMercuryResources();
|
|
1788
|
+
}
|
|
1321
1789
|
|
|
1322
|
-
if (
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1790
|
+
if (this.$config) {
|
|
1791
|
+
this.$config.enableWxBetterTogether = previousEnabled;
|
|
1792
|
+
}
|
|
1793
|
+
this.taskManager.applyEnableWxBetterTogether(previousEnabled);
|
|
1794
|
+
throw error;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
private getWxAppTelephonyTaskType(): 'Voice' | 'WebRTC' | 'unknown' {
|
|
1799
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1800
|
+
|
|
1801
|
+
if (loginOption === LoginOption.BROWSER) {
|
|
1802
|
+
return 'WebRTC';
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
if (loginOption === LoginOption.EXTENSION || loginOption === LoginOption.AGENT_DN) {
|
|
1806
|
+
return 'Voice';
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
return 'unknown';
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
private async ensureWxAppPostStationLogin(): Promise<void> {
|
|
1813
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1814
|
+
const flagEnabled = this.isWxBetterTogetherEnabled();
|
|
1815
|
+
const telephonyTaskType = this.getWxAppTelephonyTaskType();
|
|
1816
|
+
|
|
1817
|
+
if (loginOption === LoginOption.BROWSER) {
|
|
1818
|
+
if (flagEnabled) {
|
|
1819
|
+
logWxAppSessionReadiness({
|
|
1820
|
+
enableWxBetterTogether: true,
|
|
1821
|
+
loginOption,
|
|
1822
|
+
wxAppHooksApplied: false,
|
|
1823
|
+
usersubPublished: false,
|
|
1824
|
+
mercurySubscribed: false,
|
|
1825
|
+
telephonyTaskType,
|
|
1826
|
+
skipReason: 'unsupported_browser_login',
|
|
1827
|
+
});
|
|
1828
|
+
|
|
1829
|
+
this.metricsManager.trackEvent(
|
|
1830
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1831
|
+
{
|
|
1832
|
+
loginOption,
|
|
1833
|
+
enableWxBetterTogether: true,
|
|
1834
|
+
skipReason: 'unsupported_browser_login',
|
|
1835
|
+
},
|
|
1836
|
+
['operational', 'behavioral']
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
let publishedEnable = false;
|
|
1844
|
+
let wxAppInitStage: 'mercury' | 'publish' = 'mercury';
|
|
1845
|
+
|
|
1846
|
+
try {
|
|
1847
|
+
if (flagEnabled) {
|
|
1848
|
+
this.metricsManager.timeEvent([
|
|
1849
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1850
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1851
|
+
]);
|
|
1852
|
+
|
|
1853
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1854
|
+
wxAppInitStage = 'publish';
|
|
1855
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1856
|
+
publishedEnable = true;
|
|
1857
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1858
|
+
|
|
1859
|
+
const mercurySubscribed = this.wxAppTelephonyMercurySync.isSubscribed();
|
|
1860
|
+
const usersubPublished = this.webexCrossClientService.isAnswerCallsStateActive();
|
|
1861
|
+
|
|
1862
|
+
logWxAppSessionReadiness({
|
|
1863
|
+
enableWxBetterTogether: true,
|
|
1864
|
+
loginOption,
|
|
1865
|
+
wxAppHooksApplied: true,
|
|
1866
|
+
usersubPublished,
|
|
1867
|
+
mercurySubscribed,
|
|
1868
|
+
telephonyTaskType,
|
|
1869
|
+
});
|
|
1870
|
+
|
|
1871
|
+
const sessionInitReady = mercurySubscribed && usersubPublished;
|
|
1872
|
+
|
|
1873
|
+
if (sessionInitReady) {
|
|
1874
|
+
this.metricsManager.trackEvent(
|
|
1875
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1876
|
+
{loginOption, enableWxBetterTogether: true},
|
|
1877
|
+
['operational', 'behavioral']
|
|
1878
|
+
);
|
|
1879
|
+
} else {
|
|
1880
|
+
this.metricsManager.trackEvent(
|
|
1881
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1882
|
+
{
|
|
1883
|
+
loginOption,
|
|
1884
|
+
enableWxBetterTogether: true,
|
|
1885
|
+
skipReason: !usersubPublished ? 'usersub_not_published' : 'mercury_not_subscribed',
|
|
1886
|
+
},
|
|
1887
|
+
['operational', 'behavioral']
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1890
|
+
} else {
|
|
1891
|
+
await this.ensureWxAppDeviceRegistered();
|
|
1892
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
1893
|
+
this.webexCrossClientService.teardown();
|
|
1894
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1895
|
+
await this.releaseWxAppMercuryResources();
|
|
1896
|
+
|
|
1897
|
+
logWxAppSessionReadiness({
|
|
1898
|
+
enableWxBetterTogether: false,
|
|
1899
|
+
loginOption,
|
|
1900
|
+
wxAppHooksApplied: true,
|
|
1901
|
+
usersubPublished: false,
|
|
1902
|
+
mercurySubscribed: false,
|
|
1903
|
+
telephonyTaskType,
|
|
1904
|
+
skipReason: 'flag_disabled',
|
|
1905
|
+
});
|
|
1906
|
+
|
|
1907
|
+
this.metricsManager.trackEvent(
|
|
1908
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1909
|
+
{loginOption, enableWxBetterTogether: false, skipReason: 'flag_disabled'},
|
|
1910
|
+
['operational', 'behavioral']
|
|
1911
|
+
);
|
|
1912
|
+
}
|
|
1913
|
+
} catch (error) {
|
|
1914
|
+
LoggerProxy.error(`Failed to initialize wxApp post-station-login: ${error}`, {
|
|
1915
|
+
module: CC_FILE,
|
|
1916
|
+
method: METHODS.STATION_LOGIN,
|
|
1917
|
+
});
|
|
1918
|
+
|
|
1919
|
+
if (flagEnabled) {
|
|
1920
|
+
const sessionInitFailureReason =
|
|
1921
|
+
wxAppInitStage === 'mercury' ? 'mercury_subscribe_failed' : 'publish_failed';
|
|
1922
|
+
|
|
1923
|
+
this.metricsManager.trackEvent(
|
|
1924
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1925
|
+
{
|
|
1926
|
+
loginOption,
|
|
1927
|
+
enableWxBetterTogether: true,
|
|
1928
|
+
skipReason: sessionInitFailureReason,
|
|
1929
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
1930
|
+
},
|
|
1931
|
+
['operational', 'behavioral']
|
|
1932
|
+
);
|
|
1933
|
+
|
|
1934
|
+
logWxAppSessionReadiness({
|
|
1935
|
+
enableWxBetterTogether: true,
|
|
1936
|
+
loginOption,
|
|
1937
|
+
wxAppHooksApplied: false,
|
|
1938
|
+
usersubPublished:
|
|
1939
|
+
publishedEnable && this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1940
|
+
mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
|
|
1941
|
+
telephonyTaskType,
|
|
1942
|
+
skipReason: sessionInitFailureReason,
|
|
1943
|
+
});
|
|
1944
|
+
|
|
1945
|
+
if (publishedEnable) {
|
|
1946
|
+
await this.revertWxAppCrossClientPublish();
|
|
1947
|
+
} else {
|
|
1948
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1949
|
+
await this.releaseWxAppMercuryResources();
|
|
1950
|
+
}
|
|
1951
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
1952
|
+
} else {
|
|
1953
|
+
this.scheduleForcedFalsePublishRetry();
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
private clearWxAppFalsePublishRetryTimer(): void {
|
|
1959
|
+
if (this.wxAppFalsePublishRetryTimer) {
|
|
1960
|
+
clearTimeout(this.wxAppFalsePublishRetryTimer);
|
|
1961
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
private scheduleForcedFalsePublishRetry(retryAttempt = 0): void {
|
|
1966
|
+
this.scheduleFalsePublishRetry(retryAttempt, {requireDisabled: true});
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
/**
|
|
1970
|
+
* Retries compensating false publish after rollback failure, regardless of enabled flag.
|
|
1971
|
+
* @private
|
|
1972
|
+
*/
|
|
1973
|
+
private scheduleCompensatingFalsePublishRetry(retryAttempt = 0): void {
|
|
1974
|
+
this.scheduleFalsePublishRetry(retryAttempt);
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
private scheduleFalsePublishRetry(
|
|
1978
|
+
retryAttempt = 0,
|
|
1979
|
+
options: {requireDisabled?: boolean} = {}
|
|
1980
|
+
): void {
|
|
1981
|
+
if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
1986
|
+
return;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
1990
|
+
|
|
1991
|
+
this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
|
|
1992
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
1993
|
+
|
|
1994
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
if (
|
|
1999
|
+
!options?.requireDisabled &&
|
|
2000
|
+
this.isWxBetterTogetherEnabled() &&
|
|
2001
|
+
this.webexCrossClientService.isAnswerCallsStateActive()
|
|
2002
|
+
) {
|
|
2003
|
+
return;
|
|
2004
|
+
}
|
|
2005
|
+
|
|
2006
|
+
try {
|
|
2007
|
+
await this.ensureWxAppDeviceRegistered();
|
|
2008
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
2009
|
+
this.webexCrossClientService.teardown();
|
|
2010
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2011
|
+
await this.releaseWxAppMercuryResources();
|
|
2012
|
+
} catch (retryError) {
|
|
2013
|
+
LoggerProxy.error(
|
|
2014
|
+
`Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`,
|
|
2015
|
+
{
|
|
2016
|
+
module: CC_FILE,
|
|
2017
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2018
|
+
}
|
|
2019
|
+
);
|
|
2020
|
+
this.scheduleFalsePublishRetry(retryAttempt + 1, options);
|
|
2021
|
+
}
|
|
2022
|
+
}, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
/**
|
|
2026
|
+
* Best-effort rollback when usersub true was published but wxApp setup did not complete.
|
|
2027
|
+
* @private
|
|
2028
|
+
*/
|
|
2029
|
+
private async revertWxAppCrossClientPublish(): Promise<void> {
|
|
2030
|
+
try {
|
|
2031
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
2032
|
+
} catch (error) {
|
|
2033
|
+
LoggerProxy.error(
|
|
2034
|
+
`Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`,
|
|
2035
|
+
{
|
|
2036
|
+
module: CC_FILE,
|
|
2037
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2038
|
+
}
|
|
2039
|
+
);
|
|
2040
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
this.webexCrossClientService.teardown();
|
|
2044
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2045
|
+
await this.releaseWxAppMercuryResources();
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
private async ensureWxAppDeviceRegistered(): Promise<void> {
|
|
2049
|
+
const device = this.$webex.internal.device;
|
|
2050
|
+
|
|
2051
|
+
if (device && !device.registered && device.register) {
|
|
2052
|
+
await device.register();
|
|
2053
|
+
this.wxAppDeviceRegisteredByCc = true;
|
|
2054
|
+
LoggerProxy.log('WxApp: device registered for cross-client publish', {
|
|
2055
|
+
module: CC_FILE,
|
|
2056
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
private async ensureWxAppMercuryConnected(): Promise<void> {
|
|
2062
|
+
const mercury = this.$webex.internal.mercury;
|
|
2063
|
+
|
|
2064
|
+
await this.ensureWxAppDeviceRegistered();
|
|
2065
|
+
|
|
2066
|
+
if (mercury && !mercury.connected) {
|
|
2067
|
+
await mercury.connect();
|
|
2068
|
+
this.wxAppMercuryConnectedByCc = true;
|
|
2069
|
+
LoggerProxy.log('WxApp mute sync: mercury connected', {
|
|
2070
|
+
module: CC_FILE,
|
|
2071
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
/**
|
|
2077
|
+
* Disconnects Mercury and unregisters the device when wxApp mute sync opened them.
|
|
2078
|
+
* @private
|
|
2079
|
+
*/
|
|
2080
|
+
private async releaseWxAppMercuryResources(): Promise<void> {
|
|
2081
|
+
const mercury = this.$webex.internal.mercury;
|
|
2082
|
+
const device = this.$webex.internal.device;
|
|
2083
|
+
|
|
2084
|
+
if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
|
|
2085
|
+
try {
|
|
2086
|
+
await mercury.disconnect();
|
|
2087
|
+
this.wxAppMercuryConnectedByCc = false;
|
|
2088
|
+
LoggerProxy.log('WxApp mute sync: mercury disconnected', {
|
|
2089
|
+
module: CC_FILE,
|
|
2090
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2091
|
+
});
|
|
2092
|
+
} catch (error) {
|
|
2093
|
+
LoggerProxy.error(`Failed to disconnect wxApp Mercury: ${error}`, {
|
|
2094
|
+
module: CC_FILE,
|
|
2095
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
|
|
2101
|
+
try {
|
|
2102
|
+
// @ts-ignore
|
|
2103
|
+
await device.unregister();
|
|
2104
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
2105
|
+
LoggerProxy.log('WxApp mute sync: device unregistered', {
|
|
2106
|
+
module: CC_FILE,
|
|
2107
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2108
|
+
});
|
|
2109
|
+
} catch (error) {
|
|
2110
|
+
LoggerProxy.error(`Failed to unregister wxApp device: ${error}`, {
|
|
2111
|
+
module: CC_FILE,
|
|
2112
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
} else if (this.wxAppDeviceRegisteredByCc) {
|
|
2116
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
|
|
2122
|
+
* optional rethrow for deregister when callers need to surface usersub errors.
|
|
2123
|
+
* @private
|
|
2124
|
+
*/
|
|
2125
|
+
private async teardownWxAppLocalState(options?: {
|
|
2126
|
+
rethrowPublishError?: boolean;
|
|
2127
|
+
clearInitFlag?: boolean;
|
|
2128
|
+
}): Promise<{publishError?: unknown}> {
|
|
2129
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2130
|
+
let publishError: unknown;
|
|
2131
|
+
|
|
2132
|
+
try {
|
|
2133
|
+
const shouldForceFalsePublish =
|
|
2134
|
+
this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
|
|
2135
|
+
|
|
2136
|
+
await this.publishAnswerOnWebexCrossClientState(false, {
|
|
2137
|
+
force: shouldForceFalsePublish,
|
|
2138
|
+
});
|
|
2139
|
+
} catch (error) {
|
|
2140
|
+
publishError = error;
|
|
2141
|
+
LoggerProxy.error(
|
|
2142
|
+
`Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`,
|
|
2143
|
+
{
|
|
2144
|
+
module: CC_FILE,
|
|
2145
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2146
|
+
}
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
this.webexCrossClientService.teardown();
|
|
2151
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2152
|
+
await this.releaseWxAppMercuryResources();
|
|
2153
|
+
|
|
2154
|
+
if (options?.clearInitFlag) {
|
|
2155
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
if (publishError) {
|
|
2159
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
if (options?.rethrowPublishError && publishError) {
|
|
2163
|
+
throw publishError;
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
return {publishError: publishError ?? undefined};
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
private async ensureWxAppMercuryAndSubscribe(): Promise<void> {
|
|
2170
|
+
if (!this.isWxBetterTogetherEnabled()) {
|
|
2171
|
+
return;
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
const agentId = this.agentConfig?.agentId;
|
|
2179
|
+
if (!agentId) {
|
|
2180
|
+
LoggerProxy.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
|
|
2181
|
+
module: CC_FILE,
|
|
2182
|
+
method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
|
|
2183
|
+
});
|
|
2184
|
+
|
|
2185
|
+
this.metricsManager.trackEvent(
|
|
2186
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
2187
|
+
{error: 'agentId unavailable'},
|
|
2188
|
+
['operational', 'behavioral']
|
|
2189
|
+
);
|
|
2190
|
+
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
try {
|
|
2195
|
+
await this.ensureWxAppMercuryConnected();
|
|
2196
|
+
this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
|
|
2197
|
+
this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
|
|
2198
|
+
});
|
|
2199
|
+
} catch (error) {
|
|
2200
|
+
LoggerProxy.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
|
|
2201
|
+
module: CC_FILE,
|
|
2202
|
+
method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
|
|
2203
|
+
});
|
|
2204
|
+
this.metricsManager.trackEvent(
|
|
2205
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
2206
|
+
{error: error instanceof Error ? error.toString() : String(error)},
|
|
2207
|
+
['operational', 'behavioral']
|
|
2208
|
+
);
|
|
2209
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2210
|
+
await this.releaseWxAppMercuryResources();
|
|
2211
|
+
throw error;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
private async publishAnswerOnWebexCrossClientState(
|
|
2216
|
+
enable: boolean,
|
|
2217
|
+
options?: {force?: boolean}
|
|
2218
|
+
): Promise<void> {
|
|
2219
|
+
if (enable && !this.isWxBetterTogetherEnabled()) {
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
const userId = this.$webex.internal.device?.userId;
|
|
2228
|
+
if (!userId) {
|
|
2229
|
+
if (enable && this.isWxBetterTogetherEnabled()) {
|
|
2230
|
+
this.metricsManager.trackEvent(
|
|
2231
|
+
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
|
|
2232
|
+
{
|
|
2233
|
+
enableWxBetterTogether: true,
|
|
2234
|
+
skipReason: 'user_id_unavailable',
|
|
2235
|
+
error: 'User ID is unavailable for cross-client publish',
|
|
2236
|
+
},
|
|
2237
|
+
['operational', 'behavioral']
|
|
2238
|
+
);
|
|
2239
|
+
}
|
|
2240
|
+
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {
|
|
2245
|
+
userId,
|
|
2246
|
+
trackPublishMetrics: true,
|
|
2247
|
+
});
|
|
2248
|
+
|
|
2249
|
+
if (enable) {
|
|
2250
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
/**
|
|
2255
|
+
* Initializes event listeners for the Contact Center service
|
|
2256
|
+
* Sets up handlers for connection state changes and other core events
|
|
2257
|
+
* @private
|
|
2258
|
+
*/
|
|
2259
|
+
private setupEventListeners() {
|
|
2260
|
+
this.services.connectionService.on('connectionLost', this.handleConnectionLost.bind(this));
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
/**
|
|
2264
|
+
* Returns the connection configuration
|
|
2265
|
+
* @returns {SubscribeRequest} Connection configuration
|
|
2266
|
+
* @private
|
|
2267
|
+
*/
|
|
2268
|
+
private getConnectionConfig(): SubscribeRequest {
|
|
2269
|
+
return {
|
|
2270
|
+
force: this.$config?.force ?? true,
|
|
2271
|
+
isKeepAliveEnabled: this.$config?.isKeepAliveEnabled ?? false,
|
|
2272
|
+
clientType: this.$config?.clientType ?? 'WebexCCSDK',
|
|
2273
|
+
allowMultiLogin: this.$config?.allowMultiLogin ?? true,
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
/**
|
|
2278
|
+
* Handles connection lost events and reconnection attempts
|
|
2279
|
+
* @param {ConnectionLostDetails} msg Connection lost details
|
|
2280
|
+
* @private
|
|
2281
|
+
*/
|
|
2282
|
+
private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
|
|
2283
|
+
if (msg.isConnectionLost) {
|
|
2284
|
+
// TODO: Emit an event saying connection is lost
|
|
2285
|
+
LoggerProxy.log('event=handleConnectionLost | Connection lost', {
|
|
2286
|
+
module: CC_FILE,
|
|
2287
|
+
method: METHODS.HANDLE_CONNECTION_LOST,
|
|
2288
|
+
});
|
|
2289
|
+
} else if (msg.isSocketReconnected) {
|
|
2290
|
+
// TODO: Emit an event saying connection is re-estabilished
|
|
2291
|
+
LoggerProxy.log(
|
|
2292
|
+
'event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin',
|
|
2293
|
+
{module: CC_FILE, method: METHODS.HANDLE_CONNECTION_LOST}
|
|
2294
|
+
);
|
|
2295
|
+
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
2296
|
+
await this.silentRelogin();
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
/**
|
|
2302
|
+
* Handles silent relogin after registration completion
|
|
2303
|
+
* @private
|
|
2304
|
+
*/
|
|
2305
|
+
private async silentRelogin(): Promise<void> {
|
|
2306
|
+
LoggerProxy.log('Starting silent relogin process', {
|
|
2307
|
+
module: CC_FILE,
|
|
2308
|
+
method: METHODS.SILENT_RELOGIN,
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2311
|
+
try {
|
|
2312
|
+
const reLoginResponse = await this.services.agent.reload();
|
|
2313
|
+
this.updateWellnessSession(reLoginResponse.data.agentSessionId);
|
|
2314
|
+
|
|
2315
|
+
const {
|
|
2316
|
+
agentId,
|
|
2317
|
+
lastStateChangeReason,
|
|
2318
|
+
deviceType,
|
|
2319
|
+
dn,
|
|
2320
|
+
lastStateChangeTimestamp,
|
|
2321
|
+
lastIdleCodeChangeTimestamp,
|
|
2322
|
+
} = reLoginResponse.data;
|
|
2323
|
+
let {auxCodeId} = reLoginResponse.data;
|
|
2324
|
+
this.agentConfig.lastStateChangeTimestamp = lastStateChangeTimestamp;
|
|
2325
|
+
this.agentConfig.lastIdleCodeChangeTimestamp = lastIdleCodeChangeTimestamp;
|
|
2326
|
+
this.agentConfig.currentTeamId = reLoginResponse.data.teamId;
|
|
2327
|
+
await this.handleDeviceType(deviceType as LoginOption, dn);
|
|
2328
|
+
|
|
2329
|
+
if (lastStateChangeReason === 'agent-wss-disconnect') {
|
|
2330
|
+
LoggerProxy.log(
|
|
2331
|
+
'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
|
|
2332
|
+
{module: CC_FILE, method: METHODS.SILENT_RELOGIN}
|
|
2333
|
+
);
|
|
2334
|
+
auxCodeId = AGENT_STATE_AVAILABLE_ID;
|
|
1328
2335
|
const stateChangeData: StateChange = {
|
|
1329
2336
|
state: AGENT_STATE_AVAILABLE,
|
|
1330
2337
|
auxCodeId,
|
|
@@ -1349,8 +2356,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1349
2356
|
}
|
|
1350
2357
|
this.agentConfig.lastStateAuxCodeId = auxCodeId;
|
|
1351
2358
|
this.agentConfig.isAgentLoggedIn = true;
|
|
1352
|
-
|
|
1353
|
-
this.
|
|
2359
|
+
|
|
2360
|
+
await this.ensureWxAppPostStationLogin();
|
|
1354
2361
|
|
|
1355
2362
|
LoggerProxy.log(
|
|
1356
2363
|
`Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
|
|
@@ -1391,6 +2398,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1391
2398
|
this.agentConfig.deviceType = deviceType;
|
|
1392
2399
|
switch (deviceType) {
|
|
1393
2400
|
case LoginOption.BROWSER:
|
|
2401
|
+
if (this.isWebRTCRegistrationDisabled()) {
|
|
2402
|
+
LoggerProxy.info(
|
|
2403
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
2404
|
+
{
|
|
2405
|
+
module: CC_FILE,
|
|
2406
|
+
method: METHODS.HANDLE_DEVICE_TYPE,
|
|
2407
|
+
}
|
|
2408
|
+
);
|
|
2409
|
+
break;
|
|
2410
|
+
}
|
|
1394
2411
|
try {
|
|
1395
2412
|
await this.webCallingService.registerWebCallingLine();
|
|
1396
2413
|
} catch (error) {
|
|
@@ -1656,6 +2673,166 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1656
2673
|
}
|
|
1657
2674
|
}
|
|
1658
2675
|
|
|
2676
|
+
/**
|
|
2677
|
+
* Skips a campaign preview contact, requesting the next contact from the campaign.
|
|
2678
|
+
*
|
|
2679
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
2680
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
|
|
2681
|
+
* preview contact to move to the next contact in the campaign.
|
|
2682
|
+
*
|
|
2683
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
2684
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
2685
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
2686
|
+
*
|
|
2687
|
+
* @example
|
|
2688
|
+
* ```typescript
|
|
2689
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
2690
|
+
* const { interactionId } = task.data;
|
|
2691
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
2692
|
+
*
|
|
2693
|
+
* const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
|
|
2694
|
+
* });
|
|
2695
|
+
* ```
|
|
2696
|
+
*/
|
|
2697
|
+
public async skipPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
2698
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
2699
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
|
|
2700
|
+
LoggerProxy.warn('Skip action is disabled for this campaign preview contact', {
|
|
2701
|
+
module: CC_FILE,
|
|
2702
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
2703
|
+
interactionId: payload.interactionId,
|
|
2704
|
+
});
|
|
2705
|
+
throw new Error('Skip action is disabled for this campaign preview contact');
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
LoggerProxy.info('Skipping campaign preview contact', {
|
|
2709
|
+
module: CC_FILE,
|
|
2710
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
2711
|
+
});
|
|
2712
|
+
try {
|
|
2713
|
+
this.metricsManager.timeEvent([
|
|
2714
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
|
|
2715
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
2716
|
+
]);
|
|
2717
|
+
|
|
2718
|
+
const result = await this.services.dialer.skipPreviewContact({data: payload});
|
|
2719
|
+
|
|
2720
|
+
this.metricsManager.trackEvent(
|
|
2721
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
|
|
2722
|
+
{
|
|
2723
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
2724
|
+
interactionId: payload.interactionId,
|
|
2725
|
+
campaignId: payload.campaignId,
|
|
2726
|
+
},
|
|
2727
|
+
['behavioral', 'business', 'operational']
|
|
2728
|
+
);
|
|
2729
|
+
|
|
2730
|
+
LoggerProxy.log('Campaign preview contact skipped successfully', {
|
|
2731
|
+
module: CC_FILE,
|
|
2732
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
2733
|
+
trackingId: result.trackingId,
|
|
2734
|
+
interactionId: payload.interactionId,
|
|
2735
|
+
});
|
|
2736
|
+
|
|
2737
|
+
return result;
|
|
2738
|
+
} catch (error) {
|
|
2739
|
+
const failure = error.details as Failure;
|
|
2740
|
+
this.metricsManager.trackEvent(
|
|
2741
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
2742
|
+
{
|
|
2743
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
2744
|
+
interactionId: payload.interactionId,
|
|
2745
|
+
campaignId: payload.campaignId,
|
|
2746
|
+
},
|
|
2747
|
+
['behavioral', 'business', 'operational']
|
|
2748
|
+
);
|
|
2749
|
+
const {error: detailedError} = getErrorDetails(error, METHODS.SKIP_PREVIEW_CONTACT, CC_FILE);
|
|
2750
|
+
throw detailedError;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
/**
|
|
2755
|
+
* Removes a campaign preview contact from the campaign list entirely.
|
|
2756
|
+
*
|
|
2757
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
2758
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
|
|
2759
|
+
* remove the preview contact to permanently take it out of the campaign contact list.
|
|
2760
|
+
*
|
|
2761
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
2762
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
2763
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
2764
|
+
*
|
|
2765
|
+
* @example
|
|
2766
|
+
* ```typescript
|
|
2767
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
2768
|
+
* const { interactionId } = task.data;
|
|
2769
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
2770
|
+
*
|
|
2771
|
+
* const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
|
|
2772
|
+
* });
|
|
2773
|
+
* ```
|
|
2774
|
+
*/
|
|
2775
|
+
public async removePreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
2776
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
2777
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
|
|
2778
|
+
LoggerProxy.warn('Remove action is disabled for this campaign preview contact', {
|
|
2779
|
+
module: CC_FILE,
|
|
2780
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2781
|
+
interactionId: payload.interactionId,
|
|
2782
|
+
});
|
|
2783
|
+
throw new Error('Remove action is disabled for this campaign preview contact');
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
LoggerProxy.info('Removing campaign preview contact', {
|
|
2787
|
+
module: CC_FILE,
|
|
2788
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2789
|
+
});
|
|
2790
|
+
try {
|
|
2791
|
+
this.metricsManager.timeEvent([
|
|
2792
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
|
|
2793
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
2794
|
+
]);
|
|
2795
|
+
|
|
2796
|
+
const result = await this.services.dialer.removePreviewContact({data: payload});
|
|
2797
|
+
|
|
2798
|
+
this.metricsManager.trackEvent(
|
|
2799
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
|
|
2800
|
+
{
|
|
2801
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
2802
|
+
interactionId: payload.interactionId,
|
|
2803
|
+
campaignId: payload.campaignId,
|
|
2804
|
+
},
|
|
2805
|
+
['behavioral', 'business', 'operational']
|
|
2806
|
+
);
|
|
2807
|
+
|
|
2808
|
+
LoggerProxy.log('Campaign preview contact removed successfully', {
|
|
2809
|
+
module: CC_FILE,
|
|
2810
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2811
|
+
trackingId: result.trackingId,
|
|
2812
|
+
interactionId: payload.interactionId,
|
|
2813
|
+
});
|
|
2814
|
+
|
|
2815
|
+
return result;
|
|
2816
|
+
} catch (error) {
|
|
2817
|
+
const failure = error.details as Failure;
|
|
2818
|
+
this.metricsManager.trackEvent(
|
|
2819
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
2820
|
+
{
|
|
2821
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
2822
|
+
interactionId: payload.interactionId,
|
|
2823
|
+
campaignId: payload.campaignId,
|
|
2824
|
+
},
|
|
2825
|
+
['behavioral', 'business', 'operational']
|
|
2826
|
+
);
|
|
2827
|
+
const {error: detailedError} = getErrorDetails(
|
|
2828
|
+
error,
|
|
2829
|
+
METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2830
|
+
CC_FILE
|
|
2831
|
+
);
|
|
2832
|
+
throw detailedError;
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
|
|
1659
2836
|
/**
|
|
1660
2837
|
* Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
|
|
1661
2838
|
*
|
|
@@ -1900,8 +3077,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1900
3077
|
}
|
|
1901
3078
|
|
|
1902
3079
|
/**
|
|
1903
|
-
* Returns paginated entry points
|
|
1904
|
-
*
|
|
3080
|
+
* Returns paginated entry points using the EntryPoint service defaults.
|
|
3081
|
+
* Existing request parameters can override those defaults.
|
|
1905
3082
|
* @public
|
|
1906
3083
|
*/
|
|
1907
3084
|
public async getEntryPoints(
|
|
@@ -1911,8 +3088,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1911
3088
|
}
|
|
1912
3089
|
|
|
1913
3090
|
/**
|
|
1914
|
-
* Returns paginated contact service queues
|
|
1915
|
-
*
|
|
3091
|
+
* Returns paginated contact service queues using the Queue service defaults.
|
|
3092
|
+
* Existing request parameters can override those defaults.
|
|
1916
3093
|
* @public
|
|
1917
3094
|
*/
|
|
1918
3095
|
public async getQueues(
|