Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/wet-pandas-pump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@rocket.chat/model-typings': patch
'@rocket.chat/omni-core': patch
'@rocket.chat/models': patch
'@rocket.chat/meteor': patch
---

Fixes an issue where the Omnichannel routing system ignored the `Livechat_accept_chats_with_no_agents` setting. Now, offline agents are correctly considered for assignment when the setting allows it.
2 changes: 2 additions & 0 deletions apps/meteor/app/apps/server/bridges/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ export class AppLivechatBridge extends LivechatBridge {

const livechatVisitor = await registerGuest(registerData, {
shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
});

if (!livechatVisitor) {
Expand Down Expand Up @@ -255,6 +256,7 @@ export class AppLivechatBridge extends LivechatBridge {

const livechatVisitor = await registerGuest(registerData, {
shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
});

return this.orch.getConverters()?.get('visitors').convertVisitor(livechatVisitor);
Expand Down
3 changes: 2 additions & 1 deletion apps/meteor/app/livechat/imports/server/rest/sms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ const defineVisitor = async (smsNumber: string, targetDepartment?: string) => {
data.department = targetDepartment;
}

const livechatVisitor = await registerGuest(data, { shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle') });
const livechatVisitor = await registerGuest(data, { shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents')});

if (!livechatVisitor) {
throw new Meteor.Error('error-invalid-visitor', 'Invalid visitor');
Expand Down
5 changes: 4 additions & 1 deletion apps/meteor/app/livechat/server/api/v1/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,10 @@ API.v1.addRoute(
guest.connectionData = normalizeHttpHeaderData(this.request.headers);
}

visitor = await registerGuest(guest, { shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle') });
visitor = await registerGuest(guest, {
shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
});
if (!visitor) {
throw new Error('error-livechat-visitor-registration');
}
Expand Down
5 changes: 4 additions & 1 deletion apps/meteor/app/livechat/server/api/v1/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ API.v1.addRoute(
connectionData: normalizeHttpHeaderData(this.request.headers),
};

const visitor = await registerGuest(guest, { shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle') });
const visitor = await registerGuest(guest, {
shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
});
if (!visitor) {
throw new Meteor.Error('error-livechat-visitor-registration', 'Error registering visitor', {
method: 'livechat/visitor',
Expand Down
14 changes: 12 additions & 2 deletions apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,12 @@ export const forwardRoomToAgent = async (room: IOmnichannelRoom, transferData: T
if (!agentId) {
throw new Error('error-invalid-agent');
}
const user = await Users.findOneOnlineAgentById(agentId, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
const user = await Users.findOneOnlineAgentById(
agentId,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
{},
);
if (!user) {
logger.debug({
msg: 'Agent is offline. Cannot forward',
Expand Down Expand Up @@ -657,7 +662,12 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi
departmentId,
agentId,
});
const user = await Users.findOneOnlineAgentById(agentId, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
const user = await Users.findOneOnlineAgentById(
agentId,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
{},
);
if (!user) {
throw new Error('error-user-is-offline');
}
Expand Down
7 changes: 6 additions & 1 deletion apps/meteor/app/livechat/server/lib/QueueManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,12 @@ export class QueueManager {

let defaultAgent: SelectedAgent | undefined;
const isAgentAvailable = (username: string) =>
Users.findOneOnlineAgentByUserList(username, { projection: { _id: 1 } }, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
Users.findOneOnlineAgentByUserList(
username,
{ projection: { _id: 1 } },
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);

if (servedBy?.username && (await isAgentAvailable(servedBy.username))) {
defaultAgent = { agentId: servedBy._id, username: servedBy.username };
Expand Down
7 changes: 6 additions & 1 deletion apps/meteor/app/livechat/server/lib/RoutingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@ export const RoutingManager: Routing = {
if (
!agent ||
(agent.username &&
!(await Users.findOneOnlineAgentByUserList(agent.username, {}, settings.get<boolean>('Livechat_enabled_when_agent_idle'))) &&
!(await Users.findOneOnlineAgentByUserList(
agent.username,
{},
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
)) &&
!(await allowAgentSkipQueue(agent)))
) {
logger.debug({ msg: 'Agent offline or invalid. Using routing method to get next agent', inquiryId: inquiry._id });
Expand Down
7 changes: 6 additions & 1 deletion apps/meteor/app/livechat/server/lib/departmentsLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ export async function checkOnlineForDepartment(departmentId: string) {
depUsers.map((agent) => agent.username),
{ projection: { _id: 1 } },
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);

return !!onlineForDep;
Expand All @@ -304,5 +305,9 @@ export async function getOnlineForDepartment(departmentId: string) {
const agents = await LivechatDepartmentAgents.findByDepartmentId(departmentId, { projection: { username: 1 } }).toArray();
const usernames = agents.map(({ username }) => username);

return Users.findOnlineUserFromList<ILivechatAgent>([...new Set(usernames)], settings.get<boolean>('Livechat_enabled_when_agent_idle'));
return Users.findOnlineUserFromList<ILivechatAgent>(
[...new Set(usernames)],
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,16 @@ class AutoSelection implements IRoutingMethod {
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
ignoreAgentId,
extraQuery,
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);
}

return Users.getNextAgent(ignoreAgentId, extraQuery, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
return Users.getNextAgent(
ignoreAgentId,
extraQuery,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);
}
}

Expand Down
1 change: 1 addition & 0 deletions apps/meteor/app/livechat/server/lib/routing/External.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class ExternalQueue implements IRoutingMethod {
result.username,
{},
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);

if (!agent?.username) {
Expand Down
24 changes: 20 additions & 4 deletions apps/meteor/app/livechat/server/lib/service-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ import { settings } from '../../../settings/server';

export async function getOnlineAgents(department?: string, agent?: SelectedAgent | null): Promise<FindCursor<ILivechatAgent> | undefined> {
if (agent?.agentId) {
return Users.findOnlineAgents(agent.agentId, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
return Users.findOnlineAgents(
agent.agentId,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);
}

if (department) {
return getOnlineForDepartment(department);
}
return Users.findOnlineAgents(undefined, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
return Users.findOnlineAgents(
undefined,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);
}

export async function online(department?: string, skipNoAgentSetting = false, skipFallbackCheck = false): Promise<boolean> {
Expand Down Expand Up @@ -43,7 +51,11 @@ export async function online(department?: string, skipNoAgentSetting = false, sk

export async function checkOnlineAgents(department?: string, agent?: { agentId: string }, skipFallbackCheck = false): Promise<boolean> {
if (agent?.agentId) {
return Users.checkOnlineAgents(agent.agentId, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
return Users.checkOnlineAgents(
agent.agentId,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);
}

if (department) {
Expand All @@ -62,7 +74,11 @@ export async function checkOnlineAgents(department?: string, agent?: { agentId:
return checkOnlineAgents(dep?.fallbackForwardDepartment);
}

return Users.checkOnlineAgents(undefined, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
return Users.checkOnlineAgents(
undefined,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
);
}

async function countBotAgents(department?: string) {
Expand Down
8 changes: 7 additions & 1 deletion apps/meteor/app/livechat/server/lib/takeInquiry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,18 @@ export const takeInquiry = async (
});
}

const user = await Users.findOneOnlineAgentById(userId, settings.get<boolean>('Livechat_enabled_when_agent_idle'));
const user = await Users.findOneOnlineAgentById(
userId,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
{},
);
if (!user) {
throw new Meteor.Error('error-agent-status-service-offline', 'Agent status is offline or Omnichannel service is not active', {
method: 'livechat:takeInquiry',
...(process.env.TEST_MODE && {
Livechat_enabled_when_agent_idle: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
Livechat_accept_chats_with_no_agents: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
user: await Users.findOneById(userId),
}),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ const getDefaultAgent = async ({ username, id }: { username?: string; id?: strin
}

if (id) {
const agent = await Users.findOneOnlineAgentById(id, settings.get<boolean>('Livechat_enabled_when_agent_idle'), {
projection: { _id: 1, username: 1 },
});
const agent = await Users.findOneOnlineAgentById(
id,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
{
projection: { _id: 1, username: 1 },
},
);
if (agent) {
return normalizeDefaultAgent(agent);
}
Expand All @@ -43,6 +48,7 @@ const getDefaultAgent = async ({ username, id }: { username?: string; id?: strin
username || [],
{ projection: { _id: 1, username: 1 } },
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
),
);
};
Expand Down Expand Up @@ -131,6 +137,7 @@ checkDefaultAgentOnNewRoom.patch(async (_next, defaultAgent, { visitorId, source
usernameByRoom,
{ projection: { _id: 1, username: 1 } },
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
),
);
return lastRoomAgent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@ class LoadBalancing {

async getNextAgent(department?: string, ignoreAgentId?: string) {
const enabledWhenIdle = settings.get<boolean>('Livechat_enabled_when_agent_idle');
const enabledWhenOffline = settings.get<boolean>('Livechat_accept_chats_with_no_agents');

const extraQuery = await getChatLimitsQuery(department);
const unavailableUsers = await Users.getUnavailableAgents(department, extraQuery, enabledWhenIdle);
logger.debug({ msg: 'Ignoring unavailable agents from assignment', unavailableUsers, department, enabledWhenIdle });
const unavailableUsers = await Users.getUnavailableAgents(department, extraQuery, enabledWhenIdle, enabledWhenOffline);
logger.debug({ msg: 'Ignoring unavailable agents from assignment', unavailableUsers, department, enabledWhenIdle, enabledWhenOffline });

const nextAgent = await Users.getNextLeastBusyAgent(
department,
ignoreAgentId,
enabledWhenIdle,
unavailableUsers.map((u) => u.username),
enabledWhenOffline,
);
if (!nextAgent) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,18 @@ class LoadRotation {

public async getNextAgent(department?: string, ignoreAgentId?: string): Promise<IOmnichannelCustomAgent | undefined> {
const enabledWhenIdle = settings.get<boolean>('Livechat_enabled_when_agent_idle');
const enabledWhenOffline = settings.get<boolean>('Livechat_accept_chats_with_no_agents');

const extraQuery = await getChatLimitsQuery(department);
const unavailableUsers = await Users.getUnavailableAgents(department, extraQuery, enabledWhenIdle);
logger.debug({ msg: 'Ignoring unavailable agents from assignment', unavailableUsers, department, enabledWhenIdle });
const unavailableUsers = await Users.getUnavailableAgents(department, extraQuery, enabledWhenIdle, enabledWhenOffline);
logger.debug({ msg: 'Ignoring unavailable agents from assignment', unavailableUsers, department, enabledWhenIdle, enabledWhenOffline });

const nextAgent = await Users.getLastAvailableAgentRouted(
department,
ignoreAgentId,
enabledWhenIdle,
unavailableUsers.map((user) => user.username),
enabledWhenOffline,
);
if (!nextAgent?.username) {
return;
Expand Down
4 changes: 3 additions & 1 deletion apps/meteor/ee/server/models/raw/Users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare module '@rocket.chat/model-typings' {
departmentId: string,
customFilter: Filter<AvailableAgentsAggregation>,
enabledWhenIdle?: boolean,
enabledWhenOffline?: boolean,
): Promise<Pick<AvailableAgentsAggregation, 'username'>[]>;
}
}
Expand All @@ -23,6 +24,7 @@ export class UsersEE extends UsersRaw {
departmentId: string,
customFilter: Filter<AvailableAgentsAggregation>,
enabledWhenIdle = false,
enabledWhenOffline = false,
): Promise<Pick<AvailableAgentsAggregation, 'username'>[]> {
// if department is provided, remove the agents that are not from the selected department
const departmentFilter = departmentId
Expand Down Expand Up @@ -53,7 +55,7 @@ export class UsersEE extends UsersRaw {
.aggregate<AvailableAgentsAggregation>(
[
{
$match: queryStatusAgentOnline({}, enabledWhenIdle),
$match: queryStatusAgentOnline({}, enabledWhenIdle, enabledWhenOffline),
},
...departmentFilter,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ async function getGuestByEmail(email: string, name: string, department = ''): Pr
email,
department,
},
{ shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle') },
{
shouldConsiderIdleAgent: settings.get<boolean>('Livechat_enabled_when_agent_idle'),
shouldConsiderOfflineAgent: settings.get<boolean>('Livechat_accept_chats_with_no_agents'),
},
);

if (!livechatVisitor) {
Expand Down
Loading
Loading