diff --git a/.changeset/calm-rabbits-sin.md b/.changeset/calm-rabbits-sin.md new file mode 100644 index 0000000000000..94cb57708e61d --- /dev/null +++ b/.changeset/calm-rabbits-sin.md @@ -0,0 +1,6 @@ +--- +'@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. diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 7b918e0b172e8..0a874710117c6 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -156,12 +156,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/app/livechat/server/lib/Visitors.ts b/apps/meteor/app/livechat/server/lib/Visitors.ts index ccf4a13bd4721..d9a9b50ba910b 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,23 @@ export const Visitors = { const visitorEmail = email.trim().toLowerCase(); validateEmail(visitorEmail); visitorDataToUpdate.visitorEmails = [{ address: visitorEmail }]; + + const contact = await LivechatContacts.findContactByEmailAndContactManager(visitorEmail); + 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) { + 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/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts index 5dc13cfce1e08..09be46592fd60 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -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/core-typings/src/ILivechatVisitor.ts b/packages/core-typings/src/ILivechatVisitor.ts index 21819cc23f246..4b64137657fc0 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?: { + _id?: string; username: string; + name?: string; + emails?: { address: string }[]; }; activity?: string[]; disabled?: boolean; 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'); diff --git a/packages/model-typings/src/models/ILivechatContactsModel.ts b/packages/model-typings/src/models/ILivechatContactsModel.ts index e6d1ac321c947..a61e731a79b30 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 | null>; findOneByVisitor( visitor: ILivechatContactVisitorAssociation, options?: FindOptions, diff --git a/packages/models/src/models/LivechatContacts.ts b/packages/models/src/models/LivechatContacts.ts index 9a3e169d73d67..5e0a30c8dd81b 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 | null> { + return this.findOne( + { emails: { $elemMatch: { address: email } }, contactManager: { $exists: true } }, + { projection: { contactManager: 1 } }, + ); + } + private makeQueryForVisitor( visitor: ILivechatContactVisitorAssociation, extraFilters?: Filter['channels'][number]>,