/** * @module CCPlugin * @packageDocumentation * Contact Center Plugin module that provides functionality for managing contact center agents, * handling tasks, and interacting with contact center services. This module enables integration * with Webex Contact Center features through the WebexSDK. */ import { WebexPlugin } from '@webex/webex-core'; import { SetStateResponse, IContactCenter, AgentLogin, AgentProfileUpdate, StationLoginResponse, StationLogoutResponse, BuddyAgentsResponse, BuddyAgents, UploadLogsResponse, UpdateDeviceTypeResponse } from './types'; import LoggerProxy from './logger-proxy'; import { StateChange, Logout } from './services/agent/types'; import { Profile, OutdialAniEntriesResponse, OutdialAniParams } from './services/config/types'; import { TaskResponse, PreviewContactPayload } from './services/task/types'; import { AddressBook } from './services/AddressBook'; import { ApiAIAssistant } from './services/ApiAiAssistant'; import type { EntryPointListResponse, EntryPointSearchParams, ContactServiceQueuesResponse, ContactServiceQueueSearchParams } from './types'; /** * The main Contact Center plugin class that enables integration with Webex Contact Center. * * @class ContactCenter * @extends WebexPlugin * @implements IContactCenter * @description * Features: * * 1. Session Management: * - {@link register} - Initialize and register SDK with contact center * - {@link deregister} - Cleanup and disconnect SDK resources * * 2. Agent Login/Logout: * - {@link stationLogin} - Login with browser or desk phone * - {@link stationLogout} - Logout from current station * - {@link updateAgentProfile} - Update device type and settings * * 3. Agent State Control: * - {@link setAgentState} - Change agent state (Available/Idle) * * 4. Task Management: * - Inbound task handling via events * - {@link startOutdial} - Make outbound calls * * 5. Routing & Distribution: * - {@link getQueues} - Get available queues for routing * - {@link getBuddyAgents} - Get available buddy agents * * 6. Diagnostics: * - {@link uploadLogs} - Upload logs for troubleshooting * * * Key Events: * - Agent State Events: * - `agent:stateChange` - Agent's state has changed (Available, Idle, etc.) * - `agent:stateChangeSuccess` - Agent state change was successful * - `agent:stateChangeFailed` - Agent state change failed * * - Session Events: * - `agent:stationLoginSuccess` - Agent login was successful * - `agent:stationLoginFailed` - Agent login failed * - `agent:logoutSuccess` - Agent logout was successful * - `agent:logoutFailed` - Agent logout failed * * - Task Events: * - `task:incoming` - New task is being offered * - `task:hydrate` - Task data has been updated * - `task:established` - Task/call has been connected * - `task:ended` - Task/call has ended * - `task:error` - An error occurred during task handling * - `task:campaignPreviewReservation` - Campaign preview contact offered to agent * * @public * * @example * ```typescript * import Webex from 'webex'; * * // Initialize SDK with access token * const webex = new Webex({ * credentials: 'YOUR_ACCESS_TOKEN' * }); * * // Get Contact Center plugin instance * const cc = webex.cc; * * // Setup event handlers * cc.on('agent:stateChange', (event) => { * console.log('Agent state changed:', event.state); * }); * * cc.on('task:incoming', (task) => { * console.log('New task received:', task.interactionId); * }); * * // Initialize agent session * async function initializeAgent() { * try { * // Register with contact center * const profile = await cc.register(); * * // Login with browser-based calling * await cc.stationLogin({ * teamId: profile.teams[0].teamId, * loginOption: 'BROWSER' * }); * * // Set agent to Available state * await cc.setAgentState({ * state: 'Available', * auxCodeId: '0' * }); * * console.log('Agent initialized and ready'); * } catch (error) { * console.error('Initialization failed:', error); * await cc.uploadLogs(); // Upload logs for troubleshooting * } * } * * initializeAgent(); * ``` * * @public */ export default class ContactCenter extends WebexPlugin implements IContactCenter { /** * The plugin's unique namespace identifier in the Webex SDK. * Used to access the plugin via webex.cc * @type {string} * @public */ namespace: string; /** * Plugin configuration settings including connection and authentication options * @type {CCPluginConfig} * @private */ private $config; /** * Reference to the parent Webex SDK instance * Used to access core Webex functionality and credentials * @type {WebexSDK} * @private */ private $webex; /** * Event emitter for handling internal plugin events * Manages event subscriptions and notifications * @type {EventEmitter} * @private */ private eventEmitter; /** * Agent's profile and configuration data * Includes capabilities, teams, settings, and current state * @type {Profile} * @private */ private agentConfig; /** * Service for managing browser-based calling (WebRTC) * Handles audio/video streaming and device management * @type {WebCallingService} * @private */ private webCallingService; /** * Core service managers for Contact Center operations * Includes agent, connection, and configuration services * @type {Services} * @private */ private services; /** * Service for making authenticated HTTP requests to Webex APIs * Handles request/response lifecycle and error handling * @type {WebexRequest} * @private */ private webexRequest; /** * Manager for handling contact center tasks (calls, chats, etc.) * Coordinates task lifecycle events and state * @type {TaskManager} * @private */ private taskManager; /** * Manager for tracking and reporting SDK metrics and analytics * Monitors performance, errors, and usage patterns * @type {MetricsManager} * @private */ private metricsManager; /** * API instance for managing Webex Contact Center entry points * Provides functionality to fetch entry points with caching support * @type {EntryPoint} * @public * @example * ```typescript * const cc = webex.cc; * await cc.register(); * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' }); * * // Access EntryPointRecord * const response = await cc.entryPoint.getEntryPoints({ * page: 0, * pageSize: 50 * }); * ``` */ private entryPoint; /** * API instance for managing Webex Contact Center address book contacts * Provides functionality to fetch address book entries with caching support * @type {AddressBook} * @public * @example * ```typescript * const cc = webex.cc; * await cc.register(); * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' }); * * // Access AddressBook API * const response = await cc.addressBook.getEntries({ * page: 0, * pageSize: 25 * }); * ``` */ addressBook: AddressBook; /** * API instance for managing Webex Contact Center queues * Provides functionality to fetch queues with caching support * @type {Queue} * @public * @example * ```typescript * const cc = webex.cc; * await cc.register(); * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' }); * * // Access Queue API * const response = await cc.queue.getQueues({ * page: 0, * pageSize: 50 * }); * * // Filter queues by specific criteria * const filteredQueues = await cc.queue.getQueues({ * filter: 'id=="queue-id-123"' * }); * ``` */ private queue; /** * API instance for AI Assistant operations such as transcript controls. * @type {ApiAIAssistant} * @public */ apiAIAssistant: ApiAIAssistant; /** * Logger utility for Contact Center plugin * Provides consistent logging across the plugin * @type {LoggerProxy} * @public */ LoggerProxy: typeof LoggerProxy; /** * @ignore * Creates an instance of ContactCenter plugin * @param {any[]} args Arguments passed to plugin constructor */ constructor(...args: any[]); /** * Handles incoming task events and triggers appropriate notifications * @private * @param {ITask} task The incoming task object containing task details */ private handleIncomingTask; /** * Handles task hydration events for updating task data * @private * @param {ITask} task The task object to be hydrated with additional data */ private handleTaskHydrate; /** * Handles task merged events when tasks are combined eg: EPDN merge/transfer * @private * @param {ITask} task The task object that has been merged */ private handleTaskMerged; /** * Handles campaign preview reservation events when a contact is offered to the agent * @private * @param {ITask} task The campaign reservation task */ private handleCampaignPreviewReservation; /** * Sets up event listeners for incoming tasks and task hydration * Subscribes to task events from the task manager * @private */ private incomingTaskListener; /** * Initializes the Contact Center SDK by setting up the web socket connections. * This method must be called before performing any agent operations such as login, state change, or handling tasks. * * @returns {Promise} Agent profile information after successful registration. * The returned `Profile` object contains details such as: * - `agentId`: The unique identifier for the agent. * - `defaultDn`: The default dial number associated with the agent. * - `teams`: Array of teams the agent belongs to. * - `webRtcEnabled`: Indicates if WebRTC (browser calling) is enabled. * - `loginVoiceOptions`: Supported login options for the agent (e.g., BROWSER, EXTENSION). * - ...and other agent configuration details. * * @throws {Error} If registration fails. * * @public * @example * ```typescript * import Webex from 'webex'; * * const webex = Webex.init({ credentials: 'YOUR_ACCESS_TOKEN' }); * const cc = webex.cc; * * // Register the SDK and fetch agent profile * const profile = await cc.register(); * * console.log('Agent ID:', profile.agentId); * console.log('Default DN:', profile.defaultDn); * console.log('Teams:', profile.teams.map(t => t.teamId)); * console.log('WebRTC Enabled:', profile.webRtcEnabled); * console.log('Supported Login Options:', profile.loginVoiceOptions); * * // Now you can proceed with station login, state changes, etc. * await cc.stationLogin({ teamId: profile.teams[0].teamId, loginOption: 'BROWSER' }); * ``` */ register(): Promise; /** * Unregisters the Contact Center SDK by closing all web socket connections, removing event listeners, * and cleaning up internal state. * * @remarks * This method only disconnects the SDK from the backend and cleans up resources. It does NOT perform a station logout * (i.e., the agent remains logged in to the contact center unless you explicitly call {@link stationLogout}). * Use this when you want to fully tear down the SDK instance, such as during application shutdown or user sign-out. * * @returns {Promise} Resolves when deregistration and cleanup are complete. * @throws {Error} If deregistration fails. * * @public * @example * // Typical usage: clean up SDK before application exit or user logout * import Webex from 'webex'; * * const webex = Webex.init({ credentials: 'YOUR_ACCESS_TOKEN' }); * const cc = webex.cc; * * await cc.register(); * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' }); * // ... perform agent operations ... * * // If you want to log out the agent as well, call: * // await cc.stationLogout({ logoutReason: 'User signed out' }); * // On application shutdown or user sign-out: * await cc.deregister(); * */ deregister(): Promise; /** * Returns the list of buddy agents who are in the given user state and media type based on their agent profile settings * @param {BuddyAgents} data The data required to fetch buddy agents * @returns {Promise} A promise resolving to the buddy agents information * @throws {Error} If fetching buddy agents fails * @example * ```typescript * // Get list of available agents for consultation or transfer * const cc = webex.cc; * * // First ensure you're registered and logged in * await cc.register(); * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' }); * * // Get buddy agents filtered by state and media type * const response = await cc.getBuddyAgents({ * state: 'Available', // Filter by agent state ('Available', 'Idle', etc.) * mediaType: 'telephony' // Filter by media type ('telephony', 'chat', 'email', 'social') * }); * * // Process the buddy agents list * if (response.data.agentList.length > 0) { * const buddyAgents = response.data.agentList; * console.log(`Found ${buddyAgents.length} available agents`); * * // Access agent details * buddyAgents.forEach(agent => { * console.log(`Agent ID: ${agent.agentId}`); * console.log(`Name: ${agent.firstName} ${agent.lastName}`); * console.log(`State: ${agent.state}`); * console.log(`Team: ${agent.teamName}`); * }); * } * ``` */ getBuddyAgents(data: BuddyAgents): Promise; /** * Connects to the websocket and fetches the agent profile * @returns {Promise} Agent profile information * @throws {Error} If connection fails or profile cannot be fetched * @private */ private connectWebsocket; /** * Performs agent login with specified credentials and device type * @param {AgentLogin} data Login parameters including teamId, loginOption and dialNumber * @returns {Promise} Response containing login status and profile * @throws {Error} If login fails * @public * @example * ```typescript * const cc = webex.cc; * await cc.register(); * * // Primary usage: using Promise response * try { * const response = await cc.stationLogin({ * teamId: 'team123', * loginOption: 'EXTENSION', * dialNumber: '1002' * }); * console.log('Login successful:', response); * } catch (error) { * console.error('Login failed:', error); * } * * // Optional: Also listen for events elsewhere in your application * // cc.on('agent:stationLoginSuccess', (data) => { ... }); * // cc.on('agent:stationLoginFailed', (error) => { ... }); * ``` */ stationLogin(data: AgentLogin): Promise; /** * Performs a station logout operation for the agent * @remarks * A logout operation cannot happen if the agent is in an interaction or haven't logged in yet. * @param {Logout} data Logout parameters with logoutReason - a string explaining why the agent is logging out * @returns {Promise} Response indicating logout status * @throws {Error} If logout fails * @public * @example * ```typescript * // Basic logout * try { * await cc.stationLogout({ * logoutReason: 'End of shift' * }); * console.log('Logged out successfully'); * } catch (error) { * console.error('Logout failed:', error); * } * ``` */ stationLogout(data: Logout): Promise; /** * Gets the device ID based on login option and dial number * @param {string} loginOption The login option (BROWSER, EXTENSION, etc) * @param {string} dialNumber The dial number if applicable * @returns {string} The device ID * @private */ private getDeviceId; /** * Sets the state of the agent to Available or any of the Idle states. * After a state change attempt, one of the following events will be emitted: * - agent:stateChange: Emitted when agent's state changes (triggered for both local and remote changes) * - agent:stateChangeSuccess: Emitted when agent state change is successful * - agent:stateChangeFailed: Emitted when agent state change attempt fails * * @param {StateChange} data State change parameters including the new state * @returns {Promise} Response with updated state information * @throws {Error} If state change fails * @public * @example * ```typescript * const cc = webex.cc; * await cc.register(); * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' }); * * // Using promise-based approach * try { * await cc.setAgentState({ * state: 'Available', * auxCodeId: '12345', * lastStateChangeReason: 'Manual state change', * agentId: 'agent123', * }); * } catch (error) { * console.error('State change failed:', error); * } * * // Optionally, listen for events * cc.on('agent:stateChange', (eventData) => { * // Triggered for both local and remote state changes * console.log('State changed:', eventData); * }); * * cc.on('agent:stateChangeSuccess', (eventData) => { * console.log('State change succeeded:', eventData); * }); * * cc.on('agent:stateChangeFailed', (error) => { * console.error('State change failed:', error); * }); * ``` */ setAgentState(data: StateChange): Promise; /** * Processes incoming websocket messages and emits corresponding events * Handles various event types including agent state changes, login events, * and other agent-related notifications * @private * @param {string} event The raw websocket event message */ private handleWebsocketMessage; /** * Initializes event listeners for the Contact Center service * Sets up handlers for connection state changes and other core events * @private */ private setupEventListeners; /** * Returns the connection configuration * @returns {SubscribeRequest} Connection configuration * @private */ private getConnectionConfig; /** * Handles connection lost events and reconnection attempts * @param {ConnectionLostDetails} msg Connection lost details * @private */ private handleConnectionLost; /** * Handles silent relogin after registration completion * @private */ private silentRelogin; /** * Handles device type specific configuration and setup * Configures services and settings based on the login device type * @param {LoginOption} deviceType The type of device being used for login * @param {string} dn The dial number associated with the device * @returns {Promise} * @private */ private handleDeviceType; /** * Makes an outbound call to a specified phone number. * * @param {string} destination - The phone number to dial (e.g., '+1234567890'). * @param {string} origin - The contact center number that will be used while making a call to the customer. * Should include country code and be in E.164 format. * @returns {Promise} Resolves with the task response containing: * - interactionId: Unique identifier for the outbound call * - taskId: Identifier for the task instance * - data: Task details including state, queue info, and media properties * @throws {Error} If the outdial operation fails: * - "Agent not configured for outbound calls" if isOutboundEnabledForAgent is false * - "Invalid phone number format" if destination is not in E.164 format * - "Agent not in Available state" if agent's state is not Available * @public * @example * ```typescript * // Initialize and prepare agent * const cc = webex.cc; * await cc.register(); * await cc.stationLogin({ * teamId: 'team123', * loginOption: 'BROWSER' * }); * * // Set Available state before outbound call * await cc.setAgentState({ * state: 'Available', * auxCodeId: '0' * }); * * // Make outbound call with full error handling * try { * // Verify agent is properly configured for outdial * if (!cc.agentConfig.isOutboundEnabledForAgent) { * throw new Error('Agent not configured for outbound calls'); * } * * // Start the outbound call * const destination = '+1234567890'; * const task = await cc.startOutdial(destination, origin); * * // Listen for all relevant task events * task.on('task:ringing', () => { * console.log('Call is ringing'); * updateCallStatus('Ringing...'); * }); * * task.on('task:established', () => { * console.log('Call connected'); * updateCallStatus('Connected'); * enableCallControls(); // Show mute, hold, transfer buttons * }); * * task.on('task:hold', () => { * console.log('Call placed on hold'); * updateCallStatus('On Hold'); * }); * * task.on('task:error', (error) => { * console.error('Call error:', error); * updateCallStatus('Error'); * showErrorDialog(error.message); * }); * * task.on('task:ended', () => { * console.log('Call ended'); * updateCallStatus('Call Ended'); * resetCallControls(); * * // Handle wrap-up if required * if (task.data.wrapUpRequired) { * showWrapupForm(); * } * }); * * // Example call control usage * function handleMuteToggle() { * await task.toggleMute(); * } * * function handleHoldToggle() { * if (task.data.isOnHold) { * await task.resume(); * } else { * await task.hold(); * } * } * * async function handleTransfer() { * // Get available queues for transfer * const queues = await cc.getQueues(); * * // Transfer to first available queue * if (queues.length > 0) { * await task.transfer({ * to: queues[0].queueId, * destinationType: 'QUEUE' * }); * } * } * * } catch (error) { * console.error('Outdial failed:', error); * showErrorNotification('Failed to place call: ' + error.message); * } * ``` */ startOutdial(destination: string, origin: string): Promise; /** * Accepts a campaign preview contact, initiating the outbound call. * * When a campaign manager reserves a contact for an agent, the agent receives an * `AgentOfferCampaignReservation` event. The agent can then accept the preview contact * to initiate the outbound call. * * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID). * @returns {Promise} Promise resolving with agent contact on success. * @throws {Error} If the operation fails (network error, customer unavailable, etc.) * * @example * ```typescript * webex.cc.on('task:campaignPreviewReservation', async (task) => { * const { interactionId } = task.data; * // campaignId is the campaign name (e.g. "MyCampaign"), not a UUID * const campaignId = task.data.interaction.callProcessingDetails.campaignId; * * const result = await webex.cc.acceptPreviewContact({ interactionId, campaignId }); * }); * ``` */ acceptPreviewContact(payload: PreviewContactPayload): Promise; /** * Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID. * * This method retrieves the list of phone numbers that can be used as caller ID when making * outbound calls. The ANI data is associated with an outdial ANI ID and can be filtered * and paginated as needed. * * @param {string} outdialANI - The outdial ANI ID to fetch ANI data for * @param {number} [page] - Optional page number for pagination (0-based) * @param {number} [pageSize] - Optional number of items per page * @param {string} [search] - Optional search term to filter results by name or number * @param {string} [filter] - Optional filter string * @param {string} [attributes] - Optional attributes to include in response * @returns {Promise} Promise resolving to outdial ANI response containing: * - data: Array of ANI entries with number and name * - meta: Pagination metadata * @throws {Error} If the operation fails or agent is not registered * @public * @example * ```typescript * const cc = webex.cc; * await cc.register(); * * // Get agent profile to obtain outdial ANI ID * const agentProfile = cc.agentConfig; * const outdialANI = agentProfile.outdialANIId; * * // Basic usage - get all ANI data for an outdial ANI ID * const aniData = await cc.getOutdialAniEntries({ outdialANI }); * * // With pagination and search * const paginatedAni = await cc.getOutdialAniEntries({ * outdialANI, * page: 0, * pageSize: 50, * search: '555' // search for numbers containing '555' * }); * * // Process the results * paginatedAni.forEach(ani => { * console.log(`ANI: ${ani.number} - ${ani.name}`); * }); * ``` */ getOutdialAniEntries(params: OutdialAniParams): Promise; /** * Uploads logs to help troubleshoot SDK issues. * * This method collects the current SDK logs including network requests, WebSocket * messages, and client-side events, then securely submits them to Webex's diagnostics * service. The returned tracking ID, feedbackID can be provided to Webex support for faster * issue resolution. * @returns Promise Resolves with the upload logs response * @throws Error If the upload fails * @public * @example * ```typescript * const cc = webex.cc; * try { * await cc.register(); * } catch (error) { * console.error('Error:', error); * const result = await cc.uploadLogs(); * console.log('Logs uploaded. Tracking ID:', result.trackingId); * } * ``` */ uploadLogs(): Promise; /** * Updates the agent device type and login configuration. * Use this method to change how an agent connects to the contact center system (e.g., switching from browser-based calling to a desk phone extension). * Change to any field of the profile is allowed; * * @param {AgentDeviceUpdate} data Configuration containing: * - loginOption: New device type ('BROWSER', 'EXTENSION', 'AGENT_DN') * - dialNumber: Required phone number when using EXTENSION or AGENT_DN * - teamId: Optional team ID (defaults to current team if not specified) * @returns Promise Resolves with the device type update response * @throws Error If the update fails * @example * ```typescript * const cc = webex.cc; * * // Switch from browser to extension * try { * await cc.updateAgentProfile({ * loginOption: 'EXTENSION', * dialNumber: '1234', // Required for EXTENSION * teamId: 'currentTeam' // Optional: uses current team if not specified * }); * } catch (error) { * console.error('Failed to update device:', error.message); * } * ``` * @public */ updateAgentProfile(data: AgentProfileUpdate): Promise; /** * Returns paginated entry points for the organization. * Thin wrapper around internal EntryPoint instance. * @public */ getEntryPoints(params?: EntryPointSearchParams): Promise; /** * Returns paginated contact service queues for the organization. * Thin wrapper around internal Queue instance. * @public */ getQueues(params?: ContactServiceQueueSearchParams): Promise; }