@webex/contact-center 3.10.0-next.9 → 3.10.0-set-bitrate.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 +2 -1
- package/dist/cc.js.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.js.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/logger-proxy.js.map +1 -1
- package/dist/metrics/MetricsManager.js +2 -1
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +12 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +4 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +2 -3
- package/dist/services/AddressBook.js.map +1 -1
- package/dist/services/EntryPoint.js +2 -3
- package/dist/services/EntryPoint.js.map +1 -1
- package/dist/services/Queue.js +2 -3
- package/dist/services/Queue.js.map +1 -1
- package/dist/services/WebCallingService.js +1 -1
- package/dist/services/WebCallingService.js.map +1 -1
- package/dist/services/agent/index.js +1 -2
- package/dist/services/agent/index.js.map +1 -1
- package/dist/services/agent/types.js +10 -0
- package/dist/services/agent/types.js.map +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +1 -1
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/GlobalTypes.js.map +1 -1
- package/dist/services/core/Utils.js +2 -3
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/WebexRequest.js +1 -2
- package/dist/services/core/WebexRequest.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +2 -3
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/constants.js.map +1 -1
- package/dist/services/core/types.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +1 -2
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/connection-service.js +1 -1
- package/dist/services/core/websocket/connection-service.js.map +1 -1
- package/dist/services/core/websocket/keepalive.worker.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/AutoWrapup.js +1 -1
- package/dist/services/task/AutoWrapup.js.map +1 -1
- package/dist/services/task/TaskManager.js +121 -33
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +90 -1
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +3 -1
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +0 -2
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/index.js +1 -1
- package/dist/services/task/index.js.map +1 -1
- package/dist/services/task/types.js +375 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/metrics/constants.d.ts +4 -0
- package/dist/types/services/task/TaskUtils.d.ts +42 -0
- package/dist/types/services/task/constants.d.ts +2 -0
- package/dist/types/services/task/types.d.ts +32 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +1 -1
- package/dist/utils/PageCache.js.map +1 -1
- package/dist/webex-config.js.map +1 -1
- package/dist/webex.js +2 -2
- package/dist/webex.js.map +1 -1
- package/package.json +9 -9
- package/src/cc.ts +1 -0
- package/src/metrics/behavioral-events.ts +12 -0
- package/src/metrics/constants.ts +4 -0
- package/src/services/task/TaskManager.ts +135 -22
- package/src/services/task/TaskUtils.ts +109 -1
- package/src/services/task/constants.ts +2 -0
- package/src/services/task/types.ts +34 -0
- package/test/unit/spec/cc.ts +1 -0
- package/test/unit/spec/metrics/behavioral-events.ts +14 -0
- package/test/unit/spec/services/task/TaskManager.ts +378 -4
- package/test/unit/spec/services/task/TaskUtils.ts +305 -3
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
isParticipantInMainInteraction,
|
|
19
19
|
isPrimary,
|
|
20
20
|
isSecondaryEpDnAgent,
|
|
21
|
+
shouldAutoAnswerTask,
|
|
21
22
|
} from './TaskUtils';
|
|
22
23
|
|
|
23
24
|
/** @internal */
|
|
@@ -36,6 +37,7 @@ export default class TaskManager extends EventEmitter {
|
|
|
36
37
|
private static taskManager;
|
|
37
38
|
private wrapupData: WrapupData;
|
|
38
39
|
private agentId: string;
|
|
40
|
+
private webRtcEnabled: boolean;
|
|
39
41
|
/**
|
|
40
42
|
* @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
|
|
41
43
|
* @param webCallingService - Webrtc Service Layer
|
|
@@ -73,6 +75,10 @@ export default class TaskManager extends EventEmitter {
|
|
|
73
75
|
return this.agentId;
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
public setWebRtcEnabled(webRtcEnabled: boolean) {
|
|
79
|
+
this.webRtcEnabled = webRtcEnabled;
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
private handleIncomingWebCall = (call: ICall) => {
|
|
77
83
|
const currentTask = Object.values(this.taskCollection).find(
|
|
78
84
|
(task) => task.data.interaction.mediaType === 'telephony'
|
|
@@ -130,6 +136,14 @@ export default class TaskManager extends EventEmitter {
|
|
|
130
136
|
interactionId: payload.data.interactionId,
|
|
131
137
|
});
|
|
132
138
|
|
|
139
|
+
// Check if auto-answer should happen for this task
|
|
140
|
+
const shouldAutoAnswer = shouldAutoAnswerTask(
|
|
141
|
+
payload.data,
|
|
142
|
+
this.agentId,
|
|
143
|
+
this.webCallingService.loginOption,
|
|
144
|
+
this.webRtcEnabled
|
|
145
|
+
);
|
|
146
|
+
|
|
133
147
|
task = new Task(
|
|
134
148
|
this.contact,
|
|
135
149
|
this.webCallingService,
|
|
@@ -138,6 +152,7 @@ export default class TaskManager extends EventEmitter {
|
|
|
138
152
|
wrapUpRequired:
|
|
139
153
|
payload.data.interaction?.participants?.[this.agentId]?.isWrapUp || false,
|
|
140
154
|
isConferenceInProgress: getIsConferenceInProgress(payload.data),
|
|
155
|
+
isAutoAnswering: shouldAutoAnswer, // Set flag before emitting
|
|
141
156
|
},
|
|
142
157
|
this.wrapupData,
|
|
143
158
|
this.agentId
|
|
@@ -169,13 +184,22 @@ export default class TaskManager extends EventEmitter {
|
|
|
169
184
|
}
|
|
170
185
|
break;
|
|
171
186
|
|
|
172
|
-
case CC_EVENTS.AGENT_CONTACT_RESERVED:
|
|
187
|
+
case CC_EVENTS.AGENT_CONTACT_RESERVED: {
|
|
188
|
+
// Check if auto-answer should happen for this task
|
|
189
|
+
const shouldAutoAnswerReserved = shouldAutoAnswerTask(
|
|
190
|
+
payload.data,
|
|
191
|
+
this.agentId,
|
|
192
|
+
this.webCallingService.loginOption,
|
|
193
|
+
this.webRtcEnabled
|
|
194
|
+
);
|
|
195
|
+
|
|
173
196
|
task = new Task(
|
|
174
197
|
this.contact,
|
|
175
198
|
this.webCallingService,
|
|
176
199
|
{
|
|
177
200
|
...payload.data,
|
|
178
201
|
isConsulted: false,
|
|
202
|
+
isAutoAnswering: shouldAutoAnswerReserved, // Set flag before emitting
|
|
179
203
|
},
|
|
180
204
|
this.wrapupData,
|
|
181
205
|
this.agentId
|
|
@@ -190,6 +214,7 @@ export default class TaskManager extends EventEmitter {
|
|
|
190
214
|
this.emit(TASK_EVENTS.TASK_INCOMING, task);
|
|
191
215
|
}
|
|
192
216
|
break;
|
|
217
|
+
}
|
|
193
218
|
case CC_EVENTS.AGENT_OFFER_CONTACT:
|
|
194
219
|
// We don't have to emit any event here since this will be result of promise.
|
|
195
220
|
task = this.updateTaskData(task, payload.data);
|
|
@@ -199,17 +224,29 @@ export default class TaskManager extends EventEmitter {
|
|
|
199
224
|
interactionId: payload.data?.interactionId,
|
|
200
225
|
});
|
|
201
226
|
this.emit(TASK_EVENTS.TASK_OFFER_CONTACT, task);
|
|
227
|
+
|
|
228
|
+
// Handle auto-answer for offer contact
|
|
229
|
+
this.handleAutoAnswer(task);
|
|
202
230
|
break;
|
|
203
231
|
case CC_EVENTS.AGENT_OUTBOUND_FAILED:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.
|
|
232
|
+
if (task) {
|
|
233
|
+
task = this.updateTaskData(task, payload.data);
|
|
234
|
+
this.metricsManager.trackEvent(
|
|
235
|
+
METRIC_EVENT_NAMES.TASK_OUTDIAL_FAILED,
|
|
236
|
+
{
|
|
237
|
+
...MetricsManager.getCommonTrackingFieldForAQMResponse(payload.data),
|
|
238
|
+
taskId: payload.data.interactionId,
|
|
239
|
+
reason: payload.data.reasonCode || payload.data.reason,
|
|
240
|
+
},
|
|
241
|
+
['behavioral', 'operational']
|
|
242
|
+
);
|
|
243
|
+
LoggerProxy.log(`Agent outbound failed for task`, {
|
|
244
|
+
module: TASK_MANAGER_FILE,
|
|
245
|
+
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
246
|
+
interactionId: payload.data.interactionId,
|
|
247
|
+
});
|
|
248
|
+
task.emit(TASK_EVENTS.TASK_OUTDIAL_FAILED, payload.data.reason ?? 'UNKNOWN_REASON');
|
|
207
249
|
}
|
|
208
|
-
LoggerProxy.log(`Agent outbound failed for task`, {
|
|
209
|
-
module: TASK_MANAGER_FILE,
|
|
210
|
-
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
211
|
-
interactionId: payload.data?.interactionId,
|
|
212
|
-
});
|
|
213
250
|
break;
|
|
214
251
|
case CC_EVENTS.AGENT_CONTACT_ASSIGNED:
|
|
215
252
|
task = this.updateTaskData(task, payload.data);
|
|
@@ -249,18 +286,19 @@ export default class TaskManager extends EventEmitter {
|
|
|
249
286
|
}
|
|
250
287
|
case CC_EVENTS.CONTACT_ENDED:
|
|
251
288
|
// Update task data
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// Handle cleanup based on whether task should be deleted
|
|
260
|
-
this.handleTaskCleanup(task);
|
|
289
|
+
if (task) {
|
|
290
|
+
task = this.updateTaskData(task, {
|
|
291
|
+
...payload.data,
|
|
292
|
+
wrapUpRequired:
|
|
293
|
+
payload.data.interaction.state !== 'new' &&
|
|
294
|
+
!isSecondaryEpDnAgent(payload.data.interaction),
|
|
295
|
+
});
|
|
261
296
|
|
|
262
|
-
|
|
297
|
+
// Handle cleanup based on whether task should be deleted
|
|
298
|
+
this.handleTaskCleanup(task);
|
|
263
299
|
|
|
300
|
+
task?.emit(TASK_EVENTS.TASK_END, task);
|
|
301
|
+
}
|
|
264
302
|
break;
|
|
265
303
|
case CC_EVENTS.CONTACT_MERGED:
|
|
266
304
|
task = this.handleContactMerged(task, payload.data);
|
|
@@ -301,6 +339,9 @@ export default class TaskManager extends EventEmitter {
|
|
|
301
339
|
isConsulted: true, // This ensures that the task is marked as us being requested for a consult
|
|
302
340
|
});
|
|
303
341
|
task.emit(TASK_EVENTS.TASK_OFFER_CONSULT, task);
|
|
342
|
+
|
|
343
|
+
// Handle auto-answer for consult offer
|
|
344
|
+
this.handleAutoAnswer(task);
|
|
304
345
|
break;
|
|
305
346
|
case CC_EVENTS.AGENT_CONSULTING:
|
|
306
347
|
// Received when agent is in an active consult state
|
|
@@ -538,6 +579,72 @@ export default class TaskManager extends EventEmitter {
|
|
|
538
579
|
}
|
|
539
580
|
}
|
|
540
581
|
|
|
582
|
+
/**
|
|
583
|
+
* Handles auto-answer logic for incoming tasks
|
|
584
|
+
* Automatically accepts tasks when isAutoAnswering flag is set
|
|
585
|
+
* The flag is set during task creation based on:
|
|
586
|
+
* 1. WebRTC calls with auto-answer enabled in agent profile
|
|
587
|
+
* 2. Agent-initiated WebRTC outdial calls
|
|
588
|
+
* 3. Agent-initiated digital outbound (Email/SMS) without previous transfers
|
|
589
|
+
*
|
|
590
|
+
* @param task - The task to auto-answer
|
|
591
|
+
* @private
|
|
592
|
+
*/
|
|
593
|
+
private async handleAutoAnswer(task: ITask): Promise<void> {
|
|
594
|
+
if (!task || !task.data || !task.data.isAutoAnswering) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
LoggerProxy.info(`Auto-answering task`, {
|
|
599
|
+
module: TASK_MANAGER_FILE,
|
|
600
|
+
method: 'handleAutoAnswer',
|
|
601
|
+
interactionId: task.data.interactionId,
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
await task.accept();
|
|
606
|
+
LoggerProxy.info(`Task auto-answered successfully`, {
|
|
607
|
+
module: TASK_MANAGER_FILE,
|
|
608
|
+
method: 'handleAutoAnswer',
|
|
609
|
+
interactionId: task.data.interactionId,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// Track successful auto-answer
|
|
613
|
+
this.metricsManager.trackEvent(
|
|
614
|
+
METRIC_EVENT_NAMES.TASK_AUTO_ANSWER_SUCCESS,
|
|
615
|
+
{
|
|
616
|
+
taskId: task.data.interactionId,
|
|
617
|
+
mediaType: task.data.interaction.mediaType,
|
|
618
|
+
isAutoAnswered: true,
|
|
619
|
+
},
|
|
620
|
+
['behavioral', 'operational']
|
|
621
|
+
);
|
|
622
|
+
// Emit task:autoAnswered event for widgets/UI to react
|
|
623
|
+
task.emit(TASK_EVENTS.TASK_AUTO_ANSWERED, task);
|
|
624
|
+
} catch (error) {
|
|
625
|
+
// Reset isAutoAnswering flag on failure
|
|
626
|
+
task.updateTaskData({...task.data, isAutoAnswering: false});
|
|
627
|
+
LoggerProxy.error(`Failed to auto-answer task`, {
|
|
628
|
+
module: TASK_MANAGER_FILE,
|
|
629
|
+
method: 'handleAutoAnswer',
|
|
630
|
+
interactionId: task.data.interactionId,
|
|
631
|
+
error,
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// Track auto-answer failure
|
|
635
|
+
this.metricsManager.trackEvent(
|
|
636
|
+
METRIC_EVENT_NAMES.TASK_AUTO_ANSWER_FAILED,
|
|
637
|
+
{
|
|
638
|
+
taskId: task.data.interactionId,
|
|
639
|
+
mediaType: task.data.interaction.mediaType,
|
|
640
|
+
error: error?.message || 'Unknown error',
|
|
641
|
+
isAutoAnswered: false,
|
|
642
|
+
},
|
|
643
|
+
['behavioral', 'operational']
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
541
648
|
/**
|
|
542
649
|
* Handles cleanup of task resources including Desktop/WebRTC call cleanup and task removal
|
|
543
650
|
* @param task - The task to clean up
|
|
@@ -553,9 +660,15 @@ export default class TaskManager extends EventEmitter {
|
|
|
553
660
|
this.webCallingService.cleanUpCall();
|
|
554
661
|
}
|
|
555
662
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
663
|
+
const isOutdial = task.data.interaction.outboundType === 'OUTDIAL';
|
|
664
|
+
const isNew = task.data.interaction.state === 'new';
|
|
665
|
+
const needsWrapUp = task.data.agentsPendingWrapUp?.length > 0;
|
|
666
|
+
|
|
667
|
+
// 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
|
+
// For non-OUTDIAL: remove if state is 'new'
|
|
670
|
+
// Always remove if secondary EpDn agent
|
|
671
|
+
if ((isNew && !(isOutdial && needsWrapUp)) || isSecondaryEpDnAgent(task.data.interaction)) {
|
|
559
672
|
this.removeTaskFromCollection(task);
|
|
560
673
|
}
|
|
561
674
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable import/prefer-default-export */
|
|
2
|
-
import {Interaction, ITask, TaskData} from './types';
|
|
2
|
+
import {Interaction, ITask, TaskData, MEDIA_CHANNEL} from './types';
|
|
3
|
+
import {OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE, OUTBOUND_TYPE} from '../../constants';
|
|
4
|
+
import {LoginOption} from '../../types';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Determines if the given agent is the primary agent (owner) of the task
|
|
@@ -111,3 +113,109 @@ export const isSecondaryEpDnAgent = (interaction: Interaction): boolean => {
|
|
|
111
113
|
|
|
112
114
|
return interaction.mediaType === 'telephony' && isSecondaryAgent(interaction);
|
|
113
115
|
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Checks if auto-answer is enabled for the agent participant
|
|
119
|
+
* @param interaction - The interaction object
|
|
120
|
+
* @param agentId - Current agent ID
|
|
121
|
+
* @returns true if auto-answer is enabled, false otherwise
|
|
122
|
+
*/
|
|
123
|
+
export const isAutoAnswerEnabled = (interaction: Interaction, agentId: string): boolean => {
|
|
124
|
+
return interaction.participants?.[agentId]?.autoAnswerEnabled === true;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Checks if the interaction is a WebRTC call eligible for auto-answer
|
|
129
|
+
* @param interaction - The interaction object
|
|
130
|
+
* @param loginOption - The agent's login option (BROWSER, AGENT_DN, etc.)
|
|
131
|
+
* @param webRtcEnabled - Whether WebRTC is enabled for the agent
|
|
132
|
+
* @returns true if this is a WebRTC call, false otherwise
|
|
133
|
+
*/
|
|
134
|
+
export const isWebRTCCall = (
|
|
135
|
+
interaction: Interaction,
|
|
136
|
+
loginOption: string,
|
|
137
|
+
webRtcEnabled: boolean
|
|
138
|
+
): boolean => {
|
|
139
|
+
return (
|
|
140
|
+
webRtcEnabled &&
|
|
141
|
+
loginOption === LoginOption.BROWSER &&
|
|
142
|
+
interaction.mediaType === OUTDIAL_MEDIA_TYPE
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Checks if the interaction is a digital outbound (Email/SMS)
|
|
148
|
+
* @param interaction - The interaction object
|
|
149
|
+
* @returns true if this is a digital outbound, false otherwise
|
|
150
|
+
*/
|
|
151
|
+
export const isDigitalOutbound = (interaction: Interaction): boolean => {
|
|
152
|
+
return (
|
|
153
|
+
interaction.contactDirection?.type === OUTDIAL_DIRECTION &&
|
|
154
|
+
interaction.outboundType === OUTBOUND_TYPE &&
|
|
155
|
+
(interaction.mediaChannel === MEDIA_CHANNEL.EMAIL ||
|
|
156
|
+
interaction.mediaChannel === MEDIA_CHANNEL.SMS)
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Checks if the outdial was initiated by the current agent
|
|
162
|
+
* @param interaction - The interaction object
|
|
163
|
+
* @param agentId - Current agent ID
|
|
164
|
+
* @returns true if agent initiated the outdial, false otherwise
|
|
165
|
+
*/
|
|
166
|
+
export const hasAgentInitiatedOutdial = (interaction: Interaction, agentId: string): boolean => {
|
|
167
|
+
return (
|
|
168
|
+
interaction.contactDirection?.type === OUTDIAL_DIRECTION &&
|
|
169
|
+
interaction.outboundType === OUTBOUND_TYPE &&
|
|
170
|
+
interaction.callProcessingDetails?.outdialAgentId === agentId &&
|
|
171
|
+
interaction.owner === agentId &&
|
|
172
|
+
!interaction.callProcessingDetails?.BLIND_TRANSFER_IN_PROGRESS
|
|
173
|
+
);
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Determines if a task should be auto-answered based on interaction data
|
|
178
|
+
* Auto-answer logic handles:
|
|
179
|
+
* 1. WebRTC calls with auto-answer enabled in agent profile
|
|
180
|
+
* 2. Agent-initiated WebRTC outdial calls
|
|
181
|
+
* 3. Agent-initiated digital outbound (Email/SMS) without previous transfers
|
|
182
|
+
*
|
|
183
|
+
* @param taskData - The task data
|
|
184
|
+
* @param agentId - Current agent ID
|
|
185
|
+
* @param loginOption - Agent's login option
|
|
186
|
+
* @param webRtcEnabled - Whether WebRTC is enabled for the agent
|
|
187
|
+
* @returns true if task should be auto-answered, false otherwise
|
|
188
|
+
*/
|
|
189
|
+
export const shouldAutoAnswerTask = (
|
|
190
|
+
taskData: TaskData,
|
|
191
|
+
agentId: string,
|
|
192
|
+
loginOption: string,
|
|
193
|
+
webRtcEnabled: boolean
|
|
194
|
+
): boolean => {
|
|
195
|
+
const {interaction} = taskData;
|
|
196
|
+
|
|
197
|
+
if (!interaction || !agentId) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Check if auto-answer is enabled for this agent
|
|
202
|
+
const autoAnswerEnabled = isAutoAnswerEnabled(interaction, agentId);
|
|
203
|
+
|
|
204
|
+
// Check if this is an agent-initiated outdial
|
|
205
|
+
const agentInitiatedOutdial = hasAgentInitiatedOutdial(interaction, agentId);
|
|
206
|
+
|
|
207
|
+
// WebRTC telephony calls
|
|
208
|
+
if (isWebRTCCall(interaction, loginOption, webRtcEnabled)) {
|
|
209
|
+
return autoAnswerEnabled || agentInitiatedOutdial;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Digital outbound (Email/SMS)
|
|
213
|
+
if (isDigitalOutbound(interaction) && agentInitiatedOutdial) {
|
|
214
|
+
// Don't auto-answer if task has been transferred (has previous vteams)
|
|
215
|
+
const hasPreviousVteams = interaction.previousVTeams && interaction.previousVTeams.length > 0;
|
|
216
|
+
|
|
217
|
+
return !hasPreviousVteams;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return false;
|
|
221
|
+
};
|
|
@@ -34,6 +34,8 @@ export const PRESERVED_TASK_DATA_FIELDS = {
|
|
|
34
34
|
WRAP_UP_REQUIRED: 'wrapUpRequired',
|
|
35
35
|
/** Indicates if a conference is currently in progress (2+ active agents) */
|
|
36
36
|
IS_CONFERENCE_IN_PROGRESS: 'isConferenceInProgress',
|
|
37
|
+
/** Indicates if auto-answer is in progress for this task */
|
|
38
|
+
IS_AUTO_ANSWERING: 'isAutoAnswering',
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
/**
|
|
@@ -347,6 +347,18 @@ export enum TASK_EVENTS {
|
|
|
347
347
|
*/
|
|
348
348
|
TASK_REJECT = 'task:rejected',
|
|
349
349
|
|
|
350
|
+
/**
|
|
351
|
+
* Triggered when an outdial call fails
|
|
352
|
+
* @example
|
|
353
|
+
* ```typescript
|
|
354
|
+
* task.on(TASK_EVENTS.TASK_OUTDIAL_FAILED, (reason: string) => {
|
|
355
|
+
* console.log('Outdial failed:', reason);
|
|
356
|
+
* // Handle outdial failure
|
|
357
|
+
* });
|
|
358
|
+
* ```
|
|
359
|
+
*/
|
|
360
|
+
TASK_OUTDIAL_FAILED = 'task:outdialFailed',
|
|
361
|
+
|
|
350
362
|
/**
|
|
351
363
|
* Triggered when a task is populated with data
|
|
352
364
|
* @example
|
|
@@ -371,6 +383,22 @@ export enum TASK_EVENTS {
|
|
|
371
383
|
*/
|
|
372
384
|
TASK_OFFER_CONTACT = 'task:offerContact',
|
|
373
385
|
|
|
386
|
+
/**
|
|
387
|
+
* Triggered when a task has been successfully auto-answered
|
|
388
|
+
* This event is emitted after the SDK automatically accepts a task due to:
|
|
389
|
+
* - WebRTC calls with auto-answer enabled
|
|
390
|
+
* - Agent-initiated outdial calls
|
|
391
|
+
* - Other auto-answer scenarios
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* task.on(TASK_EVENTS.TASK_AUTO_ANSWERED, (task: ITask) => {
|
|
395
|
+
* console.log('Task auto-answered:', task.data.interactionId);
|
|
396
|
+
* // Update UI - enable cancel button, etc.
|
|
397
|
+
* });
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
TASK_AUTO_ANSWERED = 'task:autoAnswered',
|
|
401
|
+
|
|
374
402
|
/**
|
|
375
403
|
* Triggered when a conference is being established
|
|
376
404
|
* @example
|
|
@@ -660,6 +688,8 @@ export type Interaction = {
|
|
|
660
688
|
BLIND_TRANSFER_IN_PROGRESS?: boolean;
|
|
661
689
|
/** Desktop view configuration for Flow Control */
|
|
662
690
|
fcDesktopView?: string;
|
|
691
|
+
/** Agent ID who initiated the outdial call */
|
|
692
|
+
outdialAgentId?: string;
|
|
663
693
|
};
|
|
664
694
|
/** Main interaction identifier for related interactions */
|
|
665
695
|
mainInteractionId?: string;
|
|
@@ -785,6 +815,10 @@ export type TaskData = {
|
|
|
785
815
|
reservedAgentChannelId?: string;
|
|
786
816
|
/** Indicates if wrap-up is required for this task */
|
|
787
817
|
wrapUpRequired?: boolean;
|
|
818
|
+
/** Indicates if auto-answer is in progress for this task */
|
|
819
|
+
isAutoAnswering?: boolean;
|
|
820
|
+
/** Indicates if wrap-up is required for this task */
|
|
821
|
+
agentsPendingWrapUp?: string[];
|
|
788
822
|
};
|
|
789
823
|
|
|
790
824
|
/**
|
package/test/unit/spec/cc.ts
CHANGED
|
@@ -152,6 +152,20 @@ describe('metrics/behavioral-events', () => {
|
|
|
152
152
|
verb: 'fail',
|
|
153
153
|
});
|
|
154
154
|
|
|
155
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_AUTO_ANSWER_SUCCESS)).toEqual({
|
|
156
|
+
product,
|
|
157
|
+
agent: 'user',
|
|
158
|
+
target: 'task_auto_answer',
|
|
159
|
+
verb: 'complete',
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(getEventTaxonomy(METRIC_EVENT_NAMES.TASK_AUTO_ANSWER_FAILED)).toEqual({
|
|
163
|
+
product,
|
|
164
|
+
agent: 'user',
|
|
165
|
+
target: 'task_auto_answer',
|
|
166
|
+
verb: 'fail',
|
|
167
|
+
});
|
|
168
|
+
|
|
155
169
|
expect(getEventTaxonomy('' as METRIC_EVENT_NAMES)).toEqual(undefined);
|
|
156
170
|
});
|
|
157
171
|
});
|