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
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi
}

const { servedBy, chatQueued } = roomTaken;
if (!chatQueued && oldServedBy && servedBy && oldServedBy._id === servedBy._id) {
if (!chatQueued && oldServedBy && oldServedBy._id === servedBy?._id) {
if (!department?.fallbackForwardDepartment?.length) {
logger.debug({
msg: 'Cannot forward room. Chat assigned to agent instead',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ class LoadBalancing {
}

async getNextAgent(department?: string, ignoreAgentId?: string) {
const enabledWhenIdle = settings.get<boolean>('Livechat_enabled_when_agent_idle');
const extraQuery = await getChatLimitsQuery(department);
const unavailableUsers = await Users.getUnavailableAgents(department, extraQuery);
logger.debug({ msg: 'Ignoring unavailable agents from assignment', unavailableUsers, department });
const unavailableUsers = await Users.getUnavailableAgents(department, extraQuery, enabledWhenIdle);
logger.debug({ msg: 'Ignoring unavailable agents from assignment', unavailableUsers, department, enabledWhenIdle });

const nextAgent = await Users.getNextLeastBusyAgent(
department,
ignoreAgentId,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
enabledWhenIdle,
unavailableUsers.map((u) => u.username),
);
if (!nextAgent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ class LoadRotation {
}

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

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

const nextAgent = await Users.getLastAvailableAgentRouted(
department,
ignoreAgentId,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
enabledWhenIdle,
unavailableUsers.map((user) => user.username),
);
if (!nextAgent?.username) {
Expand Down
10 changes: 4 additions & 6 deletions apps/meteor/ee/server/models/raw/Users.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { RocketChatRecordDeleted, IUser, AvailableAgentsAggregation } from '@rocket.chat/core-typings';
import { UsersRaw } from '@rocket.chat/models';
import { queryStatusAgentOnline, UsersRaw } from '@rocket.chat/models';
import type { Db, Collection, Filter } from 'mongodb';

import { readSecondaryPreferred } from '../../../../server/database/readSecondaryPreferred';
Expand All @@ -9,6 +9,7 @@ declare module '@rocket.chat/model-typings' {
getUnavailableAgents(
departmentId: string,
customFilter: Filter<AvailableAgentsAggregation>,
enabledWhenIdle?: boolean,
): Promise<Pick<AvailableAgentsAggregation, 'username'>[]>;
}
}
Expand All @@ -21,6 +22,7 @@ export class UsersEE extends UsersRaw {
override getUnavailableAgents(
departmentId: string,
customFilter: Filter<AvailableAgentsAggregation>,
enabledWhenIdle = 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 @@ -51,11 +53,7 @@ export class UsersEE extends UsersRaw {
.aggregate<AvailableAgentsAggregation>(
[
{
$match: {
status: { $exists: true, $ne: 'offline' },
statusLivechat: 'available',
roles: 'livechat-agent',
},
$match: queryStatusAgentOnline({}, enabledWhenIdle),
},
...departmentFilter,
{
Expand Down
3 changes: 2 additions & 1 deletion packages/apps-engine/deno-runtime/lib/require.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export const require = (mod: string) => {
// However, the import maps are configured to look at the source folder for typescript files, but during
// runtime those files are not available
if (mod.startsWith('@rocket.chat/apps-engine')) {
mod = import.meta.resolve(mod).replace('file://', '').replace('src/', '');
// Only remove "src/" substring when it comes after "apps-engine/"
mod = import.meta.resolve(mod).replace('file://', '').replace('apps-engine/src/', 'apps-engine/');
}

return _require(mod);
Expand Down
1 change: 1 addition & 0 deletions packages/model-typings/src/models/IUsersModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ export interface IUsersModel extends IBaseModel<IUser> {
getUnavailableAgents(
departmentId?: string,
extraQuery?: Filter<AvailableAgentsAggregation>,
isLivechatEnabledWhenIdle?: boolean,
): Promise<Pick<AvailableAgentsAggregation, 'username'>[]>;
findOneOnlineAgentByUserList(
userList: string[] | string,
Expand Down
1 change: 1 addition & 0 deletions packages/models/src/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './omnichannel';
39 changes: 39 additions & 0 deletions packages/models/src/helpers/omnichannel/agentStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { UserStatus } from '@rocket.chat/core-typings';
import type { IUser } from '@rocket.chat/core-typings';
import type { Filter } from 'mongodb';

export const queryStatusAgentOnline = (extraFilters = {}, isLivechatEnabledWhenAgentIdle?: boolean): Filter<IUser> => ({
statusLivechat: 'available',
roles: 'livechat-agent',
// ignore deactivated users
active: true,
...(!isLivechatEnabledWhenAgentIdle && {
$or: [
{
status: {
$exists: true,
$ne: UserStatus.OFFLINE,
},
roles: {
$ne: 'bot',
},
},
{
roles: 'bot',
},
],
}),
...extraFilters,
...(isLivechatEnabledWhenAgentIdle === false && {
statusConnection: { $ne: 'away' },
}),
});

export const queryAvailableAgentsForSelection = (extraFilters = {}, isLivechatEnabledWhenAgentIdle?: boolean): Filter<IUser> => ({
...queryStatusAgentOnline(extraFilters, isLivechatEnabledWhenAgentIdle),
$and: [
{
$or: [{ agentLocked: { $exists: false } }, { agentLockedAt: { $lt: new Date(Date.now() - 5000) } }],
},
],
});
1 change: 1 addition & 0 deletions packages/models/src/helpers/omnichannel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './agentStatus';
2 changes: 2 additions & 0 deletions packages/models/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ export * from './modelClasses';

export * from './dummy/ReadReceipts';

export * from './helpers';

export { registerModel } from './proxify';
export { type Updater, UpdaterImpl } from './updater';

Expand Down
4 changes: 3 additions & 1 deletion packages/models/src/models/LivechatDepartmentAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw<ILivechatDepartmentAgen
const onlineUsernames = onlineUsers.map((user) => user.username).filter(isStringValue);

// get fully booked agents, to ignore them from the query
const currentUnavailableAgents = (await Users.getUnavailableAgents(departmentId, extraQuery)).map((u) => u.username);
const currentUnavailableAgents = (await Users.getUnavailableAgents(departmentId, extraQuery, isLivechatEnabledWhenAgentIdle)).map(
(u) => u.username,
);

const query: Filter<ILivechatDepartmentAgents> = {
departmentId,
Expand Down
40 changes: 3 additions & 37 deletions packages/models/src/models/Users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,42 +31,7 @@ import type {

import { Rooms, Subscriptions } from '../index';
import { BaseRaw } from './BaseRaw';

const queryStatusAgentOnline = (extraFilters = {}, isLivechatEnabledWhenAgentIdle?: boolean): Filter<IUser> => ({
statusLivechat: 'available',
roles: 'livechat-agent',
// ignore deactivated users
active: true,
...(!isLivechatEnabledWhenAgentIdle && {
$or: [
{
status: {
$exists: true,
$ne: UserStatus.OFFLINE,
},
roles: {
$ne: 'bot',
},
},
{
roles: 'bot',
},
],
}),
...extraFilters,
...(isLivechatEnabledWhenAgentIdle === false && {
statusConnection: { $ne: 'away' },
}),
});

const queryAvailableAgentsForSelection = (extraFilters = {}, isLivechatEnabledWhenAgentIdle?: boolean): Filter<IUser> => ({
...queryStatusAgentOnline(extraFilters, isLivechatEnabledWhenAgentIdle),
$and: [
{
$or: [{ agentLocked: { $exists: false } }, { agentLockedAt: { $lt: new Date(Date.now() - 5000) } }],
},
],
});
import { queryAvailableAgentsForSelection, queryStatusAgentOnline } from '../helpers';

export class UsersRaw extends BaseRaw<IUser, DefaultFields<IUser>> implements IUsersModel {
constructor(db: Db, trash?: Collection<RocketChatRecordDeleted<IUser>>) {
Expand Down Expand Up @@ -1687,6 +1652,7 @@ export class UsersRaw extends BaseRaw<IUser, DefaultFields<IUser>> implements IU
async getUnavailableAgents(
_departmentId?: string,
_extraQuery?: Filter<AvailableAgentsAggregation>,
_isLivechatEnabledWhenAgentIdle?: boolean,
): Promise<Pick<AvailableAgentsAggregation, 'username'>[]> {
return [];
}
Expand Down Expand Up @@ -1960,7 +1926,7 @@ export class UsersRaw extends BaseRaw<IUser, DefaultFields<IUser>> implements IU
async getNextAgent(ignoreAgentId?: string, extraQuery?: Filter<AvailableAgentsAggregation>, enabledWhenAgentIdle?: boolean) {
// TODO: Create class Agent
// fetch all unavailable agents, and exclude them from the selection
const unavailableAgents = (await this.getUnavailableAgents(undefined, extraQuery)).map((u) => u.username);
const unavailableAgents = (await this.getUnavailableAgents(undefined, extraQuery, enabledWhenAgentIdle)).map((u) => u.username);
const extraFilters = {
...(ignoreAgentId && { _id: { $ne: ignoreAgentId } }),
// limit query to remove booked agents
Expand Down
Loading