Adding 'IVA Wakeup' script used for Bot Tango example
This commit is contained in:
420
IVA-Wakeup/proxy/CAInterface_Chat.js
Normal file
420
IVA-Wakeup/proxy/CAInterface_Chat.js
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user