@webex/contact-center 3.12.0-next.7 → 3.12.0-next.71
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 +212 -23
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/constants.js.map +1 -1
- package/dist/metrics/behavioral-events.js +26 -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/config/Util.js +1 -1
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +1 -1
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/types.js +4 -0
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +37 -9
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/task/TaskManager.js +94 -8
- package/dist/services/task/TaskManager.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/dialer.js +78 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/index.js +7 -2
- package/dist/services/task/index.js.map +1 -1
- package/dist/services/task/types.js +56 -0
- package/dist/services/task/types.js.map +1 -1
- package/dist/types/cc.d.ts +61 -0
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/metrics/constants.d.ts +4 -0
- package/dist/types/services/config/types.d.ts +10 -1
- package/dist/types/services/core/Err.d.ts +4 -0
- package/dist/types/services/core/Utils.d.ts +10 -3
- package/dist/types/services/task/constants.d.ts +2 -0
- package/dist/types/services/task/dialer.d.ts +30 -0
- package/dist/types/services/task/types.d.ts +65 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +1 -1
- package/package.json +9 -9
- package/src/cc.ts +270 -24
- package/src/constants.ts +2 -0
- package/src/metrics/behavioral-events.ts +28 -0
- package/src/metrics/constants.ts +4 -0
- package/src/services/config/Util.ts +1 -1
- package/src/services/config/constants.ts +1 -1
- package/src/services/config/types.ts +6 -1
- package/src/services/core/Err.ts +2 -0
- package/src/services/core/Utils.ts +43 -8
- package/src/services/task/TaskManager.ts +106 -22
- package/src/services/task/constants.ts +2 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/index.ts +7 -2
- package/src/services/task/types.ts +70 -0
- package/src/types.ts +2 -0
- package/test/unit/spec/cc.ts +210 -20
- package/test/unit/spec/services/config/index.ts +3 -3
- package/test/unit/spec/services/core/Utils.ts +90 -7
- package/test/unit/spec/services/task/TaskManager.ts +252 -7
- package/test/unit/spec/services/task/dialer.ts +190 -0
- package/test/unit/spec/services/task/index.ts +21 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -449,6 +449,34 @@ const eventTaxonomyMap: Record<string, BehavioralEventTaxonomy> = {
|
|
|
449
449
|
target: 'campaign_preview_accept',
|
|
450
450
|
verb: 'fail',
|
|
451
451
|
},
|
|
452
|
+
|
|
453
|
+
// Campaign Preview Skip API Events
|
|
454
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS]: {
|
|
455
|
+
product,
|
|
456
|
+
agent: 'user',
|
|
457
|
+
target: 'campaign_preview_skip',
|
|
458
|
+
verb: 'complete',
|
|
459
|
+
},
|
|
460
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED]: {
|
|
461
|
+
product,
|
|
462
|
+
agent: 'user',
|
|
463
|
+
target: 'campaign_preview_skip',
|
|
464
|
+
verb: 'fail',
|
|
465
|
+
},
|
|
466
|
+
|
|
467
|
+
// Campaign Preview Remove API Events
|
|
468
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS]: {
|
|
469
|
+
product,
|
|
470
|
+
agent: 'user',
|
|
471
|
+
target: 'campaign_preview_remove',
|
|
472
|
+
verb: 'complete',
|
|
473
|
+
},
|
|
474
|
+
[METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED]: {
|
|
475
|
+
product,
|
|
476
|
+
agent: 'user',
|
|
477
|
+
target: 'campaign_preview_remove',
|
|
478
|
+
verb: 'fail',
|
|
479
|
+
},
|
|
452
480
|
};
|
|
453
481
|
|
|
454
482
|
/**
|
package/src/metrics/constants.ts
CHANGED
|
@@ -163,6 +163,10 @@ export const METRIC_EVENT_NAMES = {
|
|
|
163
163
|
// Campaign Preview API Events
|
|
164
164
|
CAMPAIGN_PREVIEW_ACCEPT_SUCCESS: 'Campaign Preview Accept Success',
|
|
165
165
|
CAMPAIGN_PREVIEW_ACCEPT_FAILED: 'Campaign Preview Accept Failed',
|
|
166
|
+
CAMPAIGN_PREVIEW_SKIP_SUCCESS: 'Campaign Preview Skip Success',
|
|
167
|
+
CAMPAIGN_PREVIEW_SKIP_FAILED: 'Campaign Preview Skip Failed',
|
|
168
|
+
CAMPAIGN_PREVIEW_REMOVE_SUCCESS: 'Campaign Preview Remove Success',
|
|
169
|
+
CAMPAIGN_PREVIEW_REMOVE_FAILED: 'Campaign Preview Remove Failed',
|
|
166
170
|
|
|
167
171
|
// AI Assistant transcript events
|
|
168
172
|
AI_ASSISTANT_SEND_EVENT_SUCCESS: 'AI Assistant Send Event Success',
|
|
@@ -189,7 +189,7 @@ function parseAgentConfigs(profileData: {
|
|
|
189
189
|
|
|
190
190
|
const finalData = {
|
|
191
191
|
teams: teamData,
|
|
192
|
-
defaultDn: userData.
|
|
192
|
+
defaultDn: userData.deafultDialledNumber,
|
|
193
193
|
forceDefaultDn: tenantData.forceDefaultDn,
|
|
194
194
|
forceDefaultDnForAgent: getDefaultAgentDN(agentProfileData.agentDNValidation),
|
|
195
195
|
regexUS: tenantData.dnDefaultRegex,
|
|
@@ -166,7 +166,7 @@ export const endPointMap = {
|
|
|
166
166
|
) =>
|
|
167
167
|
`organization/${orgId}/v2/auxiliary-code?page=${page}&pageSize=${pageSize}${
|
|
168
168
|
filter && filter.length > 0 ? `&filter=id=in=(${filter})` : ''
|
|
169
|
-
}&attributes=${attributes}`,
|
|
169
|
+
}&attributes=${attributes}&desktopProfileFilter=true`,
|
|
170
170
|
|
|
171
171
|
/**
|
|
172
172
|
* Gets the endpoint for organization info.
|
|
@@ -117,6 +117,10 @@ export const CC_TASK_EVENTS = {
|
|
|
117
117
|
CAMPAIGN_CONTACT_UPDATED: 'CampaignContactUpdated',
|
|
118
118
|
/** Event emitted when accepting a campaign preview contact fails */
|
|
119
119
|
CAMPAIGN_PREVIEW_ACCEPT_FAILED: 'CampaignPreviewAcceptFailed',
|
|
120
|
+
/** Event emitted when skipping a campaign preview contact fails */
|
|
121
|
+
CAMPAIGN_PREVIEW_SKIP_FAILED: 'CampaignPreviewSkipFailed',
|
|
122
|
+
/** Event emitted when removing a campaign preview contact fails */
|
|
123
|
+
CAMPAIGN_PREVIEW_REMOVE_FAILED: 'CampaignPreviewRemoveFailed',
|
|
120
124
|
/** Event emitted when a real-time transcript chunk is received */
|
|
121
125
|
REAL_TIME_TRANSCRIPTION: 'REAL_TIME_TRANSCRIPTION',
|
|
122
126
|
} as const;
|
|
@@ -275,8 +279,9 @@ export type AgentResponse = {
|
|
|
275
279
|
|
|
276
280
|
/**
|
|
277
281
|
* The default dialed number of the agent.
|
|
282
|
+
* Note: The API returns this field as "deafultDialledNumber" (with typo).
|
|
278
283
|
*/
|
|
279
|
-
|
|
284
|
+
deafultDialledNumber?: string;
|
|
280
285
|
};
|
|
281
286
|
|
|
282
287
|
/**
|
package/src/services/core/Err.ts
CHANGED
|
@@ -39,6 +39,8 @@ export type TaskErrorIds =
|
|
|
39
39
|
| {'Service.aqm.task.resumeRecording': Failure}
|
|
40
40
|
| {'Service.aqm.dialer.startOutdial': Failure}
|
|
41
41
|
| {'Service.aqm.dialer.acceptPreviewContact': Failure}
|
|
42
|
+
| {'Service.aqm.dialer.skipPreviewContact': Failure}
|
|
43
|
+
| {'Service.aqm.dialer.removePreviewContact': Failure}
|
|
42
44
|
| {'Service.reqs.generic.failure': {trackingId: string}};
|
|
43
45
|
|
|
44
46
|
export type ReqError =
|
|
@@ -49,34 +49,69 @@ const getAgentActionTypeFromTask = (taskData?: TaskData): 'DIAL_NUMBER' | '' =>
|
|
|
49
49
|
return isDialNumber || isEntryPointVariant ? 'DIAL_NUMBER' : '';
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Strips characters defined in the dial plan entry from the input string.
|
|
54
|
+
*
|
|
55
|
+
* @param input - The dial number to sanitize
|
|
56
|
+
* @param strippedChars - String of characters to remove from the input
|
|
57
|
+
* @returns The sanitized input with specified characters removed
|
|
58
|
+
*/
|
|
59
|
+
export const stripDialPlanChars = (input: string, strippedChars: string): string => {
|
|
60
|
+
if (!strippedChars) {
|
|
61
|
+
return input;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const charsToStrip = new Set(strippedChars.split(''));
|
|
65
|
+
|
|
66
|
+
return input
|
|
67
|
+
.split('')
|
|
68
|
+
.filter((c) => !charsToStrip.has(c))
|
|
69
|
+
.join('');
|
|
70
|
+
};
|
|
54
71
|
|
|
55
72
|
/**
|
|
56
73
|
* Validates a dial number against the provided dial plan regex patterns.
|
|
57
74
|
* A number is valid if it matches at least one regex pattern in the dial plans.
|
|
58
|
-
*
|
|
75
|
+
* Skips validation when no dial plan entries are configured, deferring to the server.
|
|
59
76
|
*
|
|
60
77
|
* @param input - The dial number to validate
|
|
61
78
|
* @param dialPlanEntries - Array of dial plan entries containing regex patterns
|
|
62
|
-
* @returns true if the input matches at least one dial plan regex pattern, false otherwise
|
|
79
|
+
* @returns true if the input matches at least one dial plan regex pattern or no entries are configured, false otherwise
|
|
63
80
|
*/
|
|
64
81
|
export const isValidDialNumber = (
|
|
65
82
|
input: string,
|
|
66
83
|
dialPlanEntries: DialPlan['dialPlanEntity']
|
|
67
84
|
): boolean => {
|
|
85
|
+
if (!input) {
|
|
86
|
+
LoggerProxy.warn('Dial number is empty or undefined.', {
|
|
87
|
+
module: 'Utils',
|
|
88
|
+
method: 'isValidDialNumber',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
68
94
|
if (!dialPlanEntries || dialPlanEntries.length === 0) {
|
|
69
|
-
LoggerProxy.
|
|
95
|
+
LoggerProxy.log(
|
|
96
|
+
'No dial plan entries found. Skipping client-side validation, deferring to server.',
|
|
97
|
+
{module: 'Utils', method: 'isValidDialNumber'}
|
|
98
|
+
);
|
|
70
99
|
|
|
71
|
-
return
|
|
100
|
+
return true;
|
|
72
101
|
}
|
|
73
102
|
|
|
74
103
|
return dialPlanEntries.some((entry) => {
|
|
75
104
|
try {
|
|
105
|
+
const sanitizedInput = stripDialPlanChars(input, entry.strippedChars);
|
|
76
106
|
const regex = new RegExp(entry.regex);
|
|
77
107
|
|
|
78
|
-
return regex.test(
|
|
79
|
-
} catch {
|
|
108
|
+
return regex.test(sanitizedInput);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
LoggerProxy.warn(`Failed to validate dial number against entry "${entry.name}": ${e}`, {
|
|
111
|
+
module: 'Utils',
|
|
112
|
+
method: 'isValidDialNumber',
|
|
113
|
+
});
|
|
114
|
+
|
|
80
115
|
return false;
|
|
81
116
|
}
|
|
82
117
|
});
|
|
@@ -301,6 +301,8 @@ export default class TaskManager extends EventEmitter {
|
|
|
301
301
|
task = this.updateTaskData(task, payload.data);
|
|
302
302
|
task.emit(TASK_EVENTS.TASK_ASSIGNED, task);
|
|
303
303
|
}
|
|
304
|
+
// We emit the event to the task manager to send the task data to the instance without mobius registration in case of multi login with desktop login
|
|
305
|
+
this.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
304
306
|
break;
|
|
305
307
|
case CC_EVENTS.AGENT_CONTACT_UNASSIGNED:
|
|
306
308
|
task = this.updateTaskData(task, {
|
|
@@ -312,14 +314,11 @@ export default class TaskManager extends EventEmitter {
|
|
|
312
314
|
case CC_EVENTS.AGENT_CONTACT_OFFER_RONA:
|
|
313
315
|
case CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED:
|
|
314
316
|
case CC_EVENTS.AGENT_INVITE_FAILED: {
|
|
315
|
-
LoggerProxy.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
interactionId: payload.data.interactionId,
|
|
321
|
-
}
|
|
322
|
-
);
|
|
317
|
+
LoggerProxy.info(`Task removal triggered by ${payload.data.type}`, {
|
|
318
|
+
module: TASK_MANAGER_FILE,
|
|
319
|
+
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
320
|
+
interactionId: payload.data.interactionId,
|
|
321
|
+
});
|
|
323
322
|
task = this.updateTaskData(task, payload.data);
|
|
324
323
|
|
|
325
324
|
const eventTypeToMetricMap: Record<string, keyof typeof METRIC_EVENT_NAMES> = {
|
|
@@ -343,19 +342,31 @@ export default class TaskManager extends EventEmitter {
|
|
|
343
342
|
break;
|
|
344
343
|
}
|
|
345
344
|
case CC_EVENTS.CONTACT_ENDED:
|
|
346
|
-
// Update task data
|
|
345
|
+
// Update task data.
|
|
347
346
|
if (task) {
|
|
348
|
-
LoggerProxy.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
347
|
+
LoggerProxy.info(`Contact ended for interaction`, {
|
|
348
|
+
module: TASK_MANAGER_FILE,
|
|
349
|
+
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
350
|
+
interactionId: payload.data.interactionId,
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
// Campaign preview tasks should never trigger wrapup on ContactEnded —
|
|
354
|
+
// they are terminal cleanup events. For all other tasks, derive
|
|
355
|
+
// wrapUpRequired from agentsPendingWrapUp as before.
|
|
356
|
+
const CAMPAIGN_OUTBOUND_TYPES = [
|
|
357
|
+
'STANDARD_PREVIEW_CAMPAIGN',
|
|
358
|
+
'DIRECT_PREVIEW_CAMPAIGN',
|
|
359
|
+
];
|
|
360
|
+
const isCampaignPreview = CAMPAIGN_OUTBOUND_TYPES.includes(
|
|
361
|
+
task.data?.interaction?.outboundType ?? ''
|
|
355
362
|
);
|
|
363
|
+
const wrapUpRequired = isCampaignPreview
|
|
364
|
+
? false
|
|
365
|
+
: payload.data.agentsPendingWrapUp?.includes(this.agentId) || false;
|
|
366
|
+
|
|
356
367
|
task = this.updateTaskData(task, {
|
|
357
368
|
...payload.data,
|
|
358
|
-
wrapUpRequired
|
|
369
|
+
wrapUpRequired,
|
|
359
370
|
});
|
|
360
371
|
|
|
361
372
|
// Handle cleanup based on whether task should be deleted
|
|
@@ -364,12 +375,83 @@ export default class TaskManager extends EventEmitter {
|
|
|
364
375
|
task?.emit(TASK_EVENTS.TASK_END, task);
|
|
365
376
|
}
|
|
366
377
|
break;
|
|
367
|
-
case CC_EVENTS.CAMPAIGN_CONTACT_UPDATED:
|
|
368
|
-
// CampaignContactUpdated is a non-terminal event (
|
|
369
|
-
//
|
|
370
|
-
//
|
|
378
|
+
case CC_EVENTS.CAMPAIGN_CONTACT_UPDATED: {
|
|
379
|
+
// CampaignContactUpdated is a non-terminal event (e.g., next contact after skip/remove).
|
|
380
|
+
// Update the task data and emit an event so consumers can react to the updated contact.
|
|
381
|
+
// Do NOT remove the task or emit TASK_END — cleanup is handled by CONTACT_ENDED.
|
|
371
382
|
if (task) {
|
|
372
|
-
|
|
383
|
+
// Carry forward campaign preview fields from existing task data since the updated
|
|
384
|
+
// contact payload may not include them, and reconcileData would delete them.
|
|
385
|
+
const existingCpd = task.data?.interaction?.callProcessingDetails;
|
|
386
|
+
const updatedData = {...payload.data};
|
|
387
|
+
|
|
388
|
+
if (existingCpd) {
|
|
389
|
+
const campaignFields = {
|
|
390
|
+
...(existingCpd.campaignPreviewAutoAction && {
|
|
391
|
+
campaignPreviewAutoAction: existingCpd.campaignPreviewAutoAction,
|
|
392
|
+
}),
|
|
393
|
+
...(existingCpd.campaignPreviewOfferTimeout && {
|
|
394
|
+
campaignPreviewOfferTimeout: existingCpd.campaignPreviewOfferTimeout,
|
|
395
|
+
}),
|
|
396
|
+
...(existingCpd.campaignPreviewSkipDisabled && {
|
|
397
|
+
campaignPreviewSkipDisabled: existingCpd.campaignPreviewSkipDisabled,
|
|
398
|
+
}),
|
|
399
|
+
...(existingCpd.campaignPreviewRemoveDisabled && {
|
|
400
|
+
campaignPreviewRemoveDisabled: existingCpd.campaignPreviewRemoveDisabled,
|
|
401
|
+
}),
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
if (!updatedData.interaction) {
|
|
405
|
+
updatedData.interaction = {} as typeof updatedData.interaction;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
updatedData.interaction = {
|
|
409
|
+
...updatedData.interaction,
|
|
410
|
+
callProcessingDetails: {
|
|
411
|
+
...campaignFields,
|
|
412
|
+
...(updatedData.interaction.callProcessingDetails || {}),
|
|
413
|
+
} as typeof existingCpd,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
LoggerProxy.log('Campaign contact updated - carrying forward preview fields', {
|
|
418
|
+
module: TASK_MANAGER_FILE,
|
|
419
|
+
method: METHODS.REGISTER_TASK_LISTENERS,
|
|
420
|
+
interactionId: payload.data.interactionId,
|
|
421
|
+
data: {
|
|
422
|
+
hasCpd: !!updatedData.interaction?.callProcessingDetails,
|
|
423
|
+
autoAction:
|
|
424
|
+
updatedData.interaction?.callProcessingDetails?.campaignPreviewAutoAction,
|
|
425
|
+
skipDisabled:
|
|
426
|
+
updatedData.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled,
|
|
427
|
+
removeDisabled:
|
|
428
|
+
updatedData.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled,
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
task = this.updateTaskData(task, updatedData);
|
|
433
|
+
task.emit(TASK_EVENTS.TASK_CAMPAIGN_CONTACT_UPDATED, task);
|
|
434
|
+
}
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
case CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED:
|
|
438
|
+
if (task) {
|
|
439
|
+
// Failure payloads are sparse (no interaction field). Spread existing
|
|
440
|
+
// task data first so reconcileData doesn't delete interaction/cpd.
|
|
441
|
+
task = this.updateTaskData(task, {...task.data, ...payload.data});
|
|
442
|
+
task.emit(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED, task);
|
|
443
|
+
}
|
|
444
|
+
break;
|
|
445
|
+
case CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED:
|
|
446
|
+
if (task) {
|
|
447
|
+
task = this.updateTaskData(task, {...task.data, ...payload.data});
|
|
448
|
+
task.emit(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_SKIP_FAILED, task);
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
case CC_EVENTS.CAMPAIGN_PREVIEW_REMOVE_FAILED:
|
|
452
|
+
if (task) {
|
|
453
|
+
task = this.updateTaskData(task, {...task.data, ...payload.data});
|
|
454
|
+
task.emit(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED, task);
|
|
373
455
|
}
|
|
374
456
|
break;
|
|
375
457
|
case CC_EVENTS.CONTACT_MERGED:
|
|
@@ -426,6 +508,8 @@ export default class TaskManager extends EventEmitter {
|
|
|
426
508
|
// Fire only if you are the agent who initiated the consult
|
|
427
509
|
task.emit(TASK_EVENTS.TASK_CONSULTING, task);
|
|
428
510
|
}
|
|
511
|
+
// Also hydrate multi-login listeners when consulting state is received.
|
|
512
|
+
this.emit(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
429
513
|
break;
|
|
430
514
|
case CC_EVENTS.AGENT_CONSULT_FAILED:
|
|
431
515
|
// This can only be received by the agent who initiated the consult.
|
|
@@ -24,6 +24,8 @@ export const CONFERENCE_EXIT = '/conference/exit';
|
|
|
24
24
|
export const CONFERENCE_TRANSFER = '/conference/transfer';
|
|
25
25
|
export const DIALER_API = '/v1/dialer';
|
|
26
26
|
export const CAMPAIGN_PREVIEW_ACCEPT = '/accept';
|
|
27
|
+
export const CAMPAIGN_PREVIEW_SKIP = '/skip';
|
|
28
|
+
export const CAMPAIGN_PREVIEW_REMOVE = '/remove';
|
|
27
29
|
/** 80-second timeout for accepting preview contact (outbound call setup takes longer than default 20s) */
|
|
28
30
|
export const TIMEOUT_PREVIEW_ACCEPT = 80000;
|
|
29
31
|
export const TASK_MANAGER_FILE = 'taskManager';
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
TASK_API,
|
|
8
8
|
DIALER_API,
|
|
9
9
|
CAMPAIGN_PREVIEW_ACCEPT,
|
|
10
|
+
CAMPAIGN_PREVIEW_SKIP,
|
|
11
|
+
CAMPAIGN_PREVIEW_REMOVE,
|
|
10
12
|
TIMEOUT_PREVIEW_ACCEPT,
|
|
11
13
|
} from './constants';
|
|
12
14
|
import * as Contact from './types';
|
|
@@ -103,5 +105,83 @@ export default function aqmDialer(aqm: AqmReqs) {
|
|
|
103
105
|
errId: 'Service.aqm.dialer.acceptPreviewContact',
|
|
104
106
|
},
|
|
105
107
|
})),
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Skips a campaign preview contact, requesting the next contact from the campaign.
|
|
111
|
+
*
|
|
112
|
+
* @param {Object} p - Parameters object.
|
|
113
|
+
* @param {Contact.PreviewContactPayload} p.data - Payload containing interactionId and campaignId.
|
|
114
|
+
* @returns {Promise<Contact.AgentContact>} A promise that resolves with agent contact on success.
|
|
115
|
+
*
|
|
116
|
+
* Emits:
|
|
117
|
+
* - `CC_EVENTS.CAMPAIGN_CONTACT_UPDATED` or `CC_EVENTS.CONTACT_ENDED` on success
|
|
118
|
+
* - `CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED` on failure
|
|
119
|
+
* @ignore
|
|
120
|
+
*/
|
|
121
|
+
skipPreviewContact: aqm.req((p: {data: Contact.PreviewContactPayload}) => ({
|
|
122
|
+
url: `${DIALER_API}/campaign/${encodeURIComponent(p.data.campaignId)}/preview-task/${
|
|
123
|
+
p.data.interactionId
|
|
124
|
+
}${CAMPAIGN_PREVIEW_SKIP}`,
|
|
125
|
+
host: WCC_API_GATEWAY,
|
|
126
|
+
data: {},
|
|
127
|
+
method: HTTP_METHODS.POST,
|
|
128
|
+
err,
|
|
129
|
+
notifSuccess: {
|
|
130
|
+
bind: {
|
|
131
|
+
type: TASK_MESSAGE_TYPE,
|
|
132
|
+
data: {
|
|
133
|
+
type: [CC_EVENTS.CAMPAIGN_CONTACT_UPDATED, CC_EVENTS.CONTACT_ENDED],
|
|
134
|
+
interactionId: p.data.interactionId,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
msg: {} as Contact.AgentContact,
|
|
138
|
+
},
|
|
139
|
+
notifFail: {
|
|
140
|
+
bind: {
|
|
141
|
+
type: TASK_MESSAGE_TYPE,
|
|
142
|
+
data: {type: CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED, campaignId: p.data.campaignId},
|
|
143
|
+
},
|
|
144
|
+
errId: 'Service.aqm.dialer.skipPreviewContact',
|
|
145
|
+
},
|
|
146
|
+
})),
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Removes a campaign preview contact from the campaign list entirely.
|
|
150
|
+
*
|
|
151
|
+
* @param {Object} p - Parameters object.
|
|
152
|
+
* @param {Contact.PreviewContactPayload} p.data - Payload containing interactionId and campaignId.
|
|
153
|
+
* @returns {Promise<Contact.AgentContact>} A promise that resolves with agent contact on success.
|
|
154
|
+
*
|
|
155
|
+
* Emits:
|
|
156
|
+
* - `CC_EVENTS.CAMPAIGN_CONTACT_UPDATED` or `CC_EVENTS.CONTACT_ENDED` on success
|
|
157
|
+
* - `CC_EVENTS.CAMPAIGN_PREVIEW_REMOVE_FAILED` on failure
|
|
158
|
+
* @ignore
|
|
159
|
+
*/
|
|
160
|
+
removePreviewContact: aqm.req((p: {data: Contact.PreviewContactPayload}) => ({
|
|
161
|
+
url: `${DIALER_API}/campaign/${encodeURIComponent(p.data.campaignId)}/preview-task/${
|
|
162
|
+
p.data.interactionId
|
|
163
|
+
}${CAMPAIGN_PREVIEW_REMOVE}`,
|
|
164
|
+
host: WCC_API_GATEWAY,
|
|
165
|
+
data: {},
|
|
166
|
+
method: HTTP_METHODS.POST,
|
|
167
|
+
err,
|
|
168
|
+
notifSuccess: {
|
|
169
|
+
bind: {
|
|
170
|
+
type: TASK_MESSAGE_TYPE,
|
|
171
|
+
data: {
|
|
172
|
+
type: [CC_EVENTS.CAMPAIGN_CONTACT_UPDATED, CC_EVENTS.CONTACT_ENDED],
|
|
173
|
+
interactionId: p.data.interactionId,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
msg: {} as Contact.AgentContact,
|
|
177
|
+
},
|
|
178
|
+
notifFail: {
|
|
179
|
+
bind: {
|
|
180
|
+
type: TASK_MESSAGE_TYPE,
|
|
181
|
+
data: {type: CC_EVENTS.CAMPAIGN_PREVIEW_REMOVE_FAILED, campaignId: p.data.campaignId},
|
|
182
|
+
},
|
|
183
|
+
errId: 'Service.aqm.dialer.removePreviewContact',
|
|
184
|
+
},
|
|
185
|
+
})),
|
|
106
186
|
};
|
|
107
187
|
}
|
|
@@ -565,7 +565,10 @@ export default class Task extends EventEmitter implements ITask {
|
|
|
565
565
|
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
566
566
|
]);
|
|
567
567
|
|
|
568
|
-
const
|
|
568
|
+
const {mainInteractionId} = this.data.interaction;
|
|
569
|
+
const defaultMediaResourceId =
|
|
570
|
+
this.data.interaction.media[mainInteractionId]?.mediaResourceId;
|
|
571
|
+
const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
|
|
569
572
|
|
|
570
573
|
const response = await this.contact.hold({
|
|
571
574
|
interactionId: this.data.interactionId,
|
|
@@ -599,7 +602,9 @@ export default class Task extends EventEmitter implements ITask {
|
|
|
599
602
|
errorData: err.data?.errorData,
|
|
600
603
|
reasonCode: err.data?.reasonCode,
|
|
601
604
|
};
|
|
602
|
-
const
|
|
605
|
+
const defaultMediaResourceId =
|
|
606
|
+
this.data.interaction.media[this.data.interaction.mainInteractionId]?.mediaResourceId;
|
|
607
|
+
const effectiveMediaResourceId = mediaResourceId ?? defaultMediaResourceId;
|
|
603
608
|
|
|
604
609
|
this.metricsManager.trackEvent(
|
|
605
610
|
METRIC_EVENT_NAMES.TASK_HOLD_FAILED,
|
|
@@ -554,6 +554,68 @@ export enum TASK_EVENTS {
|
|
|
554
554
|
* ```
|
|
555
555
|
*/
|
|
556
556
|
TASK_CAMPAIGN_PREVIEW_RESERVATION = 'task:campaignPreviewReservation',
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Triggered when accepting a campaign preview contact fails
|
|
560
|
+
* @example
|
|
561
|
+
* ```typescript
|
|
562
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED, (task: ITask) => {
|
|
563
|
+
* console.log('Campaign preview accept failed:', task.data.interactionId);
|
|
564
|
+
* // Handle accept failure
|
|
565
|
+
* });
|
|
566
|
+
* ```
|
|
567
|
+
*/
|
|
568
|
+
TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED = 'task:campaignPreviewAcceptFailed',
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Triggered when skipping a campaign preview contact fails
|
|
572
|
+
* @example
|
|
573
|
+
* ```typescript
|
|
574
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_SKIP_FAILED, (task: ITask) => {
|
|
575
|
+
* console.log('Campaign preview skip failed:', task.data.interactionId);
|
|
576
|
+
* // Handle skip failure
|
|
577
|
+
* });
|
|
578
|
+
* ```
|
|
579
|
+
*/
|
|
580
|
+
TASK_CAMPAIGN_PREVIEW_SKIP_FAILED = 'task:campaignPreviewSkipFailed',
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Triggered when removing a campaign preview contact fails
|
|
584
|
+
* @example
|
|
585
|
+
* ```typescript
|
|
586
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED, (task: ITask) => {
|
|
587
|
+
* console.log('Campaign preview remove failed:', task.data.interactionId);
|
|
588
|
+
* // Handle remove failure
|
|
589
|
+
* });
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED = 'task:campaignPreviewRemoveFailed',
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Triggered when a campaign contact is updated (e.g., after skip or remove, when the next contact is offered)
|
|
596
|
+
* @example
|
|
597
|
+
* ```typescript
|
|
598
|
+
* task.on(TASK_EVENTS.TASK_CAMPAIGN_CONTACT_UPDATED, (task: ITask) => {
|
|
599
|
+
* console.log('Campaign contact updated:', task.data.interactionId);
|
|
600
|
+
* // Handle updated campaign contact (e.g., display next contact)
|
|
601
|
+
* });
|
|
602
|
+
* ```
|
|
603
|
+
*/
|
|
604
|
+
TASK_CAMPAIGN_CONTACT_UPDATED = 'task:campaignContactUpdated',
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Triggered when a we get accept an incoming web call
|
|
608
|
+
* This is used to send task data to the instance without mobius registration in case of multi login
|
|
609
|
+
* @example
|
|
610
|
+
* ```typescript
|
|
611
|
+
* task.on(TASK_EVENTS.TASK_MULIT_LOGIN_HYDRATE, (task: ITask) => {
|
|
612
|
+
* console.log('Mulit login detected:', task.data.interactionId);
|
|
613
|
+
* // Handle mulit login
|
|
614
|
+
* });
|
|
615
|
+
* ```
|
|
616
|
+
*/
|
|
617
|
+
|
|
618
|
+
TASK_MULTI_LOGIN_HYDRATE = 'task:multiLoginHydrate',
|
|
557
619
|
}
|
|
558
620
|
|
|
559
621
|
/**
|
|
@@ -702,6 +764,14 @@ export type Interaction = {
|
|
|
702
764
|
fcDesktopView?: string;
|
|
703
765
|
/** Agent ID who initiated the outdial call */
|
|
704
766
|
outdialAgentId?: string;
|
|
767
|
+
/** Indicates if the skip action is disabled for campaign preview contacts */
|
|
768
|
+
campaignPreviewSkipDisabled?: string;
|
|
769
|
+
/** Indicates if the remove action is disabled for campaign preview contacts */
|
|
770
|
+
campaignPreviewRemoveDisabled?: string;
|
|
771
|
+
/** Auto-action to perform when campaign preview offer times out (ACCEPT, SKIP, REMOVE) */
|
|
772
|
+
campaignPreviewAutoAction?: string;
|
|
773
|
+
/** Timestamp (ms) when the campaign preview offer expires */
|
|
774
|
+
campaignPreviewOfferTimeout?: string;
|
|
705
775
|
};
|
|
706
776
|
/** Main interaction identifier for related interactions */
|
|
707
777
|
mainInteractionId?: string;
|
package/src/types.ts
CHANGED
|
@@ -156,6 +156,8 @@ export interface CCPluginConfig {
|
|
|
156
156
|
};
|
|
157
157
|
/** Configuration for the calling client */
|
|
158
158
|
callingClientConfig: CallingClientConfig;
|
|
159
|
+
/** Whether to skip Mobius/WebRTC registration for browser login flows */
|
|
160
|
+
disableWebRTCRegistration?: boolean;
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
/**
|