UNPKG

@app-connect/core

Version:
365 lines (354 loc) 16.7 kB
const axios = require('axios'); const { AdminConfigModel } = require('../models/adminConfigModel'); const connectorRegistry = require('../connector/registry'); const oauth = require('../lib/oauth'); const { RingCentral } = require('../lib/ringcentral'); const { Connector } = require('../models/dynamo/connectorSchema'); const CALL_AGGREGATION_GROUPS = ["Company", "CompanyNumbers", "Users", "Queues", "IVRs", "IVAs", "SharedLines", "UserGroups", "Sites", "Departments"] async function validateAdminRole({ rcAccessToken }) { const rcExtensionResponse = await axios.get( 'https://platform.ringcentral.com/restapi/v1.0/account/~/extension/~', { headers: { Authorization: `Bearer ${rcAccessToken}`, }, }); return { isValidated: !!rcExtensionResponse.data?.permissions?.admin?.enabled || (!!process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST && process.env.ADMIN_EXTENSION_ID_DEV_PASS_LIST.split(',').includes(rcExtensionResponse.data.id.toString())), rcAccountId: rcExtensionResponse.data.account.id }; } async function upsertAdminSettings({ hashedRcAccountId, adminSettings }) { let existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId); if (existingAdminConfig) { await existingAdminConfig.update({ ...adminSettings }); } else { await AdminConfigModel.create({ id: hashedRcAccountId, ...adminSettings }); } } async function getAdminSettings({ hashedRcAccountId }) { const existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId); return existingAdminConfig; } async function updateAdminRcTokens({ hashedRcAccountId, adminAccessToken, adminRefreshToken, adminTokenExpiry }) { const existingAdminConfig = await AdminConfigModel.findByPk(hashedRcAccountId); if (existingAdminConfig) { await existingAdminConfig.update({ adminAccessToken, adminRefreshToken, adminTokenExpiry }); } else { await AdminConfigModel.create({ id: hashedRcAccountId, adminAccessToken, adminRefreshToken, adminTokenExpiry }); } } async function getServerLoggingSettings({ user }) { const platformModule = connectorRegistry.getConnector(user.platform); if (platformModule.getServerLoggingSettings) { const serverLoggingSettings = await platformModule.getServerLoggingSettings({ user }); return serverLoggingSettings; } return {}; } async function updateServerLoggingSettings({ user, additionalFieldValues }) { const platformModule = connectorRegistry.getConnector(user.platform); const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname }))); if (platformModule.updateServerLoggingSettings) { const { successful, returnMessage } = await platformModule.updateServerLoggingSettings({ user, additionalFieldValues, oauthApp }); return { successful, returnMessage }; } return {}; } async function getAdminReport({ rcAccountId, timezone, timeFrom, timeTo, groupBy }) { try { if (!process.env.RINGCENTRAL_SERVER || !process.env.RINGCENTRAL_CLIENT_ID || !process.env.RINGCENTRAL_CLIENT_SECRET) { return { callLogStats: {} }; } const rcSDK = new RingCentral({ server: process.env.RINGCENTRAL_SERVER, clientId: process.env.RINGCENTRAL_CLIENT_ID, clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET, redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback` }); let adminConfig = await AdminConfigModel.findByPk(rcAccountId); const isTokenExpired = adminConfig.adminTokenExpiry < new Date(); if (isTokenExpired) { const { access_token, refresh_token, expire_time } = await rcSDK.refreshToken({ refresh_token: adminConfig.adminRefreshToken, expires_in: adminConfig.adminTokenExpiry, refresh_token_expires_in: adminConfig.adminTokenExpiry }); adminConfig = await AdminConfigModel.update({ adminAccessToken: access_token, adminRefreshToken: refresh_token, adminTokenExpiry: expire_time }, { where: { id: rcAccountId } }); } const callsAggregationData = await rcSDK.getCallsAggregationData({ token: { access_token: adminConfig.adminAccessToken, token_type: 'Bearer' }, timezone, timeFrom, timeTo, groupBy: groupBy == 'undefined' ? CALL_AGGREGATION_GROUPS[0] : groupBy }); var callLogStats = []; var itemKeys = []; for (const record of callsAggregationData.data.records) { if(!record?.info?.name){ continue; } itemKeys.push(record.info.name); var dataCounter = record.counters; var inboundCallCount = dataCounter.callsByDirection.values.inbound; var outboundCallCount = dataCounter.callsByDirection.values.outbound; var answeredCallCount = dataCounter.callsByResponse.values.answered; // keep 2 decimal places var answeredCallPercentage = inboundCallCount === 0 ? '0%' : `${((answeredCallCount / inboundCallCount) * 100).toFixed(2)}%`; var totalTalkTime = Number(record.timers.allCalls.values) === 0 ? 0 : Number(record.timers.allCalls.values).toFixed(2); var averageTalkTime = Number(totalTalkTime) === 0 ? 0 : (Number(totalTalkTime) / (inboundCallCount + outboundCallCount)).toFixed(2); callLogStats.push({ name: record.info.name, inboundCallCount, outboundCallCount, answeredCallCount, answeredCallPercentage, totalTalkTime, averageTalkTime }); } return { callLogStats, itemKeys, groupedBy: callsAggregationData.data.groupedBy, groupKeys: CALL_AGGREGATION_GROUPS }; } catch (error) { console.error(error); return { callLogStats: {} }; } } async function getUserReport({ rcAccountId, rcExtensionId, timezone, timeFrom, timeTo }) { try { if (!process.env.RINGCENTRAL_SERVER || !process.env.RINGCENTRAL_CLIENT_ID || !process.env.RINGCENTRAL_CLIENT_SECRET) { return { callLogStats: {} }; } const rcSDK = new RingCentral({ server: process.env.RINGCENTRAL_SERVER, clientId: process.env.RINGCENTRAL_CLIENT_ID, clientSecret: process.env.RINGCENTRAL_CLIENT_SECRET, redirectUri: `${process.env.APP_SERVER}/ringcentral/oauth/callback` }); let adminConfig = await AdminConfigModel.findByPk(rcAccountId); const isTokenExpired = adminConfig.adminTokenExpiry < new Date(); if (isTokenExpired) { const { access_token, refresh_token, expire_time } = await rcSDK.refreshToken({ refresh_token: adminConfig.adminRefreshToken, expires_in: adminConfig.adminTokenExpiry, refresh_token_expires_in: adminConfig.adminTokenExpiry }); adminConfig = await AdminConfigModel.update({ adminAccessToken: access_token, adminRefreshToken: refresh_token, adminTokenExpiry: expire_time }, { where: { id: rcAccountId } }); } const callLogData = await rcSDK.getCallLogData({ extensionId: rcExtensionId, token: { access_token: adminConfig.adminAccessToken, token_type: 'Bearer' }, timezone, timeFrom, timeTo }); // phone activity const inboundCallCount = callLogData.records.filter(call => call.direction === 'Inbound').length; const outboundCallCount = callLogData.records.filter(call => call.direction === 'Outbound').length; const answeredCallCount = callLogData.records.filter(call => call.direction === 'Inbound' && (call.result === 'Call connected' || call.result === 'Accepted' || call.result === 'Answered Not Accepted')).length; const answeredCallPercentage = answeredCallCount === 0 ? '0%' : `${((answeredCallCount / (inboundCallCount || 1)) * 100).toFixed(2)}%`; // phone engagement const totalTalkTime = Math.round(callLogData.records.reduce((acc, call) => acc + (call.duration || 0), 0) / 60) || 0; const averageTalkTime = Math.round(totalTalkTime / (inboundCallCount + outboundCallCount)) || 0; const smsLogData = await rcSDK.getSMSData({ extensionId: rcExtensionId, token: { access_token: adminConfig.adminAccessToken, token_type: 'Bearer' }, timezone, timeFrom, timeTo }); const smsSentCount = smsLogData.records.filter(sms => sms.direction === 'Outbound').length; const smsReceivedCount = smsLogData.records.filter(sms => sms.direction === 'Inbound').length; const reportStats = { callLogStats: { inboundCallCount, outboundCallCount, answeredCallCount, answeredCallPercentage, totalTalkTime, averageTalkTime }, smsLogStats: { smsSentCount, smsReceivedCount } }; return reportStats; } catch (error) { console.error(error); return null; } } async function getUserMapping({ user, hashedRcAccountId, rcExtensionList }) { const adminConfig = await getAdminSettings({ hashedRcAccountId }); const platformModule = connectorRegistry.getConnector(user.platform); if (platformModule.getUserList) { const proxyId = user.platformAdditionalInfo?.proxyId; let proxyConfig = null; if (proxyId) { proxyConfig = await Connector.getProxyConfig(proxyId); if (!proxyConfig?.operations?.getUserList) { return []; } } const authType = await platformModule.getAuthType({ proxyId, proxyConfig }); let authHeader = ''; switch (authType) { case 'oauth': const oauthApp = oauth.getOAuthApp((await platformModule.getOauthInfo({ tokenUrl: user?.platformAdditionalInfo?.tokenUrl, hostname: user?.hostname, proxyId, proxyConfig }))); // eslint-disable-next-line no-param-reassign user = await oauth.checkAndRefreshAccessToken(oauthApp, user); authHeader = `Bearer ${user.accessToken}`; break; case 'apiKey': const basicAuth = platformModule.getBasicAuth({ apiKey: user.accessToken }); authHeader = `Basic ${basicAuth}`; break; } const crmUserList = await platformModule.getUserList({ user, authHeader, proxyConfig }); const userMappingResult = []; const newUserMappings = []; for (const crmUser of crmUserList) { const existingMapping = adminConfig?.userMappings?.find(u => u.crmUserId == crmUser.id); let existingMappingRcExtensionIds = []; // TEMP: backward compatibility for string value if (existingMapping?.rcExtensionId) { if (typeof (existingMapping.rcExtensionId) === 'string') { existingMappingRcExtensionIds = [existingMapping.rcExtensionId]; } else { existingMappingRcExtensionIds = existingMapping.rcExtensionId; } } const rcExtension = rcExtensionList.filter(e => existingMappingRcExtensionIds.includes(e.id)); // Case: existing mapping if (existingMapping) { userMappingResult.push({ crmUser: { id: crmUser.id, name: crmUser.name ?? '', email: crmUser.email ?? '', }, rcUser: rcExtension.map(e => ({ extensionId: e.id, name: e?.name || `${e.firstName} ${e.lastName}`, extensionNumber: e?.extensionNumber ?? '', email: e?.email ?? '' })) }); } // Case: new mapping else { const rcExtensionForNewMapping = rcExtensionList.find(u => u.email === crmUser.email || u.name === crmUser.name || (`${u.firstName} ${u.lastName}` === crmUser.name) ); if (rcExtensionForNewMapping) { userMappingResult.push({ crmUser: { id: crmUser.id, name: crmUser.name ?? '', email: crmUser.email ?? '', }, rcUser: [{ extensionId: rcExtensionForNewMapping.id, name: rcExtensionForNewMapping.name || `${rcExtensionForNewMapping.firstName} ${rcExtensionForNewMapping.lastName}`, extensionNumber: rcExtensionForNewMapping?.extensionNumber ?? '', email: rcExtensionForNewMapping?.email ?? '' }] }); newUserMappings.push({ crmUserId: crmUser.id.toString(), rcExtensionId: [rcExtensionForNewMapping.id.toString()] }); } else { userMappingResult.push({ crmUser: { id: crmUser.id, name: crmUser.name ?? '', email: crmUser.email ?? '', }, rcUser: [] }); } } } // One-time init if (!adminConfig?.userMappings) { const initialUserMappings = []; for (const userMapping of userMappingResult) { if (userMapping.rcUser?.extensionId) { initialUserMappings.push({ crmUserId: userMapping.crmUser.id.toString(), rcExtensionId: [userMapping.rcUser.extensionId.toString()] }); } } await upsertAdminSettings({ hashedRcAccountId, adminSettings: { userMappings: initialUserMappings } }); } // Incremental update if (newUserMappings.length > 0) { // TEMP: convert string to array if (adminConfig?.userMappings) { adminConfig.userMappings = adminConfig.userMappings.map(u => ({ ...u, rcExtensionId: [u.rcExtensionId] })); await upsertAdminSettings({ hashedRcAccountId, adminSettings: { userMappings: [...adminConfig.userMappings, ...newUserMappings] } }); } else { await upsertAdminSettings({ hashedRcAccountId, adminSettings: { userMappings: [...newUserMappings] } }); } } return userMappingResult; } return []; } exports.validateAdminRole = validateAdminRole; exports.upsertAdminSettings = upsertAdminSettings; exports.getAdminSettings = getAdminSettings; exports.updateAdminRcTokens = updateAdminRcTokens; exports.getServerLoggingSettings = getServerLoggingSettings; exports.updateServerLoggingSettings = updateServerLoggingSettings; exports.getAdminReport = getAdminReport; exports.getUserReport = getUserReport; exports.getUserMapping = getUserMapping;