Here is my code that spins up the web services and downloads the sdk :
const express = require('express');
const morgan = require('morgan');
const cors = require('cors');
const fetch = require('node-fetch');
const cron = require('node-cron');
const fs = require('fs');
const path = require('path');
const app = express();
const port = process.env.PORT || 80;
// Specify the root of the /app directory to store the downloaded JS file
const appRoot = '/app';
// Ensure app root exists, just in case it's run somewhere where it might not exist
if (!fs.existsSync(appRoot)) {
fs.mkdirSync(appRoot, { recursive: true });
}
// Allowed CORS origins
const allowedOrigins = [
'https://apps.mypurecloud.com', 'https://apps.use2.us-gov-pure.cloud', 'https://apps.usw2.pure.cloud',
'https://apps.cac1.pure.cloud', 'https://apps.sae1.pure.cloud', 'https://apps.mypurecloud.de',
'https://apps.mypurecloud.ie', 'https://apps.euw2.pure.cloud', 'https://apps.euc2.pure.cloud',
'https://apps.mec1.pure.cloud', 'https://apps.aps1.pure.cloud', 'https://apps.apne2.pure.cloud',
'https://apps.mypurecloud.com.au', 'https://apps.mypurecloud.jp', 'https://apps.apne3.pure.cloud',
'https://qpc-demo-app.qpctranslate.com','https://sdk-cdn.mypurecloud.com'
];
app.use(cors({
origin: function(origin, callback) {
if (!origin) return callback(null, true);
if (allowedOrigins.includes(origin)) {
return callback(null, true);
}
return callback(new Error('The CORS policy for this site does not allow access from the specified Origin.'), false);
},
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'],
allowedHeaders: '*',
credentials: true,
optionsSuccessStatus: 200
}));
app.use(morgan('tiny'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Serve static files from /app
app.use(express.static(appRoot));
// Function to download the latest script from CDN
async function downloadLatestScript() {
// Correct URL to an absolute URL
const url = 'https://sdk-cdn.mypurecloud.com/javascript/latest/purecloud-platform-client-v2.min.js';
try {
console.log(`Attempting to download script from: ${url}`);
const response = await fetch(url);
if (response.ok) {
const data = await response.text();
const filePath = path.join(appRoot, 'purecloud-platform-client-v2.min.js');
fs.writeFileSync(filePath, data);
console.log(`Script downloaded and saved to ${filePath}`);
} else {
console.log(`Failed to fetch: ${response.status} ${response.statusText}`);
}
} catch (error) {
console.error(`Error downloading script from ${url}: ${error.message}`);
}
}
// Schedule the script download task to run at midnight every day
cron.schedule('0 0 * * *', downloadLatestScript);
// Download the script immediately when the server starts
downloadLatestScript();
app.listen(port, () => {
console.log(`[Server] HTTP server listening on port ${port}`);
});
Here is my HTML file for the front end
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Chat Listener</title>
<!-- Bulma CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
<!-- Font Awesome -->
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
<!-- Custom Styles -->
<link rel="stylesheet" href="./styles/style.css">
<!-- SDK Script -->
<script>
var script = document.createElement('script');
script.src = "https://qpc-demo-app.qpctranslate.com/purecloud-platform-client-v2.min.js";
script.onload = function() {
if (typeof platformClient !== 'undefined') {
window.platformClient = platformClient;
var mainScript = document.createElement('script');
mainScript.type = "module";
mainScript.src = "./scripts/main.js";
document.body.appendChild(mainScript);
} else {
console.error('platformClient is not loaded properly.');
}
};
script.onerror = function() {
console.error('Failed to load platformClient SDK');
};
document.head.appendChild(script);
</script>
</head>
<body>
<div class="agent-assistant">
<div class="container acd-chat-conversation chat-conversation">
<div class="field acd-message-pane message-pane" id="agent-assist"></div>
<div class="chat-box acd-chat-reply">
<form id="chat-form" name="message">
<textarea id="message-textarea" class="message-input form-control acd-chat-reply-textarea chat-text" spellcheck="true" aria-label="Message Text"></textarea>
</form>
<div class="bottom-chat-box">
<button id="btn-copy" title="Copy" type="button" class="btn-light" aria-label="Copy Message"><i class="far fa-copy"></i></button>
<button id="btn-send" title="Translate and Send" type="button" class="btn" aria-label="Translate and Send Message"><i class="fas fa-paper-plane"></i></button>
</div>
</div>
</div>
</div>
<!-- Main JavaScript is loaded dynamically after SDK is ready -->
</body>
</html>
And here is my main.js file that is not able to initialize the Api client :
import { addMessage, updateScroll } from './view.js';
import translate from './translate-service.js';
import config from './config.js';
// Placeholder variables
let client, conversationsApi;
async function initializePlatformClient() {
return new Promise((resolve, reject) => {
if (window.platformClient) {
resolve(window.platformClient);
} else {
const interval = setInterval(() => {
if (window.platformClient) {
clearInterval(interval);
resolve(window.platformClient);
}
}, 100);
setTimeout(() => {
clearInterval(interval);
reject(new Error('platformClient not loaded in time'));
}, 5000); // Wait for a maximum of 5 seconds
}
});
}
async function loadPlatformClient() {
try {
const platformClient = await initializePlatformClient();
client = platformClient.ApiClient.instance;
client.setEnvironment(config.genesysCloud.REGION);
conversationsApi = new platformClient.ConversationsApi();
console.log('Platform client loaded and initialized successfully.');
} catch (error) {
console.error('Error initializing platformClient:', error);
}
}
let currentConversationId = '';
let translationData = null;
let genesysCloudLanguage = 'en-us';
let customerEmail = '';
let customerName = '';
let agentEmail = '';
let agentName = '';
let subject = '';
async function initClient() {
if (!client) {
console.error('Client is not initialized.');
return;
}
try {
const state = JSON.stringify({ conversationId: currentConversationId, language: genesysCloudLanguage });
await client.loginImplicitGrant(config.CLIENT_ID, config.redirectUri, { state });
console.log('Logged in to Genesys Cloud');
} catch (error) {
console.error('Error logging in to Genesys Cloud:', error);
}
}
async function extractEmailBody(emailContent) {
if (!emailContent) return '';
const quotedMessagePattern = /On .* wrote:/;
const match = emailContent.search(quotedMessagePattern);
const latestMessageContent = match !== -1 ? emailContent.slice(0, match) : emailContent;
const lines = latestMessageContent.split(/\r?\n/);
let inHeader = true;
return lines.filter(line => {
if (inHeader && /\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}/.test(line)) {
inHeader = false;
return false;
}
inHeader = line === '';
return !inHeader && !/(--|Kind regards,|Best regards,|Sincerely,)/.test(line);
}).join('\n').trim();
}
async function getEmailDetails(emailData) {
console.log('[getEmailDetails] Extracting email details...');
if (!emailData || !emailData.textBody) {
console.error('[getEmailDetails] Invalid or incomplete email data:', emailData);
return;
}
const emailBody = await extractEmailBody(emailData.textBody);
customerEmail = emailData.from.email;
customerName = emailData.from.name;
agentEmail = emailData.to[0].email;
agentName = emailData.to[0].name;
subject = emailData.subject;
try {
const translatedText = await translate.translateText(emailBody, genesysCloudLanguage);
addMessage(translatedText, 'customer');
console.log('[getEmailDetails] Email body translated successfully.');
} catch (error) {
console.error('[getEmailDetails] Error translating email body:', error);
}
}
async function sendMessage() {
const message = document.getElementById('message-textarea').value;
try {
const translatedData = await translate.translateText(message, getSourceLanguage());
const messageData = {
to: [{ email: customerEmail, name: customerName }],
from: { email: agentEmail, name: agentName },
subject,
textBody: translatedData,
historyIncluded: true
};
await conversationsApi.postConversationsEmailMessages(currentConversationId, messageData);
console.log('[sendMessage] Translated email sent to customer.');
} catch (error) {
console.error('[sendMessage] Error in message sending process:', error);
}
}
function getSourceLanguage() {
return translationData ? translationData.source_language : 'en';
}
function validateOrigin(origin) {
const allowedOrigins = [
'https://apps.mypurecloud.com', 'https://apps.use2.us-gov-pure.cloud',
'https://apps.usw2.pure.cloud', 'https://apps.cac1.pure.cloud',
'https://apps.sae1.pure.cloud', 'https://apps.mypurecloud.de',
'https://apps.mypurecloud.ie', 'https://apps.euw2.pure.cloud',
'https://apps.euc2.pure.cloud', 'https://apps.mec1.pure.cloud',
'https://apps.aps1.pure.cloud', 'https://apps.apne2.pure.cloud',
'https://apps.mypurecloud.com.au', 'https://apps.mypurecloud.jp',
'https://apps.apne3.pure.cloud', 'https://qpc-demo-app.qpctranslate.com',
'https://sdk-cdn.mypurecloud.com'
];
return allowedOrigins.includes(origin);
}
window.addEventListener('message', (event) => {
if (!validateOrigin(event.origin)) {
console.error('Received message from unauthorized origin:', event.origin);
return;
}
console.log('Received message:', event.data);
});
document.addEventListener('DOMContentLoaded', async () => {
await loadPlatformClient();
const urlParams = new URLSearchParams(window.location.search);
currentConversationId = urlParams.get('conversationid');
genesysCloudLanguage = urlParams.get('language') || navigator.language || 'en-US';
if (currentConversationId) {
await initClient();
try {
const conversationData = await conversationsApi.getConversationsEmail(currentConversationId);
await getEmailDetails(conversationData);
} catch (error) {
console.error('[Initial Setup] Setup error:', error);
}
} else {
console.error('Missing conversationid in the URL parameters.');
}
});