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
JavaScript
;
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;