Adding 'IVA Wakeup' script used for Bot Tango example

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

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