From 08efbe452ef20204ba7d74a1a3d94a85886e12dc Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Tue, 17 Dec 2024 21:17:45 -0300 Subject: [PATCH 01/15] fix: New livechat conversations are not assigned to contact manager --- .../meteor/app/livechat/server/lib/LivechatTyped.ts | 5 +++-- .../server/hooks/handleNextAgentPreferredEvents.ts | 13 ++++++++----- apps/meteor/lib/callbacks.ts | 3 ++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index b94b070537ea..85c9be62b62e 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -353,11 +353,12 @@ class LivechatClass { throw new Meteor.Error('error-omnichannel-is-disabled'); } - if (await LivechatContacts.isChannelBlocked(this.makeVisitorAssociation(visitor._id, roomInfo.source))) { + const visitorAssociation = this.makeVisitorAssociation(visitor._id, roomInfo.source); + if (await LivechatContacts.isChannelBlocked(visitorAssociation)) { throw new Error('error-contact-channel-blocked'); } - const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, visitor); + const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, visitorAssociation); // 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 7146b8df4e4c..3bcd13e046c1 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -1,5 +1,5 @@ import type { IUser, SelectedAgent } from '@rocket.chat/core-typings'; -import { LivechatVisitors, LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models'; +import { LivechatVisitors, LivechatContacts, LivechatInquiry, LivechatRooms, Users } from '@rocket.chat/models'; import { notifyOnLivechatInquiryChanged } from '../../../../../app/lib/server/lib/notifyListener'; import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; @@ -88,12 +88,12 @@ settings.watch('Omnichannel_contact_manager_routing', (value) => { callbacks.add( 'livechat.checkDefaultAgentOnNewRoom', - async (defaultAgent, defaultGuest) => { - if (defaultAgent || !defaultGuest) { + async (defaultAgent, visitor) => { + if (defaultAgent || !visitor) { return defaultAgent; } - const { _id: guestId } = defaultGuest; + const { visitorId: guestId } = visitor; const guest = await LivechatVisitors.findOneEnabledById(guestId, { projection: { lastAgent: 1, token: 1, contactManager: 1 }, }); @@ -101,8 +101,11 @@ callbacks.add( return defaultAgent; } + const contact = await LivechatContacts.findOneByVisitor(visitor, { projection: { contactManager: 1 } }); + const { lastAgent, token, contactManager } = guest; - const guestManager = contactManager?.username && contactManagerPreferred && getDefaultAgent(contactManager?.username); + const contactManagerUsername = contact?.contactManager || contactManager?.username; + const guestManager = contactManager?.username && contactManagerPreferred && getDefaultAgent(contactManagerUsername); if (guestManager) { return guestManager; } diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 901c8101e034..5baf5a53b64c 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -24,6 +24,7 @@ import type { IOmnichannelRoomInfo, IOmnichannelInquiryExtraData, IOmnichannelRoomExtraData, + ILivechatContactVisitorAssociation, } from '@rocket.chat/core-typings'; import type { Updater } from '@rocket.chat/models'; import type { FilterOperators } from 'mongodb'; @@ -118,7 +119,7 @@ type ChainedCallbackSignatures = { ) => Promise; 'livechat.beforeRouteChat': (inquiry: ILivechatInquiryRecord, agent?: { agentId: string; username: string }) => ILivechatInquiryRecord; - 'livechat.checkDefaultAgentOnNewRoom': (agent: SelectedAgent, visitor?: ILivechatVisitor) => SelectedAgent | null; + 'livechat.checkDefaultAgentOnNewRoom': (agent: SelectedAgent, visitor?: ILivechatContactVisitorAssociation) => SelectedAgent | null; 'livechat.onLoadForwardDepartmentRestrictions': (params: { departmentId: string }) => Record; From f447a7863c3149fa709ca5ffe687495de8eec0c2 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Tue, 17 Dec 2024 22:36:40 -0300 Subject: [PATCH 02/15] fix agent search by id when contactManager comes from contact --- .../livechat/server/methods/takeInquiry.ts | 2 +- .../hooks/handleNextAgentPreferredEvents.ts | 19 +++++++++++++------ apps/meteor/server/models/raw/Users.js | 4 ++-- .../model-typings/src/models/IUsersModel.ts | 6 +++++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/apps/meteor/app/livechat/server/methods/takeInquiry.ts b/apps/meteor/app/livechat/server/methods/takeInquiry.ts index 19a08761c614..359dc8e282b5 100644 --- a/apps/meteor/app/livechat/server/methods/takeInquiry.ts +++ b/apps/meteor/app/livechat/server/methods/takeInquiry.ts @@ -44,7 +44,7 @@ export const takeInquiry = async ( }); } - const user = await Users.findOneOnlineAgentById(userId, settings.get('Livechat_enabled_when_agent_idle')); + const user = await Users.findOneOnlineAgentById(userId, {}, settings.get('Livechat_enabled_when_agent_idle')); if (!user) { throw new Meteor.Error('error-agent-status-service-offline', 'Agent status is offline or Omnichannel service is not active', { method: 'livechat:takeInquiry', 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 3bcd13e046c1..8f48b667ca63 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -18,12 +18,15 @@ const normalizeDefaultAgent = (agent?: Pick | null): return { agentId, username }; }; -const getDefaultAgent = async (username?: string): Promise => { - if (!username) { +const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { + if (!username && !id) { return null; } - return normalizeDefaultAgent(await Users.findOneOnlineAgentByUserList(username, { projection: { _id: 1, username: 1 } })); + if (id) { + return normalizeDefaultAgent(await Users.findOneOnlineAgentById(id, { projection: { _id: 1, username: 1 } })); + } + return normalizeDefaultAgent(await Users.findOneOnlineAgentByUserList(username || [], { projection: { _id: 1, username: 1 } })); }; settings.watch('Livechat_last_chatted_agent_routing', (value) => { @@ -104,8 +107,12 @@ callbacks.add( const contact = await LivechatContacts.findOneByVisitor(visitor, { projection: { contactManager: 1 } }); const { lastAgent, token, contactManager } = guest; - const contactManagerUsername = contact?.contactManager || contactManager?.username; - const guestManager = contactManager?.username && contactManagerPreferred && getDefaultAgent(contactManagerUsername); + const contactManagerUsernameOrId = contact?.contactManager || contactManager?.username; + const guestManager = + contactManagerUsernameOrId && + contactManagerPreferred && + (await getDefaultAgent({ username: contactManager?.username, id: contact?.contactManager })); + if (guestManager) { return guestManager; } @@ -114,7 +121,7 @@ callbacks.add( return defaultAgent; } - const guestAgent = lastAgent?.username && getDefaultAgent(lastAgent?.username); + const guestAgent = lastAgent?.username && getDefaultAgent({ username: lastAgent?.username }); if (guestAgent) { return guestAgent; } diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 0e5832b2aad9..4757f0192bfb 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1657,11 +1657,11 @@ export class UsersRaw extends BaseRaw { return this.findOne(query); } - findOneOnlineAgentById(_id, isLivechatEnabledWhenAgentIdle) { + findOneOnlineAgentById(_id, options, isLivechatEnabledWhenAgentIdle) { // TODO: Create class Agent const query = queryStatusAgentOnline({ _id }, isLivechatEnabledWhenAgentIdle); - return this.findOne(query); + return this.findOne(query, options); } findAgents() { diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index a2863aba8fe5..3b53f7d17f21 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -263,7 +263,11 @@ export interface IUsersModel extends IBaseModel { findOnlineAgents(agentId?: string): FindCursor; countOnlineAgents(agentId: string): Promise; findOneBotAgent(): Promise; - findOneOnlineAgentById(agentId: string, isLivechatEnabledWhenAgentIdle?: boolean): Promise; + findOneOnlineAgentById( + agentId: string, + options?: FindOptions, + isLivechatEnabledWhenAgentIdle?: boolean, + ): Promise; findAgents(): FindCursor; countAgents(): Promise; getNextAgent(ignoreAgentId?: string, extraQuery?: Filter): Promise<{ agentId: string; username: string } | null>; From c83d87b044450e811f5a36bfbba38861a5ca961c Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:39:27 -0300 Subject: [PATCH 03/15] Create changeset --- .changeset/popular-cameras-grin.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/popular-cameras-grin.md diff --git a/.changeset/popular-cameras-grin.md b/.changeset/popular-cameras-grin.md new file mode 100644 index 000000000000..98631d59a519 --- /dev/null +++ b/.changeset/popular-cameras-grin.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/model-typings": patch +--- + +Fixed livechat conversations not being assigned to the contact manager even when the "Assign new conversations to the contact manager setting is enabled" From 5a851b67cd0200d11ecad5d5e74e463bc881b99a Mon Sep 17 00:00:00 2001 From: Matheus Barbosa Silva <36537004+matheusbsilva137@users.noreply.github.com> Date: Tue, 17 Dec 2024 22:40:06 -0300 Subject: [PATCH 04/15] Fix changeset --- .changeset/popular-cameras-grin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/popular-cameras-grin.md b/.changeset/popular-cameras-grin.md index 98631d59a519..4ad9f6239f33 100644 --- a/.changeset/popular-cameras-grin.md +++ b/.changeset/popular-cameras-grin.md @@ -3,4 +3,4 @@ "@rocket.chat/model-typings": patch --- -Fixed livechat conversations not being assigned to the contact manager even when the "Assign new conversations to the contact manager setting is enabled" +Fixes livechat conversations not being assigned to the contact manager even when the "Assign new conversations to the contact manager" setting is enabled From 0826f39d50036b4f49461006679cb3b4855ac630 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Wed, 18 Dec 2024 21:04:24 -0300 Subject: [PATCH 05/15] migrate visitor when handling next preferred agent --- .../app/livechat/server/lib/LivechatTyped.ts | 5 +-- .../hooks/handleNextAgentPreferredEvents.ts | 40 +++++++++---------- apps/meteor/lib/callbacks.ts | 4 +- 3 files changed, 23 insertions(+), 26 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 85c9be62b62e..b8cdb38476bd 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -353,12 +353,11 @@ class LivechatClass { throw new Meteor.Error('error-omnichannel-is-disabled'); } - const visitorAssociation = this.makeVisitorAssociation(visitor._id, roomInfo.source); - if (await LivechatContacts.isChannelBlocked(visitorAssociation)) { + if (await LivechatContacts.isChannelBlocked(this.makeVisitorAssociation(visitor._id, roomInfo.source))) { throw new Error('error-contact-channel-blocked'); } - const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, visitorAssociation); + const defaultAgent = agent || (await callbacks.run('livechat.checkDefaultAgentOnNewRoom', visitor._id, 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 8f48b667ca63..be0dc64be5d7 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -3,24 +3,25 @@ import { LivechatVisitors, LivechatContacts, LivechatInquiry, LivechatRooms, Use import { notifyOnLivechatInquiryChanged } from '../../../../../app/lib/server/lib/notifyListener'; import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; +import { migrateVisitorIfMissingContact } from '../../../../../app/livechat/server/lib/contacts/migrateVisitorIfMissingContact'; import { settings } from '../../../../../app/settings/server'; import { callbacks } from '../../../../../lib/callbacks'; let contactManagerPreferred = false; let lastChattedAgentPreferred = false; -const normalizeDefaultAgent = (agent?: Pick | null): SelectedAgent | null => { +const normalizeDefaultAgent = (agent?: Pick | null): SelectedAgent | undefined => { if (!agent) { - return null; + return undefined; } const { _id: agentId, username } = agent; return { agentId, username }; }; -const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { +const getDefaultAgent = async ({ username, id }: { username?: string; id?: string }): Promise => { if (!username && !id) { - return null; + return undefined; } if (id) { @@ -91,34 +92,31 @@ settings.watch('Omnichannel_contact_manager_routing', (value) => { callbacks.add( 'livechat.checkDefaultAgentOnNewRoom', - async (defaultAgent, visitor) => { - if (defaultAgent || !visitor) { - return defaultAgent; + async (visitorId, source) => { + if (!visitorId || !source) { + return undefined; } - const { visitorId: guestId } = visitor; - const guest = await LivechatVisitors.findOneEnabledById(guestId, { + const guest = await LivechatVisitors.findOneEnabledById(visitorId, { projection: { lastAgent: 1, token: 1, contactManager: 1 }, }); if (!guest) { - return defaultAgent; + return undefined; } - const contact = await LivechatContacts.findOneByVisitor(visitor, { projection: { contactManager: 1 } }); + const contactId = await migrateVisitorIfMissingContact(visitorId, source); + const contact = contactId ? await LivechatContacts.findOneById(contactId, { projection: { contactManager: 1 } }) : null; - const { lastAgent, token, contactManager } = guest; - const contactManagerUsernameOrId = contact?.contactManager || contactManager?.username; - const guestManager = - contactManagerUsernameOrId && - contactManagerPreferred && - (await getDefaultAgent({ username: contactManager?.username, id: contact?.contactManager })); + const { lastAgent, token } = guest; + const contactManagerId = contact?.contactManager; + const guestManager = contactManagerId && contactManagerPreferred && getDefaultAgent({ id: contact?.contactManager }); if (guestManager) { return guestManager; } if (!lastChattedAgentPreferred) { - return defaultAgent; + return undefined; } const guestAgent = lastAgent?.username && getDefaultAgent({ username: lastAgent?.username }); @@ -130,19 +128,19 @@ callbacks.add( projection: { servedBy: 1 }, }); if (!room?.servedBy) { - return defaultAgent; + return undefined; } const { servedBy: { username: usernameByRoom }, } = room; if (!usernameByRoom) { - return defaultAgent; + return undefined; } const lastRoomAgent = normalizeDefaultAgent( await Users.findOneOnlineAgentByUserList(usernameByRoom, { projection: { _id: 1, username: 1 } }), ); - return lastRoomAgent ?? defaultAgent; + return lastRoomAgent ?? undefined; }, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room', diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 5baf5a53b64c..6a6af9a3a5c3 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -24,7 +24,7 @@ import type { IOmnichannelRoomInfo, IOmnichannelInquiryExtraData, IOmnichannelRoomExtraData, - ILivechatContactVisitorAssociation, + IOmnichannelSource, } from '@rocket.chat/core-typings'; import type { Updater } from '@rocket.chat/models'; import type { FilterOperators } from 'mongodb'; @@ -119,7 +119,7 @@ type ChainedCallbackSignatures = { ) => Promise; 'livechat.beforeRouteChat': (inquiry: ILivechatInquiryRecord, agent?: { agentId: string; username: string }) => ILivechatInquiryRecord; - 'livechat.checkDefaultAgentOnNewRoom': (agent: SelectedAgent, visitor?: ILivechatContactVisitorAssociation) => SelectedAgent | null; + 'livechat.checkDefaultAgentOnNewRoom': (visitorId?: string, source?: IOmnichannelSource) => Promise; 'livechat.onLoadForwardDepartmentRestrictions': (params: { departmentId: string }) => Record; From d69ca57312d58c289bfdc6bb8caa31d8bb81c029 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Wed, 18 Dec 2024 21:12:38 -0300 Subject: [PATCH 06/15] apply changes requested --- .../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 be0dc64be5d7..7f9aeb4d790d 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -119,7 +119,7 @@ callbacks.add( return undefined; } - const guestAgent = lastAgent?.username && getDefaultAgent({ username: lastAgent?.username }); + const guestAgent = lastAgent?.username && getDefaultAgent({ username: lastAgent.username }); if (guestAgent) { return guestAgent; } From f19b7cc3d7957796da6193a9db69897ffac8efad Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Thu, 19 Dec 2024 10:58:35 -0300 Subject: [PATCH 07/15] fix routing when setting is off or agent is offline --- apps/meteor/app/livechat/server/lib/LivechatTyped.ts | 2 +- .../server/hooks/handleNextAgentPreferredEvents.ts | 10 ++++------ apps/meteor/lib/callbacks.ts | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index b8cdb38476bd..e0dacbcdd943 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -357,7 +357,7 @@ class LivechatClass { throw new Error('error-contact-channel-blocked'); } - const defaultAgent = agent || (await callbacks.run('livechat.checkDefaultAgentOnNewRoom', visitor._id, roomInfo.source)); + const defaultAgent = agent && (await callbacks.run('livechat.checkDefaultAgentOnNewRoom', visitor._id, 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 7f9aeb4d790d..4345cec260f2 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -105,12 +105,9 @@ callbacks.add( } const contactId = await migrateVisitorIfMissingContact(visitorId, source); - const contact = contactId ? await LivechatContacts.findOneById(contactId, { projection: { contactManager: 1 } }) : null; - - const { lastAgent, token } = guest; - const contactManagerId = contact?.contactManager; - const guestManager = contactManagerId && contactManagerPreferred && getDefaultAgent({ id: contact?.contactManager }); + const contact = contactId ? await LivechatContacts.findOneById(contactId, { projection: { contactManager: 1 } }) : undefined; + const guestManager = contactManagerPreferred && (await getDefaultAgent({ id: contact?.contactManager })); if (guestManager) { return guestManager; } @@ -119,7 +116,8 @@ callbacks.add( return undefined; } - const guestAgent = lastAgent?.username && getDefaultAgent({ username: lastAgent.username }); + const { lastAgent, token } = guest; + const guestAgent = await getDefaultAgent({ username: lastAgent?.username }); if (guestAgent) { return guestAgent; } diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 6a6af9a3a5c3..56e6dc81e5fe 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -119,7 +119,7 @@ type ChainedCallbackSignatures = { ) => Promise; 'livechat.beforeRouteChat': (inquiry: ILivechatInquiryRecord, agent?: { agentId: string; username: string }) => ILivechatInquiryRecord; - 'livechat.checkDefaultAgentOnNewRoom': (visitorId?: string, source?: IOmnichannelSource) => Promise; + 'livechat.checkDefaultAgentOnNewRoom': (visitorId?: string, source?: IOmnichannelSource) => SelectedAgent | undefined; 'livechat.onLoadForwardDepartmentRestrictions': (params: { departmentId: string }) => Record; From 23be3c9aa4a02568feac4ddefc5f8fe304c6e0d0 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Thu, 19 Dec 2024 13:34:06 -0300 Subject: [PATCH 08/15] fix CE tests --- apps/meteor/app/livechat/server/lib/LivechatTyped.ts | 7 ++++++- .../server/hooks/handleNextAgentPreferredEvents.ts | 8 ++++---- apps/meteor/lib/callbacks.ts | 5 ++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index e0dacbcdd943..6b3a1370c7b9 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -357,7 +357,12 @@ class LivechatClass { throw new Error('error-contact-channel-blocked'); } - const defaultAgent = agent && (await callbacks.run('livechat.checkDefaultAgentOnNewRoom', visitor._id, roomInfo.source)); + const defaultAgent = + agent ?? + (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 4345cec260f2..4549a844aa29 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -92,9 +92,9 @@ settings.watch('Omnichannel_contact_manager_routing', (value) => { callbacks.add( 'livechat.checkDefaultAgentOnNewRoom', - async (visitorId, source) => { - if (!visitorId || !source) { - return undefined; + async (defaultAgent, { visitorId, source } = {}) => { + if (defaultAgent || !visitorId || !source) { + return defaultAgent; } const guest = await LivechatVisitors.findOneEnabledById(visitorId, { @@ -138,7 +138,7 @@ callbacks.add( const lastRoomAgent = normalizeDefaultAgent( await Users.findOneOnlineAgentByUserList(usernameByRoom, { projection: { _id: 1, username: 1 } }), ); - return lastRoomAgent ?? undefined; + return lastRoomAgent; }, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room', diff --git a/apps/meteor/lib/callbacks.ts b/apps/meteor/lib/callbacks.ts index 56e6dc81e5fe..746c56401b91 100644 --- a/apps/meteor/lib/callbacks.ts +++ b/apps/meteor/lib/callbacks.ts @@ -119,7 +119,10 @@ type ChainedCallbackSignatures = { ) => Promise; 'livechat.beforeRouteChat': (inquiry: ILivechatInquiryRecord, agent?: { agentId: string; username: string }) => ILivechatInquiryRecord; - 'livechat.checkDefaultAgentOnNewRoom': (visitorId?: string, source?: IOmnichannelSource) => SelectedAgent | undefined; + 'livechat.checkDefaultAgentOnNewRoom': ( + defaultAgent?: SelectedAgent, + params?: { visitorId?: string; source?: IOmnichannelSource }, + ) => SelectedAgent | undefined; 'livechat.onLoadForwardDepartmentRestrictions': (params: { departmentId: string }) => Record; From 836faea7c3806b154dd2348cdaef2338eb75070e Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 20 Dec 2024 00:15:21 -0300 Subject: [PATCH 09/15] add end-to-end tests --- .../end-to-end/api/livechat/24-routing.ts | 76 +++++++++++++++++-- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts index fb1301341069..d4b3814f4093 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts @@ -1,9 +1,11 @@ +import { faker } from '@faker-js/faker'; import type { Credentials } from '@rocket.chat/api-client'; -import { UserStatus, type ILivechatDepartment, type IUser } from '@rocket.chat/core-typings'; +import { UserStatus } from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, ILivechatDepartment, IUser } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { after, before, describe, it } from 'mocha'; -import { getCredentials, request, api } from '../../../data/api-data'; +import { getCredentials, request, api, credentials } from '../../../data/api-data'; import { createAgent, makeAgentAvailable, @@ -33,7 +35,9 @@ import { IS_EE } from '../../../e2e/config/constants'; let testUser: { user: IUser; credentials: Credentials }; let testUser2: { user: IUser; credentials: Credentials }; + let testUser3: { user: IUser; credentials: Credentials }; let testDepartment: ILivechatDepartment; + let visitor: ILivechatVisitor; before(async () => { const user = await createUser(); @@ -60,14 +64,45 @@ import { IS_EE } from '../../../e2e/config/constants'; }); before(async () => { - testDepartment = await createDepartment([{ agentId: testUser.user._id }]); + const user = await createUser(); + await createAgent(user.username); + const credentials3 = await login(user.username, password); + await makeAgentAvailable(credentials); + + testUser3 = { + user, + credentials: credentials3, + }; }); - after(async () => { - await deleteUser(testUser.user); - await deleteUser(testUser2.user); + before(async () => { + testDepartment = await createDepartment([{ agentId: testUser.user._id }, { agentId: testUser3.user._id }]); + await updateSetting('Livechat_assign_new_conversation_to_bot', true); + + const visitorName = faker.person.fullName(); + const visitorEmail = faker.internet.email().toLowerCase(); + await request + .post(api('omnichannel/contacts')) + .set(credentials) + .send({ + name: visitorName, + emails: [visitorEmail], + phones: [], + contactManager: testUser3.user._id, + }); + + visitor = await createVisitor(testDepartment._id, visitorName, visitorEmail); }); + after(async () => + Promise.all([ + deleteUser(testUser.user), + deleteUser(testUser2.user), + deleteUser(testUser3.user), + updateSetting('Livechat_assign_new_conversation_to_bot', true), + ]), + ); + it('should route a room to an available agent', async () => { const visitor = await createVisitor(testDepartment._id); const room = await createLivechatRoom(visitor.token); @@ -147,6 +182,35 @@ import { IS_EE } from '../../../e2e/config/constants'; const roomInfo = await getLivechatRoomInfo(room._id); expect(roomInfo.servedBy).to.be.undefined; }); + (IS_EE ? it : it.skip)('should route to contact manager if it is online', async () => { + await makeAgentAvailable(testUser.credentials); + await makeAgentAvailable(testUser3.credentials); + + await setUserStatus(testUser.credentials, UserStatus.ONLINE); + await setUserStatus(testUser3.credentials, UserStatus.ONLINE); + + const room = await createLivechatRoom(visitor.token); + + await sleep(5000); + + const roomInfo = await getLivechatRoomInfo(room._id); + + expect(roomInfo.servedBy).to.be.an('object'); + expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id); + }); + (IS_EE ? it : it.skip)('should route to another available agent if contact manager is unavailable', async () => { + await makeAgentUnavailable(testUser3.credentials); + await setUserStatus(testUser3.credentials, UserStatus.OFFLINE); + + const room = await createLivechatRoom(visitor.token); + + await sleep(5000); + + const roomInfo = await getLivechatRoomInfo(room._id); + + expect(roomInfo.servedBy).to.be.an('object'); + expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id); + }); }); describe('Load Balancing', () => { before(async () => { From cad3bf93b1a89a2f3bd200116a70b1c7328f2a52 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 20 Dec 2024 02:22:56 -0300 Subject: [PATCH 10/15] move end-to-end tests --- .../end-to-end/api/livechat/24-routing.ts | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts index d4b3814f4093..1325d618bf97 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts @@ -67,7 +67,7 @@ import { IS_EE } from '../../../e2e/config/constants'; const user = await createUser(); await createAgent(user.username); const credentials3 = await login(user.username, password); - await makeAgentAvailable(credentials); + await makeAgentAvailable(credentials3); testUser3 = { user, @@ -126,9 +126,20 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(roomInfo.servedBy).to.be.an('object'); expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id); }); + (IS_EE ? it : it.skip)('should route to contact manager if it is online', async () => { + const room = await createLivechatRoom(visitor.token); + + await sleep(5000); + + const roomInfo = await getLivechatRoomInfo(room._id); + + expect(roomInfo.servedBy).to.be.an('object'); + expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id); + }); it('should fail to start a conversation if there is noone available and Livechat_accept_chats_with_no_agents is false', async () => { await updateSetting('Livechat_accept_chats_with_no_agents', false); await makeAgentUnavailable(testUser.credentials); + await makeAgentUnavailable(testUser3.credentials); const visitor = await createVisitor(testDepartment._id); const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }).expect(400); @@ -182,35 +193,6 @@ import { IS_EE } from '../../../e2e/config/constants'; const roomInfo = await getLivechatRoomInfo(room._id); expect(roomInfo.servedBy).to.be.undefined; }); - (IS_EE ? it : it.skip)('should route to contact manager if it is online', async () => { - await makeAgentAvailable(testUser.credentials); - await makeAgentAvailable(testUser3.credentials); - - await setUserStatus(testUser.credentials, UserStatus.ONLINE); - await setUserStatus(testUser3.credentials, UserStatus.ONLINE); - - const room = await createLivechatRoom(visitor.token); - - await sleep(5000); - - const roomInfo = await getLivechatRoomInfo(room._id); - - expect(roomInfo.servedBy).to.be.an('object'); - expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id); - }); - (IS_EE ? it : it.skip)('should route to another available agent if contact manager is unavailable', async () => { - await makeAgentUnavailable(testUser3.credentials); - await setUserStatus(testUser3.credentials, UserStatus.OFFLINE); - - const room = await createLivechatRoom(visitor.token); - - await sleep(5000); - - const roomInfo = await getLivechatRoomInfo(room._id); - - expect(roomInfo.servedBy).to.be.an('object'); - expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id); - }); }); describe('Load Balancing', () => { before(async () => { From 03e323ad01427f5f2ab9da003d8e8ce2b5edf6d5 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 20 Dec 2024 08:11:19 -0300 Subject: [PATCH 11/15] update default setting value on tests --- apps/meteor/tests/end-to-end/api/livechat/24-routing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts index 1325d618bf97..2e534a8975c8 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts @@ -99,7 +99,7 @@ import { IS_EE } from '../../../e2e/config/constants'; deleteUser(testUser.user), deleteUser(testUser2.user), deleteUser(testUser3.user), - updateSetting('Livechat_assign_new_conversation_to_bot', true), + updateSetting('Livechat_assign_new_conversation_to_bot', false), ]), ); From 435225b1c881cf7dde8fa3654fd8fac255760567 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 20 Dec 2024 08:41:31 -0300 Subject: [PATCH 12/15] update model method --- apps/meteor/app/livechat/server/methods/takeInquiry.ts | 2 +- .../server/hooks/handleNextAgentPreferredEvents.ts | 2 +- apps/meteor/server/models/raw/Users.js | 2 +- packages/model-typings/src/models/IUsersModel.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/meteor/app/livechat/server/methods/takeInquiry.ts b/apps/meteor/app/livechat/server/methods/takeInquiry.ts index 359dc8e282b5..19a08761c614 100644 --- a/apps/meteor/app/livechat/server/methods/takeInquiry.ts +++ b/apps/meteor/app/livechat/server/methods/takeInquiry.ts @@ -44,7 +44,7 @@ export const takeInquiry = async ( }); } - const user = await Users.findOneOnlineAgentById(userId, {}, settings.get('Livechat_enabled_when_agent_idle')); + const user = await Users.findOneOnlineAgentById(userId, settings.get('Livechat_enabled_when_agent_idle')); if (!user) { throw new Meteor.Error('error-agent-status-service-offline', 'Agent status is offline or Omnichannel service is not active', { method: 'livechat:takeInquiry', 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 4549a844aa29..5dc13cfce1e0 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.ts @@ -25,7 +25,7 @@ const getDefaultAgent = async ({ username, id }: { username?: string; id?: strin } if (id) { - return normalizeDefaultAgent(await Users.findOneOnlineAgentById(id, { projection: { _id: 1, username: 1 } })); + return normalizeDefaultAgent(await Users.findOneOnlineAgentById(id, undefined, { projection: { _id: 1, username: 1 } })); } return normalizeDefaultAgent(await Users.findOneOnlineAgentByUserList(username || [], { projection: { _id: 1, username: 1 } })); }; diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index 4757f0192bfb..b305f1d50f25 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -1657,7 +1657,7 @@ export class UsersRaw extends BaseRaw { return this.findOne(query); } - findOneOnlineAgentById(_id, options, isLivechatEnabledWhenAgentIdle) { + findOneOnlineAgentById(_id, isLivechatEnabledWhenAgentIdle, options) { // TODO: Create class Agent const query = queryStatusAgentOnline({ _id }, isLivechatEnabledWhenAgentIdle); diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 3b53f7d17f21..7b3226dd9bd7 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -265,8 +265,8 @@ export interface IUsersModel extends IBaseModel { findOneBotAgent(): Promise; findOneOnlineAgentById( agentId: string, - options?: FindOptions, isLivechatEnabledWhenAgentIdle?: boolean, + options?: FindOptions, ): Promise; findAgents(): FindCursor; countAgents(): Promise; From 30d2b0c98395b68425f98dc3a7e4c0afe7e9d5d9 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 20 Dec 2024 08:43:30 -0300 Subject: [PATCH 13/15] update test description --- .../tests/end-to-end/api/livechat/24-routing.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts index 2e534a8975c8..66f813ab53bb 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts @@ -126,16 +126,19 @@ import { IS_EE } from '../../../e2e/config/constants'; expect(roomInfo.servedBy).to.be.an('object'); expect(roomInfo.servedBy?._id).to.not.be.equal(testUser2.user._id); }); - (IS_EE ? it : it.skip)('should route to contact manager if it is online', async () => { - const room = await createLivechatRoom(visitor.token); + (IS_EE ? it : it.skip)( + 'should route to contact manager if it is online and Livechat_assign_new_conversation_to_bot is enabled', + async () => { + const room = await createLivechatRoom(visitor.token); - await sleep(5000); + await sleep(5000); - const roomInfo = await getLivechatRoomInfo(room._id); + const roomInfo = await getLivechatRoomInfo(room._id); - expect(roomInfo.servedBy).to.be.an('object'); - expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id); - }); + expect(roomInfo.servedBy).to.be.an('object'); + expect(roomInfo.servedBy?._id).to.be.equal(testUser3.user._id); + }, + ); it('should fail to start a conversation if there is noone available and Livechat_accept_chats_with_no_agents is false', async () => { await updateSetting('Livechat_accept_chats_with_no_agents', false); await makeAgentUnavailable(testUser.credentials); From 7748f0130297ccbc45a89dc77a763bd92413ab35 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 20 Dec 2024 19:25:29 -0300 Subject: [PATCH 14/15] add test case for offline contact manager --- .../end-to-end/api/livechat/24-routing.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts index 66f813ab53bb..3501e3e38405 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts @@ -37,7 +37,7 @@ import { IS_EE } from '../../../e2e/config/constants'; let testUser2: { user: IUser; credentials: Credentials }; let testUser3: { user: IUser; credentials: Credentials }; let testDepartment: ILivechatDepartment; - let visitor: ILivechatVisitor; + let visitorEmail: string; before(async () => { const user = await createUser(); @@ -80,7 +80,7 @@ import { IS_EE } from '../../../e2e/config/constants'; await updateSetting('Livechat_assign_new_conversation_to_bot', true); const visitorName = faker.person.fullName(); - const visitorEmail = faker.internet.email().toLowerCase(); + visitorEmail = faker.internet.email().toLowerCase(); await request .post(api('omnichannel/contacts')) .set(credentials) @@ -90,8 +90,6 @@ import { IS_EE } from '../../../e2e/config/constants'; phones: [], contactManager: testUser3.user._id, }); - - visitor = await createVisitor(testDepartment._id, visitorName, visitorEmail); }); after(async () => @@ -129,6 +127,7 @@ import { IS_EE } from '../../../e2e/config/constants'; (IS_EE ? it : it.skip)( 'should route to contact manager if it is online and Livechat_assign_new_conversation_to_bot is enabled', async () => { + const visitor = await createVisitor(testDepartment._id, faker.person.fullName(), visitorEmail); const room = await createLivechatRoom(visitor.token); await sleep(5000); @@ -196,6 +195,21 @@ import { IS_EE } from '../../../e2e/config/constants'; const roomInfo = await getLivechatRoomInfo(room._id); expect(roomInfo.servedBy).to.be.undefined; }); + (IS_EE ? it : it.skip)( + 'should route to another available agent if contact manager is unavailable and Livechat_assign_new_conversation_to_bot is enabled', + async () => { + await makeAgentAvailable(testUser.credentials); + const visitor = await createVisitor(testDepartment._id, faker.person.fullName(), visitorEmail); + const room = await createLivechatRoom(visitor.token); + + await sleep(5000); + + const roomInfo = await getLivechatRoomInfo(room._id); + + expect(roomInfo.servedBy).to.be.an('object'); + expect(roomInfo.servedBy?._id).to.be.equal(testUser.user._id); + }, + ); }); describe('Load Balancing', () => { before(async () => { From d89c89e691a4290114bdf58eefb893a60d45dad1 Mon Sep 17 00:00:00 2001 From: matheusbsilva137 Date: Fri, 20 Dec 2024 20:03:23 -0300 Subject: [PATCH 15/15] fix lint --- apps/meteor/tests/end-to-end/api/livechat/24-routing.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts index 3501e3e38405..582d9c096b77 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/24-routing.ts @@ -1,7 +1,7 @@ import { faker } from '@faker-js/faker'; import type { Credentials } from '@rocket.chat/api-client'; import { UserStatus } from '@rocket.chat/core-typings'; -import type { ILivechatVisitor, ILivechatDepartment, IUser } from '@rocket.chat/core-typings'; +import type { ILivechatDepartment, IUser } from '@rocket.chat/core-typings'; import { expect } from 'chai'; import { after, before, describe, it } from 'mocha';