@webex/contact-center 3.11.0 → 3.12.0-mobius-socket.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cc.js +121 -28
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +5 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +13 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +9 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +173 -0
- package/dist/services/ApiAiAssistant.js.map +1 -0
- package/dist/services/agent/types.js.map +1 -1
- package/dist/services/config/Util.js +6 -2
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +12 -0
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +41 -2
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +19 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +27 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +28 -6
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +92 -17
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +20 -5
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/connection-service.js +3 -1
- package/dist/services/core/websocket/connection-service.js.map +1 -1
- package/dist/services/index.js +6 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/TaskManager.js +117 -24
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +16 -3
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +15 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +51 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/types.js +15 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +801 -0
- package/dist/types/config.d.ts +66 -0
- package/dist/types/constants.d.ts +50 -0
- package/dist/types/index.d.ts +184 -0
- package/dist/types/logger-proxy.d.ts +71 -0
- package/dist/types/metrics/MetricsManager.d.ts +223 -0
- package/dist/types/metrics/behavioral-events.d.ts +29 -0
- package/dist/types/metrics/constants.d.ts +161 -0
- package/dist/types/services/AddressBook.d.ts +74 -0
- package/dist/types/services/ApiAiAssistant.d.ts +31 -0
- package/dist/types/services/EntryPoint.d.ts +67 -0
- package/dist/types/services/Queue.d.ts +76 -0
- package/dist/types/services/WebCallingService.d.ts +1 -0
- package/dist/types/services/agent/index.d.ts +46 -0
- package/dist/types/services/agent/types.d.ts +413 -0
- package/dist/types/services/config/Util.d.ts +20 -0
- package/dist/types/services/config/constants.d.ts +249 -0
- package/dist/types/services/config/index.d.ts +177 -0
- package/dist/types/services/config/types.d.ts +1207 -0
- package/dist/types/services/constants.d.ts +110 -0
- package/dist/types/services/core/Err.d.ts +121 -0
- package/dist/types/services/core/GlobalTypes.d.ts +58 -0
- package/dist/types/services/core/Utils.d.ts +101 -0
- package/dist/types/services/core/WebexRequest.d.ts +22 -0
- package/dist/types/services/core/aqm-reqs.d.ts +65 -0
- package/dist/types/services/core/constants.d.ts +99 -0
- package/dist/types/services/core/types.d.ts +47 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +35 -0
- package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
- package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
- package/dist/types/services/core/websocket/types.d.ts +37 -0
- package/dist/types/services/index.d.ts +54 -0
- package/dist/types/services/task/AutoWrapup.d.ts +40 -0
- package/dist/types/services/task/TaskManager.d.ts +1 -0
- package/dist/types/services/task/TaskUtils.d.ts +92 -0
- package/dist/types/services/task/constants.d.ts +84 -0
- package/dist/types/services/task/contact.d.ts +69 -0
- package/dist/types/services/task/dialer.d.ts +43 -0
- package/dist/types/services/task/index.d.ts +650 -0
- package/dist/types/services/task/types.d.ts +1319 -0
- package/dist/types/types.d.ts +643 -0
- package/dist/types/utils/PageCache.d.ts +173 -0
- package/dist/types/webex-config.d.ts +53 -0
- package/dist/types/webex.d.ts +7 -0
- package/dist/types.js +14 -1
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +9 -9
- package/src/cc.ts +157 -30
- package/src/constants.ts +4 -0
- package/src/index.ts +1 -0
- package/src/metrics/behavioral-events.ts +14 -0
- package/src/metrics/constants.ts +11 -0
- package/src/services/ApiAiAssistant.ts +217 -0
- package/src/services/agent/types.ts +1 -1
- package/src/services/config/Util.ts +8 -0
- package/src/services/config/constants.ts +12 -0
- package/src/services/config/index.ts +45 -1
- package/src/services/config/types.ts +67 -0
- package/src/services/constants.ts +29 -0
- package/src/services/core/Err.ts +1 -0
- package/src/services/core/Utils.ts +32 -5
- package/src/services/core/aqm-reqs.ts +100 -22
- package/src/services/core/websocket/WebSocketManager.ts +21 -6
- package/src/services/core/websocket/connection-service.ts +5 -1
- package/src/services/index.ts +4 -0
- package/src/services/task/TaskManager.ts +174 -27
- package/src/services/task/TaskUtils.ts +12 -0
- package/src/services/task/constants.ts +16 -0
- package/src/services/task/dialer.ts +56 -1
- package/src/services/task/types.ts +24 -0
- package/src/types.ts +40 -1
- package/test/unit/spec/cc.ts +163 -23
- package/test/unit/spec/services/ApiAiAssistant.ts +115 -0
- package/test/unit/spec/services/config/index.ts +56 -0
- package/test/unit/spec/services/core/Utils.ts +63 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +82 -12
- package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
- package/test/unit/spec/services/task/TaskManager.ts +1119 -251
- package/test/unit/spec/services/task/dialer.ts +198 -112
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -5,9 +5,9 @@ import routingContact from './contact';
|
|
|
5
5
|
import WebCallingService from '../WebCallingService';
|
|
6
6
|
import {ITask, MEDIA_CHANNEL, TASK_EVENTS, TaskData, TaskId} from './types';
|
|
7
7
|
import {TASK_MANAGER_FILE} from '../../constants';
|
|
8
|
-
import {METHODS} from './constants';
|
|
8
|
+
import {METHODS, TRANSCRIPT_EVENT_MAP} from './constants';
|
|
9
9
|
import {CC_EVENTS, CC_TASK_EVENTS, WrapupData} from '../config/types';
|
|
10
|
-
import {LoginOption} from '../../types';
|
|
10
|
+
import {AIAssistantEventName, AIAssistantEventType, LoginOption} from '../../types';
|
|
11
11
|
import LoggerProxy from '../../logger-proxy';
|
|
12
12
|
import Task from '.';
|
|
13
13
|
import MetricsManager from '../../metrics/MetricsManager';
|
|
@@ -15,11 +15,13 @@ import {METRIC_EVENT_NAMES} from '../../metrics/constants';
|
|
|
15
15
|
import {
|
|
16
16
|
checkParticipantNotInInteraction,
|
|
17
17
|
getIsConferenceInProgress,
|
|
18
|
+
isCampaignPreviewReservation,
|
|
18
19
|
isParticipantInMainInteraction,
|
|
19
20
|
isPrimary,
|
|
20
21
|
isSecondaryEpDnAgent,
|
|
21
22
|
shouldAutoAnswerTask,
|
|
22
23
|
} from './TaskUtils';
|
|
24
|
+
import ApiAIAssistant from '../ApiAiAssistant';
|
|
23
25
|
|
|
24
26
|
/** @internal */
|
|
25
27
|
export default class TaskManager extends EventEmitter {
|
|
@@ -38,17 +40,20 @@ export default class TaskManager extends EventEmitter {
|
|
|
38
40
|
private wrapupData: WrapupData;
|
|
39
41
|
private agentId: string;
|
|
40
42
|
private webRtcEnabled: boolean;
|
|
43
|
+
private apiAIAssistant?: ApiAIAssistant;
|
|
41
44
|
/**
|
|
42
45
|
* @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
|
|
43
46
|
* @param webCallingService - Webrtc Service Layer
|
|
44
47
|
* @param webSocketManager - Websocket Manager to maintain websocket connection and keepalives
|
|
45
48
|
*/
|
|
46
49
|
constructor(
|
|
50
|
+
apiAIAssistant: ApiAIAssistant,
|
|
47
51
|
contact: ReturnType<typeof routingContact>,
|
|
48
52
|
webCallingService: WebCallingService,
|
|
49
53
|
webSocketManager: WebSocketManager
|
|
50
54
|
) {
|
|
51
55
|
super();
|
|
56
|
+
this.apiAIAssistant = apiAIAssistant;
|
|
52
57
|
this.contact = contact;
|
|
53
58
|
this.taskCollection = {};
|
|
54
59
|
this.webCallingService = webCallingService;
|
|
@@ -79,9 +84,39 @@ export default class TaskManager extends EventEmitter {
|
|
|
79
84
|
this.webRtcEnabled = webRtcEnabled;
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
public handleRealtimeWebsocketEvent(event: string) {
|
|
88
|
+
try {
|
|
89
|
+
const payload = JSON.parse(event);
|
|
90
|
+
|
|
91
|
+
const eventType = payload?.type || payload?.data?.notifType;
|
|
92
|
+
const interactionId = payload?.data?.data?.conversationId;
|
|
93
|
+
if (!eventType || !interactionId) return;
|
|
94
|
+
|
|
95
|
+
const task = this.taskCollection[interactionId];
|
|
96
|
+
if (!task) {
|
|
97
|
+
LoggerProxy.info(`Realtime transcription task not found`, {
|
|
98
|
+
module: TASK_MANAGER_FILE,
|
|
99
|
+
method: METHODS.HANDLE_REAL_TIME_WEBSOCKET_EVENT,
|
|
100
|
+
interactionId,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
task.emit(eventType, payload.data);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
LoggerProxy.error('Failed to parse RTD WebSocket message', {
|
|
109
|
+
module: TASK_MANAGER_FILE,
|
|
110
|
+
method: METHODS.HANDLE_REAL_TIME_WEBSOCKET_EVENT,
|
|
111
|
+
error,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
82
116
|
private handleIncomingWebCall = (call: ICall) => {
|
|
83
117
|
const currentTask = Object.values(this.taskCollection).find(
|
|
84
|
-
(task) =>
|
|
118
|
+
(task) =>
|
|
119
|
+
task.data.interaction.mediaType === 'telephony' && !isCampaignPreviewReservation(task)
|
|
85
120
|
);
|
|
86
121
|
|
|
87
122
|
if (currentTask) {
|
|
@@ -109,9 +144,11 @@ export default class TaskManager extends EventEmitter {
|
|
|
109
144
|
const payload = JSON.parse(event);
|
|
110
145
|
// Re-emit the task events to the task object
|
|
111
146
|
let task: ITask;
|
|
112
|
-
if (payload.data?.type) {
|
|
113
|
-
if (Object.values(CC_TASK_EVENTS).includes(payload.data.type)) {
|
|
114
|
-
task =
|
|
147
|
+
if (payload.data?.type || payload.type) {
|
|
148
|
+
if (Object.values(CC_TASK_EVENTS).includes(payload.data.type || payload.type)) {
|
|
149
|
+
task =
|
|
150
|
+
this.taskCollection[payload.data?.interactionId] ||
|
|
151
|
+
this.taskCollection[payload.data?.data?.conversationId];
|
|
115
152
|
}
|
|
116
153
|
LoggerProxy.info(`Handling task event ${payload.data?.type}`, {
|
|
117
154
|
module: TASK_MANAGER_FILE,
|
|
@@ -249,8 +286,21 @@ export default class TaskManager extends EventEmitter {
|
|
|
249
286
|
}
|
|
250
287
|
break;
|
|
251
288
|
case CC_EVENTS.AGENT_CONTACT_ASSIGNED:
|
|
252
|
-
|
|
253
|
-
task
|
|
289
|
+
// When a campaign preview contact is accepted, the assigned event may arrive
|
|
290
|
+
// with a new interactionId while the task is stored under the original
|
|
291
|
+
// reservationInteractionId. Fall back to that key so the task is found.
|
|
292
|
+
if (!task && payload.data.reservationInteractionId) {
|
|
293
|
+
task = this.taskCollection[payload.data.reservationInteractionId];
|
|
294
|
+
if (task) {
|
|
295
|
+
// Re-key the task under the new interaction ID and remove the old entry
|
|
296
|
+
delete this.taskCollection[payload.data.reservationInteractionId];
|
|
297
|
+
this.taskCollection[payload.data.interactionId] = task;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (task) {
|
|
301
|
+
task = this.updateTaskData(task, payload.data);
|
|
302
|
+
task.emit(TASK_EVENTS.TASK_ASSIGNED, task);
|
|
303
|
+
}
|
|
254
304
|
break;
|
|
255
305
|
case CC_EVENTS.AGENT_CONTACT_UNASSIGNED:
|
|
256
306
|
task = this.updateTaskData(task, {
|
|
@@ -262,6 +312,14 @@ export default class TaskManager extends EventEmitter {
|
|
|
262
312
|
case CC_EVENTS.AGENT_CONTACT_OFFER_RONA:
|
|
263
313
|
case CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED:
|
|
264
314
|
case CC_EVENTS.AGENT_INVITE_FAILED: {
|
|
315
|
+
LoggerProxy.warn(
|
|
316
|
+
`[DEBUG-CAMPAIGN-CLEAR] Task removal triggered by ${payload.data.type}, interactionId=${payload.data.interactionId}, taskType=${task?.data?.type}`,
|
|
317
|
+
{
|
|
318
|
+
module: TASK_MANAGER_FILE,
|
|
319
|
+
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
320
|
+
interactionId: payload.data.interactionId,
|
|
321
|
+
}
|
|
322
|
+
);
|
|
265
323
|
task = this.updateTaskData(task, payload.data);
|
|
266
324
|
|
|
267
325
|
const eventTypeToMetricMap: Record<string, keyof typeof METRIC_EVENT_NAMES> = {
|
|
@@ -287,11 +345,17 @@ export default class TaskManager extends EventEmitter {
|
|
|
287
345
|
case CC_EVENTS.CONTACT_ENDED:
|
|
288
346
|
// Update task data
|
|
289
347
|
if (task) {
|
|
348
|
+
LoggerProxy.warn(
|
|
349
|
+
`[DEBUG-CAMPAIGN-CLEAR] CONTACT_ENDED, interactionId=${payload.data.interactionId}, taskType=${task?.data?.type}, state=${task?.data?.interaction?.state}`,
|
|
350
|
+
{
|
|
351
|
+
module: TASK_MANAGER_FILE,
|
|
352
|
+
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
353
|
+
interactionId: payload.data.interactionId,
|
|
354
|
+
}
|
|
355
|
+
);
|
|
290
356
|
task = this.updateTaskData(task, {
|
|
291
357
|
...payload.data,
|
|
292
|
-
wrapUpRequired:
|
|
293
|
-
payload.data.interaction.state !== 'new' &&
|
|
294
|
-
!isSecondaryEpDnAgent(payload.data.interaction),
|
|
358
|
+
wrapUpRequired: payload.data.agentsPendingWrapUp?.includes(this.agentId) || false,
|
|
295
359
|
});
|
|
296
360
|
|
|
297
361
|
// Handle cleanup based on whether task should be deleted
|
|
@@ -300,6 +364,14 @@ export default class TaskManager extends EventEmitter {
|
|
|
300
364
|
task?.emit(TASK_EVENTS.TASK_END, task);
|
|
301
365
|
}
|
|
302
366
|
break;
|
|
367
|
+
case CC_EVENTS.CAMPAIGN_CONTACT_UPDATED:
|
|
368
|
+
// CampaignContactUpdated is a non-terminal event (intermediate update during accept).
|
|
369
|
+
// Only update the task data — do NOT remove the task or emit TASK_END.
|
|
370
|
+
// Task cleanup is handled by CONTACT_ENDED or other terminal events.
|
|
371
|
+
if (task) {
|
|
372
|
+
task = this.updateTaskData(task, payload.data);
|
|
373
|
+
}
|
|
374
|
+
break;
|
|
303
375
|
case CC_EVENTS.CONTACT_MERGED:
|
|
304
376
|
task = this.handleContactMerged(task, payload.data);
|
|
305
377
|
break;
|
|
@@ -482,12 +554,55 @@ export default class TaskManager extends EventEmitter {
|
|
|
482
554
|
task = this.updateTaskData(task, payload.data);
|
|
483
555
|
task.emit(TASK_EVENTS.TASK_POST_CALL_ACTIVITY, task);
|
|
484
556
|
break;
|
|
557
|
+
case CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION: {
|
|
558
|
+
// Campaign preview contact offered to agent
|
|
559
|
+
// Create a task in the collection so subsequent events (e.g. AGENT_CONTACT_ASSIGNED
|
|
560
|
+
// after acceptPreviewContact) can find and update it.
|
|
561
|
+
// Emit TASK_CAMPAIGN_PREVIEW_RESERVATION instead of TASK_INCOMING so the call
|
|
562
|
+
// does not ring out to the customer before the agent explicitly accepts the preview contact.
|
|
563
|
+
LoggerProxy.log('Campaign preview reservation received', {
|
|
564
|
+
module: TASK_MANAGER_FILE,
|
|
565
|
+
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
566
|
+
interactionId: payload.data.interactionId,
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
if (!this.taskCollection[payload.data.interactionId]) {
|
|
570
|
+
task = new Task(
|
|
571
|
+
this.contact,
|
|
572
|
+
this.webCallingService,
|
|
573
|
+
{
|
|
574
|
+
...payload.data,
|
|
575
|
+
wrapUpRequired: false,
|
|
576
|
+
isConferenceInProgress: false,
|
|
577
|
+
isAutoAnswering: false,
|
|
578
|
+
},
|
|
579
|
+
this.wrapupData,
|
|
580
|
+
this.agentId
|
|
581
|
+
);
|
|
582
|
+
this.taskCollection[payload.data.interactionId] = task;
|
|
583
|
+
} else {
|
|
584
|
+
task = this.updateTaskData(task, payload.data);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
this.emit(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
|
|
485
591
|
default:
|
|
486
592
|
break;
|
|
487
593
|
}
|
|
488
594
|
if (task) {
|
|
489
595
|
task.emit(payload.data.type, payload.data);
|
|
490
596
|
}
|
|
597
|
+
|
|
598
|
+
const transcriptInteractionId =
|
|
599
|
+
payload.data?.interactionId ||
|
|
600
|
+
payload.data?.data?.conversationId ||
|
|
601
|
+
task?.data?.interactionId;
|
|
602
|
+
|
|
603
|
+
if (TRANSCRIPT_EVENT_MAP[payload.data.type] && transcriptInteractionId) {
|
|
604
|
+
this.requestRealTimeTranscripts(payload.data.type, transcriptInteractionId);
|
|
605
|
+
}
|
|
491
606
|
}
|
|
492
607
|
});
|
|
493
608
|
}
|
|
@@ -662,23 +777,54 @@ export default class TaskManager extends EventEmitter {
|
|
|
662
777
|
|
|
663
778
|
const isOutdial = task.data.interaction.outboundType === 'OUTDIAL';
|
|
664
779
|
const isNew = task.data.interaction.state === 'new';
|
|
665
|
-
const needsWrapUp = task.data.agentsPendingWrapUp?.
|
|
780
|
+
const needsWrapUp = task.data.agentsPendingWrapUp?.includes(this.agentId) ?? false;
|
|
666
781
|
|
|
667
782
|
// For OUTDIAL: only remove if NOT terminated (user-declined, no wrap-up follows)
|
|
668
|
-
// If terminated, keep task for wrap-up flow (CONTACT_ENDED → AGENT_WRAPUP)
|
|
669
783
|
// For non-OUTDIAL: remove if state is 'new'
|
|
670
784
|
// Always remove if secondary EpDn agent
|
|
671
|
-
if (
|
|
785
|
+
if (
|
|
786
|
+
(isNew && !(isOutdial && needsWrapUp)) ||
|
|
787
|
+
isSecondaryEpDnAgent(task.data.interaction) ||
|
|
788
|
+
(!needsWrapUp && isOutdial) // For outdial tasks, needs wrap-up is false and state is "WRAPUP". We need to just remove the task.
|
|
789
|
+
) {
|
|
672
790
|
this.removeTaskFromCollection(task);
|
|
673
791
|
}
|
|
674
792
|
}
|
|
675
793
|
|
|
676
794
|
/**
|
|
677
|
-
*
|
|
795
|
+
* Sends transcript start/stop event based on the CC event type.
|
|
796
|
+
* Fire-and-forget; errors are logged but do not interrupt event processing.
|
|
678
797
|
*/
|
|
679
|
-
|
|
798
|
+
private requestRealTimeTranscripts(eventType: string, interactionId: string): void {
|
|
799
|
+
const action = TRANSCRIPT_EVENT_MAP[eventType];
|
|
800
|
+
if (
|
|
801
|
+
!action ||
|
|
802
|
+
!this.apiAIAssistant ||
|
|
803
|
+
this.apiAIAssistant.aiFeature?.realtimeTranscripts?.enable === false
|
|
804
|
+
)
|
|
805
|
+
return;
|
|
806
|
+
|
|
807
|
+
this.apiAIAssistant
|
|
808
|
+
.sendEvent(
|
|
809
|
+
this.agentId,
|
|
810
|
+
interactionId,
|
|
811
|
+
AIAssistantEventType.CUSTOM_EVENT,
|
|
812
|
+
AIAssistantEventName.GET_TRANSCRIPTS,
|
|
813
|
+
action
|
|
814
|
+
)
|
|
815
|
+
.catch((error) => {
|
|
816
|
+
LoggerProxy.error(`Failed to send transcript ${action} event`, {
|
|
817
|
+
module: TASK_MANAGER_FILE,
|
|
818
|
+
method: 'requestRealTimeTranscripts',
|
|
819
|
+
interactionId,
|
|
820
|
+
error,
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
public getTask(taskId: TaskId): ITask {
|
|
680
826
|
return this.taskCollection[taskId];
|
|
681
|
-
}
|
|
827
|
+
}
|
|
682
828
|
|
|
683
829
|
/**
|
|
684
830
|
* @param taskId - Unique identifier for each task
|
|
@@ -687,20 +833,21 @@ export default class TaskManager extends EventEmitter {
|
|
|
687
833
|
return this.taskCollection;
|
|
688
834
|
};
|
|
689
835
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
* @param webCallingService - Webrtc Service Layer
|
|
693
|
-
* @param webSocketManager - Websocket Manager to maintain websocket connection and keepalives
|
|
694
|
-
*/
|
|
695
|
-
public static getTaskManager = (
|
|
836
|
+
public static getTaskManager(
|
|
837
|
+
apiAIAssistant: ApiAIAssistant,
|
|
696
838
|
contact: ReturnType<typeof routingContact>,
|
|
697
839
|
webCallingService: WebCallingService,
|
|
698
840
|
webSocketManager: WebSocketManager
|
|
699
|
-
): TaskManager
|
|
700
|
-
if (!
|
|
701
|
-
|
|
841
|
+
): TaskManager {
|
|
842
|
+
if (!TaskManager.taskManager) {
|
|
843
|
+
TaskManager.taskManager = new TaskManager(
|
|
844
|
+
apiAIAssistant,
|
|
845
|
+
contact,
|
|
846
|
+
webCallingService,
|
|
847
|
+
webSocketManager
|
|
848
|
+
);
|
|
702
849
|
}
|
|
703
850
|
|
|
704
851
|
return this.taskManager;
|
|
705
|
-
}
|
|
852
|
+
}
|
|
706
853
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable import/prefer-default-export */
|
|
2
2
|
import {Interaction, ITask, TaskData, MEDIA_CHANNEL} from './types';
|
|
3
|
+
import {CC_EVENTS} from '../config/types';
|
|
3
4
|
import {OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE, OUTBOUND_TYPE} from '../../constants';
|
|
4
5
|
import {LoginOption} from '../../types';
|
|
5
6
|
|
|
@@ -219,3 +220,14 @@ export const shouldAutoAnswerTask = (
|
|
|
219
220
|
|
|
220
221
|
return false;
|
|
221
222
|
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Checks if a task is a campaign preview reservation that has not yet been accepted.
|
|
226
|
+
* Campaign preview tasks should not trigger incoming call handling until the agent
|
|
227
|
+
* explicitly accepts the preview contact.
|
|
228
|
+
* @param task - The task to check
|
|
229
|
+
* @returns true if the task is a pending campaign preview reservation, false otherwise
|
|
230
|
+
*/
|
|
231
|
+
export const isCampaignPreviewReservation = (task: ITask): boolean => {
|
|
232
|
+
return task?.data?.type === CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION;
|
|
233
|
+
};
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* @ignore
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {CC_EVENTS} from '../config/types';
|
|
8
|
+
|
|
7
9
|
export const TASK_MESSAGE_TYPE = 'RoutingMessage';
|
|
8
10
|
export const TASK_API = '/v1/tasks/';
|
|
9
11
|
export const HOLD = '/hold';
|
|
@@ -20,6 +22,10 @@ export const END = '/end';
|
|
|
20
22
|
export const CONSULT_CONFERENCE = '/consult/conference';
|
|
21
23
|
export const CONFERENCE_EXIT = '/conference/exit';
|
|
22
24
|
export const CONFERENCE_TRANSFER = '/conference/transfer';
|
|
25
|
+
export const DIALER_API = '/v1/dialer';
|
|
26
|
+
export const CAMPAIGN_PREVIEW_ACCEPT = '/accept';
|
|
27
|
+
/** 80-second timeout for accepting preview contact (outbound call setup takes longer than default 20s) */
|
|
28
|
+
export const TIMEOUT_PREVIEW_ACCEPT = 80000;
|
|
23
29
|
export const TASK_MANAGER_FILE = 'taskManager';
|
|
24
30
|
export const TASK_FILE = 'task';
|
|
25
31
|
|
|
@@ -69,6 +75,7 @@ export const METHODS = {
|
|
|
69
75
|
// TaskManager class methods
|
|
70
76
|
HANDLE_INCOMING_WEB_CALL: 'handleIncomingWebCall',
|
|
71
77
|
REGISTER_TASK_LISTENERS: 'registerTaskListeners',
|
|
78
|
+
HANDLE_REAL_TIME_WEBSOCKET_EVENT: 'handleRealtimeWebsocketEvent',
|
|
72
79
|
REMOVE_TASK_FROM_COLLECTION: 'removeTaskFromCollection',
|
|
73
80
|
HANDLE_TASK_CLEANUP: 'handleTaskCleanup',
|
|
74
81
|
GET_TASK: 'getTask',
|
|
@@ -77,3 +84,12 @@ export const METHODS = {
|
|
|
77
84
|
SETUP_AUTO_WRAPUP_TIMER: 'setupAutoWrapupTimer',
|
|
78
85
|
CANCEL_AUTO_WRAPUP_TIMER: 'cancelAutoWrapupTimer',
|
|
79
86
|
};
|
|
87
|
+
|
|
88
|
+
export const TRANSCRIPT_EVENT_MAP = {
|
|
89
|
+
[CC_EVENTS.AGENT_CONTACT_ASSIGNED]: 'START',
|
|
90
|
+
[CC_EVENTS.AGENT_CONSULTING]: 'START',
|
|
91
|
+
[CC_EVENTS.AGENT_CONSULT_CONFERENCED]: 'START',
|
|
92
|
+
[CC_EVENTS.AGENT_WRAPUP]: 'STOP',
|
|
93
|
+
[CC_EVENTS.AGENT_CONSULT_ENDED]: 'STOP',
|
|
94
|
+
[CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE]: 'STOP',
|
|
95
|
+
};
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import {CC_EVENTS} from '../config/types';
|
|
2
2
|
import {WCC_API_GATEWAY} from '../constants';
|
|
3
|
+
import {HTTP_METHODS} from '../../types';
|
|
3
4
|
import {createErrDetailsObject as err} from '../core/Utils';
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
TASK_MESSAGE_TYPE,
|
|
7
|
+
TASK_API,
|
|
8
|
+
DIALER_API,
|
|
9
|
+
CAMPAIGN_PREVIEW_ACCEPT,
|
|
10
|
+
TIMEOUT_PREVIEW_ACCEPT,
|
|
11
|
+
} from './constants';
|
|
5
12
|
import * as Contact from './types';
|
|
6
13
|
import AqmReqs from '../core/aqm-reqs';
|
|
7
14
|
|
|
@@ -48,5 +55,53 @@ export default function aqmDialer(aqm: AqmReqs) {
|
|
|
48
55
|
errId: 'Service.aqm.dialer.startOutdial',
|
|
49
56
|
},
|
|
50
57
|
})),
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Accepts a campaign preview contact, initiating the outbound call.
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} p - Parameters object.
|
|
63
|
+
* @param {Contact.PreviewContactPayload} p.data - Payload containing interactionId and campaignId.
|
|
64
|
+
* @returns {Promise<Contact.AgentContact>} A promise that resolves with agent contact on success.
|
|
65
|
+
*
|
|
66
|
+
* Emits:
|
|
67
|
+
* - `CC_EVENTS.AGENT_CONTACT_ASSIGNED` on success
|
|
68
|
+
* - `CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED` on failure
|
|
69
|
+
* @ignore
|
|
70
|
+
*/
|
|
71
|
+
acceptPreviewContact: aqm.req((p: {data: Contact.PreviewContactPayload}) => ({
|
|
72
|
+
url: `${DIALER_API}/campaign/${encodeURIComponent(p.data.campaignId)}/preview-task/${
|
|
73
|
+
p.data.interactionId
|
|
74
|
+
}${CAMPAIGN_PREVIEW_ACCEPT}`,
|
|
75
|
+
host: WCC_API_GATEWAY,
|
|
76
|
+
data: {},
|
|
77
|
+
method: HTTP_METHODS.POST,
|
|
78
|
+
timeout: TIMEOUT_PREVIEW_ACCEPT,
|
|
79
|
+
err,
|
|
80
|
+
notifSuccess: {
|
|
81
|
+
bind: {
|
|
82
|
+
type: TASK_MESSAGE_TYPE,
|
|
83
|
+
data: {
|
|
84
|
+
type: [CC_EVENTS.AGENT_CONTACT_ASSIGNED, CC_EVENTS.CONTACT_ENDED],
|
|
85
|
+
__typeMap: {
|
|
86
|
+
typeField: 'type',
|
|
87
|
+
conditions: {
|
|
88
|
+
[CC_EVENTS.AGENT_CONTACT_ASSIGNED]: {
|
|
89
|
+
reservationInteractionId: p.data.interactionId,
|
|
90
|
+
},
|
|
91
|
+
[CC_EVENTS.CONTACT_ENDED]: {interactionId: p.data.interactionId},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
msg: {} as Contact.AgentContact,
|
|
97
|
+
},
|
|
98
|
+
notifFail: {
|
|
99
|
+
bind: {
|
|
100
|
+
type: TASK_MESSAGE_TYPE,
|
|
101
|
+
data: {type: CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED, campaignId: p.data.campaignId},
|
|
102
|
+
},
|
|
103
|
+
errId: 'Service.aqm.dialer.acceptPreviewContact',
|
|
104
|
+
},
|
|
105
|
+
})),
|
|
51
106
|
};
|
|
52
107
|
}
|
|
@@ -542,6 +542,18 @@ export enum TASK_EVENTS {
|
|
|
542
542
|
* ```
|
|
543
543
|
*/
|
|
544
544
|
TASK_POST_CALL_ACTIVITY = 'task:postCallActivity',
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Triggered when a campaign preview contact is offered to the agent
|
|
548
|
+
* @example
|
|
549
|
+
* ```typescript
|
|
550
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, (data: AgentContact) => {
|
|
551
|
+
* console.log('Campaign preview contact received:', data.interactionId);
|
|
552
|
+
* // Handle campaign preview reservation
|
|
553
|
+
* });
|
|
554
|
+
* ```
|
|
555
|
+
*/
|
|
556
|
+
TASK_CAMPAIGN_PREVIEW_RESERVATION = 'task:campaignPreviewReservation',
|
|
545
557
|
}
|
|
546
558
|
|
|
547
559
|
/**
|
|
@@ -1117,6 +1129,18 @@ export type DialerPayload = {
|
|
|
1117
1129
|
origin: string;
|
|
1118
1130
|
};
|
|
1119
1131
|
|
|
1132
|
+
/**
|
|
1133
|
+
* Payload for campaign preview contact operations (accept, skip, remove)
|
|
1134
|
+
* @public
|
|
1135
|
+
*/
|
|
1136
|
+
export type PreviewContactPayload = {
|
|
1137
|
+
/** The interaction ID from the campaign reservation */
|
|
1138
|
+
interactionId: string;
|
|
1139
|
+
/** The campaign name (not a UUID). Available from the reservation event at
|
|
1140
|
+
* `task.data.interaction.callProcessingDetails.campaignId` or `task.data.campaignId`. */
|
|
1141
|
+
campaignId: string;
|
|
1142
|
+
};
|
|
1143
|
+
|
|
1120
1144
|
/**
|
|
1121
1145
|
* Data structure for cleaning up contact resources
|
|
1122
1146
|
* @public
|
package/src/types.ts
CHANGED
|
@@ -295,6 +295,8 @@ interface IWebexInternal {
|
|
|
295
295
|
get: (service: string) => string;
|
|
296
296
|
/** Wait for service catalog to be loaded */
|
|
297
297
|
waitForCatalog: (service: string) => Promise<void>;
|
|
298
|
+
/** Check if current environment is INT (integration) */
|
|
299
|
+
isIntegrationEnvironment: () => boolean;
|
|
298
300
|
/** Host catalog for service discovery */
|
|
299
301
|
_hostCatalog: Record<string, ServiceHost[]>;
|
|
300
302
|
/** Service URLs cache */
|
|
@@ -539,7 +541,8 @@ export type RequestBody =
|
|
|
539
541
|
| Contact.ConsultTransferPayLoad
|
|
540
542
|
| Contact.cancelCtq
|
|
541
543
|
| Contact.WrapupPayLoad
|
|
542
|
-
| Contact.DialerPayload
|
|
544
|
+
| Contact.DialerPayload
|
|
545
|
+
| Contact.PreviewContactPayload;
|
|
543
546
|
|
|
544
547
|
/**
|
|
545
548
|
* Represents the options to fetch buddy agents for the logged in agent.
|
|
@@ -817,3 +820,39 @@ export type BuddyAgentsResponse = Agent.BuddyAgentsSuccess | Error;
|
|
|
817
820
|
* function handleUpdateDeviceType(resp: UpdateDeviceTypeResponse) { ... }
|
|
818
821
|
*/
|
|
819
822
|
export type UpdateDeviceTypeResponse = Agent.DeviceTypeUpdateSuccess | Error;
|
|
823
|
+
|
|
824
|
+
export type TranscriptAction = 'START' | 'STOP';
|
|
825
|
+
|
|
826
|
+
export const AIAssistantEventType = {
|
|
827
|
+
CUSTOM_EVENT: 'CUSTOM_EVENT',
|
|
828
|
+
CTI_EVENT: 'CTI_EVENT',
|
|
829
|
+
} as const;
|
|
830
|
+
|
|
831
|
+
export type AIAssistantEventType = Enum<typeof AIAssistantEventType>;
|
|
832
|
+
|
|
833
|
+
export const AIAssistantEventName = {
|
|
834
|
+
GET_TRANSCRIPTS: 'GET_TRANSCRIPTS',
|
|
835
|
+
GET_MID_CALL_SUMMARY: 'GET_MID_CALL_SUMMARY',
|
|
836
|
+
GET_POST_CALL_SUMMARY: 'GET_POST_CALL_SUMMARY',
|
|
837
|
+
MID_CALL_SUMMARY_RESPONSE: 'MID_CALL_SUMMARY_RESPONSE',
|
|
838
|
+
POST_CALL_SUMMARY_RESPONSE: 'POST_CALL_SUMMARY_RESPONSE',
|
|
839
|
+
SUGGESTED_RESPONSES_DIGITAL: 'SUGGESTED_RESPONSES_DIGITAL',
|
|
840
|
+
} as const;
|
|
841
|
+
|
|
842
|
+
export type AIAssistantEventName = Enum<typeof AIAssistantEventName>;
|
|
843
|
+
|
|
844
|
+
export type TranscriptMessage = {
|
|
845
|
+
role: string;
|
|
846
|
+
content: string;
|
|
847
|
+
messageId: string;
|
|
848
|
+
publishTimestamp: number;
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
export type HistoricTranscriptsResponse = {
|
|
852
|
+
orgId: string;
|
|
853
|
+
agentId: string;
|
|
854
|
+
conversationId: string | null;
|
|
855
|
+
interactionId: string;
|
|
856
|
+
source: string;
|
|
857
|
+
data: TranscriptMessage[];
|
|
858
|
+
};
|