diff --git a/app/livechat/server/api/lib/inquiries.js b/app/livechat/server/api/lib/inquiries.js index eefba5f313e88..e56392d69b145 100644 --- a/app/livechat/server/api/lib/inquiries.js +++ b/app/livechat/server/api/lib/inquiries.js @@ -43,7 +43,15 @@ export async function findInquiries({ userId, department: filterDepartment, stat const filter = { ...status && { status }, - ...department && { department }, + $or: [ + { + $and: [ + { defaultAgent: { $exists: true } }, + { 'defaultAgent.agentId': userId }, + ], + }, + { ...department && { department } }, + ], }; const cursor = LivechatInquiry.find(filter, options); 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..e762f51f0db54 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 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 33cf8dcf45eef..febd9cbf59ba5 100644 --- a/app/livechat/server/lib/QueueManager.js +++ b/app/livechat/server/lib/QueueManager.js @@ -1,24 +1,19 @@ 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'; const queueInquiry = async (room, inquiry, defaultAgent) => { - if (!defaultAgent) { - defaultAgent = RoutingManager.getMethod().delegateAgent(defaultAgent, inquiry); - } + const inquiryAgent = RoutingManager.delegateAgent(defaultAgent, inquiry); + await callbacks.run('livechat.beforeRouteChat', inquiry, inquiryAgent); + inquiry = LivechatInquiry.findOneById(inquiry._id); - inquiry = await callbacks.run('livechat.beforeRouteChat', inquiry, defaultAgent); if (inquiry.status === 'ready') { - return RoutingManager.delegateInquiry(inquiry, defaultAgent); - } - - if (inquiry.status === 'queued') { - Meteor.defer(() => callbacks.run('livechat.chatQueued', room)); + return RoutingManager.delegateInquiry(inquiry, inquiryAgent); } }; export const QueueManager = { @@ -46,7 +41,6 @@ export const QueueManager = { LivechatRooms.updateRoomCount(); await queueInquiry(room, inquiry, agent); - return room; }, @@ -66,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); 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/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 } }); 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/client/providers/OmniChannelProvider.tsx b/client/providers/OmniChannelProvider.tsx index df7e1735f3958..e78fc0cf81fb1 100644 --- a/client/providers/OmniChannelProvider.tsx +++ b/client/providers/OmniChannelProvider.tsx @@ -44,6 +44,10 @@ const useOmnichannelInquiries = (): Array => { return useReactiveValue(useCallback(() => LivechatInquiry.find({ status: 'queued', + $or: [ + { defaultAgent: { $exists: false } }, + { 'defaultAgent.agentId': uid }, + ], }, { sort: { queueOrder: 1, @@ -51,7 +55,7 @@ const useOmnichannelInquiries = (): Array => { estimatedServiceTimeAt: 1, }, limit: omnichannelPoolMaxIncoming, - }).fetch(), [omnichannelPoolMaxIncoming])); + }).fetch(), [omnichannelPoolMaxIncoming, uid])); }; const OmnichannelDisabledProvider: FC = ({ children }) => ; diff --git a/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js b/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js index c83069a3d161d..5ef7c2138cdc7 100644 --- a/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js +++ b/ee/app/livechat-enterprise/server/hooks/beforeRoutingChat.js @@ -2,6 +2,7 @@ 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, agent) => { if (!settings.get('Livechat_waiting_queue')) { @@ -18,7 +19,11 @@ callbacks.add('livechat.beforeRouteChat', async (inquiry, agent) => { return inquiry; } - LivechatInquiry.queueInquiry(_id, agent); + if (agent && allowAgentSkipQueue(agent)) { + return inquiry; + } + + 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/hooks/handleLastChattedAgentPreferredEvents.js b/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js similarity index 70% rename from ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js rename to ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.js index ffe2f6463db06..42f180d270f40 100644 --- a/ee/app/livechat-enterprise/server/hooks/handleLastChattedAgentPreferredEvents.js +++ b/ee/app/livechat-enterprise/server/hooks/handleNextAgentPreferredEvents.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; @@ -12,26 +15,36 @@ 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 (!RoutingManager.getConfig().autoAssignAgent) { + const { _id: guestId } = defaultGuest; + const guest = LivechatVisitors.findOneById(guestId, { fields: { lastAgent: 1, token: 1, contactManager: 1 } }); + if (!guest) { return defaultAgent; } - const { _id: guestId } = defaultGuest; - const guest = LivechatVisitors.findOneById(guestId, { fields: { lastAgent: 1, token: 1 } }); - const { lastAgent: { username: usernameByVisitor } = {}, token } = guest; + const { lastAgent, token, contactManager } = guest; + const guestManager = contactManagerPreferred && getDefaultAgent(contactManager?.username); + if (guestManager) { + return guestManager; + } - const lastGuestAgent = usernameByVisitor && normalizeDefaultAgent(Users.findOneOnlineAgentByUsername(usernameByVisitor, { fields: { _id: 1, username: 1 } })); - if (lastGuestAgent) { - return lastGuestAgent; + if (!lastChattedAgentPreferred) { + return defaultAgent; + } + + const guestAgent = getDefaultAgent(lastAgent?.username); + if (guestAgent) { + return guestAgent; } const room = LivechatRooms.findOneLastServedAndClosedByVisitorToken(token, { fields: { servedBy: 1 } }); - if (!room || !room.servedBy) { + if (!room?.servedBy) { return defaultAgent; } @@ -74,14 +87,20 @@ 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'); 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'); + callbacks.add('livechat.onMaxNumberSimultaneousChatsReached', onMaxNumberSimultaneousChatsReached, callbacks.priority.MEDIUM, 'livechat-on-max-number-simultaneous-chats-reached'); }); + +settings.get('Omnichannel_contact_manager_routing', function(key, value) { + contactManagerPreferred = value; +}); + + +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 14e92a66cb9eb..0f6006affbed4 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/lib/Helper.js b/ee/app/livechat-enterprise/server/lib/Helper.js index 3a843835bce1b..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, @@ -125,14 +123,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); diff --git a/ee/app/livechat-enterprise/server/settings.js b/ee/app/livechat-enterprise/server/settings.js index 29c0ea99db908..89c15ea1890f4 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: [ @@ -124,5 +123,16 @@ export const createSettings = () => { }); }); + settings.add('Omnichannel_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 59aa6d56837e9..5978370713505 100644 --- a/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/packages/rocketchat-i18n/i18n/en.i18n.json @@ -2869,6 +2869,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 4d9d089ddb467..5458a14040cdc 100644 --- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json +++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json @@ -2465,6 +2465,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)",