Skip to content

Commit

Permalink
chat-core-zendesk: workaround to inject ticket id to bot message (#60)
Browse files Browse the repository at this point in the history
This is part of a workaround in order enable the bot message to include the ticket ID when live messaging session has ended.
Zendesk does not have native support to easily access ticket ID within Sunco system and Zendesk system also cannot easily access Sunco conversation ID. So the workaround is as follow:
- SDK will provide the conversation ID in a custom ticket field
- Zendesk will have a trigger upon ticket creation with the agent handoff tag AND the conversation ID field is present, which will invoke a webhook to call a Sunco API to inject the ticket ID into the Sunco conversation's metadata
- When the messaging session is closed. The bot in the switchboard API will grab the ticket in the metadata and include it in its messaging when informing user of the closed session.

This PR implements the first step of the workaround. Additionally, the closing message on CSAT message type is removed as it should be now handled by the bot webhook in the switchboard as well.

Thread context: https://yext.slack.com/archives/C01CSDL5US0/p1733870484402519

J=CLIP-1649
TEST=manual&auto

added unit tests
setup trigger and webhook in the zendesk sandbox account and spin up local test site. Verified that the created ticket for the live messaging session contains the conversation ID in the custom field
  • Loading branch information
yen-tt authored Dec 18, 2024
1 parent 40e801f commit c4e117f
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 18 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/chat-core-aws-connect/THIRD-PARTY-NOTICES
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ The following NPM packages may be included in this product:
- @types/[email protected]
- @types/[email protected]
- @types/[email protected]
- @types/[email protected].1
- @types/[email protected].2
- @types/[email protected]
- @types/[email protected]
- @types/[email protected]
Expand Down
2 changes: 1 addition & 1 deletion packages/chat-core-zendesk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@yext/chat-core-zendesk",
"version": "0.3.1",
"version": "0.4.0",
"description": "Typescript Networking Library for the Yext Chat API Integration with Zendesk",
"main": "./dist/commonjs/index.js",
"module": "./dist/esm/index.mjs",
Expand Down
37 changes: 23 additions & 14 deletions packages/chat-core-zendesk/src/infra/ChatCoreZendeskImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,21 @@ export class ChatCoreZendeskImpl implements ChatCoreZendesk {
}
this.conversationId = convo.id;
Smooch.loadConversation(convo.id);

// This is a temporary solution until Zendesk have a more robust way of fetching the ticket id.
// A Zendesk trigger is setup to make a Sunco API call, using this conversation ID placed inside
// a Zendesk custom field, to append the corresponding ticket id into the Sunco conversation's
// metadata. The bot then pull from metadata to inform the user of the ticket id when the messaging
// session ends.
const convoIdTicketField = Object.keys(ticketFields).find((key) => {
return ticketFields[key] === "SUNCO_CONVERSATION_ID_PLACEHOLDER";
});
if (convoIdTicketField) {
await Smooch.updateConversation(convo.id, {
metadata: { [convoIdTicketField]: convo.id },
});
}

Smooch.sendMessage(
`SUMMARY: ${
messageRsp.notes.conversationSummary ??
Expand All @@ -179,29 +194,23 @@ export class ChatCoreZendeskImpl implements ChatCoreZendesk {
if (data.conversation.id !== this.conversationId) {
return;
}
if (message.type !== "text" || message.role !== "business") {
if (
message.type !== "text" ||
message.role !== "business" ||
// @ts-ignore - metadata is not in the Smooch types but it's in the actual data
message["metadata"]?.type === "csat"
) {
return;
}
let msg: string = message.text;

// If the message is of type CSAT, indicating the agent has resolved the ticket, then omit this message.
// Instead, send a message to inform user and reset the session
// @ts-ignore - metadata is not in the Smooch types but it's in the actual data
if (message["metadata"]?.type === "csat") {
this.resetSession();
this.eventListeners["close"]?.forEach((cb) => cb(data));
msg =
"The agent has resolved the ticket. Further assistance will now be provided by the bot.";
}

// If the message is from a bot, indicating the agent has deleted the ticket, then reset the session
// If the message is from a bot, indicating the agent has ended the messaging session, then reset the session
// @ts-ignore - subroles is not in the Smooch types but it's in the actual data
if (message["subroles"]?.includes("AI")) {
this.resetSession();
this.eventListeners["close"]?.forEach((cb) => cb(data));
}

this.eventListeners["message"]?.forEach((cb) => cb(msg));
this.eventListeners["message"]?.forEach((cb) => cb(message.text));
this.eventListeners["typing"]?.forEach((cb) => cb(false));
}
);
Expand Down
29 changes: 29 additions & 0 deletions packages/chat-core-zendesk/tests/ChatCoreZendesk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jest.mock("smooch", () => ({
render: jest.fn(),
init: jest.fn(),
createConversation: jest.fn(),
updateConversation: jest.fn(),
loadConversation: jest.fn(),
sendMessage: jest.fn(),
on: jest.fn(),
Expand Down Expand Up @@ -300,3 +301,31 @@ it("sets ticket tags defined in config on handoff", async () => {
},
});
});

it("set conversation id to custom field on handoff", async () => {
const createConversationSpy = jest.spyOn(SmoochLib, "createConversation");
const updateConversationSpy = jest.spyOn(SmoochLib, "updateConversation");
const chatCoreZendesk = provideChatCoreZendesk(mockConfig);

await chatCoreZendesk.init({
...mockMessageResponse(),
integrationDetails: {
zendeskHandoff: {
ticketFields: '{"123456": "SUNCO_CONVERSATION_ID_PLACEHOLDER"}',
},
},
});
expect(createConversationSpy).toBeCalledWith({
metadata: {
"zen:ticket:tags": "yext-chat-agent-handoff",
YEXT_CHAT_SDK: true,
"zen:ticket_field:123456": "SUNCO_CONVERSATION_ID_PLACEHOLDER",
},
});

expect(updateConversationSpy).toBeCalledWith(mockConversationId, {
metadata: {
"zen:ticket_field:123456": mockConversationId,
},
});
});
2 changes: 1 addition & 1 deletion test-sites/test-browser-esm/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c4e117f

Please sign in to comment.