From c4cf84307c4e8e9a85afa9b025e4cc94f85e8e9e Mon Sep 17 00:00:00 2001 From: Guilherme Gazzo Date: Fri, 8 Jan 2021 16:12:06 -0300 Subject: [PATCH 01/11] Update index.js --- client/views/room/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/views/room/index.js b/client/views/room/index.js index e37a0faaeb4ba..593ebec9cbf70 100644 --- a/client/views/room/index.js +++ b/client/views/room/index.js @@ -22,7 +22,7 @@ const Room = () => { const openUserInfo = useTabBarOpenUserInfo(); const hideFlexTab = useUserPreference('hideFlexTab'); - const isOpen = useMutableCallback(() => !(tab && tab.template)); + const isOpen = useMutableCallback(() => !!(tab && tab.template)); const tabBar = useMemo(() => ({ open, close, isOpen, openUserInfo }), [open, close, isOpen, openUserInfo]); From edeff045ea9441e0fb581c265c415180d5397ad2 Mon Sep 17 00:00:00 2001 From: murtaza98 Date: Mon, 18 Jan 2021 11:59:16 +0530 Subject: [PATCH 02/11] Livechat - auto assign visitor to contact-manager --- app/livechat/server/api/lib/inquiries.js | 9 ++++ client/providers/OmniChannelProvider.tsx | 33 +++++++++++---- .../handleLastChattedAgentPreferredEvents.js | 41 +++++++++++++++---- ee/app/livechat-enterprise/server/settings.js | 12 +++++- packages/rocketchat-i18n/i18n/en.i18n.json | 4 +- 5 files changed, 80 insertions(+), 19 deletions(-) diff --git a/app/livechat/server/api/lib/inquiries.js b/app/livechat/server/api/lib/inquiries.js index eefba5f313e88..509784651828c 100644 --- a/app/livechat/server/api/lib/inquiries.js +++ b/app/livechat/server/api/lib/inquiries.js @@ -44,6 +44,15 @@ export async function findInquiries({ userId, department: filterDepartment, stat const filter = { ...status && { status }, ...department && { department }, + $or: [ + { defaultAgent: { $exists: false } }, + { + $and: [ + { defaultAgent: { $exists: true } }, + { 'defaultAgent.agentId': userId }, + ], + }, + ], }; const cursor = LivechatInquiry.find(filter, options); diff --git a/client/providers/OmniChannelProvider.tsx b/client/providers/OmniChannelProvider.tsx index df7e1735f3958..19e2f4fcd89c8 100644 --- a/client/providers/OmniChannelProvider.tsx +++ b/client/providers/OmniChannelProvider.tsx @@ -44,6 +44,15 @@ const useOmnichannelInquiries = (): Array => { return useReactiveValue(useCallback(() => LivechatInquiry.find({ status: 'queued', + $or: [ + { defaultAgent: { $exists: false } }, + { + $and: [ + { defaultAgent: { $exists: true } }, + { 'defaultAgent.agentId': uid }, + ], + }, + ], }, { sort: { queueOrder: 1, @@ -51,23 +60,27 @@ const useOmnichannelInquiries = (): Array => { estimatedServiceTimeAt: 1, }, limit: omnichannelPoolMaxIncoming, - }).fetch(), [omnichannelPoolMaxIncoming])); + }).fetch(), [omnichannelPoolMaxIncoming, uid])); }; const OmnichannelDisabledProvider: FC = ({ children }) => ; const OmnichannelManualSelectionProvider: FC<{ value: OmnichannelContextValue }> = ({ value, children }) => { const queue = useOmnichannelInquiries(); + console.log('---queue', queue); const showOmnichannelQueueLink = useSetting('Livechat_show_queue_list_link') as boolean && value.agentAvailable; - const contextValue = useMemo(() => ({ - ...value, - inquiries: { - enabled: true, - queue, - }, - showOmnichannelQueueLink, - }), [value, queue, showOmnichannelQueueLink]); + const contextValue = useMemo(() => { + console.log('showOmnichannelQueueLink', showOmnichannelQueueLink); + return { + ...value, + inquiries: { + enabled: true, + queue, + }, + showOmnichannelQueueLink, + }; + }, [value, queue, showOmnichannelQueueLink]); return ; }; @@ -99,6 +112,8 @@ const OmnichannelEnabledProvider: FC = ({ children }) => { return ; } + console.log('--- manual test', canViewOmnichannelQueue && routeConfig.showQueue && !routeConfig.autoAssignAgent && contextValue.agentAvailable); + if (canViewOmnichannelQueue && routeConfig.showQueue && !routeConfig.autoAssignAgent && contextValue.agentAvailable) { return ; } diff --git a/ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js b/ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js index ffe2f6463db06..e6f726f6a94c8 100644 --- a/ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js +++ b/ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js @@ -3,6 +3,9 @@ import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingMa import { settings } from '../../../../../app/settings/server'; import { LivechatRooms, LivechatInquiry, LivechatVisitors, Users } from '../../../../../app/models/server'; +let contactManagerPreferred = false; +let lastChattedAgentPreferred = false; + const normalizeDefaultAgent = (agent) => { if (!agent) { return; @@ -17,20 +20,24 @@ const checkDefaultAgentOnNewRoom = (defaultAgent, defaultGuest) => { return defaultAgent; } - if (!RoutingManager.getConfig().autoAssignAgent) { + if (!contactManagerPreferred && !lastChattedAgentPreferred) { return defaultAgent; } const { _id: guestId } = defaultGuest; - const guest = LivechatVisitors.findOneById(guestId, { fields: { lastAgent: 1, token: 1 } }); - const { lastAgent: { username: usernameByVisitor } = {}, token } = guest; + const guest = LivechatVisitors.findOneById(guestId, { fields: { lastAgent: 1, token: 1, contactManager: 1 } }); + const { lastAgent: { username: usernameByVisitor } = {}, token, contactManager: { username: contactManagerUsername } = {} } = guest; + const contactManager = contactManagerUsername && normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(contactManagerUsername, { fields: { _id: 1, username: 1 } })); + if (contactManager) { + return contactManager; + } - const lastGuestAgent = usernameByVisitor && normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(usernameByVisitor, { fields: { _id: 1, username: 1 } })); + const lastGuestAgent = lastChattedAgentPreferred && usernameByVisitor && normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(usernameByVisitor, { fields: { _id: 1, username: 1 } })); if (lastGuestAgent) { return lastGuestAgent; } - const room = LivechatRooms.findOneLastServedAndClosedByVisitorToken(token, { fields: { servedBy: 1 } }); + const room = lastChattedAgentPreferred && LivechatRooms.findOneLastServedAndClosedByVisitorToken(token, { fields: { servedBy: 1 } }); if (!room || !room.servedBy) { return defaultAgent; } @@ -74,14 +81,32 @@ const afterTakeInquiry = (inquiry, agent) => { return inquiry; }; settings.get('Livechat_last_chatted_agent_routing', function(key, value) { - if (!value) { - callbacks.remove('livechat.checkDefaultAgentOnNewRoom', 'livechat-check-default-agent-new-room'); + lastChattedAgentPreferred = value; + if (!lastChattedAgentPreferred) { callbacks.remove('livechat.onMaxNumberSimultaneousChatsReached', 'livechat-on-max-number-simultaneous-chats-reached'); callbacks.remove('livechat.afterTakeInquiry', 'livechat-save-default-agent-after-take-inquiry'); + if (!contactManagerPreferred) { + callbacks.remove('livechat.checkDefaultAgentOnNewRoom', 'livechat-check-default-agent-new-room'); + } return; } - callbacks.add('livechat.checkDefaultAgentOnNewRoom', checkDefaultAgentOnNewRoom, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room'); callbacks.add('livechat.onMaxNumberSimultaneousChatsReached', onMaxNumberSimultaneousChatsReached, callbacks.priority.MEDIUM, 'livechat-on-max-number-simultaneous-chats-reached'); callbacks.add('livechat.afterTakeInquiry', afterTakeInquiry, callbacks.priority.MEDIUM, 'livechat-save-default-agent-after-take-inquiry'); + if (!contactManagerPreferred) { + callbacks.add('livechat.checkDefaultAgentOnNewRoom', checkDefaultAgentOnNewRoom, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room'); + } +}); + +settings.get('Livechat_contact_manager_routing', function(key, value) { + contactManagerPreferred = value; + if (!contactManagerPreferred) { + if (!lastChattedAgentPreferred) { + callbacks.remove('livechat.checkDefaultAgentOnNewRoom', 'livechat-check-default-agent-new-room'); + } + return; + } + if (!lastChattedAgentPreferred) { + callbacks.add('livechat.checkDefaultAgentOnNewRoom', checkDefaultAgentOnNewRoom, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room'); + } }); diff --git a/ee/app/livechat-enterprise/server/settings.js b/ee/app/livechat-enterprise/server/settings.js index ee5b5aa41a6f1..b33ffbcb69ee9 100644 --- a/ee/app/livechat-enterprise/server/settings.js +++ b/ee/app/livechat-enterprise/server/settings.js @@ -82,7 +82,6 @@ export const createSettings = () => { type: 'boolean', group: 'Omnichannel', section: 'Routing', - enableQuery: { _id: 'Livechat_Routing_Method', value: { $ne: 'Manual_Selection' } }, enterprise: true, invalidValue: false, modules: [ @@ -112,5 +111,16 @@ export const createSettings = () => { }); }); + settings.add('Livechat_contact_manager_routing', true, { + type: 'boolean', + group: 'Omnichannel', + section: 'Routing', + enterprise: true, + invalidValue: false, + modules: [ + 'livechat-enterprise', + ], + }); + Settings.addOptionValueById('Livechat_Routing_Method', { key: 'Load_Balancing', i18nLabel: 'Load_Balancing' }); }; diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index ce2a83c4e69c3..34a1dcc00800b 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2341,6 +2341,8 @@ "Livechat_Appearance": "Livechat Appearance", "Livechat_business_hour_type": "Business Hour Type (Single or Multiple)", "Livechat_chat_transcript_sent": "Chat transcript sent: __transcript__", + "Livechat_contact_manager_routing": "Contact Manager Preferred", + "Livechat_contact_manager_routing_Description": "This setting allocates a chat to the assigned Contact Manager, provided the Contact Manager is online when the chat starts.", "Livechat_custom_fields_options_placeholder": "Comma-separated list used to select a pre-configured value. Spaces between elements are not accepted.", "Livechat_custom_fields_public_description": "Public custom fields will be displayed in external applications, such as Livechat, etc.", "Livechat_Dashboard": "Omnichannel Dashboard", @@ -4198,4 +4200,4 @@ "Your_temporary_password_is_password": "Your temporary password is [password].", "Your_TOTP_has_been_reset": "Your Two Factor TOTP has been reset.", "Your_workspace_is_ready": "Your workspace is ready to use 🎉" -} \ No newline at end of file +} From 40732c61cec4fe2b8d1a2b3991bb26215509d6a8 Mon Sep 17 00:00:00 2001 From: murtaza98 Date: Mon, 18 Jan 2021 12:51:53 +0530 Subject: [PATCH 03/11] remove console logs --- client/providers/OmniChannelProvider.tsx | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/client/providers/OmniChannelProvider.tsx b/client/providers/OmniChannelProvider.tsx index 19e2f4fcd89c8..db43baa391a8b 100644 --- a/client/providers/OmniChannelProvider.tsx +++ b/client/providers/OmniChannelProvider.tsx @@ -67,20 +67,16 @@ const OmnichannelDisabledProvider: FC = ({ children }) => = ({ value, children }) => { const queue = useOmnichannelInquiries(); - console.log('---queue', queue); const showOmnichannelQueueLink = useSetting('Livechat_show_queue_list_link') as boolean && value.agentAvailable; - const contextValue = useMemo(() => { - console.log('showOmnichannelQueueLink', showOmnichannelQueueLink); - return { - ...value, - inquiries: { - enabled: true, - queue, - }, - showOmnichannelQueueLink, - }; - }, [value, queue, showOmnichannelQueueLink]); + const contextValue = useMemo(() => ({ + ...value, + inquiries: { + enabled: true, + queue, + }, + showOmnichannelQueueLink, + }), [value, queue, showOmnichannelQueueLink]); return ; }; @@ -112,8 +108,6 @@ const OmnichannelEnabledProvider: FC = ({ children }) => { return ; } - console.log('--- manual test', canViewOmnichannelQueue && routeConfig.showQueue && !routeConfig.autoAssignAgent && contextValue.agentAvailable); - if (canViewOmnichannelQueue && routeConfig.showQueue && !routeConfig.autoAssignAgent && contextValue.agentAvailable) { return ; } From 2b2910d5d993f8f5ac291d7782a4aa5d46783e3f Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Mon, 18 Jan 2021 14:56:01 -0300 Subject: [PATCH 04/11] Add improvements to Omnichannel Queue. --- app/livechat/server/api/lib/inquiries.js | 7 +-- client/providers/OmniChannelProvider.tsx | 7 +-- ...s.js => handleNextAgentPreferredEvents.js} | 54 +++++++++---------- ee/app/livechat-enterprise/server/index.js | 2 +- ee/app/livechat-enterprise/server/settings.js | 2 +- packages/rocketchat-i18n/i18n/en.i18n.json | 4 +- 6 files changed, 30 insertions(+), 46 deletions(-) rename ee/app/livechat-enterprise/server/hooks/{handleLastChattedAgentPreferredEvents.js => handleNextAgentPreferredEvents.js} (61%) diff --git a/app/livechat/server/api/lib/inquiries.js b/app/livechat/server/api/lib/inquiries.js index 509784651828c..6551d8bd3b73f 100644 --- a/app/livechat/server/api/lib/inquiries.js +++ b/app/livechat/server/api/lib/inquiries.js @@ -46,12 +46,7 @@ export async function findInquiries({ userId, department: filterDepartment, stat ...department && { department }, $or: [ { defaultAgent: { $exists: false } }, - { - $and: [ - { defaultAgent: { $exists: true } }, - { 'defaultAgent.agentId': userId }, - ], - }, + { 'defaultAgent.agentId': userId }, ], }; diff --git a/client/providers/OmniChannelProvider.tsx b/client/providers/OmniChannelProvider.tsx index db43baa391a8b..e78fc0cf81fb1 100644 --- a/client/providers/OmniChannelProvider.tsx +++ b/client/providers/OmniChannelProvider.tsx @@ -46,12 +46,7 @@ const useOmnichannelInquiries = (): Array => { status: 'queued', $or: [ { defaultAgent: { $exists: false } }, - { - $and: [ - { defaultAgent: { $exists: true } }, - { 'defaultAgent.agentId': uid }, - ], - }, + { 'defaultAgent.agentId': uid }, ], }, { sort: { diff --git a/ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js b/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js similarity index 61% rename from ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js rename to ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js index e6f726f6a94c8..70cf553eb8ba1 100644 --- a/ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js +++ b/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js @@ -15,35 +15,41 @@ const normalizeDefaultAgent = (agent) => { return { agentId, username }; }; +const getDefaultAgent = (username) => username && normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(username, { fields: { _id: 1, username: 1 } })); + const checkDefaultAgentOnNewRoom = (defaultAgent, defaultGuest) => { if (defaultAgent || !defaultGuest) { return defaultAgent; } - if (!contactManagerPreferred && !lastChattedAgentPreferred) { - return defaultAgent; - } - const { _id: guestId } = defaultGuest; const guest = LivechatVisitors.findOneById(guestId, { fields: { lastAgent: 1, token: 1, contactManager: 1 } }); - const { lastAgent: { username: usernameByVisitor } = {}, token, contactManager: { username: contactManagerUsername } = {} } = guest; - const contactManager = contactManagerUsername && normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(contactManagerUsername, { fields: { _id: 1, username: 1 } })); - if (contactManager) { - return contactManager; + if (!guest) { + console.log('guest'); + console.log(guest); + return defaultAgent; } - const lastGuestAgent = lastChattedAgentPreferred && usernameByVisitor && normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(usernameByVisitor, { fields: { _id: 1, username: 1 } })); - if (lastGuestAgent) { - return lastGuestAgent; + const { lastAgent, token, contactManager } = guest; + const guestAgent = (contactManagerPreferred && getDefaultAgent(contactManager?.username)) || (lastChattedAgentPreferred && getDefaultAgent(lastAgent?.username)); + console.log('guestAgent'); + console.log(guestAgent); + + if (guestAgent) { + return guestAgent; } - const room = lastChattedAgentPreferred && LivechatRooms.findOneLastServedAndClosedByVisitorToken(token, { fields: { servedBy: 1 } }); - if (!room || !room.servedBy) { + const room = LivechatRooms.findOneLastServedAndClosedByVisitorToken(token, { fields: { servedBy: 1 } }); + if (!room?.servedBy) { + console.log('!room?.servedBy'); return defaultAgent; } const { servedBy: { username: usernameByRoom } } = room; const lastRoomAgent = normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(usernameByRoom, { fields: { _id: 1, username: 1 } })); + console.log('lastRoomAgent'); + console.log(lastRoomAgent); + return lastRoomAgent || defaultAgent; }; @@ -85,28 +91,16 @@ settings.get('Livechat_last_chatted_agent_routing', function(key, value) { if (!lastChattedAgentPreferred) { callbacks.remove('livechat.onMaxNumberSimultaneousChatsReached', 'livechat-on-max-number-simultaneous-chats-reached'); callbacks.remove('livechat.afterTakeInquiry', 'livechat-save-default-agent-after-take-inquiry'); - if (!contactManagerPreferred) { - callbacks.remove('livechat.checkDefaultAgentOnNewRoom', 'livechat-check-default-agent-new-room'); - } return; } - callbacks.add('livechat.onMaxNumberSimultaneousChatsReached', onMaxNumberSimultaneousChatsReached, callbacks.priority.MEDIUM, 'livechat-on-max-number-simultaneous-chats-reached'); callbacks.add('livechat.afterTakeInquiry', afterTakeInquiry, callbacks.priority.MEDIUM, 'livechat-save-default-agent-after-take-inquiry'); - if (!contactManagerPreferred) { - callbacks.add('livechat.checkDefaultAgentOnNewRoom', checkDefaultAgentOnNewRoom, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room'); - } + callbacks.add('livechat.onMaxNumberSimultaneousChatsReached', onMaxNumberSimultaneousChatsReached, callbacks.priority.MEDIUM, 'livechat-on-max-number-simultaneous-chats-reached'); }); -settings.get('Livechat_contact_manager_routing', function(key, value) { +settings.get('Omnichannel_contact_manager_routing', function(key, value) { contactManagerPreferred = value; - if (!contactManagerPreferred) { - if (!lastChattedAgentPreferred) { - callbacks.remove('livechat.checkDefaultAgentOnNewRoom', 'livechat-check-default-agent-new-room'); - } - return; - } - if (!lastChattedAgentPreferred) { - callbacks.add('livechat.checkDefaultAgentOnNewRoom', checkDefaultAgentOnNewRoom, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room'); - } }); + + +callbacks.add('livechat.checkDefaultAgentOnNewRoom', checkDefaultAgentOnNewRoom, callbacks.priority.MEDIUM, 'livechat-check-default-agent-new-room'); diff --git a/ee/app/livechat-enterprise/server/index.js b/ee/app/livechat-enterprise/server/index.js index 4dd422dbb92ff..d26327a654f13 100644 --- a/ee/app/livechat-enterprise/server/index.js +++ b/ee/app/livechat-enterprise/server/index.js @@ -20,7 +20,7 @@ import './hooks/beforeNewInquiry'; import './hooks/beforeNewRoom'; import './hooks/beforeRoutingChat'; import './hooks/checkAgentBeforeTakeInquiry'; -import './hooks/handleLastChattedAgentPreferredEvents'; +import './hooks/handleNextAgentPreferredEvents'; import './hooks/onCheckRoomParamsApi'; import './hooks/onLoadConfigApi'; import './hooks/onCloseLivechat'; diff --git a/ee/app/livechat-enterprise/server/settings.js b/ee/app/livechat-enterprise/server/settings.js index b33ffbcb69ee9..7d414fbc50289 100644 --- a/ee/app/livechat-enterprise/server/settings.js +++ b/ee/app/livechat-enterprise/server/settings.js @@ -111,7 +111,7 @@ export const createSettings = () => { }); }); - settings.add('Livechat_contact_manager_routing', true, { + settings.add('Omnichannel_contact_manager_routing', true, { type: 'boolean', group: 'Omnichannel', section: 'Routing', diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index 34a1dcc00800b..e98b5da2ab248 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2341,8 +2341,8 @@ "Livechat_Appearance": "Livechat Appearance", "Livechat_business_hour_type": "Business Hour Type (Single or Multiple)", "Livechat_chat_transcript_sent": "Chat transcript sent: __transcript__", - "Livechat_contact_manager_routing": "Contact Manager Preferred", - "Livechat_contact_manager_routing_Description": "This setting allocates a chat to the assigned Contact Manager, provided the Contact Manager is online when the chat starts.", + "Omnichannel_contact_manager_routing": "Contact Manager Preferred", + "Omnichannel_contact_manager_routing_Description": "This setting allocates a chat to the assigned Contact Manager, provided the Contact Manager is online when the chat starts.", "Livechat_custom_fields_options_placeholder": "Comma-separated list used to select a pre-configured value. Spaces between elements are not accepted.", "Livechat_custom_fields_public_description": "Public custom fields will be displayed in external applications, such as Livechat, etc.", "Livechat_Dashboard": "Omnichannel Dashboard", From a66872bcc54e3d9a46c42b8b1e2ef0248cbcfbaa Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Mon, 18 Jan 2021 22:51:54 -0300 Subject: [PATCH 05/11] Imoprovements on codebase. --- .../hooks/handleNextAgentPreferredEvents.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js b/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js index 70cf553eb8ba1..42f180d270f40 100644 --- a/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js +++ b/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js @@ -25,31 +25,31 @@ const checkDefaultAgentOnNewRoom = (defaultAgent, defaultGuest) => { const { _id: guestId } = defaultGuest; const guest = LivechatVisitors.findOneById(guestId, { fields: { lastAgent: 1, token: 1, contactManager: 1 } }); if (!guest) { - console.log('guest'); - console.log(guest); return defaultAgent; } const { lastAgent, token, contactManager } = guest; - const guestAgent = (contactManagerPreferred && getDefaultAgent(contactManager?.username)) || (lastChattedAgentPreferred && getDefaultAgent(lastAgent?.username)); - console.log('guestAgent'); - console.log(guestAgent); + const guestManager = contactManagerPreferred && getDefaultAgent(contactManager?.username); + if (guestManager) { + return guestManager; + } + + if (!lastChattedAgentPreferred) { + return defaultAgent; + } + const guestAgent = getDefaultAgent(lastAgent?.username); if (guestAgent) { return guestAgent; } const room = LivechatRooms.findOneLastServedAndClosedByVisitorToken(token, { fields: { servedBy: 1 } }); if (!room?.servedBy) { - console.log('!room?.servedBy'); return defaultAgent; } const { servedBy: { username: usernameByRoom } } = room; const lastRoomAgent = normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(usernameByRoom, { fields: { _id: 1, username: 1 } })); - console.log('lastRoomAgent'); - console.log(lastRoomAgent); - return lastRoomAgent || defaultAgent; }; From 6d99797bc3c3bce2ae43e67a54f0c25bd7798b7f Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Wed, 20 Jan 2021 10:04:49 -0300 Subject: [PATCH 06/11] Final queue improvements. --- ...GetNextAgent.js => beforeDelegateAgent.js} | 14 +++- app/livechat/server/index.js | 2 +- app/livechat/server/lib/Helper.js | 76 +++++++++++++++++-- app/livechat/server/lib/Livechat.js | 9 ++- app/livechat/server/lib/QueueManager.js | 13 +--- app/livechat/server/lib/RoutingManager.js | 24 +++--- .../server/lib/routing/AutoSelection.js | 4 - app/livechat/server/lib/routing/External.js | 4 - .../server/lib/routing/ManualSelection.js | 62 --------------- app/models/server/models/LivechatInquiry.js | 13 +++- app/models/server/raw/Users.js | 2 +- .../server/hooks/beforeRoutingChat.js | 4 +- .../hooks/checkAgentBeforeTakeInquiry.js | 3 +- .../livechat-enterprise/server/lib/Helper.js | 8 -- .../server/lib/routing/LoadBalancing.js | 4 - 15 files changed, 122 insertions(+), 120 deletions(-) rename app/livechat/server/hooks/{beforeGetNextAgent.js => beforeDelegateAgent.js} (57%) diff --git a/app/livechat/server/hooks/beforeGetNextAgent.js b/app/livechat/server/hooks/beforeDelegateAgent.js similarity index 57% rename from app/livechat/server/hooks/beforeGetNextAgent.js rename to app/livechat/server/hooks/beforeDelegateAgent.js index 091a7cfb9c0b4..101d53e797d21 100644 --- a/app/livechat/server/hooks/beforeGetNextAgent.js +++ b/app/livechat/server/hooks/beforeDelegateAgent.js @@ -3,14 +3,20 @@ import { callbacks } from '../../../callbacks'; import { settings } from '../../../settings'; import { Users, LivechatDepartmentAgents } from '../../../models'; -callbacks.add('livechat.beforeGetNextAgent', (department, ignoreAgentId) => { +callbacks.add('livechat.beforeDelegateAgent', (options = {}) => { + const { department, agent } = options; + + if (agent) { + return agent; + } + if (!settings.get('Livechat_assign_new_conversation_to_bot')) { return null; } if (department) { - return LivechatDepartmentAgents.getNextBotForDepartment(department, ignoreAgentId); + return LivechatDepartmentAgents.getNextBotForDepartment(department); } - return Users.getNextBotAgent(ignoreAgentId); -}, callbacks.priority.HIGH, 'livechat-before-get-next-agent'); + return Users.getNextBotAgent(); +}, callbacks.priority.HIGH, 'livechat-before-delegate-agent'); diff --git a/app/livechat/server/index.js b/app/livechat/server/index.js index 435627266cf42..9d2c6fdf3b355 100644 --- a/app/livechat/server/index.js +++ b/app/livechat/server/index.js @@ -6,7 +6,7 @@ import '../lib/messageTypes'; import './config'; import './roomType'; import './hooks/beforeCloseRoom'; -import './hooks/beforeGetNextAgent'; +import './hooks/beforeDelegateAgent'; import './hooks/leadCapture'; import './hooks/markRoomResponded'; import './hooks/offlineMessage'; diff --git a/app/livechat/server/lib/Helper.js b/app/livechat/server/lib/Helper.js index 81c5a3482fa72..85aae4514f955 100644 --- a/app/livechat/server/lib/Helper.js +++ b/app/livechat/server/lib/Helper.js @@ -1,7 +1,9 @@ import { Meteor } from 'meteor/meteor'; +import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import { Match, check } from 'meteor/check'; import { LivechatTransferEventType } from '@rocket.chat/apps-engine/definition/livechat'; +import { hasRole } from '../../../authorization'; import { Messages, LivechatRooms, Rooms, Subscriptions, Users, LivechatInquiry, LivechatDepartment, LivechatDepartmentAgents } from '../../../models/server'; import { Livechat } from './Livechat'; import { RoutingManager } from './RoutingManager'; @@ -9,6 +11,15 @@ import { callbacks } from '../../../callbacks/server'; import { settings } from '../../../settings'; import { Apps, AppEvents } from '../../../apps/server'; import notifications from '../../../notifications/server/lib/Notifications'; +import { sendNotification } from '../../../lib/server'; + +export const allowAgentSkipQueue = (agent) => { + check(agent, Match.ObjectIncluding({ + agentId: String, + })); + + return settings.get('Livechat_assign_new_conversation_to_bot') && hasRole(agent.agentId, 'bot'); +}; export const createLivechatRoom = (rid, name, guest, roomInfo = {}, extraData = {}) => { check(rid, String); @@ -199,6 +210,61 @@ export const dispatchAgentDelegated = (rid, agentId) => { }); }; +export const dispatchInquiryQueued = (inquiry, agent) => { + if (!inquiry?._id) { + return; + } + + const { department, rid, v } = inquiry; + const room = LivechatRooms.findOneById(rid); + Meteor.defer(() => callbacks.run('livechat.chatQueued', room)); + + if (RoutingManager.getConfig().autoAssignAgent) { + return; + } + + if (!agent || !allowAgentSkipQueue(agent)) { + LivechatInquiry.queueInquiry(inquiry._id); + } + + // Alert only the online agents of the queued request + const onlineAgents = Livechat.getOnlineAgents(department, agent); + + const notificationUserName = v && (v.name || v.username); + + onlineAgents.forEach((agent) => { + if (agent.agentId) { + agent = Users.findOneById(agent.agentId); + } + const { _id, active, emails, language, status, statusConnection, username } = agent; + sendNotification({ + // fake a subscription in order to make use of the function defined above + subscription: { + rid, + t: 'l', + u: { + _id, + }, + receiver: [{ + active, + emails, + language, + status, + statusConnection, + username, + }], + }, + sender: v, + hasMentionToAll: true, // consider all agents to be in the room + hasMentionToHere: false, + message: Object.assign({}, { u: v }), + notificationMessage: TAPi18n.__('User_started_a_new_conversation', { username: notificationUserName }, language), + room: Object.assign(room, { name: TAPi18n.__('New_chat_in_queue', {}, language) }), + mentionIds: [], + }); + }); +}; + export const forwardRoomToAgent = async (room, transferData) => { if (!room || !room.open) { return false; @@ -351,13 +417,13 @@ export const normalizeTransferredByData = (transferredBy, room) => { }; export const checkServiceStatus = ({ guest, agent }) => { - if (agent) { - const { agentId } = agent; - const users = Users.findOnlineAgents(agentId); - return users && users.count() > 0; + if (!agent) { + return Livechat.online(guest.department); } - return Livechat.online(guest.department); + const { agentId } = agent; + const users = Users.findOnlineAgents(agentId); + return users && users.count() > 0; }; export const userCanTakeInquiry = (user) => { diff --git a/app/livechat/server/lib/Livechat.js b/app/livechat/server/lib/Livechat.js index 198b5da2eed1b..9e407614bd186 100644 --- a/app/livechat/server/lib/Livechat.js +++ b/app/livechat/server/lib/Livechat.js @@ -63,7 +63,7 @@ export const Livechat = { } const onlineAgents = Livechat.getOnlineAgents(department); - return (onlineAgents && onlineAgents.count() > 0) || settings.get('Livechat_accept_chats_with_no_agents'); + return onlineAgents && onlineAgents.count() > 0; }, getNextAgent(department) { @@ -78,7 +78,11 @@ export const Livechat = { return Users.findAgents(); }, - getOnlineAgents(department) { + getOnlineAgents(department, agent) { + if (agent?.agentId) { + return Users.findOnlineAgents(agent.agentId); + } + if (department) { return LivechatDepartmentAgents.getOnlineForDepartment(department); } @@ -622,7 +626,6 @@ export const Livechat = { try { this.saveTransferHistory(room, transferData); RoutingManager.unassignAgent(inquiry, departmentId); - Meteor.defer(() => callbacks.run('livechat.chatQueued', LivechatRooms.findOneById(rid))); } catch (e) { console.error(e); throw new Meteor.Error('error-returning-inquiry', 'Error returning inquiry to the queue', { method: 'livechat:returnRoomAsInquiry' }); diff --git a/app/livechat/server/lib/QueueManager.js b/app/livechat/server/lib/QueueManager.js index 46ed51b7a14e6..0b926e9afac9f 100644 --- a/app/livechat/server/lib/QueueManager.js +++ b/app/livechat/server/lib/QueueManager.js @@ -30,17 +30,12 @@ export const QueueManager = { LivechatRooms.updateRoomCount(); - if (!agent) { - agent = RoutingManager.getMethod().delegateAgent(agent, inquiry); - } + await callbacks.run('livechat.beforeRouteChat', inquiry); + const defaultAgent = RoutingManager.delegateAgent(agent, inquiry); + inquiry = LivechatInquiry.findOneById(inquiry._id); - inquiry = await callbacks.run('livechat.beforeRouteChat', inquiry, agent); if (inquiry.status === 'ready') { - return RoutingManager.delegateInquiry(inquiry, agent); - } - - if (inquiry.status === 'queued') { - Meteor.defer(() => callbacks.run('livechat.chatQueued', room)); + return RoutingManager.delegateInquiry(inquiry, defaultAgent); } return room; diff --git a/app/livechat/server/lib/RoutingManager.js b/app/livechat/server/lib/RoutingManager.js index 0cba8fc3fe89c..566741ed1b776 100644 --- a/app/livechat/server/lib/RoutingManager.js +++ b/app/livechat/server/lib/RoutingManager.js @@ -5,6 +5,7 @@ import { settings } from '../../../settings/server'; import { createLivechatSubscription, dispatchAgentDelegated, + dispatchInquiryQueued, forwardRoomToAgent, forwardRoomToDepartment, removeAgentFromSubscription, @@ -38,13 +39,7 @@ export const RoutingManager = { }, async getNextAgent(department, ignoreAgentId) { - let agent = callbacks.run('livechat.beforeGetNextAgent', department, ignoreAgentId); - - if (!agent) { - agent = await this.getMethod().getNextAgent(department, ignoreAgentId); - } - - return agent; + return this.getMethod().getNextAgent(department, ignoreAgentId); }, async delegateInquiry(inquiry, agent) { @@ -85,7 +80,7 @@ export const RoutingManager = { }, unassignAgent(inquiry, departmentId) { - const { _id, rid, department } = inquiry; + const { rid, department } = inquiry; const room = LivechatRooms.findOneById(rid); if (!room || !room.open) { @@ -110,8 +105,7 @@ export const RoutingManager = { dispatchAgentDelegated(rid, null); } - LivechatInquiry.queueInquiry(_id); - this.getMethod().delegateAgent(null, inquiry); + dispatchInquiryQueued(inquiry); return true; }, @@ -161,6 +155,16 @@ export const RoutingManager = { return false; }, + + delegateAgent(agent, inquiry) { + const defaultAgent = callbacks.run('livechat.beforeDelegateAgent', { agent, department: inquiry?.department }); + if (defaultAgent) { + LivechatInquiry.setDefaultAgentById(inquiry._id, defaultAgent); + } + + dispatchInquiryQueued(inquiry, defaultAgent); + return defaultAgent; + }, }; settings.get('Livechat_Routing_Method', function(key, value) { diff --git a/app/livechat/server/lib/routing/AutoSelection.js b/app/livechat/server/lib/routing/AutoSelection.js index 3e615f420015f..39d0d211df6e0 100644 --- a/app/livechat/server/lib/routing/AutoSelection.js +++ b/app/livechat/server/lib/routing/AutoSelection.js @@ -26,10 +26,6 @@ class AutoSelection { return Users.getNextAgent(ignoreAgentId); } - - delegateAgent(agent) { - return agent; - } } RoutingManager.registerMethod('Auto_Selection', AutoSelection); diff --git a/app/livechat/server/lib/routing/External.js b/app/livechat/server/lib/routing/External.js index 1323f63ad4a2b..d3771871970f8 100644 --- a/app/livechat/server/lib/routing/External.js +++ b/app/livechat/server/lib/routing/External.js @@ -51,10 +51,6 @@ class ExternalQueue { } throw new Meteor.Error('no-agent-online', 'Sorry, no online agents'); } - - delegateAgent(agent) { - return agent; - } } RoutingManager.registerMethod('External', ExternalQueue); diff --git a/app/livechat/server/lib/routing/ManualSelection.js b/app/livechat/server/lib/routing/ManualSelection.js index 1c14fe05e1857..94100bfd95f05 100644 --- a/app/livechat/server/lib/routing/ManualSelection.js +++ b/app/livechat/server/lib/routing/ManualSelection.js @@ -1,11 +1,4 @@ -import { Meteor } from 'meteor/meteor'; -import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; - -import { Livechat } from '../Livechat'; import { RoutingManager } from '../RoutingManager'; -import { sendNotification } from '../../../../lib/server'; -import { LivechatRooms, LivechatInquiry, Users } from '../../../../models/server'; -import { callbacks } from '../../../../callbacks/server'; /* Manual Selection Queuing Method: * @@ -32,61 +25,6 @@ class ManualSelection { getNextAgent() { } - - delegateAgent(agent, inquiry) { - const { department, rid, v } = inquiry; - const allAgents = Livechat.getAgents(department); - - if (allAgents.count() === 0) { - throw new Meteor.Error('no-agent-available', 'Sorry, no available agents.'); - } - - // remove agent from room in case the rooms is being transferred or returned to the Queue - LivechatRooms.removeAgentByRoomId(rid); - LivechatInquiry.queueInquiry(inquiry._id); - - // Alert only the online agents of the queued request - const onlineAgents = Livechat.getOnlineAgents(department); - - const room = LivechatRooms.findOneById(rid); - const notificationUserName = v && (v.name || v.username); - - onlineAgents.forEach((agent) => { - if (agent.agentId) { - agent = Users.findOneById(agent.agentId); - } - const { _id, active, emails, language, status, statusConnection, username } = agent; - sendNotification({ - // fake a subscription in order to make use of the function defined above - subscription: { - rid, - t: 'l', - u: { - _id, - }, - receiver: [{ - active, - emails, - language, - status, - statusConnection, - username, - }], - }, - sender: v, - hasMentionToAll: true, // consider all agents to be in the room - hasMentionToHere: false, - message: Object.assign({}, { u: v }), - notificationMessage: TAPi18n.__('User_started_a_new_conversation', { username: notificationUserName }, language), - room: Object.assign(room, { name: TAPi18n.__('New_chat_in_queue', {}, language) }), - mentionIds: [], - }); - }); - - Meteor.defer(() => callbacks.run('livechat.chatQueued', room)); - - return agent; - } } RoutingManager.registerMethod('Manual_Selection', ManualSelection); diff --git a/app/models/server/models/LivechatInquiry.js b/app/models/server/models/LivechatInquiry.js index 9dc6251a17cb8..8d2aef5ec4f5c 100644 --- a/app/models/server/models/LivechatInquiry.js +++ b/app/models/server/models/LivechatInquiry.js @@ -63,13 +63,12 @@ export class LivechatInquiry extends Base { /* * mark inquiry as queued */ - queueInquiry(inquiryId, defaultAgent) { + queueInquiry(inquiryId) { return this.update({ _id: inquiryId, }, { $set: { status: 'queued', - ...defaultAgent && { defaultAgent }, }, }); } @@ -109,6 +108,16 @@ export class LivechatInquiry extends Base { return this.update(query, update); } + setDefaultAgentById(inquiryId, defaultAgent) { + return this.update({ + _id: inquiryId, + }, { + $set: { + defaultAgent, + }, + }); + } + setNameByRoomId(rid, name) { const query = { rid }; diff --git a/app/models/server/raw/Users.js b/app/models/server/raw/Users.js index 5893c5ed7a846..aa04c127fe222 100644 --- a/app/models/server/raw/Users.js +++ b/app/models/server/raw/Users.js @@ -191,7 +191,7 @@ export class UsersRaw extends BaseRaw { const aggregate = [ { $match: { _id: userId, status: { $exists: true, $ne: 'offline' }, statusLivechat: 'available', roles: 'livechat-agent' } }, { $lookup: { from: 'rocketchat_subscription', localField: '_id', foreignField: 'u._id', as: 'subs' } }, - { $project: { agentId: '$_id', username: 1, lastAssignTime: 1, lastRoutingTime: 1, 'queueInfo.chats': { $size: '$subs' } } }, + { $project: { agentId: '$_id', username: 1, lastAssignTime: 1, lastRoutingTime: 1, 'queueInfo.chats': { $size: { $filter: { input: '$subs', as: 'sub', cond: { $eq: ['$$sub.t', 'l'] } } } } } }, { $sort: { 'queueInfo.chats': 1, lastAssignTime: 1, lastRoutingTime: 1, username: 1 } }, ]; diff --git a/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js b/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js index c83069a3d161d..9aee0af993f78 100644 --- a/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js +++ b/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js @@ -3,7 +3,7 @@ import { settings } from '../../../../../app/settings'; import { LivechatInquiry } from '../../../../../app/models/server'; import { dispatchInquiryPosition } from '../lib/Helper'; -callbacks.add('livechat.beforeRouteChat', async (inquiry, agent) => { +callbacks.add('livechat.beforeRouteChat', async (inquiry) => { if (!settings.get('Livechat_waiting_queue')) { return inquiry; } @@ -18,7 +18,7 @@ callbacks.add('livechat.beforeRouteChat', async (inquiry, agent) => { return inquiry; } - LivechatInquiry.queueInquiry(_id, agent); + LivechatInquiry.queueInquiry(_id); const [inq] = await LivechatInquiry.getCurrentSortedQueueAsync({ _id }); if (inq) { diff --git a/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js b/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js index fb0824f3c0dc7..3dcb33c08d1de 100644 --- a/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js +++ b/ee/app/livechat-enterprise/server/hooks/checkAgentBeforeTakeInquiry.js @@ -3,8 +3,9 @@ import { Meteor } from 'meteor/meteor'; import { callbacks } from '../../../../../app/callbacks'; import { Users } from '../../../../../app/models/server/raw'; import { settings } from '../../../../../app/settings'; -import { allowAgentSkipQueue, getMaxNumberSimultaneousChat } from '../lib/Helper'; +import { getMaxNumberSimultaneousChat } from '../lib/Helper'; import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; +import { allowAgentSkipQueue } from '../../../../../app/livechat/server/lib/Helper'; callbacks.add('livechat.checkAgentBeforeTakeInquiry', async (agent, inquiry) => { if (!settings.get('Livechat_waiting_queue')) { diff --git a/ee/app/livechat-enterprise/server/lib/Helper.js b/ee/app/livechat-enterprise/server/lib/Helper.js index 3a843835bce1b..db2594f2bb951 100644 --- a/ee/app/livechat-enterprise/server/lib/Helper.js +++ b/ee/app/livechat-enterprise/server/lib/Helper.js @@ -125,14 +125,6 @@ export const processWaitingQueue = async (department) => { await dispatchWaitingQueueStatus(departmentId); }; -export const allowAgentSkipQueue = (agent) => { - check(agent, Match.ObjectIncluding({ - agentId: String, - })); - - return settings.get('Livechat_assign_new_conversation_to_bot') && hasRole(agent.agentId, 'bot'); -}; - export const setPredictedVisitorAbandonmentTime = (room) => { if (!room.v || !room.v.lastMessageTs || !settings.get('Livechat_auto_close_abandoned_rooms')) { return; diff --git a/ee/app/livechat-enterprise/server/lib/routing/LoadBalancing.js b/ee/app/livechat-enterprise/server/lib/routing/LoadBalancing.js index 770ae5b97a739..9e3ff997c064c 100644 --- a/ee/app/livechat-enterprise/server/lib/routing/LoadBalancing.js +++ b/ee/app/livechat-enterprise/server/lib/routing/LoadBalancing.js @@ -27,10 +27,6 @@ class LoadBalancing { const { agentId, username } = nextAgent; return { agentId, username }; } - - delegateAgent(agent) { - return agent; - } } RoutingManager.registerMethod('Load_Balancing', LoadBalancing); From e143ef6cc3332f53862300c29581de22bb99fad6 Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Wed, 20 Jan 2021 10:24:11 -0300 Subject: [PATCH 07/11] Removed unnecessary imports. --- ee/app/livechat-enterprise/server/lib/Helper.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/ee/app/livechat-enterprise/server/lib/Helper.js b/ee/app/livechat-enterprise/server/lib/Helper.js index db2594f2bb951..b3f8f675f32da 100644 --- a/ee/app/livechat-enterprise/server/lib/Helper.js +++ b/ee/app/livechat-enterprise/server/lib/Helper.js @@ -1,8 +1,6 @@ import { Meteor } from 'meteor/meteor'; -import { Match, check } from 'meteor/check'; import moment from 'moment'; -import { hasRole } from '../../../../../app/authorization'; import { LivechatDepartment, Users, From 1bb654d6423c37e8ca64b1fc4949bd05d0b8635e Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Wed, 20 Jan 2021 10:38:24 -0300 Subject: [PATCH 08/11] Fix translations missing. --- packages/rocketchat-i18n/i18n/en.i18n.json | 4 ++-- packages/rocketchat-i18n/i18n/pt-BR.i18n.json | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json index ffe86bcd702ff..e2b8dcb24e419 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2344,8 +2344,6 @@ "Livechat_auto_transfer_chat_timeout_description" : "This event takes place only when the chat has just started. After the first transfering for inactivity, the room is no longer monitored.", "Livechat_business_hour_type": "Business Hour Type (Single or Multiple)", "Livechat_chat_transcript_sent": "Chat transcript sent: __transcript__", - "Omnichannel_contact_manager_routing": "Contact Manager Preferred", - "Omnichannel_contact_manager_routing_Description": "This setting allocates a chat to the assigned Contact Manager, provided the Contact Manager is online when the chat starts.", "Livechat_custom_fields_options_placeholder": "Comma-separated list used to select a pre-configured value. Spaces between elements are not accepted.", "Livechat_custom_fields_public_description": "Public custom fields will be displayed in external applications, such as Livechat, etc.", "Livechat_Dashboard": "Omnichannel Dashboard", @@ -2852,6 +2850,8 @@ "Omnichannel_Directory": "Omnichannel Directory", "Omnichannel_appearance": "Omnichannel Appearance", "Omnichannel_Contact_Center": "Omnichannel Contact Center", + "Omnichannel_contact_manager_routing": "Assign new conversations to the contact manager", + "Omnichannel_contact_manager_routing_Description": "This setting allocates a chat to the assigned Contact Manager, as long as the Contact Manager is online when the chat starts", "Omnichannel_External_Frame": "External Frame", "Omnichannel_External_Frame_Enabled": "External frame enabled", "Omnichannel_External_Frame_Encryption_JWK": "Encryption key (JWK)", diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json index 9e37416bd6469..d4be10e338a5b 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -2455,6 +2455,8 @@ "Older_than": "Mais velho que", "Omnichannel_Directory": "Diretório Omnichannel", "Omnichannel_Contact_Center": "Central de Contatos Omnichannel", + "Omnichannel_contact_manager_routing": "Atribuir novas conversas para o Gerente do Contato", + "Omnichannel_contact_manager_routing_Description": "Aloca novos chats para o Gerente do Contato(quando atribuído), desde que o Gerente do Contato esteja online quando a conversa é iniciado", "Omnichannel_External_Frame": "Frame Externo", "Omnichannel_External_Frame_Enabled": "Frame Externo habilitado", "Omnichannel_External_Frame_Encryption_JWK": "Chave de criptografia (JWK)", From ba40ddbee9166af6740e8dd16784acb6c5708f3c Mon Sep 17 00:00:00 2001 From: murtaza98 Date: Thu, 21 Jan 2021 13:37:20 +0530 Subject: [PATCH 09/11] Allow contact manager from different department to view the chat in their queue --- app/livechat/server/api/lib/inquiries.js | 10 +++++++--- .../server/roomAccessValidator.compatibility.js | 10 +++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/livechat/server/api/lib/inquiries.js b/app/livechat/server/api/lib/inquiries.js index 6551d8bd3b73f..e56392d69b145 100644 --- a/app/livechat/server/api/lib/inquiries.js +++ b/app/livechat/server/api/lib/inquiries.js @@ -43,10 +43,14 @@ export async function findInquiries({ userId, department: filterDepartment, stat const filter = { ...status && { status }, - ...department && { department }, $or: [ - { defaultAgent: { $exists: false } }, - { 'defaultAgent.agentId': userId }, + { + $and: [ + { defaultAgent: { $exists: true } }, + { 'defaultAgent.agentId': userId }, + ], + }, + { ...department && { department } }, ], }; diff --git a/app/livechat/server/roomAccessValidator.compatibility.js b/app/livechat/server/roomAccessValidator.compatibility.js index 9e65fb506ccbb..9eb7929fdf8a5 100644 --- a/app/livechat/server/roomAccessValidator.compatibility.js +++ b/app/livechat/server/roomAccessValidator.compatibility.js @@ -41,7 +41,15 @@ export const validators = [ const filter = { rid: room._id, - ...departmentIds && departmentIds.length > 0 && { department: { $in: departmentIds } }, + $or: [ + { + $and: [ + { defaultAgent: { $exists: true } }, + { 'defaultAgent.agentId': user._id }, + ], + }, + { ...departmentIds && departmentIds.length > 0 && { department: { $in: departmentIds } } }, + ], }; const inquiry = LivechatInquiry.findOne(filter, { fields: { status: 1 } }); From e139a64c6d6802222ecd443f46400e2fecdfc365 Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Fri, 22 Jan 2021 14:44:59 -0300 Subject: [PATCH 10/11] Fix Omnichannel Queue delegation. --- app/livechat/server/lib/Helper.js | 2 +- app/livechat/server/lib/QueueManager.js | 2 +- .../livechat-enterprise/server/hooks/beforeRoutingChat.js | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/app/livechat/server/lib/Helper.js b/app/livechat/server/lib/Helper.js index 85aae4514f955..e762f51f0db54 100644 --- a/app/livechat/server/lib/Helper.js +++ b/app/livechat/server/lib/Helper.js @@ -18,7 +18,7 @@ export const allowAgentSkipQueue = (agent) => { agentId: String, })); - return settings.get('Livechat_assign_new_conversation_to_bot') && hasRole(agent.agentId, 'bot'); + return hasRole(agent.agentId, 'bot'); }; export const createLivechatRoom = (rid, name, guest, roomInfo = {}, extraData = {}) => { diff --git a/app/livechat/server/lib/QueueManager.js b/app/livechat/server/lib/QueueManager.js index 0b926e9afac9f..949babddcff98 100644 --- a/app/livechat/server/lib/QueueManager.js +++ b/app/livechat/server/lib/QueueManager.js @@ -30,8 +30,8 @@ export const QueueManager = { LivechatRooms.updateRoomCount(); - await callbacks.run('livechat.beforeRouteChat', inquiry); const defaultAgent = RoutingManager.delegateAgent(agent, inquiry); + await callbacks.run('livechat.beforeRouteChat', inquiry, defaultAgent); inquiry = LivechatInquiry.findOneById(inquiry._id); if (inquiry.status === 'ready') { diff --git a/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js b/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js index 9aee0af993f78..5ef7c2138cdc7 100644 --- a/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js +++ b/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js @@ -2,8 +2,9 @@ import { callbacks } from '../../../../../app/callbacks'; import { settings } from '../../../../../app/settings'; import { LivechatInquiry } from '../../../../../app/models/server'; import { dispatchInquiryPosition } from '../lib/Helper'; +import { allowAgentSkipQueue } from '../../../../../app/livechat/server/lib/Helper'; -callbacks.add('livechat.beforeRouteChat', async (inquiry) => { +callbacks.add('livechat.beforeRouteChat', async (inquiry, agent) => { if (!settings.get('Livechat_waiting_queue')) { return inquiry; } @@ -18,6 +19,10 @@ callbacks.add('livechat.beforeRouteChat', async (inquiry) => { return inquiry; } + if (agent && allowAgentSkipQueue(agent)) { + return inquiry; + } + LivechatInquiry.queueInquiry(_id); const [inq] = await LivechatInquiry.getCurrentSortedQueueAsync({ _id }); From 00ab686e562ce08fc3bc0d42ffa71943332e1926 Mon Sep 17 00:00:00 2001 From: Renato Becker Date: Fri, 22 Jan 2021 15:34:59 -0300 Subject: [PATCH 11/11] Fix default agent when unarchiving chats. --- app/livechat/server/lib/QueueManager.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/livechat/server/lib/QueueManager.js b/app/livechat/server/lib/QueueManager.js index 03f35177c9a7c..febd9cbf59ba5 100644 --- a/app/livechat/server/lib/QueueManager.js +++ b/app/livechat/server/lib/QueueManager.js @@ -1,7 +1,7 @@ import { Meteor } from 'meteor/meteor'; import { Match, check } from 'meteor/check'; -import { LivechatRooms, LivechatInquiry } from '../../../models/server'; +import { LivechatRooms, LivechatInquiry, Users } from '../../../models/server'; import { checkServiceStatus, createLivechatRoom, createLivechatInquiry } from './Helper'; import { callbacks } from '../../../callbacks/server'; import { RoutingManager } from './RoutingManager'; @@ -60,7 +60,10 @@ export const QueueManager = { ...department && { department }, }; - const defaultAgent = servedBy && { agentId: servedBy._id, username: servedBy.username }; + let defaultAgent; + if (servedBy && Users.findOneOnlineAgentByUsername(servedBy.username)) { + defaultAgent = { agentId: servedBy._id, username: servedBy.username }; + } LivechatRooms.unarchiveOneById(rid); const room = LivechatRooms.findOneById(rid);