Skip to content
5 changes: 5 additions & 0 deletions .changeset/brown-houses-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rocket.chat/meteor": patch
---

Fixed a problem that caused Business Hours feature (Multiple) to make bot agents not available when turning on the feature, and not making them available after that. Now, Business Hours will ignore Bot users, allowing admins to decide manually if a bot should be or not be active during a period of time
18 changes: 13 additions & 5 deletions apps/meteor/server/models/raw/Users.js
Original file line number Diff line number Diff line change
Expand Up @@ -1036,7 +1036,7 @@ export class UsersRaw extends BaseRaw {
updateLivechatStatusBasedOnBusinessHours(userIds = []) {
const query = {
$or: [{ openBusinessHours: { $exists: false } }, { openBusinessHours: { $size: 0 } }],
roles: 'livechat-agent',
$and: [{ roles: 'livechat-agent' }, { roles: { $ne: 'bot' } }],
// exclude deactivated users
active: true,
// Avoid unnecessary updates
Expand Down Expand Up @@ -1075,10 +1075,18 @@ export class UsersRaw extends BaseRaw {
async isAgentWithinBusinessHours(agentId) {
const query = {
_id: agentId,
openBusinessHours: {
$exists: true,
$not: { $size: 0 },
},
$or: [
{
openBusinessHours: {
$exists: true,
$not: { $size: 0 },
},
},
{
// Bots can ignore Business Hours and be always available
roles: 'bot',
},
],
};
return (await this.col.countDocuments(query)) > 0;
}
Expand Down
20 changes: 16 additions & 4 deletions apps/meteor/tests/data/livechat/department.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,23 @@ new Promise((resolve, reject) => {
});
});

export const createDepartmentWithAnOnlineAgent = async (): Promise<{department: ILivechatDepartment, agent: {
credentials: IUserCredentialsHeader;
type OnlineAgent = {
user: WithRequiredProperty<IUser, 'username'>;
}}> => {
const { user, credentials } = await createAnOnlineAgent();
credentials: IUserCredentialsHeader;
};

export const createDepartmentWithAnOnlineAgent = async (overrideUser?: OnlineAgent): Promise<{department: ILivechatDepartment, agent: OnlineAgent }> => {
Comment thread
KevLehman marked this conversation as resolved.
Resolved
let user: OnlineAgent['user'];
let credentials: OnlineAgent['credentials'];

if (!overrideUser) {
const agent = await createAnOnlineAgent();
user = agent.user;
credentials = agent.credentials;
} else {
user = overrideUser.user;
credentials = overrideUser.credentials;
}

const department = await createDepartmentWithMethod() as ILivechatDepartment;

Expand Down
17 changes: 16 additions & 1 deletion apps/meteor/tests/end-to-end/api/livechat/19-business-hours.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,21 @@ describe('LIVECHAT - business hours', function () {
expect(latestAgent.openBusinessHours).to.be.an('array').of.length(0);
expect(latestAgent.statusLivechat).to.be.equal(ILivechatAgentStatus.NOT_AVAILABLE);
});

it('should create a custom business hour which is closed by default, but a bot agent shouldnt be affected', async () => {
const bot = await createUser({ roles: ['bot', 'livechat-agent'] });
const creds = await login(bot.username, password);
await makeAgentAvailable(creds);

const { department } = await createDepartmentWithAnOnlineAgent({ user: bot, credentials: creds });

await createCustomBusinessHour([department._id], false);

const latestAgent: ILivechatAgent = await getMe(creds);
expect(latestAgent).to.be.an('object');
expect(latestAgent.openBusinessHours).to.be.an('array').of.length(0);
expect(latestAgent.statusLivechat).to.be.equal(ILivechatAgentStatus.AVAILABLE);
});
Comment thread
KevLehman marked this conversation as resolved.
Resolved
});

// Scenario: Assume we have a BH linked to a department, and we archive the department
Expand Down Expand Up @@ -872,8 +887,8 @@ describe('LIVECHAT - business hours', function () {
it('should verify if agent becomes unavailable to take chats when user is activated, if business hour is inactive', async () => {
await openOrCloseBusinessHour(defaultBH, false);

await setUserActiveStatus(agent._id, false);
await setUserActiveStatus(agent._id, true);
await setUserActiveStatus(agent._id, false);
Comment thread
KevLehman marked this conversation as resolved.
Resolved
Comment thread
KevLehman marked this conversation as resolved.
Resolved

const latestAgent = await getUserByUsername(agent.username);

Expand Down