Issues with the platformClient SDK not being executed or accessible within a JavaScript module despite successful script loading

Summary: While integrating the platformClient SDK in a web application, the script is successfully loaded (HTTP status 200 OK), but it appears not to be executed or accessible within the scope of a JavaScript module (main.js). This results in errors indicating that platformClient is undefined when attempting to access it via platformClient.ApiClient.instance.

Technical Details:

  • Script Loading:
    • URL: https://qpc-demo-app.qpctranslate.com/purecloud-platform-client-v2.min.js
    • Status: 200 OK
    • Content-Type: application/javascript; charset=UTF-8
    • Cache-Control: public, max-age=0
  • CORS and Fetch Settings:
    • Access-Control-Allow-Credentials: true
    • Sec-Fetch-Mode: no-cors
  • Browser Execution Environment:
    • The script is loaded but not accessible within the module's scope, resulting in TypeError: Cannot read properties of undefined (reading 'ApiClient').

Steps to Reproduce:

  1. Load the application in a web browser with clear cache or in incognito mode.
  2. Observe the network requests to confirm the script is loaded without errors.
  3. Check the JavaScript console for errors related to platformClient upon executing functions or accessing properties from the SDK.

Expected vs. Actual Behavior:

  • Expected: The platformClient SDK should be accessible and functional within the application, allowing API client instantiation and subsequent API calls.
  • Actual: The script is loaded but not recognized within the modular JavaScript environment, resulting in runtime errors indicating platformClient is undefined.

Request for Assistance:

Given these details, I am seeking:

  1. Clarification on the correct usage of the SDK with modern JavaScript modules.
  2. Information on any known issues with script loading in different browser environments or specific configurations that might prevent the SDK from being recognized or executed correctly.
  3. Recommendations for best practices in handling the SDK loading to ensure compatibility and functionality across various browser environments.

Issue Description:

  • Problem Summary: The platform-client-sdk-javascript successfully loads (HTTP status 200 OK) on our web application, but the SDK does not function as expected. Attempts to access methods on window.platformClient.ApiClient.instance result in runtime errors, indicating window.platformClient is undefined.
  • Impact: This issue prevents our application from initializing the client and managing conversations, critical functionalities provided by the SDK.

Technical Details:

  • Request URL: https://qpc-demo-app.qpctranslate.com/purecloud-platform-client-v2.min.js
  • Response Status: 200 OK
  • Content Type: application/javascript; charset=UTF-8
  • Browser Console Errors: Uncaught TypeError: Cannot read properties of undefined (reading 'ApiClient')

Steps to Reproduce:

  1. Open the application in a browser (Chrome, version 124).
  2. Use the browser's developer tools to confirm the SDK loads correctly.
  3. Execute the script that attempts to initialize platformClient.ApiClient.instance.

Expected vs. Actual Behavior:

  • Expected: After loading, platformClient should be accessible globally, allowing API client instantiation and API operations.
  • Actual: The script loads but seems not to be recognized or is inaccessible in the JavaScript module scope, resulting in platformClient being undefined.

Additional Context and Configuration:

  • Environment: The issue has been replicated in multiple environments, including development and staging, under similar network conditions without CSP restrictions that would affect script execution.
  • SDK Version: Latest as fetched from https://qpc-demo-app.qpctranslate.com/purecloud-platform-client-v2.min.js. This is downloaded on schedule midnight every day from sdk-cdn.mypurecloud.com/javascript/latest/purecloud-platform-client-v2.min.js.
  • Other Observations: The script tag for the SDK is included in the HTML before any scripts that use it, and no browser extensions or console errors related to security or CORS have been observed that might explain the malfunction.

This is typically caused by how you're packaging your web app for deployment. You'll need to investigate your webpack/rollup/browserify/etc. config to ensure that it's using an appropriate entry point and including the library correctly.

There's a blueprint for React <18 here: https://developer.genesys.cloud/blueprints/react-app-with-genesys-cloud-sdk/.

issue we're encountering with the platformClient SDK script. Despite the script being loaded successfully, it is not accessible within our module's scope, resulting in a TypeError: Cannot read properties of undefined (reading 'ApiClient').

Context and Configuration:

  1. Environment Setup:
  • We are using a Node.js server to serve our static files and handle script downloads.
  • The server is configured to download the latest version of the platformClient SDK every midnight from the Genesys CDN and serve it from a local directory.
  1. Script Download and Serving:
  • The script is downloaded and saved locally upon server start and subsequently scheduled to download at midnight every day.
  • We serve the static files, including the downloaded SDK, from the /app directory.
  1. HTML Script Loading:
  • In our index.html, we dynamically load the platformClient script and ensure it is loaded before our main application script (main.js).

Findings:

  1. Script Download and Serving:
  • The script is downloaded and saved correctly upon server start and scheduled for daily updates.
  • The script is served from the /app directory and accessible via the URL used in index.html.
  1. HTML Script Loading:
  • The platformClient script is dynamically loaded and ensures it is available before the main application script runs.
  • Detailed console logging indicates the sequence of script loading events.

Given these steps and configurations, it seems that the issue might be related to how the script is being accessed within the module scope. We have ensured that the script is loaded correctly and accessible globally before the main application script is executed. However, if there are any specific configurations or best practices for packaging the web app to include the library correctly, please advise.

You haven't provided any details about what your client app is or does, so I can't provide any suggestions. I can confirm that the CDN example in the SDK documentation works exactly as-is. https://developer.genesys.cloud/devapps/sdk/docexplorer/purecloudjavascript/

Application Context:

My application is a web-based email translation tool using the Genesys Cloud API. It dynamically loads the platformClient SDK, initializes the API client, and translates emails between agents and customers.

Implementation Details:

  • Server-Side: Node.js server downloads the latest SDK version nightly and serves it.
  • Client-Side: The script is dynamically loaded in index.html and the platformClient is used to handle API interactions.

Problem:

The SDK script loads but isn't accessible within the module's scope, resulting in TypeError: Cannot read properties of undefined (reading 'ApiClient').

Steps Taken:

  1. Verified Script Path and Access: Confirmed the script is correctly downloaded and accessible from the server.
  2. Initialization Check: Implemented a mechanism to ensure platformClient is loaded before using it.

Request for Assistance:

Could you provide guidance on any specific configurations or best practices to ensure the platformClient SDK is correctly included and accessible within the module's scope?

Please see my previous reply. Thanks.

Hi Tim

I am experiencing the same issue after following the guide you provided, please let me know if you require any code snippets as I will gladly provide them to show that I have followed the suggestions and still encounter the issue on the client side

Yes, providing details about your client app would be helpful.

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.');
    }
});

This condition is always going to be false because you never attempt to set or initialize the variable platformClient.

Please refer to the For direct use in a browser script section here. It has one line of code. You need to use it in your app.

Hi Tim

Thank you for the feedback but still having the same error :

conversationid=7444…5&language=en-us:25 
 platformClient is not loaded properly.
script.onload	@	?conversationid=7444…5&language=en-us:25
load (async)		
(anonymous)	@	?conversationid=7444…5&language=en-us:15

Here is the updated code to ensure the modules are loaded :

<!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>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
  <link rel="stylesheet" href="./styles/style.css">
  <script>
    console.log('Creating script element to load platformClient SDK');
    var script = document.createElement('script');
    script.src = "https://qpc-demo-app.qpctranslate.com/purecloud-platform-client-v2.min.js";
    script.onload = function() {
      console.log('Script onload triggered');
      if (typeof window.platformClient !== 'undefined') {
        console.log('platformClient script loaded successfully');
        var mainScript = document.createElement('script');
        mainScript.type = "module";
        mainScript.src = "./scripts/main.js";
        document.body.appendChild(mainScript);
        console.log('main.js script appended to body');
      } else {
        console.error('platformClient is not loaded properly.');
      }
    };
    script.onerror = function() {
      console.error('Failed to load platformClient SDK');
    };
    document.head.appendChild(script);
    console.log('platformClient script appended to head');
  </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="send-message-button" 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>
</body>
</html>

and the code giving the error :

import { addMessage, updateScroll } from './view.js';
import translate from './translate-service.js';
import config from './config.js';

let client, conversationsApi;

async function initializePlatformClient() {
    return new Promise((resolve, reject) => {
        console.log('Checking for platformClient');
        if (window.platformClient) {
            console.log('platformClient found');
            resolve(window.platformClient);
        } else {
            console.log('Waiting for platformClient');
            const interval = setInterval(() => {
                if (window.platformClient) {
                    clearInterval(interval);
                    console.log('platformClient found');
                    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 {
        console.log('Initializing platformClient');
        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.errorconsole.error('Missing conversationid in the URL parameters.');
    }

    // Add event listener for the send message button
    document.getElementById('send-message-button').addEventListener('click', sendMessage);
});

I would expect so, you need to use the code in the example.

Hi Tim

I have updated my code according to the guide you provided and now I get this error :

Initializing platformClient
main.js:142 Received message: {protocol: 'purecloud-client-apps', purecloudEventType: 'appLifecycleHook', hook: 'bootstrap'}hook: "bootstrap"protocol: "purecloud-client-apps"purecloudEventType: "appLifecycleHook"[[Prototype]]: Object
main.js:32  Error initializing platformClient: Error: platformClient not loaded in time
    at main.js:18:20
loadPlatformClient @ main.js:32
await in loadPlatformClient (async)
(anonymous) @ main.js:146
main.js:47  Client is not initialized.
initClient @ main.js:47
(anonymous) @ main.js:152
main.js:157  [Initial Setup] Setup error: TypeError: Cannot read properties of undefined (reading 'getConversationsEmail')
    at HTMLDocument.<anonymous> (main.js:154:61)

Here is my updated code :

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chat Listener</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
  <link rel="stylesheet" href="./styles/style.css">
  <script src="https://sdk-cdn.mypurecloud.com/javascript/latest/purecloud-platform-client-v2.min.js"></script>
  <script type="module" src="./scripts/main.js"></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="send-message-button" 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>
</body>
</html>

and

import { addMessage, updateScroll } from './view.js';
import translate from './translate-service.js';
import config from './config.js';

let client, conversationsApi;

function waitForPlatformClient() {
    return new Promise((resolve, reject) => {
        const checkInterval = setInterval(() => {
            if (window.platformClient) {
                clearInterval(checkInterval);
                resolve(window.platformClient);
            }
        }, 100);

        setTimeout(() => {
            clearInterval(checkInterval);
            reject(new Error('platformClient not loaded in time'));
        }, 30000); // Wait for a maximum of 30 seconds
    });
}

async function loadPlatformClient() {
    try {
        console.log('Initializing platformClient');
        const platformClient = await waitForPlatformClient();
        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.');
    }

    // Add event listener for the send message button
    document.getElementById('send-message-button').addEventListener('click', sendMessage);
});

Can you please adice how to resolve this error

I apologize if I've been unclear about which one line of code in the example you should be using. It's this one:

Nothing automatically injects the SDK into a variable in the window. You must follow the documentation and use the module in your code.

Hi Tim

Thank you

I have updated my code accordingly

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chat Listener</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
  <link rel="stylesheet" href="./styles/style.css">
  <script src="https://sdk-cdn.mypurecloud.com/javascript/latest/purecloud-platform-client-v2.min.js"></script>
  <script type="text/javascript">
    // Obtain a reference to the platformClient object
    const platformClient = require('platformClient');
  </script>
  <script type="module" src="./scripts/main.js"></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="send-message-button" 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>
</body>
</html>

i still get this error

Received message: {rootOffset: DOMRect, rootTitle: 'Integrations - Genesys Cloud Admin', rootNativePortId: 103, rootOrigin: 'https://apps.euw2.pure.cloud'}
main.js:32  Error initializing platformClient: Error: platformClient not loaded in time
    at main.js:18:20
loadPlatformClient @ main.js:32
await in loadPlatformClient (async)
(anonymous) @ main.js:146
main.js:47  Client is not initialized.
initClient @ main.js:47
(anonymous) @ main.js:152
main.js:157  [Initial Setup] Setup error: TypeError: Cannot read properties of undefined (reading 'getConversationsEmail')
    at HTMLDocument.<anonymous> (main.js:154:61)

Those are entirely separate and unrelated variables. When you set the value to one variable, you need to then use that same variable later on when you access it.

This topic was automatically closed 31 days after the last reply. New replies are no longer allowed.