Adding 'IVA Wakeup' script used for Bot Tango example

This commit is contained in:
Peter Morton 2025-10-27 12:27:23 -05:00
parent f5d69bbc01
commit 22a9e9e77a
8 changed files with 1618 additions and 0 deletions

17
IVA-Wakeup/UI/After.js Normal file
View File

@ -0,0 +1,17 @@
store.snippets.get('disabler').toggle(payload?.metadata?.outputs?.disableMessengerInput, payload?.metadata?.outputs?.messengerInputPlaceholder || 'Enter question here');
console.log('disable?');
console.log(payload?.metadata?.outputs?.disableMessengerInput);
if (payload?.metadata?.outputs?.hideMessenger == true) {
store.main.hideMessenger();
};
if (payload?.metadata?.outputs?.minimizeAfterFiveSeconds == true) {
setTimeout(() => { store.main.isMessengerVisible = false }, 5000);
};
if (payload?.metadata?.outputs?.disableMessengerInput === true) {
store.main.disableMessengerInput();
}
callback(payload);

60
IVA-Wakeup/UI/Before.js Normal file
View File

@ -0,0 +1,60 @@
console.log("inside before addd");
console.log(payload);
let pramstudioConvoId = "";
if (payload?.conversationId) {
console.log('inside');
console.log(payload?.conversationId);
pramstudioConvoId = payload?.conversationId;
}
store.isAuthenticated = checkCookieExists("PGXREGSITE_LOGIN") || (window.location.href.includes('ivastudio.ai') && window.location.href.includes('testAuth=1'));
payload.metadata = {
channel: "WEB",
companyName: "Lexington Law",
companyWebsite: "lexingtonlaw.com",
agentType: store.isAuthenticated ? "Paralegal" : "Credit Representative",
callerType: "client",
serviceType: "case",
tenantName: "lexingtonLaw",
location: window.location,
isAuthenticated: store.isAuthenticated,
isSaleCompleted: sessionStorage.getItem("signupComplete") || "", //Conversion tracking snippet, string
//studioConvoId = pramstudioConvoId
};
payload.metadata.studioConvoId = pramstudioConvoId;
try {
const customerData = JSON.parse(sessionStorage.getItem("customerData") || "{}");
const userDetails = JSON.parse(sessionStorage.getItem("userDetails") || "{}");
const ucidSession = sessionStorage.getItem("ucid");
// --- UCID priority: userDetails > customerData > sessionStorage ---
payload.metadata.ucid =
userDetails?.ucid ??
customerData?.ucid ??
(ucidSession ? ucidSession.replace(/^"|"$/g, "") : undefined) ??
payload.metadata.ucid;
// --- First pass: customerData ---
payload.metadata.firstName = customerData?.firstName ?? payload.metadata.firstName;
payload.metadata.lastName = customerData?.lastName ?? payload.metadata.lastName;
payload.metadata.email = customerData?.email ?? payload.metadata.email;
// --- Second pass: userDetails (overrides customerData if present) ---
payload.metadata.firstName = userDetails?.firstName ?? payload.metadata.firstName;
payload.metadata.lastName = userDetails?.lastName ?? payload.metadata.lastName;
payload.metadata.email = userDetails?.email ?? payload.metadata.email;
// --- ClientID only if authenticated ---
if (store.isAuthenticated) {
payload.metadata.clientID = userDetails?.clientID ?? payload.metadata.clientID;
}
// const data1 = JSON.parse(sessionStorage.getItem("XSRF-TOKEN"));
// payload.metadata.xsrf_token = data1;
} catch (e) {
//payload.metadata.isAuthenticated = false;
}
callback(payload);

685
IVA-Wakeup/UI/OnLoad.js Normal file
View File

@ -0,0 +1,685 @@
// START TEMPLATING
window.store = store;
window.checkCookieExists = (cookieName) => {
return document.cookie.includes(`${cookieName}=`);
}
function isMobile() {
return /Mobi|Android/i.test(navigator.userAgent);
}
window.mobileRedirectionCheck = () => {
if (isMobile()) {
store.main.hideMessenger();
}
}
function renderTemplate(template, data) {
// Replace variables like {{variable}} with their corresponding values in data
template = template.replace(/{{\s*(\w+)\s*}}/g, (_, key) => {
return data[key] || '';
});
// Handle v-if directives
template = template.replace(/<(\w+)\s*v-if="([^"]+)"\s*>([\s\S]*?)<\/\1>/g, (_, tag, condition, truePart) => {
// Evaluate the condition in the context of data
const conditionValue = eval(condition.replace(/\b(\w+)\b/g, (match) => data[match] !== undefined ? data[match] : match)); // Replace variables with their values
if (conditionValue) {
// If the condition is true, render the v-if block
return `<${tag}>${truePart}</${tag}>`;
}
return ''; // If the condition is false, render nothing
});
return template;
}
// store.isAuthenticated = checkCookieExists("PGXREGSITE_LOGIN");
// allows user to test authentication if they add &testAuth=1 onto the end of the url and refresh
store.isAuthenticated = checkCookieExists("PGXREGSITE_LOGIN") || (window.location.href.includes('ivastudio.ai') && window.location.href.includes('testAuth=1'));
const template = `${payload.settings.newConversationPlaceholder.code.html}`;
payload.settings.newConversationPlaceholder.code.html = renderTemplate(template, {
isAuthenticated: store.isAuthenticated
});
store.main.setSettings(payload.settings);
(() => {
let lastData = checkCookieExists("PGXREGSITE_LOGIN");
setInterval(() => {
let currentData = store.isAuthenticated = checkCookieExists("PGXREGSITE_LOGIN") || store.isAuthenticated;
payload.settings.newConversationPlaceholder.code.html = renderTemplate(template, {
isAuthenticated: store.isAuthenticated
});
if (store.isAuthenticated && !isLiveChat()) {
// disable
store.main.disableMessengerInput(); // 2-26-2025 commented out so Travis can test user inputs in Dev only
}
if (currentData !== lastData) {
lastData = currentData;
store.main.setSettings(payload.settings);
store.conversation.$reset();
store.conversationEvent.$reset();
store.main.setView("main");
}
}, 1000);
})();
// END TEMPLATING
//VERSION 1.1
store.random = true;
//console.log("payload", payload);
const liveChatOnLaunch = payload.settings.chatOnlyMode == true;
const closeBtns = payload.settings.desktopCloseBtn == true;
if (closeBtns) {
delete store.main.isCloseButtonVisible;
payload.settings.desktopCloseBtn = false;
}
store.main.setSettings(payload.settings);
const getCloseSVG = () => {
return `<svg fill="currentColor" height="12px" width="12px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 492 492" xml:space="preserve"><g><g><path d="M300.188,246L484.14,62.04c5.06-5.064,7.852-11.82,7.86-19.024c0-7.208-2.792-13.972-7.86-19.028L468.02,7.872 c-5.068-5.076-11.824-7.856-19.036-7.856c-7.2,0-13.956,2.78-19.024,7.856L246.008,191.82L62.048,7.872 c-5.06-5.076-11.82-7.856-19.028-7.856c-7.2,0-13.96,2.78-19.02,7.856L7.872,23.988c-10.496,10.496-10.496,27.568,0,38.052 L191.828,246L7.872,429.952c-5.064,5.072-7.852,11.828-7.852,19.032c0,7.204,2.788,13.96,7.852,19.028l16.124,16.116 c5.06,5.072,11.824,7.856,19.02,7.856c7.208,0,13.968-2.784,19.028-7.856l183.96-183.952l183.952,183.952 c5.068,5.072,11.824,7.856,19.024,7.856h0.008c7.204,0,13.96-2.784,19.028-7.856l16.12-16.116 c5.06-5.064,7.852-11.824,7.852-19.028c0-7.204-2.792-13.96-7.852-19.028L300.188,246z"/></g></g></svg>`;
};
const getMinimizeSVG = () => {
return '<svg fill="currentColor" height="12px" width="12px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 492 492" xml:space="preserve"><g> <g><path d="M465.064,207.562H26.908C12.076,207.562,0,219.698,0,234.53v22.804c0,14.832,12.072,27.104,26.908,27.104h438.156 c14.84,0,26.936-12.272,26.936-27.104V234.53C492,219.698,479.904,207.562,465.064,207.562z"/></g></g></svg>';
};
const getCloseLiveChatSVG = () => {
return `<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 52 52" enable-background="new 0 0 52 52" xml:space="preserve"><path d="M25.1,12.3v-3c4,0,7.2,3.2,7.2,7.2h-3l4.6,5.7c0.3,0.3,0.7,0.3,0.9,0l4.6-5.7h-3c0-6.3-5.2-11.4-11.4-11.4 v-3l-5.7,4.6C19,7,19.1,7.4,19.4,7.6L25.1,12.3z"/><path d="M26.3,33.6c-1.2-1.6-3.5-2.7-5.8-3.8c-2.4-1-2.7-2-2.7-3c0-1,0.6-2.1,1.4-2.8c1.3-1.3,2.1-3.2,2.1-5.3 c0-4-2.4-7.5-6.5-7.5h-0.4c-4.1,0-6.5,3.5-6.5,7.5c0,2.2,0.7,4,2.1,5.3c0.8,0.7,1.4,1.7,1.4,2.8c0,1-0.3,2-2.7,3 c-3.4,1.5-6.6,3.1-6.7,6.3C2.3,38.4,3.8,40,5.8,40h17.9c0.3,0,0.7-0.1,1-0.2C24.8,37.6,25.3,35.5,26.3,33.6z"/><path d="M31.8,34.6l6,6l-6,6c-0.6,0.6-0.6,1.6,0,2.1l0.7,0.7c0.6,0.6,1.6,0.6,2.1,0l6-6l6,6c0.6,0.6,1.6,0.6,2.1,0 l0.7-0.7c0.6-0.6,0.6-1.6,0-2.1l-6-6l6-6c0.6-0.6,0.6-1.6,0-2.1l-0.7-0.7c-0.6-0.6-1.6-0.6-2.1,0l-6,6l-6-6c-0.6-0.6-1.6-0.6-2.1,0 l-0.7,0.7C31.2,33,31.2,34,31.8,34.6z"/></svg>`;
};
const addScript = (url, root, callback) => {
const script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
script.onload = callback;
root.appendChild(script);
};
const addLink = (url, root) => {
const link = document.createElement("link");
link.type = "text/css";
link.href = url;
link.rel = "stylesheet";
root.appendChild(link);
};
const waitForElm = function (selector) {
return new Promise((resolve) => {
const root = document.querySelector("body ivas-messenger-ui").shadowRoot;
if (root.querySelector(selector)) return resolve(root.querySelector(selector));
const observer = new MutationObserver((mutations) => {
if (root.querySelector(selector)) {
resolve(root.querySelector(selector));
observer.disconnect();
}
});
observer.observe(root, {
childList: true,
subtree: true,
});
});
};
const injectStyle = () => {
waitForElm("div.app").then((elem) => {
elem.insertAdjacentHTML(
"afterbegin",
`<style>
button#close {
position: absolute;
top: 10px;
right: 15px;
z-index: 11111;
color: #999
}
#close:hover {
color: #666
}
button#minimize {
position: absolute;
top: 10px;
right: 40px;
z-index: 11111;
color: #999
}
#minimize:hover {
color: #666
}
button#livechat {
z-index: 11111;
}
button#debug {
z-index: 11111;
}
.footer{
justify-self:normal !important;
}
</style>`
);
});
};
const sendLiveChatOnOff = (on) => {
let input = on ? "Connecting to live chat" : "Live chat ended";
if (on && !store.main.conversationId) {
store.conversationEvent.sendMessage({
input: input,
metadata: { liveChat: on, nluSupressDefaultAnswer: true },
});
} else if (store.main.conversationId) {
for (const webhook of payload.webhooks) {
if (webhook.enabled) {
axios({
method: 'post',
url: webhook.url,
data: {
event: {
conversationId: store.main.conversationId,
input,
external: true,
metadata: { liveChat: on }
}
}
});
}
}
}
};
const delayLiveChatOnOff = (on, msDelay) => {
setTimeout(() => {
console.log("livechat startup");
sendLiveChatOnOff(on);
}, msDelay);
};
let typingTimeout;
const TYPING_DELAY_MS = 500;
const handleTyping = () => {
clearTimeout(typingTimeout);
typingTimeout = setTimeout(() => {
console.log("TYPING");
console.log("store.main.conversationId");
console.log(store.main.conversationId);
try {
fetch("https://router.usw.ivastudio.ai/ProxyScript/run/66ce521b47ec88c50fb8adf4/current/userTyping", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"event": "TYPING",
"sessionId": store.main.conversationId
}),
});
}
catch (error) {
console.log("something errored when sending typing event");
}
}, TYPING_DELAY_MS);
}
const showHideLiveChat = (livechat) => {
const root = document.querySelector("body ivas-messenger-ui").shadowRoot;
const el = root.getElementById("livechat");
if (livechat === true) {
waitForElm(".footer").then(() => {
let textArea = root.querySelector(".footer textarea");
textArea.addEventListener("input", handleTyping);
});
if (!el) {
//store.main.enableMessengerInput();
// COMMENTING OUT THE FOLLOWING LINES TO HIDE END LIVE CHAT BUTTON PER CREDITS REQUEST
// waitForElm(".footer").then((el) => {
// const div = document.createElement("button");
// div.id = "livechat";
// div.setAttribute("aria-label", "End Live Chat");
// div.title = "End Live Chat";
// div.style.color = payload.settings.actionColor;
// div.innerHTML = getCloseLiveChatSVG();
// div.style.paddingRight = "10px";
// div.onclick = () => {
// sendLiveChatOnOff(false);
// if (store.isAuthenticated) {
// store.main.disableMessengerInput();
// }
// store.conversationEvent.sendMessage("Live Chat Session ended. Can I help you with anything else?");
// //Add a chip titled "Live Chat"
// div.remove();
// };
// el.appendChild(div);
// });
}
} else if (el) {
// livechat == false
el.remove();
}
};
const isLiveChat = () => {
let livechat = false;
for (const [id, participant] of Object.entries(store.conversationEvent.participants)) {
if (participant.type == "agent") livechat = true;
}
return livechat;
};
const debugButtons = (liveChatOn) => {
waitForElm(".footer").then((el) => {
const div = document.createElement("button");
div.id = "debug";
div.setAttribute("aria-label", "Start Live Chat");
div.innerHTML = "Connect to live chat";
div.title = "Connect to live chat";
div.onclick = () => {
store.conversationEvent.sendMessage({
input: "Connect to live chat",
metadata: { liveChat: true },
});
};
el.parentNode.insertBefore(div, el.nextSibling);
});
};
// state variables: connecting to live chat automatically in the messenger visibility event has a sequencing problem. It will get called before axios is loaded and error out.
// This delays the live chat connect until after axios is loaded.
let isAxiosLoaded = false;
let awaitingConnect = false;
addScript("https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js", document.head, () => {
if (awaitingConnect) {
awaitingConnect = false;
delayLiveChatOnOff(true, 1500);
}
isAxiosLoaded = true;
});
window.addEventListener("beforeunload", (event) => {
// doesn't work
if (liveChatOnLaunch && isLiveChat()) {
sendLiveChatOnOff(false);
}
});
// update the state of livechat based on the participants. If there is an "agent" participant type, it's a live chat.
vm.$watch(
() => store.conversationEvent.participants,
async (participants) => {
try {
showHideLiveChat(isLiveChat());
} catch (e) {
console.error(e);
}
}
);
vm.$watch(
() => store.main.isMessengerVisible,
async (isVisible) => {
try {
console.log("messenger visible =", isVisible, store.main.conversationId);
if (isVisible == true) {
const livechat = isLiveChat();
injectStyle();
showHideLiveChat(livechat);
//debugButtons(livechat);
if (closeBtns) {
waitForElm(".app").then((el) => {
const div = document.createElement("button");
div.id = "minimize";
div.title = "Minimize";
div.innerHTML = getMinimizeSVG();
div.onclick = () => {
store.main.hideMessenger();
};
el.appendChild(div);
});
waitForElm(".app").then((el) => {
const div = document.createElement("button");
div.id = "close";
div.title = "Close";
div.innerHTML = getCloseSVG();
div.onclick = () => {
//Added the following line and commented out the proceeding if statement at credit's request in order to ONLY minimize the UI when in live chat instead of closing the session
store.main.hideMessenger();
// if (!isLiveChat() || window.confirm("Live Chat is running. Are you sure you want to close?")) {
// store.main.hideMessenger();
// if (isLiveChat()) {
// sendLiveChatOnOff(false);
// }
// //store.main.setNewUserSession();
// store.conversation.$reset();
// store.conversationEvent.$reset();
// store.main.setView("chat");
// store.main.setNewConversation(true);
// }
};
el.appendChild(div);
});
}
if (liveChatOnLaunch) {
if (!livechat) {
if (isAxiosLoaded === true) {
delayLiveChatOnOff(true, 1500);
} else {
awaitingConnect = true;
}
}
}
}
} catch (e) {
console.error(e);
}
},
{
deep: true,
immediate: true
}
);
window.vm = vm;
window.store = store;
addScript("https://router.usw.ivastudio.ai/ProxyScript/run/66ce521b47ec88c50fb8adf4/current/adaptiveCardImplementation_64dbc87628bd839f69b0c962", document.head, () => {
// Uncomment the following line to load the optional host config script
// addScript('https://router.usw.ivastudio.ai/ProxyScript/run/66ce521b47ec88c50fb8adf4/current/adaptiveCardHostConfig_64dbc87628bd839f69b0c962', document.head);
});
//supplemental code for feedback functionality including waitForElm lines
// const waitForElm = function (selector) {
// return new Promise((resolve) => {
// const root = document.querySelector('body ivas-messenger-ui').shadowRoot;
// if (root.querySelector(selector)) return resolve(root.querySelector(selector));
// const observer = new MutationObserver((mutations) => {
// if (root.querySelector(selector)) {
// resolve(root.querySelector(selector));
// observer.disconnect();
// }
// });
// observer.observe(root, {
// childList: true,
// subtree: true
// });
// });
// };
//END GENERAL ON ENGAGEMENT LOAD CODE
//BEGIN FEEDBACK SPECIFIC CODE
vm.$watch(() => store.conversationEvent.messages, async (value) => {
value.forEach((message, index) => {
if (message.sentBy.hasOwnProperty('type')) {
if (localStorage.getItem(`feedback-${message._id}`) || message?.metadata?.feedbackable != true) {
} else {
window.showFeedback(message._id, message.conversationId, message?.metadata?.transactionId);
}
}
});
}, {
deep: true
});
window.showFeedback = (id, conversationId, transactionId) => {
waitForElm(`div#message-${id}`).then((wrap) => {
try {
// const wrap = document.querySelector("body > se-messenger").shadowRoot.querySelector("#se-messenger-app > div.se-messenger-frame > div > div.se-messenger-body > div");
const scrollToTop = () => {
wrap.scrollTop = wrap.scrollHeight;
};
let devWrap = wrap.querySelector(`div.feedback-wrap`);
if (!devWrap) {
devWrap = document.createElement('div');
devWrap.classList.add('feedback-wrap');
devWrap.innerHTML = `<div class="feedback" style="top: -10px; right: -20px; display: flex; flex-flow: row nowrap; padding-left: 35px; padding: 5px 0 5px 35px;"><a id="se-messenger-feedback-like-${id}" href="#" style=" display: block; padding: 5px;">👍</a><a id="se-messenger-feedback-dislike-${id}" href="#" style=" display: block; padding: 5px;">👎</a></div>`;
wrap.parentElement.querySelector(`div#message-${id} > div`).after(devWrap);
}
devWrap.querySelector(`a#se-messenger-feedback-like-${id}`).onclick = (e) => {
e.preventDefault();
console.log("like!");
devWrap.parentElement.removeChild(devWrap);
send("LIKE");
}
devWrap.querySelector(`a#se-messenger-feedback-dislike-${id}`).onclick = (e) => {
e.preventDefault();
console.log("dislike!");
devWrap.parentElement.removeChild(devWrap);
send("DISLIKE");
}
wrap.parentElement.parentElement.parentElement.scrollTop =
wrap.parentElement.parentElement.parentElement.scrollHeight;
const send = (event) => {
localStorage.setItem(`feedback-${id}`, true);
//update DQ_URL with the URL of the LTS version of your feedback query
fetch('https://router.usw.ivastudio.ai/DynamicQuery/run/66ce521b47ec88c50fb8adf4/current/Feedback_6529ac38d8f54e4b61bc0a99', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
"event": event,
"_id": transactionId
}),
});
};
} catch (e) {
console.log(e);
}
});
};
//END FEEDBACK CODE
//Watcher to keep IVA Open When in LIVE CHAT
vm.$watch(() => store.conversationEvent.messages, async (value) => {
try {
console.log("Msg received");
const livechat = isLiveChat();
if (livechat) {
if (!store.main.isMessengerVisible) {
store.main.showMessenger();
}
}
console.log(livechat);
} catch (e) {
console.error(e);
}
}, {
deep: true
});
window.askIVA =
window.askSky =
window.askSkyWithData =
(input, postBack, configuration, isNew) => {
store.main.showMessenger();
if (isNew) {
store.conversation.$reset();
store.conversationEvent.$reset();
store.main.setView("chat");
store.main.setNewConversation(true);
} else {
store.main.setView("chat");
}
store.conversationEvent.sendMessage({
input,
postBack,
configuration
});
};
window.openChat = () => {
store.main.showMessenger();
// start new
store.conversation.$reset();
store.conversationEvent.$reset();
// start new
store.main.setView("chat");
// start new
store.main.setNewConversation(true);
};
window.closeChat = () => {
store.main.hideMessenger();
};
//BEGIN MASKING
const debounce = (func, timeout = 300) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => { func.apply(this, args); }, timeout);
};
};
const de = debounce((value) => {
value.forEach((message, index) => {
if (message.sentBy.hasOwnProperty('type')) {
if (localStorage.getItem(`feedback-${message._id}`) || message?.metadata?.feedbackable != true) {
} else {
window.showFeedback(message._id, message.conversationId, message?.metadata?.transactionId);
}
} else {
if (message.input) {
let matchRes = window.isMask(message.input);
if (matchRes.includes("####") || matchRes.includes("#%*!")) {
message.input = matchRes;
}
}
}
});
}, 1000);
vm.$watch(() => store.conversationEvent.messages, de, {
deep: true
});
// sendMessage(input, metadata, conversationId) {
// try {
// debugLogging(`Sending message ${input} and metadata: ${JSON.stringify(metadata)}`);
// window.liveChat.sendLiveChatMessage(input);
// const proxyEndpoint = `${store.main.settings.liveChat.apiBaseUrl}/recordUserMessage`;
// const id = window.liveChat.convoId;
// input = window.isMask(input);
// }
// }//abbreviated
// #region Masking
window.isMask = (message) => {
const ssnRegex = /\b(\d{3}[-. ]?\d{2}[-. ]?\d{4}|\d{2}[-. ]\d{4}|\d{9}|\d{3}-\d{2}-\d{4})\b/g;
const ssnRegex2 = /\b(\d{2}(.)\d{4}|\d{3}([- .])\d{3}\3\d{3})\b/g;
const ccRegex = /\b(\d{4}[-. ]?\d{6}[-. ]?\d{4,5}|\d{4}[-. ]?\d{4}[-. ]?\d{4}[-. ]?\d{4})\b/g;
const acctNoRegex = /\b(\d{4}[-.\s]?\d{4}[-.\s]?\d{4}[-.\s]?\d{4})\b/g;
const emailRegex = /\b[a-z0-9._%\+\-—|]+@[a-z0-9.\-—|]+\.[a-z|]{2,6}\b/g;
const birthDateRegex = /\b^(3[01]|[12][0-9]|0?[1-9])(\/|-)(1[0-2]|0?[1-9])\2([0-9]{2})?[0-9]{2}$\b/g;
const phoneNumberRegex = /^(\+\d{1,2}\s)?\(?\d{3}\)?[\s.-]\d{3}[\s.-]\d{4}$\b/g;
let regexArr = [];
//regexArr.push(acctNoRegex);
regexArr.push(ccRegex);
regexArr.push(ssnRegex);
regexArr.push(ssnRegex2);
//regexArr.push(emailRegex);
//regexArr.push(birthDateRegex);
//regexArr.push(phoneNumberRegex);
regexArr.forEach((element) => {
message = message.replace(element, "####");
})
let badWords = [
"anal",
"anus",
"arse",
"ass",
"ballsack",
"bastard",
"bitch",
"biatch",
"blowjob",
"bollock",
"bollok",
"buttplug",
"clitoris",
"cock",
"coon",
"cunt",
"damn",
"dick",
"Dildo",
"dyke",
"fag",
"faggot",
"feck",
"fellate",
"fellatio",
"fuck",
"fucking",
"fucks",
"f u c k",
"Goddamn",
"God damn",
"hell",
"homo",
"jerk",
"jizz",
"knobend",
"knob end",
"labia",
"lmao",
"lmfao",
"nigger",
"nigga",
"prick",
"pube",
"pussy",
"queer",
"retard",
"sex",
"bullshit",
"shit",
"slut",
"tit",
"turd",
"twat",
"whore",
"wtf"
];
const regexbadWords = new RegExp(`\\b(?:${badWords.join('|')})\\b`, 'gi');
message = message.replace(regexbadWords, '#%*!');
return message;
};
//END Masking
// #region Response Link Click Handling
store.conversationEvent.chipOnClick = (data) => {
console.log(JSON.stringify(data));
if (data.url) {
window.open(data.url, '_blank');
} else if (data.action) {
switch (data.action) {
case "hideMessenger":
console.log('Got to hide messenger');
store.main.hideMessenger();
break;
}
}
else {
store.conversationEvent.sendMessage(data);
}
}
// endregion

View File

@ -0,0 +1,68 @@
return {
async fetchConversationHistory(conversationId, limit) {
let datahistory = await db.collection("analytics_transactions").aggregate([
{
$match: {
$and: [
{ "conversationId": conversationId },
{ "outputs.datacollect": true }
]
}
}
]).toArray();
console.log('10 line');
let lastElement = datahistory[datahistory.length - 1];
console.log(lastElement?.metadata?.actionType);
let messages = [];
const tx_msg = (direction, text, sentTime) => {
return {
conversationId: conversationId,
direction,
text,
dateSent: new Date(sentTime).toISOString(),
};
};
let counts = { agent: 0, ivas: 0 };
let agentName;
let is_async = null;
for (let msg of datahistory) {
if (msg?.answers.length) {
if (msg?.input !== "") {
messages.push(tx_msg("ToAgent", msg?.input, (msg?.metadata?.createdAt ?? Date.now())));
}
counts.ivas++;
for (let i = 0; i < msg?.answers.length; i++) {
let answer = msg.answers[i];
if (typeof answer !== "string") {
answer = JSON.stringify(answer ?? null);
}
messages.push(tx_msg("ToCustomer", answer, (msg?.metadata?.createdAt ?? Date.now()) + 50 + i));
counts.ivas++;
}
}
if (limit > 0 && messages.length >= limit) break;
}
const summary = {
conversationId: conversationId,
steps: []
};
messages.forEach(msg => {
summary.steps.push({
who: msg.direction === "ToAgent" ? "Customer" : "System",
text: msg.text,
time: new Date(msg.dateSent).toLocaleString()
});
});
let history = ``;
summary.steps.forEach((step, index) => {
history += `<strong>${step.who}:</strong> ${step.text}<br/>`;
});
history+= `<strong>ActionType:</strong> ${lastElement?.metadata?.actionType}`
return history;
}
};

View File

@ -0,0 +1,39 @@
return {
async handleIVAWakeupEvent(event, req, session, settings, redis, smStr, channel, eventPayload) {
try {
const ivaWakeupText = `<p><br data-mce-bogus="1"></p><p><br data-mce-bogus="1"></p><p>IVA-Wakeup</p>`;
if (event.text === ivaWakeupText) {
const nluSettings = settings.nlu;
const result = await axios.post(
`${nluSettings.apiBaseURL}Model/run/${req.params.workspaceId}/${req.params.branch}/${nluSettings.modelName}`,
{
input: "CaseSetup",
conversationId: session.conversationId,
settings: nluSettings.settings,
sessionId: event.sessionId
}
);
eventPayload.options = result?.data?.options;
eventPayload.metadata = {
outputs: result.data?.outputs,
channel: channel
};
eventPayload.input = result.data.answers[0];
eventPayload.metadata.outputs.disableMessengerInput = true;
eventPayload.metadata.outputs.datacollect = true;
await redis.hSet(smStr, {
formFlow: "true",
datacollect: "true",
sentdata: "false"
});
}
} catch (e) {
console.info("Error with IVA-Wakeup");
console.error(e);
}
}
};

View File

@ -0,0 +1,420 @@
console.info("===== Live Chat Proxy Entry - v16.3 =====");
const casettings = CAInterface_settings;
const CA = CAInterface();
const msgrSettings = eval(`settings${casettings.hubSuffixMessenger}`);
let nlu = msgrSettings?.nlu;
const _SELF = `${nlu.apiBaseURL}ProxyScript/run/${req.params.workspaceId}/${req.params.branch}/CAInterface_chat${casettings.hubSuffix}`;
// API Specific
const headers = {
Authorization: `Basic ${buffer.Buffer.from(casettings.token.basicAuth).toString("base64")}`,
"Accept-Language": "en-US,en;q=0.5",
};
const studioToken = msgrSettings ? { Authorization: `Bearer ${msgrSettings.messenger.token}` } : {};
console.info({
"Express Request": {
body: req.body,
},
});
const agentAltId = "G3YxOTO1LO_1NlJXEIfk";
// gather fields from request body
const { conversationId, event, input, location, logged, options, queryParams, request, transcript } = req.body || {};
const channel = req?.body?.event?.metadata?.channel ?? req?.body?.channel ?? "web";
const metadata = req.body.event?.metadata;
const connectedWithAgent = (session) => session?.liveChatRequest && session?.agent?.userId;
const loopScale = 3;
const smStr = `session-map-${conversationId}`;
(async () => {
const session = await redis.hGetAll(smStr);
// parse session from string to object
Object.keys(session).forEach((key) => {
try {
session[key] = JSON.parse(session[key]);
} catch (e) {}
});
const elapsed = (Date.now() - session?.lastActivity) / 1000;
const qelapsed = (Date.now() - session?.lastQueueStatus) / 1000;
console.info(`Time since last user activity: ${elapsed}s`);
session.warned ||= false;
if (!session?.discoveryUrls) {
const { urls } = await CA.restUrls();
session.discoveryUrls = urls;
await redis.hSet(smStr, {
discoveryUrls: JSON.stringify(urls),
});
}
if (event != "FETCH") {
console.info({ Session: session });
}
const delayCallFetch = async (delay) => {
const id = uuidv4();
await redis.hSet(smStr, { fetchid: id });
let numSeconds = typeof delay === "number" ? delay : loopScale;
setTimeout(async (smStr, id) => {
const session = await redis.hGetAll(smStr);
if (session.fetchid == id) {
axios({
method: "post",
url: _SELF,
headers: { "Content-Type": "application/json" },
data: { channel, conversationId, event: "FETCH" },
});
} else {
console.info("Fetch: loop stopped (old id)");
}
}, 1000 * numSeconds, smStr, id);
};
switch (event) {
case "JOIN": {
console.info("Joining live chat session");
const chatRequestDefaults = {
customerFirstName: "",
customerLastName: "",
customerEmail: "",
chatLaunchMode: "CHAT_ONLY",
refererURL: "example.com",
launchIdentifier: casettings.liveChat.launchIdentifier,
launchCode: "TextChatCustomerDataED",
transcript,
};
const customFieldsDefaults = { company: "Verint" };
const chatRequest = { ...chatRequestDefaults, ...(request ?? {}) };
chatRequest.customFields = { ...customFieldsDefaults, ...(request?.customFields ?? {}) };
const data = await CA.axios(`Posting data to "${session.discoveryUrls.client}"`, {
method: "post",
url: session.discoveryUrls.client,
headers,
data: chatRequest,
});
// Clean up _links
const links = {};
Object.keys(data._links).forEach((key) => {
links[key] = data._links[key].href;
});
data._links = links;
console.info(data);
await redis.hSet(smStr, {
liveChatRequest: "true",
chatDetails: JSON.stringify(data),
lastActivity: JSON.stringify(Date.now()),
lastQueueStatus: JSON.stringify(Date.now()),
});
const queueStatus = await CA.axios(`Getting queue status from "${data._links.queuedstatus}"`,
{ method: "get", url: data._links.queuedstatus, headers },
async () => {
await CA.postEvent(channel, conversationId, { input: casettings.liveChat.queueDisconnectMessage });
}
);
if (queueStatus.message) {
await CA.postEvent(channel, conversationId, { input: queueStatus.message });
}
delayCallFetch();
break;
}
case "LEAVE": {
console.info("Leaving live chat session");
try {
const { data } = await axios({
method: "post",
url: session.chatDetails._links.logout,
headers,
});
console.info({ "Logout Response": data });
if (connectedWithAgent(session)) {
await CA.postConversationLeave(
channel,
conversationId,
session.agent.userId || casettings.liveChat.participantId || agentAltId,
"liveChatEnded"
);
}
} catch (e) {
console.error(e);
}
console.info("reset session map");
await redis.hSet(smStr, { liveChatRequest: "false", agent: "{}" });
break;
}
case "FETCH": {
const State = { waiting: 0, leaving: 1, offline: 2 };
let state = State.waiting;
if (session.liveChatRequest == false) {
console.info("Live Chat is off");
state = State.offline;
} else if (session && session.chatDetails) {
// queue status check
if (!connectedWithAgent(session) && qelapsed > 30) {
const queueStatus = await CA.axios(
`Getting queue status from "${session.chatDetails._links.queuedstatus}"`,
{ method: "get", url: session.chatDetails._links.queuedstatus, headers },
async () => {
await CA.postEvent(channel, conversationId, { input: casettings.liveChat.queueDisconnectMessage });
}
);
if (queueStatus.message && queueStatus.message != session.lastQueueMessage) {
await CA.postEvent(channel, conversationId, { input: queueStatus.message });
await redis.hSet(smStr, {
lastQueueStatus: JSON.stringify(Date.now()),
lastQueueMessage: queueStatus.message
});
}
}
const data = await CA.axios(
`Getting Events from "${session.chatDetails._links.event}"`,
{ method: "get", url: session.chatDetails._links.event, headers }
);
data.events = data.events.map((event) => JSON.parse(event));
if (data.events.length > 0) console.info(data.events);
const sagent = session.agent || {};
const au_default = {
userId: casettings.liveChat.participantId || agentAltId,
avatar: casettings.liveChat.avatar,
type: "agent",
};
let agent_user = { ...au_default, ...sagent };
for (const event of data.events) {
const agentUser = { ...agent_user, name: event.userDisplayName };
if (event.userType === "GUEST") {
console.info(event);
switch (event.type) {
case "MessageReceived": {
console.info(`Guest said "${event.text}"`);
await CA.postEvent(channel, conversationId, {
conversationId,
input: event.text,
sentBy: agentUser,
ping: nanoid(),
sentAt: Date.now(),
});
CA.addTransaction(session.conversationId, event.text,
{ type: "agent", name: event.userDisplayName, userId: event.userId },
{ liveChat: true }
);
break;
}
}
} else if (event.userType === "AGENT") {
console.info(event);
switch (event.type) {
case "UserTyping":
await CA.postTyping(channel, conversationId);
break;
case "AttachmentMessageReceived": {
let url = `${casettings.liveChat.attachment.endpoint}/ProxyScript/run/${req.params.workspaceId}/${req.params.branch}/CA_attachments${casettings.hubSuffix}`;
url += `?url=${encodeURIComponent(event.url)}&sessionId=${event.sessionId}&fileName=${encodeURIComponent(event.fileName)}`;
const response = await CA.downloadFile(event.url, event.sessionId);
if (CA.channelIsExternal(channel) && response.headers['content-type']) {
const attachment = { url, mimeType: response.headers['content-type'], fileName: event.fileName };
CA.postEvent(channel, conversationId, { metadata: { channel, attachments: [attachment] } });
}
break;
}
case "MessageReceived": {
console.info(`Agent ${event.userDisplayName} said "${event.text}"`);
agent_user.name = event.userDisplayName;
let eventPayload = {
conversationId,
input: event.text,
sentBy: agentUser,
ping: nanoid(),
sentAt: Date.now(),
};
if (data.events.some((x) => x.type == "UserExited")) {
console.info("Setting liveChat to false");
eventPayload.metadata = { outputs: { liveChat: false } };
}
await gvf_handleIVAWakeupEvent().handleIVAWakeupEvent(
event, req, session, settings_66a1322d44405adda4fe9f53, redis, smStr, channel, eventPayload
);
if (session.formFlow !== true) {
await CA.postEvent(channel, conversationId, eventPayload);
}
CA.addTransaction(
session.conversationId,
event.text,
{ type: "agent", name: event.userDisplayName, userId: event.userID },
eventPayload?.metadata?.outputs || { liveChat: true }
);
break;
}
case "UserEntered":
console.info(`Agent ${event.userDisplayName} joined`);
agent_user.name = event.userDisplayName;
await redis.hSet(smStr, { agent: JSON.stringify(agent_user) });
await CA.postConversationJoin(channel, conversationId, agent_user);
await CA.postEvent(channel, conversationId, {
conversationId,
input: `You are now connected with ${event.userDisplayName}`,
sentBy: { ...agentUser, type: "host" },
ping: nanoid(),
sentAt: Date.now(),
});
break;
case "UserExited":
state = State.leaving;
console.info(`Agent ${event.userDisplayName} left`);
break;
}
}
}
} else {
console.info("Session does not exist!");
break;
}
// State handling
switch (state) {
case State.waiting: {
const idle = casettings.liveChat.idle;
if (elapsed > idle.disconnect.seconds && session.warned) {
CA.addTransaction(session.conversationId, "Live Chat Timeout", { type: "host" }, { liveChat: false });
await CA.postEvent(channel, conversationId, { input: idle.disconnect.message });
state = State.leaving;
await axios({
method: "post",
url: _SELF,
headers: studioToken,
data: { channel, conversationId, event: "LEAVE", session },
});
break;
} else if (elapsed > idle.warning.seconds && !session.warned) {
await CA.postEvent(channel, conversationId, { input: idle.warning.message });
await redis.hSet(smStr, { warned: "true" });
}
delayCallFetch();
break;
}
case State.leaving:
console.info("Leaving session");
await axios({
method: "post",
url: _SELF,
headers: studioToken,
data: { channel, conversationId, event: "LEAVE", session },
});
break;
default:
state = State.offline;
break;
}
break;
}
default: {
console.info("Live chat ongoing");
if (input && session && session.chatDetails) {
console.info(session.chatDetails);
console.info(`Sending "${input}" to live chat`);
await axios({
method: "post",
url: session.chatDetails._links.event,
headers,
data: [{ type: "MessageSent", text: input }],
});
await redis.hSet(smStr, {
lastActivity: JSON.stringify(Date.now()),
warned: "false",
lastQueueMessage: "",
lastQueueStatus: 0,
});
delayCallFetch();
}
}
}
})()
.catch(async (error) => {
console.error(`Caught error: ${error}`);
if (error.response) console.error(`Caught error: ${error.response.data}`);
const session = await redis.hGetAll(smStr);
if (session) {
await redis.hSet(smStr, { liveChatRequest: "false", agent: "{}" });
if (session && session.agent && session.chatDetails) {
session.agent = JSON.parse(session.agent);
session.chatDetails = JSON.parse(session.chatDetails);
if (connectedWithAgent(session)) {
CA.postConversationLeave(
channel,
conversationId,
session.agent.userId || casettings.liveChat.participantId || agentAltId,
"AgentExited"
);
}
axios({ method: "post", url: session.chatDetails._links.logout, headers });
}
}
if (conversationId && session.conversationId) {
CA.addTransaction(session.conversationId, "Live Chat Error", { type: "host" }, { liveChat: false });
await CA.postEvent(channel, conversationId, {
input: "There was a connection issue with the Live Chat, and you were disconnected.",
});
}
})
.finally(() => {
res.send(200);
});

View File

@ -0,0 +1,132 @@
console.log("===== Main Messenger Proxy - v17.9 =====");
const casettings = CAInterface_settings;
console.log(req);
const external = req.body.event?.external;
const sessionId = req.body.event?.conversationId;
const metadata = req.body.event?.metadata;
const msgrSettings = eval(`settings${casettings.hubSuffixMessenger}`);
let nlu = msgrSettings?.nlu;
if (!nlu) {
console.log("Install Basic Messenger Webhook.");
console.log("Validate the value of hubSuffixMessenger in CAInterface_settings");
}
const messengerUrl = `${nlu.apiBaseURL}ProxyScript/run/${req.params.workspaceId}/${req.params.branch}/messenger${casettings.hubSuffixMessenger}`;
const CA = CAInterface();
const channel = req?.body?.event?.metadata?.channel ?? req?.body?.channel;
const attachments = req?.body?.event?.metadata?.attachments;
const validEventInput = (event) => {
console.log(20);
console.log(event);
return event?.input && event.input.length != 0;
};
const getGatewayHeaders = async () => {
const header = {};
if (channel == "whatsapp") {
const notify = casettings.notifications ?? [];
const handler = notify.find(v => v.name === "VMGateway_eventHandler");
if (handler) {
const suffix = handler.hubSuffix || "";
const gatewaySettings = eval("VMGateway_settings" + suffix);
const { refreshGatewayKey } = eval("VMGateway_support" + suffix);
if (gatewaySettings?.gateway) {
header["x-auth-apiKey"] = await refreshGatewayKey(gatewaySettings.gateway);
}
}
}
return header;
}
(async () => {
try {
const smStr = `session-map-${sessionId}`;
//await redis.set("sentdata", "false");
const sessionMap = await redis.hGetAll(smStr);
console.log(sessionMap);
console.log(`47 line`);
console.log(smStr);
console.log({
"Session Map": sessionMap,
});
if (sessionMap.chatDetails) {
sessionMap.chatDetails = JSON.parse(sessionMap.chatDetails);
}
if (attachments?.length && sessionMap.liveChatRequest !== "true") {
res.send({
conversationId: sessionId, workspaceId: req.params.workspaceId,
answers: [casettings.liveChat.attachment.noLiveChat]
});
} else if (sessionMap.liveChatRequest == "true" && sessionMap.formFlow !== "true") {
if (attachments && attachments.length && sessionMap?.chatDetails?.userId) {
const headers = await getGatewayHeaders();
for (let attachment of attachments) {
CA.sendAttachmentToAgent(sessionMap.chatDetails.userId, attachment, headers);
console.log({ attachment });
}
}
if (metadata?.liveChat === false) {
await CA.leaveLiveChat(sessionId, sessionMap.conversationId, channel,
req.body.event?.input, req.body.event?.sentBy?.userId);
} else if (validEventInput(req.body.event)) {
await CA.sendToLiveChat(sessionId, sessionMap.conversationId, channel,
req.body.event?.input, req.body.event?.sentBy?.userId);
}
res.send({
conversationId: sessionId, workspaceId: req.params.workspaceId,
input: req.body.event?.input, answers: [],
classificationResults: []
});
// }
}
else if (validEventInput(req.body.event)) {
console.log(79);
console.log(metadata);
// call the webhook
const { data: recognizedData } = await axios({
url: messengerUrl,
method: "post",
data: req.body,
});
if (recognizedData?.formFlowEnd) {
let sentdata = sessionMap?.sentdata;
if (sessionMap?.datacollect === "true" && sentdata === "false") {
let history = await gvf_ConversationHistory().fetchConversationHistory(sessionMap?.conversationId, 0);
if (metadata?.liveChat === false) {
await CA.leaveLiveChat(sessionId, sessionMap.conversationId, channel,
history, req.body.event?.sentBy?.userId);
} else if (validEventInput(req.body.event)) {
await CA.sendToLiveChat(sessionId, sessionMap.conversationId, channel,
history, req.body.event?.sentBy?.userId);
}
await redis.hSet(smStr, {
datacollect: "false",
sentdata: "true"
});
}
}
if (recognizedData?.outputs?.liveChat === true || metadata?.liveChat == true) {
if (external && metadata?.liveChat == true) {
// need to log invisible chat messages
CA.addTransaction(recognizedData.conversationId, "Start Live Chat", "user", {
liveChat: true,
});
}
CA.joinLiveChat(
channel,
sessionId,
recognizedData.conversationId,
recognizedData?.outputs?.liveChatRequest
);
}
res.send(recognizedData);
}
} catch (error) {
console.log(error.message);
res.send({
answers: ["Something went wrong. Please try again."],
outputs: {},
});
}
})();

File diff suppressed because one or more lines are too long