Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: GetRoom #31386

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft
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
25 changes: 0 additions & 25 deletions apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.ts

This file was deleted.

1 change: 0 additions & 1 deletion apps/meteor/app/livechat/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import './livechat';
import './startup';
import '../lib/messageTypes';
import './hooks/beforeDelegateAgent';
import './hooks/leadCapture';
import './hooks/markRoomResponded';
import './hooks/offlineMessage';
Expand Down
60 changes: 15 additions & 45 deletions apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,6 @@ export const createLivechatRoom = async (
roomInfo: Partial<IOmnichannelRoom> = {},
extraData = {},
) => {
check(rid, String);
check(name, String);
check(
guest,
Match.ObjectIncluding({
_id: String,
username: String,
status: Match.Maybe(String),
department: Match.Maybe(String),
}),
);

const extraRoomInfo = await callbacks.run('livechat.beforeRoom', roomInfo, extraData);
const { _id, username, token, department: departmentId, status = 'online' } = guest;
const newRoomAt = new Date();
Expand Down Expand Up @@ -117,11 +105,10 @@ export const createLivechatRoom = async (
);

const roomId = (await Rooms.insertOne(room)).insertedId;
await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room);

void Apps.triggerEvent(AppEvents.IPostLivechatRoomStarted, room);
await callbacks.run('livechat.newRoom', room);

await sendMessage(guest, { t: 'livechat-started', msg: '', groupable: false }, room);
void callbacks.run('livechat.newRoom', room);

return roomId;
};
Expand All @@ -135,33 +122,18 @@ export const createLivechatInquiry = async ({
extraData,
}: {
rid: string;
name?: string;
name: string;
guest?: Pick<ILivechatVisitor, '_id' | 'username' | 'status' | 'department' | 'name' | 'token' | 'activity'>;
message?: Pick<IMessage, 'msg'>;
initialStatus?: LivechatInquiryStatus;
extraData?: Pick<ILivechatInquiryRecord, 'source'>;
}) => {
check(rid, String);
check(name, String);
check(
guest,
Match.ObjectIncluding({
_id: String,
username: String,
status: Match.Maybe(String),
department: Match.Maybe(String),
activity: Match.Maybe([String]),
}),
);
check(
message,
Match.ObjectIncluding({
msg: String,
}),
);

const extraInquiryInfo = await callbacks.run('livechat.beforeInquiry', extraData);

if (!guest || !message) {
throw new Meteor.Error('error-invalid-params');
}

const { _id, username, token, department, status = UserStatus.ONLINE, activity } = guest;
const { msg } = message;
const ts = new Date();
Expand Down Expand Up @@ -193,7 +165,6 @@ export const createLivechatInquiry = async ({
};

const result = (await LivechatInquiry.insertOne(inquiry)).insertedId;
logger.debug(`Inquiry ${result} created for visitor ${_id}`);

return result;
};
Expand Down Expand Up @@ -338,7 +309,6 @@ export const dispatchInquiryQueued = async (inquiry: ILivechatInquiryRecord, age
if (!inquiry?._id) {
return;
}
logger.debug(`Notifying agents of new inquiry ${inquiry._id} queued`);

const { department, rid, v } = inquiry;
const room = await LivechatRooms.findOneById(rid);
Expand All @@ -358,12 +328,12 @@ export const dispatchInquiryQueued = async (inquiry: ILivechatInquiryRecord, age

// Alert only the online agents of the queued request
const onlineAgents = await LivechatTyped.getOnlineAgents(department, agent);
if (!onlineAgents) {
const total = await onlineAgents?.count();
if (!onlineAgents || !total) {
logger.debug('Cannot notify agents of queued inquiry. No online agents found');
return;
}

logger.debug(`Notifying ${await onlineAgents.count()} agents of new inquiry`);
const notificationUserName = v && (v.name || v.username);

for await (const agent of onlineAgents) {
Expand Down Expand Up @@ -436,7 +406,7 @@ export const forwardRoomToAgent = async (room: IOmnichannelRoom, transferData: T
// There are some Enterprise features that may interrupt the forwarding process
// Due to that we need to check whether the agent has been changed or not
logger.debug(`Forwarding inquiry ${inquiry._id} to agent ${agent.agentId}`);
const roomTaken = await RoutingManager.takeInquiry(inquiry, agent, {
const roomTaken = await RoutingManager.takeInquiry(inquiry, agent, room, {
...(clientAction && { clientAction }),
});
if (!roomTaken) {
Expand Down Expand Up @@ -549,7 +519,7 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi
// Fake the department to forward the inquiry - Case the forward process does not success
// the inquiry will stay in the same original department
inquiry.department = departmentId;
const roomTaken = await RoutingManager.delegateInquiry(inquiry, agent, {
const roomTaken = await RoutingManager.delegateInquiry(inquiry, agent, room, {
forwardingToDepartment: { oldDepartmentId },
...(clientAction && { clientAction }),
});
Expand Down Expand Up @@ -591,16 +561,16 @@ export const forwardRoomToDepartment = async (room: IOmnichannelRoom, guest: ILi

if (chatQueued) {
logger.debug(`Forwarding succesful. Marking inquiry ${inquiry._id} as ready`);
await LivechatInquiry.readyInquiry(inquiry._id);
await LivechatRooms.removeAgentByRoomId(rid);
await dispatchAgentDelegated(rid);
await Promise.all([LivechatInquiry.readyInquiry(inquiry._id), LivechatRooms.removeAgentByRoomId(rid)]);
void dispatchAgentDelegated(rid);

const newInquiry = await LivechatInquiry.findOneById(inquiry._id);
if (!newInquiry) {
logger.debug(`Inquiry ${inquiry._id} not found`);
throw new Error('error-invalid-inquiry');
}

await queueInquiry(newInquiry);
await queueInquiry(newInquiry, room);
logger.debug(`Inquiry ${inquiry._id} queued succesfully`);
}

Expand Down
127 changes: 79 additions & 48 deletions apps/meteor/app/livechat/server/lib/LivechatTyped.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,24 +343,23 @@ class LivechatClass {
}

async getRequiredDepartment(onlineRequired = true) {
const departments = LivechatDepartment.findEnabledWithAgents();
if (!onlineRequired) {
return LivechatDepartment.findOneEnabledWithAgentsAndAvailableOnRegistration();
}

const departments = LivechatDepartment.findEnabledWithAgentsAndAvailableOnRegistration();
for await (const dept of departments) {
if (!dept.showOnRegistration) {
continue;
}
if (!onlineRequired) {
return dept;
}

const onlineAgents = await LivechatDepartmentAgents.getOnlineForDepartment(dept._id);
if (onlineAgents && (await onlineAgents.count())) {
const onlineAgents = await LivechatDepartmentAgents.countOnlineForDepartment(
dept._id,
settings.get<boolean>('Livechat_enabled_when_agent_idle'),
);
if (onlineAgents > 0) {
return dept;
}
}
}

async getRoom(
async getNewRoom(
guest: ILivechatVisitor,
message: Pick<IMessage, 'rid' | 'msg' | 'token'>,
roomInfo: {
Expand All @@ -370,19 +369,35 @@ class LivechatClass {
agent?: SelectedAgent,
extraData?: Record<string, unknown>,
) {
if (!this.enabled()) {
throw new Meteor.Error('error-omnichannel-is-disabled');
const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, guest);
// if no department selected verify if there is at least one active and pick the first
if (!defaultAgent && !guest.department) {
const department = await this.getRequiredDepartment();
if (department) {
this.logger.debug({
msg: 'No department or default agent selected. Assigning to department',
visitorId: guest._id,
department,
});
guest.department = department._id;
}
}
Livechat.logger.debug(`Attempting to find or create a room for visitor ${guest._id}`);
let room = await LivechatRooms.findOneById(message.rid);
let newRoom = false;

if (room && !room.open) {
Livechat.logger.debug(`Last room for visitor ${guest._id} closed. Creating new one`);
message.rid = Random.id();
room = null;
}
// delegate room creation to QueueManager
this.logger.debug({
msg: 'Calling QueueManager to request a room',
visitorId: guest._id,
});
return QueueManager.requestRoom({
guest,
message,
roomInfo,
agent: defaultAgent,
extraData,
});
}

async validateGuestDepartment(guest: ILivechatVisitor) {
if (
guest.department &&
!(await LivechatDepartment.findOneById<Pick<ILivechatDepartment, '_id'>>(guest.department, { projection: { _id: 1 } }))
Expand All @@ -394,42 +409,58 @@ class LivechatClass {
}
}

if (room == null) {
const defaultAgent = await callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, guest);
// if no department selected verify if there is at least one active and pick the first
if (!defaultAgent && !guest.department) {
const department = await this.getRequiredDepartment();
Livechat.logger.debug(`No department or default agent selected for ${guest._id}`);

if (department) {
Livechat.logger.debug(`Assigning ${guest._id} to department ${department._id}`);
guest.department = department._id;
}
}
return guest;
}

async getRoom(
guest: ILivechatVisitor,
message: Pick<IMessage, 'rid' | 'msg' | 'token'>,
roomInfo: {
source?: IOmnichannelRoom['source'];
[key: string]: unknown;
},
agent?: SelectedAgent,
extraData?: Record<string, unknown>,
) {
if (!this.enabled()) {
throw new Meteor.Error('error-omnichannel-is-disabled');
}

this.logger.debug({
msg: 'Getting room for visitor',
visitor: guest,
});

let room = await LivechatRooms.findOneById(message.rid);
let newRoom = false;

// delegate room creation to QueueManager
Livechat.logger.debug(`Calling QueueManager to request a room for visitor ${guest._id}`);
room = await QueueManager.requestRoom({
guest,
message,
roomInfo,
agent: defaultAgent,
extraData,
if (room && !room.open) {
message.rid = Random.id();
room = null;

this.logger.debug({
msg: 'Room is closed. Opening new one',
room: message.rid,
});
newRoom = true;
}

guest = await this.validateGuestDepartment(guest);

Livechat.logger.debug(`Room obtained for visitor ${guest._id} -> ${room._id}`);
if (room == null) {
room = await this.getNewRoom(guest, message, roomInfo, agent, extraData);
await Messages.setRoomIdByToken(guest.token, room._id);
newRoom = true;
}

if (!room || room.v.token !== guest.token) {
Livechat.logger.debug(`Visitor ${guest._id} trying to access another visitor's room`);
this.logger.error({
msg: 'Room does not exist or belongs to another visitor',
visitorId: guest._id,
roomId: room?._id,
});
throw new Meteor.Error('cannot-access-room');
}

if (newRoom) {
await Messages.setRoomIdByToken(guest.token, room._id);
}

return { room, newRoom };
}

Expand Down
Loading
Loading