UNPKG

n8n-nodes-genesys-cloud-mcp-plus

Version:

n8n community node for Genesys Cloud MCP Plus - 15 comprehensive tools for contact center analytics, real-time monitoring, and wrap-up code analysis

611 lines (610 loc) 22.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GenesysCloudMcpPlusService = void 0; class GenesysCloudMcpPlusService { constructor(credentials) { this.accessToken = null; this.tokenExpiry = null; this.credentials = credentials; } getBaseUrl() { const region = this.credentials['region']; console.log('[GenesysCloudMcpPlusService] Selected region value:', region); // Map regions to their API base URLs const regionMap = { 'mypurecloud.com': 'https://api.mypurecloud.com', 'mypurecloud.de': 'https://api.mypurecloud.de', 'mypurecloud.ie': 'https://api.mypurecloud.ie', 'mypurecloud.com.au': 'https://api.mypurecloud.com.au', 'mypurecloud.jp': 'https://api.mypurecloud.jp', 'cac1.pure.cloud': 'https://api.cac1.pure.cloud', 'euw2.pure.cloud': 'https://api.euw2.pure.cloud', 'euc2.pure.cloud': 'https://api.euc2.pure.cloud', 'aps1.pure.cloud': 'https://api.aps1.pure.cloud', 'apne2.pure.cloud': 'https://api.apne2.pure.cloud', 'apne3.pure.cloud': 'https://api.apne3.pure.cloud', 'sae1.pure.cloud': 'https://api.sae1.pure.cloud', 'usw2.pure.cloud': 'https://api.usw2.pure.cloud', 'mec1.pure.cloud': 'https://api.mec1.pure.cloud', }; const baseUrl = regionMap[region] || `https://api.${region}`; console.log('[GenesysCloudMcpPlusService] Using base URL:', baseUrl); return baseUrl; } async authenticate() { try { // Check if we have a valid token if (this.accessToken && this.tokenExpiry && Date.now() < this.tokenExpiry) { return; // Token is still valid } const baseUrl = this.getBaseUrl(); const authUrl = `${baseUrl}/oauth/token`; // Create base64 encoded credentials const credentials = Buffer.from(`${this.credentials['clientId']}:${this.credentials['clientSecret']}`).toString('base64'); const response = await fetch(authUrl, { method: 'POST', headers: { 'Authorization': `Basic ${credentials}`, 'Content-Type': 'application/x-www-form-urlencoded', }, body: 'grant_type=client_credentials', }); if (!response.ok) { throw new Error(`Authentication failed: ${response.status} ${response.statusText}`); } const data = await response.json(); this.accessToken = data.access_token; this.tokenExpiry = Date.now() + (data.expires_in * 1000) - 60000; // Expire 1 minute early } catch (error) { throw new Error(`Authentication failed: ${error.message}`); } } async makeRequest(endpoint, method = 'GET', body) { await this.authenticate(); const baseUrl = this.getBaseUrl(); const url = `${baseUrl}${endpoint}`; const headers = { 'Authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json', }; const options = { method, headers, }; if (body) { options.body = JSON.stringify(body); } const response = await fetch(url, options); if (!response.ok) { throw new Error(`API request failed: ${response.status} ${response.statusText}`); } return await response.json(); } async searchQueues(params) { try { const queryParams = new URLSearchParams({ name: params.name, pageSize: (params.pageSize || 100).toString(), }); const response = await this.makeRequest(`/api/v2/routing/queues?${queryParams}`); return { success: true, data: response.entities.map((queue) => ({ id: queue.id, name: queue.name, description: queue.description, memberCount: queue.memberCount, })), pagination: { pageNumber: response.pageNumber, pageSize: response.pageSize, total: response.total, }, }; } catch (error) { return { success: false, error: error.message, }; } } async queryQueueVolumes(params) { try { const body = { interval: `${params.startDate}/${params.endDate}`, groupBy: ['queueId'], metrics: ['nOffered', 'nHandled', 'nAbandoned'], filter: { type: 'and', clauses: [ { type: 'or', predicates: params.queueIds.map(queueId => ({ type: 'dimension', dimension: 'queueId', operator: 'matches', value: queueId, })), }, ], }, }; const response = await this.makeRequest('/api/v2/analytics/conversations/aggregates/query', 'POST', body); return { success: true, data: response.results.map((result) => ({ queueId: result.group.queueId, metrics: result.data.reduce((acc, metric) => { acc[metric.metric] = metric.stats?.sum || 0; return acc; }, {}), })), }; } catch (error) { return { success: false, error: error.message, }; } } async sampleConversationsByQueue(params) { try { const body = { interval: `${params.startDate}/${params.endDate}`, groupBy: ['conversationId'], metrics: ['nOffered'], filter: { type: 'and', clauses: [ { type: 'or', predicates: [ { type: 'dimension', dimension: 'queueId', operator: 'matches', value: params.queueId, }, ], }, ], }, }; const response = await this.makeRequest('/api/v2/analytics/conversations/aggregates/query', 'POST', body); return { success: true, data: response.results.map((result) => ({ conversationId: result.group.conversationId, })), }; } catch (error) { return { success: false, error: error.message, }; } } async voiceCallQuality(params) { try { const body = { conversationIds: params.conversationIds, }; const response = await this.makeRequest('/api/v2/analytics/conversations/details/query', 'POST', body); return { success: true, data: response.conversations.map((conversation) => ({ conversationId: conversation.conversationId, mos: conversation.participants .flatMap((p) => p.sessions) .flatMap((s) => s.segments) .filter((seg) => seg.mos) .map((seg) => seg.mos), })), }; } catch (error) { return { success: false, error: error.message, }; } } async conversationSentiment(params) { try { const body = { conversationIds: params.conversationIds, interval: '2023-01-01T00:00:00.000Z/2023-12-31T23:59:59.999Z' }; const response = await this.makeRequest('/api/v2/analytics/conversations/details/query', 'POST', body); return { success: true, data: response.conversations.map((conversation) => ({ conversationId: conversation.conversationId, sentiment: conversation.participants .flatMap((p) => p.sessions) .flatMap((s) => s.segments) .filter((seg) => seg.sentiment) .map((seg) => seg.sentiment), })), }; } catch (error) { return { success: false, error: error.message, }; } } async conversationTopics(params) { try { const response = await this.makeRequest(`/api/v2/conversations/${params.conversationId}/topics`); return { success: true, data: { conversationId: params.conversationId, topics: response.topics || [], }, }; } catch (error) { return { success: false, error: error.message, }; } } async conversationTranscript(params) { try { const response = await this.makeRequest(`/api/v2/conversations/${params.conversationId}/recording`); return { success: true, data: { conversationId: params.conversationId, transcript: response.transcript || [], }, }; } catch (error) { return { success: false, error: error.message, }; } } async enhancedConversationSearch(params) { try { const body = { interval: `${params.startDate}/${params.endDate}`, groupBy: ['conversationId', 'mediaType'], metrics: ['nOffered'], }; // Add phone number filter if provided if (params.phoneNumber) { body.filter = { type: 'and', clauses: [ { type: 'or', predicates: [ { type: 'dimension', dimension: 'ani', operator: 'matches', value: params.phoneNumber, }, ], }, ], }; } // Add media type filter if provided if (params.mediaTypes && params.mediaTypes.length > 0) { if (!body.filter) { body.filter = { type: 'and', clauses: [], }; } body.filter.clauses.push({ type: 'or', predicates: params.mediaTypes.map(mediaType => ({ type: 'dimension', dimension: 'mediaType', operator: 'matches', value: mediaType, })), }); } const response = await this.makeRequest('/api/v2/analytics/conversations/aggregates/query', 'POST', body); return { success: true, data: response.results.map((result) => ({ conversationId: result.group.conversationId, mediaType: result.group.mediaType, })), }; } catch (error) { return { success: false, error: error.message, }; } } async agentMediaTypePerformance(params) { try { const body = { interval: `${params.startDate}/${params.endDate}`, groupBy: ['userId', 'mediaType'], metrics: ['nHandled', 'tHandle'], }; // Add user filter if provided if (params.userIds && params.userIds.length > 0) { body.filter = { type: 'and', clauses: [ { type: 'or', predicates: params.userIds.map(userId => ({ type: 'dimension', dimension: 'userId', operator: 'matches', value: userId, })), }, ], }; } const response = await this.makeRequest('/api/v2/analytics/conversations/aggregates/query', 'POST', body); return { success: true, data: response.results.map((result) => ({ userId: result.group.userId, mediaType: result.group.mediaType, handled: result.data.find((d) => d.metric === 'nHandled')?.stats?.sum || 0, handleTime: result.data.find((d) => d.metric === 'tHandle')?.stats?.sum || 0, })), }; } catch (error) { return { success: false, error: error.message, }; } } async realTimeQueueStatus(params) { try { const response = await this.makeRequest('/api/v2/routing/queues'); const filteredQueues = response.entities.filter((queue) => params.queueIds.includes(queue.id)); return { success: true, data: filteredQueues.map((queue) => ({ id: queue.id, name: queue.name, memberCount: queue.memberCount, activeMemberCount: queue.activeMemberCount, })), }; } catch (error) { return { success: false, error: error.message, }; } } async realTimeAgentPresence(params) { try { const response = await this.makeRequest('/api/v2/users'); const filteredUsers = response.entities.filter((user) => params.userIds.includes(user.id)); return { success: true, data: filteredUsers.map((user) => ({ id: user.id, name: user.name, email: user.email, state: user.state, })), }; } catch (error) { return { success: false, error: error.message, }; } } async liveConversationMonitoring(params) { try { const body = { interval: '2023-01-01T00:00:00.000Z/2023-12-31T23:59:59.999Z', groupBy: ['conversationId', 'queueId'], metrics: ['nOffered'], filter: { type: 'and', clauses: [ { type: 'or', predicates: params.queueIds.map(queueId => ({ type: 'dimension', dimension: 'queueId', operator: 'matches', value: queueId, })), }, ], }, }; const response = await this.makeRequest('/api/v2/analytics/conversations/aggregates/query', 'POST', body); const results = response.results.slice(0, params.maxResults || 50); return { success: true, data: results.map((result) => ({ conversationId: result.group.conversationId, queueId: result.group.queueId, })), }; } catch (error) { return { success: false, error: error.message, }; } } async wrapUpCodeAnalytics(params) { try { const body = { interval: `${params.startDate}/${params.endDate}`, groupBy: ['wrapUpCode'], metrics: ['nOffered'], filter: { type: 'and', clauses: [ { type: 'or', predicates: [ { type: 'dimension', dimension: 'wrapUpCode', operator: 'exists', }, ], }, ], }, }; // Add queue filter if provided if (params.queueIds && params.queueIds.length > 0) { body.filter.clauses.push({ type: 'or', predicates: params.queueIds.map(queueId => ({ type: 'dimension', dimension: 'queueId', operator: 'matches', value: queueId, })), }); } // Add wrap-up code filter if provided if (params.wrapUpCodes && params.wrapUpCodes.length > 0) { body.filter.clauses.push({ type: 'or', predicates: params.wrapUpCodes.map(wrapUpCode => ({ type: 'dimension', dimension: 'wrapUpCode', operator: 'matches', value: wrapUpCode, })), }); } const response = await this.makeRequest('/api/v2/analytics/conversations/aggregates/query', 'POST', body); return { success: true, data: response.results.map((result) => ({ wrapUpCode: result.group.wrapUpCode, count: result.data.find((d) => d.metric === 'nOffered')?.stats?.sum || 0, })), }; } catch (error) { return { success: false, error: error.message, }; } } async agentStatusMonitoring(params) { try { const response = await this.makeRequest('/api/v2/users'); let filteredUsers = response.entities; // Filter by specific users if provided if (params.userIds && params.userIds.length > 0) { filteredUsers = filteredUsers.filter((user) => params.userIds.includes(user.id)); } // Filter out inactive users unless specifically requested if (!params.includeInactive) { filteredUsers = filteredUsers.filter((user) => user.state === 'active'); } // Apply page size limit if (params.pageSize) { filteredUsers = filteredUsers.slice(0, params.pageSize); } return { success: true, data: filteredUsers.map((user) => ({ id: user.id, name: user.name, email: user.email, state: user.state, department: user.department, title: user.title, })), }; } catch (error) { return { success: false, error: error.message, }; } } async searchVoiceConversations(params) { try { const body = { interval: `${params.startDate}/${params.endDate}`, groupBy: ['conversationId'], metrics: ['nOffered'], filter: { type: 'and', clauses: [ { type: 'or', predicates: [ { type: 'dimension', dimension: 'mediaType', operator: 'matches', value: 'voice', }, ], }, ], }, }; // Add phone number filter if provided if (params.phoneNumber) { body.filter.clauses.push({ type: 'or', predicates: [ { type: 'dimension', dimension: 'ani', operator: 'matches', value: params.phoneNumber, }, ], }); } const response = await this.makeRequest('/api/v2/analytics/conversations/aggregates/query', 'POST', body); return { success: true, data: response.results.map((result) => ({ conversationId: result.group.conversationId, })), }; } catch (error) { return { success: false, error: error.message, }; } } } exports.GenesysCloudMcpPlusService = GenesysCloudMcpPlusService;