From 8000c78e0fc9b0ee45ced1e5ef539095a461d5d0 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Thu, 13 Feb 2025 22:31:14 -0300 Subject: [PATCH 1/9] fix: adds contact manager check before assigning to agent --- apps/meteor/app/livechat/server/lib/LivechatTyped.ts | 11 +++++------ .../server/hooks/handleNextAgentPreferredEvents.ts | 9 +++++++-- packages/livechat/src/lib/room.js | 7 ++++++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 193f6cdbc60ac..003c4e470473b 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -359,12 +359,11 @@ class LivechatClass { throw new Error('error-contact-channel-blocked'); } - const defaultAgent = - agent ?? - (await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, { - visitorId: visitor._id, - source: roomInfo.source, - })); + const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, { + visitorId: visitor._id, + source: roomInfo.source, + }); + // if no department selected verify if there is at least one active and pick the first if (!defaultAgent && !visitor.department) { const department = await getRequiredDepartment(); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts index 5dc13cfce1e08..80b708f0e80ba 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -19,7 +19,7 @@ const normalizeDefaultAgent = (agent?: Pick | null): return { agentId, username }; }; -const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { +export const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { if (!username && !id) { return undefined; } @@ -93,7 +93,7 @@ settings.watch('Omnichannel_contact_manager_routing', (value) => { callbacks.add( 'livechat.checkDefaultAgentOnNewRoom', async (defaultAgent, { visitorId, source } = {}) => { - if (defaultAgent || !visitorId || !source) { + if (!visitorId || !source) { return defaultAgent; } @@ -104,6 +104,11 @@ callbacks.add( return undefined; } + const hasDivergentContactManager = defaultAgent?.agentId !== guest?.contactManager; + if (!hasDivergentContactManager && defaultAgent) { + return defaultAgent; + } + const contactId = await migrateVisitorIfMissingContact(visitorId, source); const contact = contactId ? await LivechatContacts.findOneById(contactId, { projection: { contactManager: 1 } }) : undefined; diff --git a/packages/livechat/src/lib/room.js b/packages/livechat/src/lib/room.js index f7e7e97df5f4e..74c54004e0cef 100644 --- a/packages/livechat/src/lib/room.js +++ b/packages/livechat/src/lib/room.js @@ -182,7 +182,12 @@ const isAgentHidden = () => { }; const transformAgentInformationOnMessage = (message) => { - const { user } = store.state; + const { user, agent } = store.state; + + if (message.u._id !== user._id && message.u._id !== agent._id) { + store.setState({ agent: { ...agent, username: message.u.username, name: message.u.name } }); + } + if (message && user && message.u && message.u._id !== user._id && isAgentHidden()) { return { ...message, u: { _id: message.u._id } }; } From 4eb8d7f6b18b4ab3542814814cf81826fdf3ad59 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Thu, 13 Feb 2025 22:32:05 -0300 Subject: [PATCH 2/9] fix: adds contact manager check before assigning to agent --- .../server/hooks/handleNextAgentPreferredEvents.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts index 80b708f0e80ba..09be46592fd60 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -19,7 +19,7 @@ const normalizeDefaultAgent = (agent?: Pick | null): return { agentId, username }; }; -export const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { +const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { if (!username && !id) { return undefined; } From cdd01becbb956bf9f7d1c4a3e56fa71c32e8a827 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 14 Feb 2025 08:39:33 -0300 Subject: [PATCH 3/9] makes messages without type to be discarded --- packages/livechat/src/lib/room.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/livechat/src/lib/room.js b/packages/livechat/src/lib/room.js index 74c54004e0cef..dace7b6c315da 100644 --- a/packages/livechat/src/lib/room.js +++ b/packages/livechat/src/lib/room.js @@ -184,7 +184,7 @@ const isAgentHidden = () => { const transformAgentInformationOnMessage = (message) => { const { user, agent } = store.state; - if (message.u._id !== user._id && message.u._id !== agent._id) { + if (!message.t && message.u._id !== user._id && message.u._id !== agent._id) { store.setState({ agent: { ...agent, username: message.u.username, name: message.u.name } }); } From b8460d0a703f87e5b2590c9131da8aaa02183f9c Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 14 Feb 2025 09:20:01 -0300 Subject: [PATCH 4/9] changeset --- .changeset/calm-rabbits-sin.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/calm-rabbits-sin.md diff --git a/.changeset/calm-rabbits-sin.md b/.changeset/calm-rabbits-sin.md new file mode 100644 index 0000000000000..b3b07b7855baa --- /dev/null +++ b/.changeset/calm-rabbits-sin.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/livechat': minor +'@rocket.chat/meteor': minor +--- + +Fixes livechat routing algorithm to ensure conversations are correctly assigned to the contact manager when triggers and/or automatic agent routing are enabled. From ee5d9d7952fee7bdce66bf98ea916e99ec98f412 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Fri, 14 Feb 2025 14:27:13 -0300 Subject: [PATCH 5/9] remove widget modifications --- packages/livechat/src/lib/room.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/livechat/src/lib/room.js b/packages/livechat/src/lib/room.js index dace7b6c315da..b1b46e2b84b29 100644 --- a/packages/livechat/src/lib/room.js +++ b/packages/livechat/src/lib/room.js @@ -182,11 +182,7 @@ const isAgentHidden = () => { }; const transformAgentInformationOnMessage = (message) => { - const { user, agent } = store.state; - - if (!message.t && message.u._id !== user._id && message.u._id !== agent._id) { - store.setState({ agent: { ...agent, username: message.u.username, name: message.u.name } }); - } + const { user } = store.state; if (message && user && message.u && message.u._id !== user._id && isAgentHidden()) { return { ...message, u: { _id: message.u._id } }; From 771758ff3f23bf7c9fc633f5a7d6decd6cd2b103 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Sat, 15 Feb 2025 13:06:52 -0300 Subject: [PATCH 6/9] makes get visitor config to return contactManager --- .../app/livechat/server/lib/Visitors.ts | 21 ++++++++++++++++++- packages/core-typings/src/ILivechatVisitor.ts | 5 ++++- packages/livechat/src/lib/room.js | 1 - .../livechat/src/routes/Register/index.tsx | 5 ++++- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/Visitors.ts b/apps/meteor/app/livechat/server/lib/Visitors.ts index ccf4a13bd4721..6be1e5b7edbd3 100644 --- a/apps/meteor/app/livechat/server/lib/Visitors.ts +++ b/apps/meteor/app/livechat/server/lib/Visitors.ts @@ -1,6 +1,6 @@ import { UserStatus, type ILivechatVisitor } from '@rocket.chat/core-typings'; import { Logger } from '@rocket.chat/logger'; -import { LivechatDepartment, LivechatVisitors } from '@rocket.chat/models'; +import { LivechatContacts, LivechatDepartment, LivechatVisitors, Users } from '@rocket.chat/models'; import { validateEmail } from './Helper'; import { settings } from '../../../settings/server'; @@ -46,6 +46,25 @@ export const Visitors = { const visitorEmail = email.trim().toLowerCase(); validateEmail(visitorEmail); visitorDataToUpdate.visitorEmails = [{ address: visitorEmail }]; + + const contact = await LivechatContacts.findOne( + { emails: { $elemMatch: { address: visitorEmail } }, contactManager: { $exists: true } }, + { projection: { contactManager: 1 } }, + ); + if (contact && contact.contactManager) { + const agent = await Users.findOneOnlineAgentById(contact.contactManager, undefined, { + projection: { _id: 1, username: 1, name: 1, emails: 1 }, + }); + if (agent && agent.username && agent.name && agent.emails) { + visitorDataToUpdate.contactManager = { + _id: agent._id, + username: agent.username, + name: agent.name, + emails: agent.emails, + }; + logger.debug(`Assigning visitor ${token} to agent ${agent.username}`); + } + } } const livechatVisitor = await LivechatVisitors.getVisitorByToken(token, { projection: { _id: 1 } }); diff --git a/packages/core-typings/src/ILivechatVisitor.ts b/packages/core-typings/src/ILivechatVisitor.ts index 21819cc23f246..0ab119e77ae7b 100644 --- a/packages/core-typings/src/ILivechatVisitor.ts +++ b/packages/core-typings/src/ILivechatVisitor.ts @@ -45,7 +45,10 @@ export interface ILivechatVisitor extends IRocketChatRecord { }; livechatData?: ILivechatData; contactManager?: { - username: string; + _id?: string; + username?: string; + name?: string; + emails?: { address: string }[]; }; activity?: string[]; disabled?: boolean; diff --git a/packages/livechat/src/lib/room.js b/packages/livechat/src/lib/room.js index b1b46e2b84b29..f7e7e97df5f4e 100644 --- a/packages/livechat/src/lib/room.js +++ b/packages/livechat/src/lib/room.js @@ -183,7 +183,6 @@ const isAgentHidden = () => { const transformAgentInformationOnMessage = (message) => { const { user } = store.state; - if (message && user && message.u && message.u._id !== user._id && isAgentHidden()) { return { ...message, u: { _id: message.u._id } }; } diff --git a/packages/livechat/src/routes/Register/index.tsx b/packages/livechat/src/routes/Register/index.tsx index 4ec7be702179a..bb009142febce 100644 --- a/packages/livechat/src/routes/Register/index.tsx +++ b/packages/livechat/src/routes/Register/index.tsx @@ -93,7 +93,10 @@ export const Register: FunctionalComponent<{ path: string }> = () => { try { const { visitor: user } = await Livechat.grantVisitor({ visitor: { ...fields, token } }); - await dispatch({ user } as Omit); + await dispatch({ + user, + ...(user.contactManager && { agent: user.contactManager }), + } as Omit); parentCall('callback', 'pre-chat-form-submit', fields); Triggers.callbacks?.emit('chat-visitor-registered'); From 24fad255d834a5d77574f15c5a75050006cccb04 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Mon, 17 Feb 2025 07:08:26 -0300 Subject: [PATCH 7/9] moves findOne to specific model function --- apps/meteor/app/livechat/server/lib/Visitors.ts | 5 +---- .../model-typings/src/models/ILivechatContactsModel.ts | 1 + packages/models/src/models/LivechatContacts.ts | 7 +++++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/Visitors.ts b/apps/meteor/app/livechat/server/lib/Visitors.ts index 6be1e5b7edbd3..8b65d08b97c4a 100644 --- a/apps/meteor/app/livechat/server/lib/Visitors.ts +++ b/apps/meteor/app/livechat/server/lib/Visitors.ts @@ -47,10 +47,7 @@ export const Visitors = { validateEmail(visitorEmail); visitorDataToUpdate.visitorEmails = [{ address: visitorEmail }]; - const contact = await LivechatContacts.findOne( - { emails: { $elemMatch: { address: visitorEmail } }, contactManager: { $exists: true } }, - { projection: { contactManager: 1 } }, - ); + const contact = await LivechatContacts.findContactByEmailAndContactManager(visitorEmail); if (contact && contact.contactManager) { const agent = await Users.findOneOnlineAgentById(contact.contactManager, undefined, { projection: { _id: 1, username: 1, name: 1, emails: 1 }, diff --git a/packages/model-typings/src/models/ILivechatContactsModel.ts b/packages/model-typings/src/models/ILivechatContactsModel.ts index e6d1ac321c947..eca5b1b8f5915 100644 --- a/packages/model-typings/src/models/ILivechatContactsModel.ts +++ b/packages/model-typings/src/models/ILivechatContactsModel.ts @@ -36,6 +36,7 @@ export interface ILivechatContactsModel extends IBaseModel { lastChat: ILivechatContact['lastChat'], ): Promise; findContactMatchingVisitor(visitor: AtLeast): Promise; + findContactByEmailAndContactManager(email: string): Promise; findOneByVisitor( visitor: ILivechatContactVisitorAssociation, options?: FindOptions, diff --git a/packages/models/src/models/LivechatContacts.ts b/packages/models/src/models/LivechatContacts.ts index 9a3e169d73d67..20aeed255026e 100644 --- a/packages/models/src/models/LivechatContacts.ts +++ b/packages/models/src/models/LivechatContacts.ts @@ -175,6 +175,13 @@ export class LivechatContactsRaw extends BaseRaw implements IL return this.findOne(query); } + async findContactByEmailAndContactManager(email: string): Promise { + return this.findOne( + { emails: { $elemMatch: { address: email } }, contactManager: { $exists: true } }, + { projection: { contactManager: 1 } }, + ); + } + private makeQueryForVisitor( visitor: ILivechatContactVisitorAssociation, extraFilters?: Filter['channels'][number]>, From bb6acd7d9da346bc49e77d3096e35d39566dbce5 Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Tue, 18 Feb 2025 07:59:06 -0300 Subject: [PATCH 8/9] pr suggestions fixes --- apps/meteor/app/livechat/server/lib/Visitors.ts | 5 +++-- packages/core-typings/src/ILivechatVisitor.ts | 2 +- packages/model-typings/src/models/ILivechatContactsModel.ts | 2 +- packages/models/src/models/LivechatContacts.ts | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/Visitors.ts b/apps/meteor/app/livechat/server/lib/Visitors.ts index 8b65d08b97c4a..d9a9b50ba910b 100644 --- a/apps/meteor/app/livechat/server/lib/Visitors.ts +++ b/apps/meteor/app/livechat/server/lib/Visitors.ts @@ -48,8 +48,9 @@ export const Visitors = { visitorDataToUpdate.visitorEmails = [{ address: visitorEmail }]; const contact = await LivechatContacts.findContactByEmailAndContactManager(visitorEmail); - if (contact && contact.contactManager) { - const agent = await Users.findOneOnlineAgentById(contact.contactManager, undefined, { + if (contact?.contactManager) { + const shouldConsiderIdleAgent = settings.get('Livechat_enabled_when_agent_idle'); + const agent = await Users.findOneOnlineAgentById(contact.contactManager, shouldConsiderIdleAgent, { projection: { _id: 1, username: 1, name: 1, emails: 1 }, }); if (agent && agent.username && agent.name && agent.emails) { diff --git a/packages/core-typings/src/ILivechatVisitor.ts b/packages/core-typings/src/ILivechatVisitor.ts index 0ab119e77ae7b..4b64137657fc0 100644 --- a/packages/core-typings/src/ILivechatVisitor.ts +++ b/packages/core-typings/src/ILivechatVisitor.ts @@ -46,7 +46,7 @@ export interface ILivechatVisitor extends IRocketChatRecord { livechatData?: ILivechatData; contactManager?: { _id?: string; - username?: string; + username: string; name?: string; emails?: { address: string }[]; }; diff --git a/packages/model-typings/src/models/ILivechatContactsModel.ts b/packages/model-typings/src/models/ILivechatContactsModel.ts index eca5b1b8f5915..a61e731a79b30 100644 --- a/packages/model-typings/src/models/ILivechatContactsModel.ts +++ b/packages/model-typings/src/models/ILivechatContactsModel.ts @@ -36,7 +36,7 @@ export interface ILivechatContactsModel extends IBaseModel { lastChat: ILivechatContact['lastChat'], ): Promise; findContactMatchingVisitor(visitor: AtLeast): Promise; - findContactByEmailAndContactManager(email: string): Promise; + findContactByEmailAndContactManager(email: string): Promise | null>; findOneByVisitor( visitor: ILivechatContactVisitorAssociation, options?: FindOptions, diff --git a/packages/models/src/models/LivechatContacts.ts b/packages/models/src/models/LivechatContacts.ts index 20aeed255026e..5e0a30c8dd81b 100644 --- a/packages/models/src/models/LivechatContacts.ts +++ b/packages/models/src/models/LivechatContacts.ts @@ -175,7 +175,7 @@ export class LivechatContactsRaw extends BaseRaw implements IL return this.findOne(query); } - async findContactByEmailAndContactManager(email: string): Promise { + async findContactByEmailAndContactManager(email: string): Promise | null> { return this.findOne( { emails: { $elemMatch: { address: email } }, contactManager: { $exists: true } }, { projection: { contactManager: 1 } }, From c2d8ed6308d7e1e88ad580d63ed14a612ccf272a Mon Sep 17 00:00:00 2001 From: Ricardo Garim Date: Tue, 18 Feb 2025 13:10:55 -0300 Subject: [PATCH 9/9] Update .changeset/calm-rabbits-sin.md Co-authored-by: Marcos Spessatto Defendi --- .changeset/calm-rabbits-sin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/calm-rabbits-sin.md b/.changeset/calm-rabbits-sin.md index b3b07b7855baa..94cb57708e61d 100644 --- a/.changeset/calm-rabbits-sin.md +++ b/.changeset/calm-rabbits-sin.md @@ -1,6 +1,6 @@ --- -'@rocket.chat/livechat': minor -'@rocket.chat/meteor': minor +'@rocket.chat/livechat': patch +'@rocket.chat/meteor': patch --- Fixes livechat routing algorithm to ensure conversations are correctly assigned to the contact manager when triggers and/or automatic agent routing are enabled.