@webex/contact-center 3.12.0-next.15 → 3.12.0-next.151
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 +887 -0
- package/AGENTS.md +94 -0
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +62 -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 +1010 -47
- package/dist/cc.js.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +40 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js +54 -17
- 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 +211 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +58 -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 +351 -14
- 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 +225 -0
- package/dist/services/WebexCrossClientService.js.map +1 -0
- package/dist/services/WxAppTelephonyMercurySync.js +111 -0
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
- package/dist/services/config/Util.js +48 -48
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +56 -78
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +100 -226
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +97 -24
- 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 +955 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +50 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +1272 -523
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +221 -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 +21 -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 +259 -5
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1504 -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/wxAppOfferObservability.js +145 -0
- package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js +395 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
- package/dist/services/wxAppDiagnosticLogging.js +159 -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 +196 -9
- package/dist/types/config.d.ts +13 -0
- package/dist/types/constants.d.ts +38 -1
- package/dist/types/index.d.ts +54 -20
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +52 -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 +58 -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/Util.d.ts +4 -8
- package/dist/types/services/config/constants.d.ts +49 -76
- package/dist/types/services/config/index.d.ts +16 -64
- package/dist/types/services/config/types.d.ts +248 -114
- 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 +186 -0
- package/dist/types/services/task/TaskFactory.d.ts +14 -0
- package/dist/types/services/task/TaskUtils.d.ts +54 -2
- package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
- package/dist/types/services/task/constants.d.ts +19 -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 +23 -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 +790 -66
- package/dist/types/services/task/voice/Voice.d.ts +241 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +54 -0
- package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +68 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +63 -0
- package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
- package/dist/types/types.d.ts +213 -11
- package/dist/types/utils/PageCache.d.ts +20 -3
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +168 -6
- 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 +1338 -121
- package/src/config.ts +13 -0
- package/src/constants.ts +41 -1
- package/src/index.ts +62 -15
- 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 +971 -0
- package/src/metrics/behavioral-events.ts +218 -0
- package/src/metrics/constants.ts +63 -4
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +239 -0
- package/src/services/ApiAiAssistant.ts +470 -15
- 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 +298 -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 +54 -84
- package/src/services/config/ai-docs/AGENTS.md +250 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +413 -0
- package/src/services/config/ai-docs/config-spec.md +669 -0
- package/src/services/config/constants.ts +63 -85
- package/src/services/config/index.ts +116 -277
- package/src/services/config/types.ts +233 -125
- 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 +33 -1
- 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 +1211 -0
- package/src/services/task/TaskFactory.ts +82 -0
- package/src/services/task/TaskManager.ts +1584 -643
- package/src/services/task/TaskUtils.ts +316 -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 +1501 -0
- package/src/services/task/constants.ts +20 -0
- package/src/services/task/contact.ts +30 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/digital/Digital.ts +102 -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 +974 -70
- package/src/services/task/voice/Voice.ts +1878 -0
- package/src/services/task/voice/WebRTC.ts +194 -0
- package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +575 -0
- package/src/services/wxAppDiagnosticLogging.ts +286 -0
- package/src/services/wxAppTelephonyUtils.ts +14 -0
- package/src/types.ts +272 -13
- 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/fixtures/aiSummaryTestUtils.ts +40 -0
- package/test/unit/spec/cc.ts +3705 -150
- package/test/unit/spec/index.ts +885 -0
- 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 +429 -23
- 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 +509 -0
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
- package/test/unit/spec/services/config/Util.ts +501 -0
- package/test/unit/spec/services/config/index.ts +317 -547
- package/test/unit/spec/services/core/Utils.ts +425 -8
- package/test/unit/spec/services/core/WebexRequest.ts +8 -7
- 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 +2723 -0
- package/test/unit/spec/services/task/TaskFactory.ts +116 -0
- package/test/unit/spec/services/task/TaskManager.ts +4781 -1585
- package/test/unit/spec/services/task/TaskUtils.ts +307 -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 +2355 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
- package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +719 -0
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +179 -0
- package/test/unit/spec/wellnessSampleUtils.ts +288 -0
- package/typedoc.md +1 -1
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1530
- 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 -1806
- package/test/unit/spec/services/task/index.ts +0 -2205
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,
|
|
@@ -159,7 +170,7 @@ import type {
|
|
|
159
170
|
*
|
|
160
171
|
* // Login with browser-based calling
|
|
161
172
|
* await cc.stationLogin({
|
|
162
|
-
* teamId: profile.teams[0].
|
|
173
|
+
* teamId: profile.teams[0].id,
|
|
163
174
|
* loginOption: 'BROWSER'
|
|
164
175
|
* });
|
|
165
176
|
*
|
|
@@ -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,127 @@ 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
|
+
let payload: unknown;
|
|
647
|
+
try {
|
|
648
|
+
payload = JSON.parse(event);
|
|
649
|
+
} catch {
|
|
650
|
+
// TaskManager retains its existing malformed RTD message handling.
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (isRecord(payload) && this.handleWellnessBreakWebsocketPayload(payload)) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
436
658
|
};
|
|
437
659
|
|
|
438
660
|
/**
|
|
@@ -443,6 +665,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
443
665
|
private incomingTaskListener() {
|
|
444
666
|
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
445
667
|
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
668
|
+
this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
446
669
|
this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
447
670
|
this.taskManager.on(
|
|
448
671
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
@@ -478,16 +701,18 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
478
701
|
*
|
|
479
702
|
* console.log('Agent ID:', profile.agentId);
|
|
480
703
|
* console.log('Default DN:', profile.defaultDn);
|
|
481
|
-
* console.log('Teams:', profile.teams.map(t => t.
|
|
704
|
+
* console.log('Teams:', profile.teams.map(t => t.id));
|
|
482
705
|
* console.log('WebRTC Enabled:', profile.webRtcEnabled);
|
|
483
706
|
* console.log('Supported Login Options:', profile.loginVoiceOptions);
|
|
484
707
|
*
|
|
485
708
|
* // Now you can proceed with station login, state changes, etc.
|
|
486
|
-
* await cc.stationLogin({ teamId: profile.teams[0].
|
|
709
|
+
* await cc.stationLogin({ teamId: profile.teams[0].id, loginOption: 'BROWSER' });
|
|
487
710
|
* ```
|
|
488
711
|
*/
|
|
489
712
|
public async register(): Promise<Profile> {
|
|
490
|
-
|
|
713
|
+
this.wellnessRegistrationGeneration += 1;
|
|
714
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
715
|
+
LoggerProxy.log('Starting CC SDK registration', {
|
|
491
716
|
module: CC_FILE,
|
|
492
717
|
method: METHODS.REGISTER,
|
|
493
718
|
});
|
|
@@ -497,6 +722,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
497
722
|
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
|
|
498
723
|
]);
|
|
499
724
|
this.setupEventListeners();
|
|
725
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
500
726
|
|
|
501
727
|
const resp = await this.connectWebsocket();
|
|
502
728
|
// Ensure 'dn' is always populated from 'defaultDn'
|
|
@@ -515,7 +741,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
515
741
|
method: METHODS.REGISTER,
|
|
516
742
|
});
|
|
517
743
|
|
|
518
|
-
|
|
744
|
+
// Registration can silently restore a station session without emitting a
|
|
745
|
+
// relogin event. Project the active session for consumers that must
|
|
746
|
+
// recover session-scoped state after a page refresh.
|
|
747
|
+
return resp.isAgentLoggedIn && this.currentAgentSessionId
|
|
748
|
+
? {...resp, agentSessionId: this.currentAgentSessionId}
|
|
749
|
+
: resp;
|
|
519
750
|
} catch (error) {
|
|
520
751
|
this.metricsManager.trackEvent(
|
|
521
752
|
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
|
|
@@ -568,14 +799,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
568
799
|
|
|
569
800
|
*/
|
|
570
801
|
public async deregister(): Promise<void> {
|
|
802
|
+
this.wellnessRegistrationGeneration += 1;
|
|
803
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
804
|
+
this.updateWellnessSession();
|
|
571
805
|
try {
|
|
572
806
|
this.metricsManager.timeEvent([
|
|
573
807
|
METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS,
|
|
574
808
|
METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_FAIL,
|
|
575
809
|
]);
|
|
576
810
|
|
|
811
|
+
this.taskManager.clearAISummaryState();
|
|
577
812
|
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
578
813
|
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
814
|
+
this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
579
815
|
this.taskManager.off(
|
|
580
816
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
581
817
|
this.handleCampaignPreviewReservation
|
|
@@ -586,6 +822,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
586
822
|
this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
|
|
587
823
|
this.services.connectionService.off('connectionLost', this.handleConnectionLost);
|
|
588
824
|
|
|
825
|
+
const {publishError: wxAppPublishError} = await this.teardownWxAppLocalState({
|
|
826
|
+
clearInitFlag: true,
|
|
827
|
+
});
|
|
828
|
+
|
|
589
829
|
if (
|
|
590
830
|
this.agentConfig.webRtcEnabled &&
|
|
591
831
|
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
@@ -606,13 +846,22 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
606
846
|
if (!this.services.webSocketManager.isSocketClosed) {
|
|
607
847
|
this.services.webSocketManager.close(false, 'Unregistering the SDK');
|
|
608
848
|
}
|
|
849
|
+
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
850
|
+
this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
|
|
851
|
+
}
|
|
609
852
|
|
|
610
853
|
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
611
854
|
this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
|
|
612
855
|
}
|
|
613
856
|
|
|
614
857
|
// Clear any cached agent configuration
|
|
858
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
615
859
|
this.agentConfig = null;
|
|
860
|
+
this.updateWellnessSession();
|
|
861
|
+
|
|
862
|
+
if (wxAppPublishError) {
|
|
863
|
+
throw wxAppPublishError;
|
|
864
|
+
}
|
|
616
865
|
|
|
617
866
|
LoggerProxy.log('Deregistered successfully', {
|
|
618
867
|
module: CC_FILE,
|
|
@@ -676,6 +925,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
676
925
|
* ```
|
|
677
926
|
*/
|
|
678
927
|
public async getBuddyAgents(data: BuddyAgents): Promise<BuddyAgentsResponse> {
|
|
928
|
+
const {action, state, mediaType = 'telephony'} = data;
|
|
929
|
+
const buddyAgentState = state ?? (action === 'Transfer' ? AGENT_STATE_AVAILABLE : undefined);
|
|
930
|
+
|
|
679
931
|
LoggerProxy.info('Fetching buddy agents', {
|
|
680
932
|
module: CC_FILE,
|
|
681
933
|
method: METHODS.GET_BUDDY_AGENTS,
|
|
@@ -686,15 +938,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
686
938
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
|
|
687
939
|
]);
|
|
688
940
|
const resp = await this.services.agent.buddyAgents({
|
|
689
|
-
data: {
|
|
941
|
+
data: {
|
|
942
|
+
agentProfileId: this.agentConfig.agentProfileID,
|
|
943
|
+
mediaType,
|
|
944
|
+
...(buddyAgentState ? {state: buddyAgentState} : {}),
|
|
945
|
+
},
|
|
690
946
|
});
|
|
691
947
|
|
|
692
948
|
this.metricsManager.trackEvent(
|
|
693
949
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS,
|
|
694
950
|
{
|
|
695
951
|
...MetricsManager.getCommonTrackingFieldForAQMResponse(resp),
|
|
696
|
-
mediaType
|
|
697
|
-
buddyAgentState
|
|
952
|
+
mediaType,
|
|
953
|
+
buddyAgentState,
|
|
698
954
|
buddyAgentCount: resp.data.agentList.length,
|
|
699
955
|
},
|
|
700
956
|
['operational']
|
|
@@ -714,8 +970,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
714
970
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
|
|
715
971
|
{
|
|
716
972
|
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failureResp),
|
|
717
|
-
mediaType
|
|
718
|
-
buddyAgentState
|
|
973
|
+
mediaType,
|
|
974
|
+
buddyAgentState,
|
|
719
975
|
},
|
|
720
976
|
['operational']
|
|
721
977
|
);
|
|
@@ -731,7 +987,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
731
987
|
* @private
|
|
732
988
|
*/
|
|
733
989
|
private async connectWebsocket() {
|
|
734
|
-
LoggerProxy.
|
|
990
|
+
LoggerProxy.log('Connecting to websocket', {
|
|
735
991
|
module: CC_FILE,
|
|
736
992
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
737
993
|
});
|
|
@@ -751,19 +1007,49 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
751
1007
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
752
1008
|
});
|
|
753
1009
|
|
|
1010
|
+
const configFlags: ConfigFlags = {
|
|
1011
|
+
isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
|
|
1012
|
+
isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
|
|
1013
|
+
webRtcEnabled: this.agentConfig.webRtcEnabled,
|
|
1014
|
+
autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
|
|
1015
|
+
aiFeature: this.agentConfig.aiFeature,
|
|
1016
|
+
consultTransfer: {
|
|
1017
|
+
allowConsultToQueue: this.agentConfig.allowConsultToQueue,
|
|
1018
|
+
accessQueue: this.agentConfig.accessQueue,
|
|
1019
|
+
accessEntryPoint: this.agentConfig.accessEntryPoint,
|
|
1020
|
+
accessBuddyTeam: this.agentConfig.accessBuddyTeam,
|
|
1021
|
+
},
|
|
1022
|
+
enableWxBetterTogether: this.isWxBetterTogetherEnabled(),
|
|
1023
|
+
getWxAppUsersubPublished: () => this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1024
|
+
};
|
|
1025
|
+
this.taskManager.setConfigFlags(configFlags);
|
|
754
1026
|
// TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
|
|
755
1027
|
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
756
1028
|
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
1029
|
+
this.taskManager.setAgentName(this.agentConfig.agentName);
|
|
757
1030
|
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
758
1031
|
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
1032
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
1033
|
+
this.updateWellnessSession();
|
|
759
1034
|
|
|
760
|
-
|
|
1035
|
+
/**
|
|
1036
|
+
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
1037
|
+
* Extend this condition when additional AI RTD features are introduced.
|
|
1038
|
+
* Wellness break notifications also arrive on this websocket.
|
|
1039
|
+
*/
|
|
1040
|
+
if (
|
|
1041
|
+
this.agentConfig.aiFeature?.realtimeTranscripts?.enable ||
|
|
1042
|
+
this.agentConfig.aiFeature?.suggestedResponses?.enable ||
|
|
1043
|
+
this.agentConfig.aiFeature?.generatedSummaries?.wrapUpSummariesEnabled === true ||
|
|
1044
|
+
this.agentConfig.aiFeature?.generatedSummaries?.consultTransferSummariesEnabled === true ||
|
|
1045
|
+
this.agentConfig.isWellnessBreakEnabled
|
|
1046
|
+
) {
|
|
761
1047
|
LoggerProxy.info('Connecting to RTD websocket', {
|
|
762
1048
|
module: CC_FILE,
|
|
763
1049
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
764
1050
|
});
|
|
765
1051
|
|
|
766
|
-
|
|
1052
|
+
this.services.rtdWebSocketManager
|
|
767
1053
|
.initWebSocket({
|
|
768
1054
|
body: this.getConnectionConfig(),
|
|
769
1055
|
resource: RTD_SUBSCRIBE_API,
|
|
@@ -776,7 +1062,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
776
1062
|
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
777
1063
|
})
|
|
778
1064
|
.catch((error) => {
|
|
779
|
-
LoggerProxy.error(`Error
|
|
1065
|
+
LoggerProxy.error(`Error connecting to RTD websocket ${error}`, {
|
|
780
1066
|
module: CC_FILE,
|
|
781
1067
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
782
1068
|
});
|
|
@@ -785,7 +1071,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
785
1071
|
|
|
786
1072
|
if (
|
|
787
1073
|
this.agentConfig.webRtcEnabled &&
|
|
788
|
-
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
1074
|
+
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
|
|
1075
|
+
!this.isWebRTCRegistrationDisabled()
|
|
789
1076
|
) {
|
|
790
1077
|
try {
|
|
791
1078
|
await this.$webex.internal.mercury.connect();
|
|
@@ -799,14 +1086,28 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
799
1086
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
800
1087
|
});
|
|
801
1088
|
}
|
|
1089
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
1090
|
+
LoggerProxy.info(
|
|
1091
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
1092
|
+
{
|
|
1093
|
+
module: CC_FILE,
|
|
1094
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
1095
|
+
}
|
|
1096
|
+
);
|
|
802
1097
|
}
|
|
803
1098
|
|
|
804
1099
|
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
805
1100
|
await this.silentRelogin();
|
|
1101
|
+
} else {
|
|
1102
|
+
LoggerProxy.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
|
|
1103
|
+
module: CC_FILE,
|
|
1104
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
1105
|
+
});
|
|
806
1106
|
}
|
|
807
1107
|
|
|
808
1108
|
return this.agentConfig;
|
|
809
1109
|
} catch (error) {
|
|
1110
|
+
this.updateWellnessSession();
|
|
810
1111
|
LoggerProxy.error(`Error during register: ${error}`, {
|
|
811
1112
|
module: CC_FILE,
|
|
812
1113
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
@@ -845,36 +1146,48 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
845
1146
|
* ```
|
|
846
1147
|
*/
|
|
847
1148
|
public async stationLogin(data: AgentLogin): Promise<StationLoginResponse> {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
method: METHODS.STATION_LOGIN,
|
|
851
|
-
});
|
|
1149
|
+
const loggerContext = {module: CC_FILE, method: METHODS.STATION_LOGIN};
|
|
1150
|
+
|
|
852
1151
|
try {
|
|
1152
|
+
LoggerProxy.log(
|
|
1153
|
+
`Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`,
|
|
1154
|
+
loggerContext
|
|
1155
|
+
);
|
|
853
1156
|
this.metricsManager.timeEvent([
|
|
854
1157
|
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
855
1158
|
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
856
1159
|
]);
|
|
857
1160
|
|
|
858
1161
|
const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
|
|
859
|
-
if (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1162
|
+
if (data.loginOption === LoginOption.AGENT_DN) {
|
|
1163
|
+
LoggerProxy.log(
|
|
1164
|
+
`Validating dial number | dialPlanEnabled: ${!!this.agentConfig
|
|
1165
|
+
?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
1166
|
+
loggerContext
|
|
1167
|
+
);
|
|
1168
|
+
|
|
1169
|
+
if (!isValidDialNumber(data.dialNumber, dialPlanEntries)) {
|
|
1170
|
+
LoggerProxy.log(
|
|
1171
|
+
`Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
1172
|
+
loggerContext
|
|
1173
|
+
);
|
|
1174
|
+
const error = new Error('INVALID_DIAL_NUMBER');
|
|
1175
|
+
// @ts-ignore - adding custom key to the error object
|
|
1176
|
+
error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
|
|
866
1177
|
|
|
867
|
-
|
|
1178
|
+
throw error;
|
|
1179
|
+
}
|
|
868
1180
|
}
|
|
869
1181
|
|
|
1182
|
+
const deviceId = this.getDeviceId(data.loginOption, data.dialNumber);
|
|
870
1183
|
const loginResponse = await this.services.agent.stationLogin({
|
|
871
1184
|
data: {
|
|
872
|
-
|
|
873
|
-
|
|
1185
|
+
// BROWSER DN must be webrtc-{agentId} so VPOP/RTMS do not treat a bare UUID as PSTN.
|
|
1186
|
+
dialNumber: data.loginOption === LoginOption.BROWSER ? deviceId : data.dialNumber,
|
|
874
1187
|
teamId: data.teamId,
|
|
875
1188
|
deviceType: data.loginOption,
|
|
876
1189
|
isExtension: data.loginOption === LoginOption.EXTENSION,
|
|
877
|
-
deviceId
|
|
1190
|
+
deviceId,
|
|
878
1191
|
roles: [AGENT],
|
|
879
1192
|
teamName: EMPTY_STRING,
|
|
880
1193
|
siteId: EMPTY_STRING,
|
|
@@ -883,12 +1196,29 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
883
1196
|
},
|
|
884
1197
|
});
|
|
885
1198
|
|
|
886
|
-
if (
|
|
1199
|
+
if (
|
|
1200
|
+
this.agentConfig.webRtcEnabled &&
|
|
1201
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
1202
|
+
!this.isWebRTCRegistrationDisabled()
|
|
1203
|
+
) {
|
|
887
1204
|
await this.webCallingService.registerWebCallingLine();
|
|
1205
|
+
} else if (
|
|
1206
|
+
this.agentConfig.webRtcEnabled &&
|
|
1207
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
1208
|
+
this.isWebRTCRegistrationDisabled()
|
|
1209
|
+
) {
|
|
1210
|
+
LoggerProxy.info(
|
|
1211
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
1212
|
+
{
|
|
1213
|
+
module: CC_FILE,
|
|
1214
|
+
method: METHODS.STATION_LOGIN,
|
|
1215
|
+
}
|
|
1216
|
+
);
|
|
888
1217
|
}
|
|
889
1218
|
|
|
890
1219
|
const resp = await loginResponse;
|
|
891
1220
|
const {channelsMap, ...loginData} = resp.data;
|
|
1221
|
+
this.updateWellnessSession(resp.data.agentSessionId);
|
|
892
1222
|
this.agentConfig.currentTeamId = resp.data.teamId;
|
|
893
1223
|
const response = {
|
|
894
1224
|
...loginData,
|
|
@@ -902,6 +1232,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
902
1232
|
};
|
|
903
1233
|
|
|
904
1234
|
this.webCallingService.setLoginOption(data.loginOption);
|
|
1235
|
+
if (this.agentConfig) {
|
|
1236
|
+
this.agentConfig.deviceType = data.loginOption;
|
|
1237
|
+
if (
|
|
1238
|
+
data.loginOption === LoginOption.AGENT_DN ||
|
|
1239
|
+
data.loginOption === LoginOption.EXTENSION
|
|
1240
|
+
) {
|
|
1241
|
+
this.agentConfig.defaultDn = data.dialNumber;
|
|
1242
|
+
this.agentConfig.dn = data.dialNumber;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
905
1245
|
this.metricsManager.trackEvent(
|
|
906
1246
|
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
907
1247
|
{
|
|
@@ -923,6 +1263,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
923
1263
|
}
|
|
924
1264
|
);
|
|
925
1265
|
|
|
1266
|
+
await this.ensureWxAppPostStationLogin();
|
|
1267
|
+
|
|
926
1268
|
return response;
|
|
927
1269
|
} catch (error) {
|
|
928
1270
|
const failure = error.details as Failure;
|
|
@@ -991,6 +1333,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
991
1333
|
this.webCallingService.deregisterWebCallingLine();
|
|
992
1334
|
}
|
|
993
1335
|
|
|
1336
|
+
await this.teardownWxAppLocalState();
|
|
1337
|
+
this.updateWellnessSession();
|
|
1338
|
+
|
|
994
1339
|
LoggerProxy.log(`Agent station logout completed successfully`, {
|
|
995
1340
|
module: CC_FILE,
|
|
996
1341
|
method: METHODS.STATION_LOGOUT,
|
|
@@ -1091,7 +1436,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1091
1436
|
{
|
|
1092
1437
|
...MetricsManager.getCommonTrackingFieldForAQMResponse(agentStatusResponse),
|
|
1093
1438
|
requestedState: data.state,
|
|
1094
|
-
teamId: this.agentConfig?.teams[0]?.
|
|
1439
|
+
teamId: this.agentConfig?.teams[0]?.id ?? EMPTY_STRING,
|
|
1095
1440
|
status: agentStatusResponse.data?.status,
|
|
1096
1441
|
subStatus: agentStatusResponse.data?.subStatus,
|
|
1097
1442
|
auxCodeId: data.auxCodeId,
|
|
@@ -1127,6 +1472,77 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1127
1472
|
}
|
|
1128
1473
|
}
|
|
1129
1474
|
|
|
1475
|
+
/**
|
|
1476
|
+
* Returns the system-managed `WellbeingBreak` idle code for the current registration.
|
|
1477
|
+
* The value is fetched outside Desktop Profile filtering and cached until deregistration.
|
|
1478
|
+
* @returns The normalized system idle-code entity
|
|
1479
|
+
* @throws Structured Contact Center error when wellness is disabled or the code is unavailable
|
|
1480
|
+
* @example
|
|
1481
|
+
* const idleCode = await webex.cc.getWellbeingBreakIdleCode();
|
|
1482
|
+
* @public
|
|
1483
|
+
*/
|
|
1484
|
+
public async getWellbeingBreakIdleCode(): Promise<Entity> {
|
|
1485
|
+
const method = METHODS.GET_WELLBEING_BREAK_IDLE_CODE;
|
|
1486
|
+
try {
|
|
1487
|
+
if (!this.agentConfig?.isWellnessBreakEnabled) {
|
|
1488
|
+
const validationError = new Error('WELLNESS_BREAK_NOT_ENABLED') as GenericError;
|
|
1489
|
+
validationError.details = {
|
|
1490
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1491
|
+
orgId: this.$webex.credentials.getOrgId(),
|
|
1492
|
+
trackingId: EMPTY_STRING,
|
|
1493
|
+
data: {reason: 'WELLNESS_BREAK_NOT_ENABLED'},
|
|
1494
|
+
};
|
|
1495
|
+
throw validationError;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
if (this.wellbeingBreakIdleCode) {
|
|
1499
|
+
return this.wellbeingBreakIdleCode;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
this.metricsManager.timeEvent([
|
|
1503
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1504
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1505
|
+
]);
|
|
1506
|
+
const registrationGeneration = this.wellnessRegistrationGeneration;
|
|
1507
|
+
const orgId = this.$webex.credentials.getOrgId();
|
|
1508
|
+
const idleCode = await this.services.config.getWellbeingBreakIdleCode(orgId);
|
|
1509
|
+
|
|
1510
|
+
if (
|
|
1511
|
+
registrationGeneration !== this.wellnessRegistrationGeneration ||
|
|
1512
|
+
orgId !== this.$webex.credentials.getOrgId() ||
|
|
1513
|
+
!this.agentConfig?.isWellnessBreakEnabled
|
|
1514
|
+
) {
|
|
1515
|
+
const staleRegistrationError = new Error(
|
|
1516
|
+
'WELLNESS_BREAK_REGISTRATION_CHANGED'
|
|
1517
|
+
) as GenericError;
|
|
1518
|
+
staleRegistrationError.details = {
|
|
1519
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1520
|
+
orgId,
|
|
1521
|
+
trackingId: EMPTY_STRING,
|
|
1522
|
+
data: {reason: 'WELLNESS_BREAK_REGISTRATION_CHANGED'},
|
|
1523
|
+
};
|
|
1524
|
+
throw staleRegistrationError;
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
this.wellbeingBreakIdleCode = idleCode;
|
|
1528
|
+
this.metricsManager.trackEvent(
|
|
1529
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1530
|
+
{},
|
|
1531
|
+
['operational']
|
|
1532
|
+
);
|
|
1533
|
+
|
|
1534
|
+
return idleCode;
|
|
1535
|
+
} catch (error) {
|
|
1536
|
+
this.metricsManager.trackEvent(
|
|
1537
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1538
|
+
{reason: error instanceof Error ? error.message : UNKNOWN_ERROR},
|
|
1539
|
+
['operational']
|
|
1540
|
+
);
|
|
1541
|
+
const {error: detailedError} = getErrorDetails(error, method, CC_FILE);
|
|
1542
|
+
throw detailedError;
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1130
1546
|
/**
|
|
1131
1547
|
* Processes incoming websocket messages and emits corresponding events
|
|
1132
1548
|
* Handles various event types including agent state changes, login events,
|
|
@@ -1165,8 +1581,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1165
1581
|
]);
|
|
1166
1582
|
}
|
|
1167
1583
|
|
|
1584
|
+
if (this.handleWellnessBreakWebsocketPayload(eventData)) {
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1168
1588
|
switch (eventData.type) {
|
|
1169
1589
|
case CC_EVENTS.AGENT_MULTI_LOGIN:
|
|
1590
|
+
if (
|
|
1591
|
+
eventData.data?.type === 'AgentMultiLoginCloseSession' &&
|
|
1592
|
+
(!eventData.data.agentSessionId ||
|
|
1593
|
+
eventData.data.agentSessionId === this.currentAgentSessionId)
|
|
1594
|
+
) {
|
|
1595
|
+
this.updateWellnessSession();
|
|
1596
|
+
}
|
|
1170
1597
|
// @ts-ignore
|
|
1171
1598
|
this.emit(AGENT_EVENTS.AGENT_MULTI_LOGIN, eventData.data);
|
|
1172
1599
|
break;
|
|
@@ -1185,6 +1612,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1185
1612
|
switch (eventData.data.type) {
|
|
1186
1613
|
case CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS: {
|
|
1187
1614
|
const {channelsMap, ...loginData} = eventData.data;
|
|
1615
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1188
1616
|
const stationLoginData = {
|
|
1189
1617
|
...loginData,
|
|
1190
1618
|
mmProfile: {
|
|
@@ -1200,23 +1628,23 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1200
1628
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, stationLoginData);
|
|
1201
1629
|
break;
|
|
1202
1630
|
}
|
|
1203
|
-
case CC_EVENTS.AGENT_RELOGIN_SUCCESS:
|
|
1204
|
-
{
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1631
|
+
case CC_EVENTS.AGENT_RELOGIN_SUCCESS: {
|
|
1632
|
+
const {channelsMap, ...loginData} = eventData.data;
|
|
1633
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1634
|
+
const stationReLoginData = {
|
|
1635
|
+
...loginData,
|
|
1636
|
+
mmProfile: {
|
|
1637
|
+
chat: channelsMap.chat?.length,
|
|
1638
|
+
email: channelsMap.email?.length,
|
|
1639
|
+
social: channelsMap.social?.length,
|
|
1640
|
+
telephony: channelsMap.telephony?.length,
|
|
1641
|
+
},
|
|
1642
|
+
notifsTrackingId: eventData.trackingId,
|
|
1643
|
+
};
|
|
1644
|
+
// @ts-ignore
|
|
1645
|
+
this.emit(AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
|
|
1219
1646
|
break;
|
|
1647
|
+
}
|
|
1220
1648
|
case CC_EVENTS.AGENT_STATE_CHANGE_SUCCESS:
|
|
1221
1649
|
// @ts-ignore
|
|
1222
1650
|
this.emit(AGENT_EVENTS.AGENT_STATE_CHANGE_SUCCESS, eventData.data);
|
|
@@ -1230,6 +1658,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1230
1658
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_FAILED, eventData.data);
|
|
1231
1659
|
break;
|
|
1232
1660
|
case CC_EVENTS.AGENT_LOGOUT_SUCCESS:
|
|
1661
|
+
if (
|
|
1662
|
+
!eventData.data.agentSessionId ||
|
|
1663
|
+
eventData.data.agentSessionId === this.currentAgentSessionId
|
|
1664
|
+
) {
|
|
1665
|
+
this.updateWellnessSession();
|
|
1666
|
+
}
|
|
1233
1667
|
// @ts-ignore
|
|
1234
1668
|
this.emit(AGENT_EVENTS.AGENT_LOGOUT_SUCCESS, eventData.data);
|
|
1235
1669
|
break;
|
|
@@ -1247,87 +1681,700 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1247
1681
|
};
|
|
1248
1682
|
|
|
1249
1683
|
/**
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1684
|
+
* Checks whether Mobius/WebRTC registration should be skipped by config
|
|
1685
|
+
* @returns {boolean} True when browser WebRTC registration is disabled
|
|
1252
1686
|
* @private
|
|
1253
1687
|
*/
|
|
1254
|
-
private
|
|
1255
|
-
this
|
|
1688
|
+
private isWebRTCRegistrationDisabled(): boolean {
|
|
1689
|
+
return this.$config?.disableWebRTCRegistration === true;
|
|
1256
1690
|
}
|
|
1257
1691
|
|
|
1258
1692
|
/**
|
|
1259
|
-
*
|
|
1260
|
-
* @returns {SubscribeRequest} Connection configuration
|
|
1693
|
+
* Validates contact-center plugin configuration before service initialization
|
|
1261
1694
|
* @private
|
|
1262
1695
|
*/
|
|
1263
|
-
private
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1696
|
+
private validatePluginConfig(): void {
|
|
1697
|
+
if (
|
|
1698
|
+
this.$config?.disableWebRTCRegistration === true &&
|
|
1699
|
+
this.$config?.allowMultiLogin === false
|
|
1700
|
+
) {
|
|
1701
|
+
throw new Error(
|
|
1702
|
+
'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.'
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1270
1705
|
}
|
|
1271
1706
|
|
|
1272
1707
|
/**
|
|
1273
|
-
*
|
|
1274
|
-
* @
|
|
1708
|
+
* Whether wxApp thick-client answer is enabled for this SDK instance.
|
|
1709
|
+
* @public
|
|
1710
|
+
*/
|
|
1711
|
+
public isWxBetterTogetherEnabled(): boolean {
|
|
1712
|
+
return this.$config?.enableWxBetterTogether === true;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
/**
|
|
1716
|
+
* Current station login option (Extension, Dial Number, or Browser/Desktop).
|
|
1717
|
+
* Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
|
|
1275
1718
|
* @private
|
|
1276
1719
|
*/
|
|
1277
|
-
private
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1720
|
+
private getCurrentStationLoginOption(): LoginOption | undefined {
|
|
1721
|
+
return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
/**
|
|
1725
|
+
* wxApp thick-client answer is supported for Extension and Dial Number station login only —
|
|
1726
|
+
* not Browser/Desktop (WebRTC) login.
|
|
1727
|
+
* @private
|
|
1728
|
+
*/
|
|
1729
|
+
private assertWxAppStationLoginSupportedForEnable(): void {
|
|
1730
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1731
|
+
if (loginOption !== LoginOption.EXTENSION && loginOption !== LoginOption.AGENT_DN) {
|
|
1732
|
+
throw new Error(
|
|
1733
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.'
|
|
1289
1734
|
);
|
|
1290
|
-
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
1291
|
-
await this.silentRelogin();
|
|
1292
|
-
}
|
|
1293
1735
|
}
|
|
1294
1736
|
}
|
|
1295
1737
|
|
|
1296
1738
|
/**
|
|
1297
|
-
*
|
|
1739
|
+
* Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
|
|
1298
1740
|
* @private
|
|
1299
1741
|
*/
|
|
1300
|
-
private
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1742
|
+
private resetEnableWxBetterTogetherConfig(): void {
|
|
1743
|
+
if (this.$config) {
|
|
1744
|
+
this.$config.enableWxBetterTogether = false;
|
|
1745
|
+
}
|
|
1746
|
+
this.taskManager.applyEnableWxBetterTogether(false);
|
|
1747
|
+
}
|
|
1305
1748
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1749
|
+
/**
|
|
1750
|
+
* Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
|
|
1751
|
+
*
|
|
1752
|
+
* **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
|
|
1753
|
+
* Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
|
|
1754
|
+
* To change the flag after the SDK is initialized, re-init the SDK with the updated config.
|
|
1755
|
+
*
|
|
1756
|
+
* **Supported public read API:** {@link isWxBetterTogetherEnabled}.
|
|
1757
|
+
* **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
|
|
1758
|
+
* `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
|
|
1759
|
+
*
|
|
1760
|
+
* **Production lifecycle does not call this method.** Station login, silent relogin, logout,
|
|
1761
|
+
* and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
|
|
1762
|
+
* (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
|
|
1763
|
+
*
|
|
1764
|
+
* This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
|
|
1765
|
+
* It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
|
|
1766
|
+
* (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
|
|
1767
|
+
*
|
|
1768
|
+
* @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
|
|
1769
|
+
* @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
|
|
1770
|
+
* @internal
|
|
1771
|
+
* @see enableWxBetterTogether (CCPluginConfig init flag)
|
|
1772
|
+
* @see ensureWxAppPostStationLogin
|
|
1773
|
+
* @see teardownWxAppLocalState
|
|
1774
|
+
*/
|
|
1775
|
+
private async setManageWebexCallingInWxcc(enabled: boolean): Promise<void> {
|
|
1776
|
+
if (enabled && !this.$webex.internal.device?.userId) {
|
|
1777
|
+
throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
|
|
1778
|
+
}
|
|
1321
1779
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1780
|
+
if (enabled) {
|
|
1781
|
+
this.assertWxAppStationLoginSupportedForEnable();
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
const previousEnabled = this.isWxBetterTogetherEnabled();
|
|
1785
|
+
|
|
1786
|
+
if (this.$config) {
|
|
1787
|
+
this.$config.enableWxBetterTogether = enabled;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
this.taskManager.applyEnableWxBetterTogether(enabled);
|
|
1791
|
+
|
|
1792
|
+
let publishedEnable = false;
|
|
1793
|
+
|
|
1794
|
+
try {
|
|
1795
|
+
if (enabled) {
|
|
1796
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1797
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1798
|
+
publishedEnable = true;
|
|
1799
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1800
|
+
this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
|
|
1801
|
+
} else {
|
|
1802
|
+
await this.publishAnswerOnWebexCrossClientState(false);
|
|
1803
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1804
|
+
await this.releaseWxAppMercuryResources();
|
|
1805
|
+
}
|
|
1806
|
+
} catch (error) {
|
|
1807
|
+
if (publishedEnable) {
|
|
1808
|
+
await this.revertWxAppCrossClientPublish();
|
|
1809
|
+
} else if (enabled) {
|
|
1810
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1811
|
+
await this.releaseWxAppMercuryResources();
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
if (this.$config) {
|
|
1815
|
+
this.$config.enableWxBetterTogether = previousEnabled;
|
|
1816
|
+
}
|
|
1817
|
+
this.taskManager.applyEnableWxBetterTogether(previousEnabled);
|
|
1818
|
+
throw error;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
private getWxAppTelephonyTaskType(): 'Voice' | 'WebRTC' | 'unknown' {
|
|
1823
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1824
|
+
|
|
1825
|
+
if (loginOption === LoginOption.BROWSER) {
|
|
1826
|
+
return 'WebRTC';
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
if (loginOption === LoginOption.EXTENSION || loginOption === LoginOption.AGENT_DN) {
|
|
1830
|
+
return 'Voice';
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
return 'unknown';
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
private async ensureWxAppPostStationLogin(): Promise<void> {
|
|
1837
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1838
|
+
const flagEnabled = this.isWxBetterTogetherEnabled();
|
|
1839
|
+
const telephonyTaskType = this.getWxAppTelephonyTaskType();
|
|
1840
|
+
|
|
1841
|
+
if (loginOption === LoginOption.BROWSER) {
|
|
1842
|
+
if (flagEnabled) {
|
|
1843
|
+
logWxAppSessionReadiness({
|
|
1844
|
+
enableWxBetterTogether: true,
|
|
1845
|
+
loginOption,
|
|
1846
|
+
wxAppHooksApplied: false,
|
|
1847
|
+
usersubPublished: false,
|
|
1848
|
+
mercurySubscribed: false,
|
|
1849
|
+
telephonyTaskType,
|
|
1850
|
+
skipReason: 'unsupported_browser_login',
|
|
1851
|
+
});
|
|
1852
|
+
|
|
1853
|
+
this.metricsManager.trackEvent(
|
|
1854
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1855
|
+
{
|
|
1856
|
+
loginOption,
|
|
1857
|
+
enableWxBetterTogether: true,
|
|
1858
|
+
skipReason: 'unsupported_browser_login',
|
|
1859
|
+
},
|
|
1860
|
+
['operational', 'behavioral']
|
|
1861
|
+
);
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
let publishedEnable = false;
|
|
1868
|
+
let wxAppInitStage: 'mercury' | 'publish' = 'mercury';
|
|
1869
|
+
|
|
1870
|
+
try {
|
|
1871
|
+
if (flagEnabled) {
|
|
1872
|
+
this.metricsManager.timeEvent([
|
|
1873
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1874
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1875
|
+
]);
|
|
1876
|
+
|
|
1877
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1878
|
+
wxAppInitStage = 'publish';
|
|
1879
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1880
|
+
publishedEnable = true;
|
|
1881
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1882
|
+
this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
|
|
1883
|
+
|
|
1884
|
+
const mercurySubscribed = this.wxAppTelephonyMercurySync.isSubscribed();
|
|
1885
|
+
const usersubPublished = this.webexCrossClientService.isAnswerCallsStateActive();
|
|
1886
|
+
|
|
1887
|
+
logWxAppSessionReadiness({
|
|
1888
|
+
enableWxBetterTogether: true,
|
|
1889
|
+
loginOption,
|
|
1890
|
+
wxAppHooksApplied: true,
|
|
1891
|
+
usersubPublished,
|
|
1892
|
+
mercurySubscribed,
|
|
1893
|
+
telephonyTaskType,
|
|
1894
|
+
});
|
|
1895
|
+
|
|
1896
|
+
const sessionInitReady = mercurySubscribed && usersubPublished;
|
|
1897
|
+
|
|
1898
|
+
if (sessionInitReady) {
|
|
1899
|
+
this.metricsManager.trackEvent(
|
|
1900
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1901
|
+
{
|
|
1902
|
+
loginOption,
|
|
1903
|
+
enableWxBetterTogether: true,
|
|
1904
|
+
usersubPublished,
|
|
1905
|
+
mercurySubscribed,
|
|
1906
|
+
telephonyTaskType,
|
|
1907
|
+
},
|
|
1908
|
+
['operational', 'behavioral']
|
|
1909
|
+
);
|
|
1910
|
+
} else {
|
|
1911
|
+
this.metricsManager.trackEvent(
|
|
1912
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1913
|
+
{
|
|
1914
|
+
loginOption,
|
|
1915
|
+
enableWxBetterTogether: true,
|
|
1916
|
+
usersubPublished,
|
|
1917
|
+
mercurySubscribed,
|
|
1918
|
+
telephonyTaskType,
|
|
1919
|
+
skipReason: !usersubPublished ? 'usersub_not_published' : 'mercury_not_subscribed',
|
|
1920
|
+
},
|
|
1921
|
+
['operational', 'behavioral']
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
} else {
|
|
1925
|
+
await this.ensureWxAppDeviceRegistered();
|
|
1926
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
1927
|
+
this.webexCrossClientService.teardown();
|
|
1928
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1929
|
+
await this.releaseWxAppMercuryResources();
|
|
1930
|
+
|
|
1931
|
+
logWxAppSessionReadiness({
|
|
1932
|
+
enableWxBetterTogether: false,
|
|
1933
|
+
loginOption,
|
|
1934
|
+
wxAppHooksApplied: true,
|
|
1935
|
+
usersubPublished: false,
|
|
1936
|
+
mercurySubscribed: false,
|
|
1937
|
+
telephonyTaskType,
|
|
1938
|
+
skipReason: 'flag_disabled',
|
|
1939
|
+
});
|
|
1940
|
+
|
|
1941
|
+
this.metricsManager.trackEvent(
|
|
1942
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1943
|
+
{loginOption, enableWxBetterTogether: false, skipReason: 'flag_disabled'},
|
|
1944
|
+
['operational', 'behavioral']
|
|
1945
|
+
);
|
|
1946
|
+
}
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
LoggerProxy.error(`Failed to initialize wxApp post-station-login: ${error}`, {
|
|
1949
|
+
module: CC_FILE,
|
|
1950
|
+
method: METHODS.STATION_LOGIN,
|
|
1951
|
+
});
|
|
1952
|
+
|
|
1953
|
+
if (flagEnabled) {
|
|
1954
|
+
const sessionInitFailureReason =
|
|
1955
|
+
wxAppInitStage === 'mercury' ? 'mercury_subscribe_failed' : 'publish_failed';
|
|
1956
|
+
|
|
1957
|
+
this.metricsManager.trackEvent(
|
|
1958
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1959
|
+
{
|
|
1960
|
+
loginOption,
|
|
1961
|
+
enableWxBetterTogether: true,
|
|
1962
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1963
|
+
mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
|
|
1964
|
+
telephonyTaskType,
|
|
1965
|
+
skipReason: sessionInitFailureReason,
|
|
1966
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
1967
|
+
},
|
|
1968
|
+
['operational', 'behavioral']
|
|
1969
|
+
);
|
|
1970
|
+
|
|
1971
|
+
logWxAppSessionReadiness({
|
|
1972
|
+
enableWxBetterTogether: true,
|
|
1973
|
+
loginOption,
|
|
1974
|
+
wxAppHooksApplied: false,
|
|
1975
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1976
|
+
mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
|
|
1977
|
+
telephonyTaskType,
|
|
1978
|
+
skipReason: sessionInitFailureReason,
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
if (publishedEnable) {
|
|
1982
|
+
await this.revertWxAppCrossClientPublish();
|
|
1983
|
+
} else {
|
|
1984
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1985
|
+
await this.releaseWxAppMercuryResources();
|
|
1986
|
+
}
|
|
1987
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
1988
|
+
} else {
|
|
1989
|
+
this.scheduleForcedFalsePublishRetry();
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
private clearWxAppFalsePublishRetryTimer(): void {
|
|
1995
|
+
if (this.wxAppFalsePublishRetryTimer) {
|
|
1996
|
+
clearTimeout(this.wxAppFalsePublishRetryTimer);
|
|
1997
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
private scheduleForcedFalsePublishRetry(retryAttempt = 0): void {
|
|
2002
|
+
this.scheduleFalsePublishRetry(retryAttempt, {requireDisabled: true});
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
/**
|
|
2006
|
+
* Retries compensating false publish after rollback failure, regardless of enabled flag.
|
|
2007
|
+
* @private
|
|
2008
|
+
*/
|
|
2009
|
+
private scheduleCompensatingFalsePublishRetry(retryAttempt = 0): void {
|
|
2010
|
+
this.scheduleFalsePublishRetry(retryAttempt);
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
private scheduleFalsePublishRetry(
|
|
2014
|
+
retryAttempt = 0,
|
|
2015
|
+
options: {requireDisabled?: boolean} = {}
|
|
2016
|
+
): void {
|
|
2017
|
+
if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2026
|
+
|
|
2027
|
+
this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
|
|
2028
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
2029
|
+
|
|
2030
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
if (
|
|
2035
|
+
!options?.requireDisabled &&
|
|
2036
|
+
this.isWxBetterTogetherEnabled() &&
|
|
2037
|
+
this.webexCrossClientService.isAnswerCallsStateActive()
|
|
2038
|
+
) {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
try {
|
|
2043
|
+
await this.ensureWxAppDeviceRegistered();
|
|
2044
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
2045
|
+
this.webexCrossClientService.teardown();
|
|
2046
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2047
|
+
await this.releaseWxAppMercuryResources();
|
|
2048
|
+
} catch (retryError) {
|
|
2049
|
+
LoggerProxy.error(
|
|
2050
|
+
`Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`,
|
|
2051
|
+
{
|
|
2052
|
+
module: CC_FILE,
|
|
2053
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2054
|
+
}
|
|
2055
|
+
);
|
|
2056
|
+
this.scheduleFalsePublishRetry(retryAttempt + 1, options);
|
|
2057
|
+
}
|
|
2058
|
+
}, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
/**
|
|
2062
|
+
* Best-effort rollback when usersub true was published but wxApp setup did not complete.
|
|
2063
|
+
* @private
|
|
2064
|
+
*/
|
|
2065
|
+
private async revertWxAppCrossClientPublish(): Promise<void> {
|
|
2066
|
+
try {
|
|
2067
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
2068
|
+
} catch (error) {
|
|
2069
|
+
LoggerProxy.error(
|
|
2070
|
+
`Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`,
|
|
2071
|
+
{
|
|
2072
|
+
module: CC_FILE,
|
|
2073
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2074
|
+
}
|
|
2075
|
+
);
|
|
2076
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
this.webexCrossClientService.teardown();
|
|
2080
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2081
|
+
await this.releaseWxAppMercuryResources();
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
private async ensureWxAppDeviceRegistered(): Promise<void> {
|
|
2085
|
+
const device = this.$webex.internal.device;
|
|
2086
|
+
|
|
2087
|
+
if (device && !device.registered && device.register) {
|
|
2088
|
+
await device.register();
|
|
2089
|
+
this.wxAppDeviceRegisteredByCc = true;
|
|
2090
|
+
LoggerProxy.log('WxApp: device registered for cross-client publish', {
|
|
2091
|
+
module: CC_FILE,
|
|
2092
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
private async ensureWxAppMercuryConnected(): Promise<void> {
|
|
2098
|
+
const mercury = this.$webex.internal.mercury;
|
|
2099
|
+
|
|
2100
|
+
await this.ensureWxAppDeviceRegistered();
|
|
2101
|
+
|
|
2102
|
+
if (mercury && !mercury.connected) {
|
|
2103
|
+
await mercury.connect();
|
|
2104
|
+
this.wxAppMercuryConnectedByCc = true;
|
|
2105
|
+
LoggerProxy.log('WxApp mute sync: mercury connected', {
|
|
2106
|
+
module: CC_FILE,
|
|
2107
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
/**
|
|
2113
|
+
* Disconnects Mercury and unregisters the device when wxApp mute sync opened them.
|
|
2114
|
+
* @private
|
|
2115
|
+
*/
|
|
2116
|
+
private async releaseWxAppMercuryResources(): Promise<void> {
|
|
2117
|
+
const mercury = this.$webex.internal.mercury;
|
|
2118
|
+
const device = this.$webex.internal.device;
|
|
2119
|
+
|
|
2120
|
+
if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
|
|
2121
|
+
try {
|
|
2122
|
+
await mercury.disconnect();
|
|
2123
|
+
this.wxAppMercuryConnectedByCc = false;
|
|
2124
|
+
LoggerProxy.log('WxApp mute sync: mercury disconnected', {
|
|
2125
|
+
module: CC_FILE,
|
|
2126
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2127
|
+
});
|
|
2128
|
+
} catch (error) {
|
|
2129
|
+
LoggerProxy.error(`Failed to disconnect wxApp Mercury: ${error}`, {
|
|
2130
|
+
module: CC_FILE,
|
|
2131
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
|
|
2137
|
+
try {
|
|
2138
|
+
// @ts-ignore
|
|
2139
|
+
await device.unregister();
|
|
2140
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
2141
|
+
LoggerProxy.log('WxApp mute sync: device unregistered', {
|
|
2142
|
+
module: CC_FILE,
|
|
2143
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2144
|
+
});
|
|
2145
|
+
} catch (error) {
|
|
2146
|
+
LoggerProxy.error(`Failed to unregister wxApp device: ${error}`, {
|
|
2147
|
+
module: CC_FILE,
|
|
2148
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
2151
|
+
} else if (this.wxAppDeviceRegisteredByCc) {
|
|
2152
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
/**
|
|
2157
|
+
* Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
|
|
2158
|
+
* optional rethrow for deregister when callers need to surface usersub errors.
|
|
2159
|
+
* @private
|
|
2160
|
+
*/
|
|
2161
|
+
private async teardownWxAppLocalState(options?: {
|
|
2162
|
+
rethrowPublishError?: boolean;
|
|
2163
|
+
clearInitFlag?: boolean;
|
|
2164
|
+
}): Promise<{publishError?: unknown}> {
|
|
2165
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2166
|
+
let publishError: unknown;
|
|
2167
|
+
|
|
2168
|
+
try {
|
|
2169
|
+
const shouldForceFalsePublish =
|
|
2170
|
+
this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
|
|
2171
|
+
|
|
2172
|
+
await this.publishAnswerOnWebexCrossClientState(false, {
|
|
2173
|
+
force: shouldForceFalsePublish,
|
|
2174
|
+
});
|
|
2175
|
+
} catch (error) {
|
|
2176
|
+
publishError = error;
|
|
2177
|
+
LoggerProxy.error(
|
|
2178
|
+
`Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`,
|
|
2179
|
+
{
|
|
2180
|
+
module: CC_FILE,
|
|
2181
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2182
|
+
}
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
this.webexCrossClientService.teardown();
|
|
2187
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2188
|
+
await this.releaseWxAppMercuryResources();
|
|
2189
|
+
|
|
2190
|
+
if (options?.clearInitFlag) {
|
|
2191
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2194
|
+
if (publishError) {
|
|
2195
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
if (options?.rethrowPublishError && publishError) {
|
|
2199
|
+
throw publishError;
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2202
|
+
return {publishError: publishError ?? undefined};
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
private async ensureWxAppMercuryAndSubscribe(): Promise<void> {
|
|
2206
|
+
if (!this.isWxBetterTogetherEnabled()) {
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
const agentId = this.agentConfig?.agentId;
|
|
2215
|
+
if (!agentId) {
|
|
2216
|
+
LoggerProxy.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
|
|
2217
|
+
module: CC_FILE,
|
|
2218
|
+
method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
|
|
2219
|
+
});
|
|
2220
|
+
|
|
2221
|
+
this.metricsManager.trackEvent(
|
|
2222
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
2223
|
+
{error: 'agentId unavailable', mercurySubscribed: false},
|
|
2224
|
+
['operational', 'behavioral']
|
|
2225
|
+
);
|
|
2226
|
+
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
try {
|
|
2231
|
+
await this.ensureWxAppMercuryConnected();
|
|
2232
|
+
this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
|
|
2233
|
+
this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
|
|
2234
|
+
});
|
|
2235
|
+
} catch (error) {
|
|
2236
|
+
LoggerProxy.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
|
|
2237
|
+
module: CC_FILE,
|
|
2238
|
+
method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
|
|
2239
|
+
});
|
|
2240
|
+
this.metricsManager.trackEvent(
|
|
2241
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
2242
|
+
{
|
|
2243
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
2244
|
+
mercurySubscribed: false,
|
|
2245
|
+
},
|
|
2246
|
+
['operational', 'behavioral']
|
|
2247
|
+
);
|
|
2248
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2249
|
+
await this.releaseWxAppMercuryResources();
|
|
2250
|
+
throw error;
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2254
|
+
private async publishAnswerOnWebexCrossClientState(
|
|
2255
|
+
enable: boolean,
|
|
2256
|
+
options?: {force?: boolean}
|
|
2257
|
+
): Promise<void> {
|
|
2258
|
+
if (enable && !this.isWxBetterTogetherEnabled()) {
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
|
|
2263
|
+
return;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
const userId = this.$webex.internal.device?.userId;
|
|
2267
|
+
if (!userId) {
|
|
2268
|
+
if (enable && this.isWxBetterTogetherEnabled()) {
|
|
2269
|
+
this.metricsManager.trackEvent(
|
|
2270
|
+
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
|
|
2271
|
+
{
|
|
2272
|
+
enableWxBetterTogether: true,
|
|
2273
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
2274
|
+
skipReason: 'user_id_unavailable',
|
|
2275
|
+
error: 'User ID is unavailable for cross-client publish',
|
|
2276
|
+
},
|
|
2277
|
+
['operational', 'behavioral']
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {
|
|
2285
|
+
userId,
|
|
2286
|
+
trackPublishMetrics: true,
|
|
2287
|
+
});
|
|
2288
|
+
|
|
2289
|
+
if (enable) {
|
|
2290
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
/**
|
|
2295
|
+
* Initializes event listeners for the Contact Center service
|
|
2296
|
+
* Sets up handlers for connection state changes and other core events
|
|
2297
|
+
* @private
|
|
2298
|
+
*/
|
|
2299
|
+
private setupEventListeners() {
|
|
2300
|
+
this.services.connectionService.on('connectionLost', this.handleConnectionLost.bind(this));
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
/**
|
|
2304
|
+
* Returns the connection configuration
|
|
2305
|
+
* @returns {SubscribeRequest} Connection configuration
|
|
2306
|
+
* @private
|
|
2307
|
+
*/
|
|
2308
|
+
private getConnectionConfig(): SubscribeRequest {
|
|
2309
|
+
return {
|
|
2310
|
+
force: this.$config?.force ?? true,
|
|
2311
|
+
isKeepAliveEnabled: this.$config?.isKeepAliveEnabled ?? false,
|
|
2312
|
+
clientType: this.$config?.clientType ?? 'WebexCCSDK',
|
|
2313
|
+
allowMultiLogin: this.$config?.allowMultiLogin ?? true,
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
/**
|
|
2318
|
+
* Handles connection lost events and reconnection attempts
|
|
2319
|
+
* @param {ConnectionLostDetails} msg Connection lost details
|
|
2320
|
+
* @private
|
|
2321
|
+
*/
|
|
2322
|
+
private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
|
|
2323
|
+
if (msg.isConnectionLost) {
|
|
2324
|
+
// TODO: Emit an event saying connection is lost
|
|
2325
|
+
LoggerProxy.log('event=handleConnectionLost | Connection lost', {
|
|
2326
|
+
module: CC_FILE,
|
|
2327
|
+
method: METHODS.HANDLE_CONNECTION_LOST,
|
|
2328
|
+
});
|
|
2329
|
+
} else if (msg.isSocketReconnected) {
|
|
2330
|
+
// TODO: Emit an event saying connection is re-estabilished
|
|
2331
|
+
LoggerProxy.log(
|
|
2332
|
+
'event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin',
|
|
2333
|
+
{module: CC_FILE, method: METHODS.HANDLE_CONNECTION_LOST}
|
|
2334
|
+
);
|
|
2335
|
+
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
2336
|
+
await this.silentRelogin();
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
/**
|
|
2342
|
+
* Handles silent relogin after registration completion
|
|
2343
|
+
* @private
|
|
2344
|
+
*/
|
|
2345
|
+
private async silentRelogin(): Promise<void> {
|
|
2346
|
+
LoggerProxy.log('Starting silent relogin process', {
|
|
2347
|
+
module: CC_FILE,
|
|
2348
|
+
method: METHODS.SILENT_RELOGIN,
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
try {
|
|
2352
|
+
const reLoginResponse = await this.services.agent.reload();
|
|
2353
|
+
this.updateWellnessSession(reLoginResponse.data.agentSessionId);
|
|
2354
|
+
|
|
2355
|
+
const {
|
|
2356
|
+
agentId,
|
|
2357
|
+
lastStateChangeReason,
|
|
2358
|
+
deviceType,
|
|
2359
|
+
dn,
|
|
2360
|
+
lastStateChangeTimestamp,
|
|
2361
|
+
lastIdleCodeChangeTimestamp,
|
|
2362
|
+
} = reLoginResponse.data;
|
|
2363
|
+
let {auxCodeId} = reLoginResponse.data;
|
|
2364
|
+
this.agentConfig.lastStateChangeTimestamp = lastStateChangeTimestamp;
|
|
2365
|
+
this.agentConfig.lastIdleCodeChangeTimestamp = lastIdleCodeChangeTimestamp;
|
|
2366
|
+
this.agentConfig.currentTeamId = reLoginResponse.data.teamId;
|
|
2367
|
+
await this.handleDeviceType(deviceType as LoginOption, dn);
|
|
2368
|
+
|
|
2369
|
+
if (lastStateChangeReason === 'agent-wss-disconnect') {
|
|
2370
|
+
LoggerProxy.log(
|
|
2371
|
+
'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
|
|
2372
|
+
{module: CC_FILE, method: METHODS.SILENT_RELOGIN}
|
|
2373
|
+
);
|
|
2374
|
+
auxCodeId = AGENT_STATE_AVAILABLE_ID;
|
|
2375
|
+
const stateChangeData: StateChange = {
|
|
2376
|
+
state: AGENT_STATE_AVAILABLE,
|
|
2377
|
+
auxCodeId,
|
|
1331
2378
|
lastStateChangeReason,
|
|
1332
2379
|
agentId,
|
|
1333
2380
|
};
|
|
@@ -1349,8 +2396,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1349
2396
|
}
|
|
1350
2397
|
this.agentConfig.lastStateAuxCodeId = auxCodeId;
|
|
1351
2398
|
this.agentConfig.isAgentLoggedIn = true;
|
|
1352
|
-
|
|
1353
|
-
this.
|
|
2399
|
+
|
|
2400
|
+
await this.ensureWxAppPostStationLogin();
|
|
1354
2401
|
|
|
1355
2402
|
LoggerProxy.log(
|
|
1356
2403
|
`Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
|
|
@@ -1391,6 +2438,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1391
2438
|
this.agentConfig.deviceType = deviceType;
|
|
1392
2439
|
switch (deviceType) {
|
|
1393
2440
|
case LoginOption.BROWSER:
|
|
2441
|
+
if (this.isWebRTCRegistrationDisabled()) {
|
|
2442
|
+
LoggerProxy.info(
|
|
2443
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
2444
|
+
{
|
|
2445
|
+
module: CC_FILE,
|
|
2446
|
+
method: METHODS.HANDLE_DEVICE_TYPE,
|
|
2447
|
+
}
|
|
2448
|
+
);
|
|
2449
|
+
break;
|
|
2450
|
+
}
|
|
1394
2451
|
try {
|
|
1395
2452
|
await this.webCallingService.registerWebCallingLine();
|
|
1396
2453
|
} catch (error) {
|
|
@@ -1656,6 +2713,166 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1656
2713
|
}
|
|
1657
2714
|
}
|
|
1658
2715
|
|
|
2716
|
+
/**
|
|
2717
|
+
* Skips a campaign preview contact, requesting the next contact from the campaign.
|
|
2718
|
+
*
|
|
2719
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
2720
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
|
|
2721
|
+
* preview contact to move to the next contact in the campaign.
|
|
2722
|
+
*
|
|
2723
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
2724
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
2725
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
2726
|
+
*
|
|
2727
|
+
* @example
|
|
2728
|
+
* ```typescript
|
|
2729
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
2730
|
+
* const { interactionId } = task.data;
|
|
2731
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
2732
|
+
*
|
|
2733
|
+
* const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
|
|
2734
|
+
* });
|
|
2735
|
+
* ```
|
|
2736
|
+
*/
|
|
2737
|
+
public async skipPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
2738
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
2739
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
|
|
2740
|
+
LoggerProxy.warn('Skip action is disabled for this campaign preview contact', {
|
|
2741
|
+
module: CC_FILE,
|
|
2742
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
2743
|
+
interactionId: payload.interactionId,
|
|
2744
|
+
});
|
|
2745
|
+
throw new Error('Skip action is disabled for this campaign preview contact');
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
LoggerProxy.info('Skipping campaign preview contact', {
|
|
2749
|
+
module: CC_FILE,
|
|
2750
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
2751
|
+
});
|
|
2752
|
+
try {
|
|
2753
|
+
this.metricsManager.timeEvent([
|
|
2754
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
|
|
2755
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
2756
|
+
]);
|
|
2757
|
+
|
|
2758
|
+
const result = await this.services.dialer.skipPreviewContact({data: payload});
|
|
2759
|
+
|
|
2760
|
+
this.metricsManager.trackEvent(
|
|
2761
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
|
|
2762
|
+
{
|
|
2763
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
2764
|
+
interactionId: payload.interactionId,
|
|
2765
|
+
campaignId: payload.campaignId,
|
|
2766
|
+
},
|
|
2767
|
+
['behavioral', 'business', 'operational']
|
|
2768
|
+
);
|
|
2769
|
+
|
|
2770
|
+
LoggerProxy.log('Campaign preview contact skipped successfully', {
|
|
2771
|
+
module: CC_FILE,
|
|
2772
|
+
method: METHODS.SKIP_PREVIEW_CONTACT,
|
|
2773
|
+
trackingId: result.trackingId,
|
|
2774
|
+
interactionId: payload.interactionId,
|
|
2775
|
+
});
|
|
2776
|
+
|
|
2777
|
+
return result;
|
|
2778
|
+
} catch (error) {
|
|
2779
|
+
const failure = error.details as Failure;
|
|
2780
|
+
this.metricsManager.trackEvent(
|
|
2781
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
2782
|
+
{
|
|
2783
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
2784
|
+
interactionId: payload.interactionId,
|
|
2785
|
+
campaignId: payload.campaignId,
|
|
2786
|
+
},
|
|
2787
|
+
['behavioral', 'business', 'operational']
|
|
2788
|
+
);
|
|
2789
|
+
const {error: detailedError} = getErrorDetails(error, METHODS.SKIP_PREVIEW_CONTACT, CC_FILE);
|
|
2790
|
+
throw detailedError;
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
/**
|
|
2795
|
+
* Removes a campaign preview contact from the campaign list entirely.
|
|
2796
|
+
*
|
|
2797
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
2798
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
|
|
2799
|
+
* remove the preview contact to permanently take it out of the campaign contact list.
|
|
2800
|
+
*
|
|
2801
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
2802
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
2803
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
2804
|
+
*
|
|
2805
|
+
* @example
|
|
2806
|
+
* ```typescript
|
|
2807
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
2808
|
+
* const { interactionId } = task.data;
|
|
2809
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
2810
|
+
*
|
|
2811
|
+
* const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
|
|
2812
|
+
* });
|
|
2813
|
+
* ```
|
|
2814
|
+
*/
|
|
2815
|
+
public async removePreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
|
|
2816
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
2817
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
|
|
2818
|
+
LoggerProxy.warn('Remove action is disabled for this campaign preview contact', {
|
|
2819
|
+
module: CC_FILE,
|
|
2820
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2821
|
+
interactionId: payload.interactionId,
|
|
2822
|
+
});
|
|
2823
|
+
throw new Error('Remove action is disabled for this campaign preview contact');
|
|
2824
|
+
}
|
|
2825
|
+
|
|
2826
|
+
LoggerProxy.info('Removing campaign preview contact', {
|
|
2827
|
+
module: CC_FILE,
|
|
2828
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2829
|
+
});
|
|
2830
|
+
try {
|
|
2831
|
+
this.metricsManager.timeEvent([
|
|
2832
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
|
|
2833
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
2834
|
+
]);
|
|
2835
|
+
|
|
2836
|
+
const result = await this.services.dialer.removePreviewContact({data: payload});
|
|
2837
|
+
|
|
2838
|
+
this.metricsManager.trackEvent(
|
|
2839
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
|
|
2840
|
+
{
|
|
2841
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
|
|
2842
|
+
interactionId: payload.interactionId,
|
|
2843
|
+
campaignId: payload.campaignId,
|
|
2844
|
+
},
|
|
2845
|
+
['behavioral', 'business', 'operational']
|
|
2846
|
+
);
|
|
2847
|
+
|
|
2848
|
+
LoggerProxy.log('Campaign preview contact removed successfully', {
|
|
2849
|
+
module: CC_FILE,
|
|
2850
|
+
method: METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2851
|
+
trackingId: result.trackingId,
|
|
2852
|
+
interactionId: payload.interactionId,
|
|
2853
|
+
});
|
|
2854
|
+
|
|
2855
|
+
return result;
|
|
2856
|
+
} catch (error) {
|
|
2857
|
+
const failure = error.details as Failure;
|
|
2858
|
+
this.metricsManager.trackEvent(
|
|
2859
|
+
METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
2860
|
+
{
|
|
2861
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
2862
|
+
interactionId: payload.interactionId,
|
|
2863
|
+
campaignId: payload.campaignId,
|
|
2864
|
+
},
|
|
2865
|
+
['behavioral', 'business', 'operational']
|
|
2866
|
+
);
|
|
2867
|
+
const {error: detailedError} = getErrorDetails(
|
|
2868
|
+
error,
|
|
2869
|
+
METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2870
|
+
CC_FILE
|
|
2871
|
+
);
|
|
2872
|
+
throw detailedError;
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
|
|
1659
2876
|
/**
|
|
1660
2877
|
* Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
|
|
1661
2878
|
*
|
|
@@ -1900,8 +3117,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1900
3117
|
}
|
|
1901
3118
|
|
|
1902
3119
|
/**
|
|
1903
|
-
* Returns paginated entry points
|
|
1904
|
-
*
|
|
3120
|
+
* Returns paginated entry points using the EntryPoint service defaults.
|
|
3121
|
+
* Existing request parameters can override those defaults.
|
|
1905
3122
|
* @public
|
|
1906
3123
|
*/
|
|
1907
3124
|
public async getEntryPoints(
|
|
@@ -1911,8 +3128,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1911
3128
|
}
|
|
1912
3129
|
|
|
1913
3130
|
/**
|
|
1914
|
-
* Returns paginated contact service queues
|
|
1915
|
-
*
|
|
3131
|
+
* Returns paginated contact service queues using the Queue service defaults.
|
|
3132
|
+
* Existing request parameters can override those defaults.
|
|
1916
3133
|
* @public
|
|
1917
3134
|
*/
|
|
1918
3135
|
public async getQueues(
|