Skip to content

Commit

Permalink
refactor: Move functions out of Livechat.js (#30650)
Browse files Browse the repository at this point in the history
  • Loading branch information
KevLehman authored Oct 17, 2023
1 parent dd5b236 commit d6fa895
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 232 deletions.
5 changes: 3 additions & 2 deletions apps/meteor/app/apps/server/bridges/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ export class AppLivechatBridge extends LivechatBridge {
message: await this.orch.getConverters()?.get('messages').convertAppMessage(message),
};

await Livechat.updateMessage(data);
// @ts-expect-error IVisitor vs ILivechatVisitor :(
await LivechatTyped.updateMessage(data);
}

protected async createRoom(visitor: IVisitor, agent: IUser, appId: string, extraParams?: IExtraRoomParams): Promise<ILivechatRoom> {
Expand Down Expand Up @@ -208,7 +209,7 @@ export class AppLivechatBridge extends LivechatBridge {
userId = transferredTo._id;
}

return Livechat.transfer(
return LivechatTyped.transfer(
await this.orch.getConverters()?.get('rooms').convertAppRoom(currentRoom),
this.orch.getConverters()?.get('visitors').convertAppVisitor(visitor),
{ userId, departmentId, transferredBy, transferredTo },
Expand Down
3 changes: 1 addition & 2 deletions apps/meteor/app/livechat/server/api/lib/livechat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { Meteor } from 'meteor/meteor';
import { callbacks } from '../../../../../lib/callbacks';
import { i18n } from '../../../../../server/lib/i18n';
import { normalizeAgent } from '../../lib/Helper';
import { Livechat } from '../../lib/Livechat';
import { Livechat as LivechatTyped } from '../../lib/LivechatTyped';

export function online(department: string, skipSettingCheck = false, skipFallbackCheck = false): Promise<boolean> {
Expand Down Expand Up @@ -139,7 +138,7 @@ export function normalizeHttpHeaderData(headers: Record<string, string | string[

export async function settings({ businessUnit = '' }: { businessUnit?: string } = {}): Promise<Record<string, string | number | any>> {
// Putting this ugly conversion while we type the livechat service
const initSettings = (await Livechat.getInitSettings()) as unknown as Record<string, string | number | any>;
const initSettings = await LivechatTyped.getInitSettings();
const triggers = await findTriggers();
const departments = await findDepartments(businessUnit);
const sound = `${Meteor.absoluteUrl()}sounds/chime.mp3`;
Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/app/livechat/server/api/v1/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,9 @@ API.v1.addRoute(
throw new Error('invalid-message');
}

const result = await Livechat.updateMessage({
const result = await LivechatTyped.updateMessage({
guest,
message: { _id: msg._id, msg: this.bodyParams.msg },
message: { _id: msg._id, msg: this.bodyParams.msg, rid: msg.rid },
});
if (!result) {
return API.v1.failure();
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/livechat/server/api/v1/offlineMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { isPOSTLivechatOfflineMessageParams } from '@rocket.chat/rest-typings';

import { i18n } from '../../../../../server/lib/i18n';
import { API } from '../../../../api/server';
import { Livechat } from '../../lib/Livechat';
import { Livechat } from '../../lib/LivechatTyped';

API.v1.addRoute(
'livechat/offline.message',
Expand Down
14 changes: 9 additions & 5 deletions apps/meteor/app/livechat/server/api/v1/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ API.v1.addRoute(
const { _id, username, name } = guest;
const transferredBy = normalizeTransferredByData({ _id, username, name, userType: 'visitor' }, room);

if (!(await Livechat.transfer(room, guest, { roomId: rid, departmentId: department, transferredBy }))) {
if (!(await LivechatTyped.transfer(room, guest, { departmentId: department, transferredBy }))) {
return API.v1.failure();
}

Expand Down Expand Up @@ -312,10 +312,10 @@ API.v1.addRoute(
{ authRequired: true, permissionsRequired: ['view-l-room', 'transfer-livechat-guest'], validateParams: isLiveChatRoomForwardProps },
{
async post() {
const transferData: typeof this.bodyParams & {
transferredBy?: unknown;
const transferData = this.bodyParams as typeof this.bodyParams & {
transferredBy: TransferByData;
transferredTo?: { _id: string; username?: string; name?: string };
} = this.bodyParams;
};

const room = await LivechatRooms.findOneById(this.bodyParams.roomId);
if (!room || room.t !== 'l') {
Expand All @@ -327,6 +327,10 @@ API.v1.addRoute(
}

const guest = await LivechatVisitors.findOneEnabledById(room.v?._id);
if (!guest) {
throw new Error('error-invalid-visitor');
}

const transferedBy = this.user satisfies TransferByData;
transferData.transferredBy = normalizeTransferredByData(transferedBy, room);
if (transferData.userId) {
Expand All @@ -340,7 +344,7 @@ API.v1.addRoute(
}
}

const chatForwardedResult = await Livechat.transfer(room, guest, transferData);
const chatForwardedResult = await LivechatTyped.transfer(room, guest, transferData);
if (!chatForwardedResult) {
throw new Error('error-forwarding-chat');
}
Expand Down
3 changes: 3 additions & 0 deletions apps/meteor/app/livechat/server/lib/Helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,9 @@ export const forwardRoomToAgent = async (room: IOmnichannelRoom, transferData: T
logger.debug(`Forwarding room ${room._id} to agent ${transferData.userId}`);

const { userId: agentId, clientAction } = transferData;
if (!agentId) {
throw new Error('error-invalid-agent');
}
const user = await Users.findOneOnlineAgentById(agentId);
if (!user) {
logger.debug(`Agent ${agentId} is offline. Cannot forward`);
Expand Down
213 changes: 0 additions & 213 deletions apps/meteor/app/livechat/server/lib/Livechat.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
// Note: Please don't add any new methods to this file, since its still in js and we are migrating to ts
// Please add new methods to LivechatTyped.ts

import dns from 'dns';
import util from 'util';

import { Message } from '@rocket.chat/core-services';
import { Logger } from '@rocket.chat/logger';
import {
LivechatVisitors,
LivechatCustomField,
Settings,
LivechatRooms,
LivechatInquiry,
Subscriptions,
Messages,
LivechatDepartment as LivechatDepartmentRaw,
LivechatDepartmentAgents,
Rooms,
Users,
ReadReceipts,
Expand All @@ -34,7 +28,6 @@ import { hasPermissionAsync } from '../../../authorization/server/functions/hasP
import { FileUpload } from '../../../file-upload/server';
import { deleteMessage } from '../../../lib/server/functions/deleteMessage';
import { sendMessage } from '../../../lib/server/functions/sendMessage';
import { updateMessage } from '../../../lib/server/functions/updateMessage';
import * as Mailer from '../../../mailer/server/api';
import { settings } from '../../../settings/server';
import { businessHourManager } from '../business-hour';
Expand All @@ -45,8 +38,6 @@ import { RoutingManager } from './RoutingManager';

const logger = new Logger('Livechat');

const dnsResolveMx = util.promisify(dns.resolveMx);

export const Livechat = {
Analytics,

Expand All @@ -63,28 +54,6 @@ export const Livechat = {
});
},

async updateMessage({ guest, message }) {
check(message, Match.ObjectIncluding({ _id: String }));

const originalMessage = await Messages.findOneById(message._id);
if (!originalMessage || !originalMessage._id) {
return;
}

const editAllowed = settings.get('Message_AllowEditing');
const editOwn = originalMessage.u && originalMessage.u._id === guest._id;

if (!editAllowed || !editOwn) {
throw new Meteor.Error('error-action-not-allowed', 'Message editing not allowed', {
method: 'livechatUpdateMessage',
});
}

await updateMessage(message, guest);

return true;
},

async deleteMessage({ guest, message }) {
Livechat.logger.debug(`Attempting to delete a message by visitor ${guest._id}`);
check(message, Match.ObjectIncluding({ _id: String }));
Expand Down Expand Up @@ -188,50 +157,6 @@ export const Livechat = {
return 0;
},

async getInitSettings() {
const rcSettings = {};

await Settings.findNotHiddenPublic([
'Livechat_title',
'Livechat_title_color',
'Livechat_enable_message_character_limit',
'Livechat_message_character_limit',
'Message_MaxAllowedSize',
'Livechat_enabled',
'Livechat_registration_form',
'Livechat_allow_switching_departments',
'Livechat_offline_title',
'Livechat_offline_title_color',
'Livechat_offline_message',
'Livechat_offline_success_message',
'Livechat_offline_form_unavailable',
'Livechat_display_offline_form',
'Omnichannel_call_provider',
'Language',
'Livechat_enable_transcript',
'Livechat_transcript_message',
'Livechat_fileupload_enabled',
'FileUpload_Enabled',
'Livechat_conversation_finished_message',
'Livechat_conversation_finished_text',
'Livechat_name_field_registration_form',
'Livechat_email_field_registration_form',
'Livechat_registration_form_message',
'Livechat_force_accept_data_processing_consent',
'Livechat_data_processing_consent_text',
'Livechat_show_agent_info',
'Livechat_clear_local_storage_when_chat_ended',
]).forEach((setting) => {
rcSettings[setting._id] = setting.value;
});

rcSettings.Livechat_history_monitor_type = settings.get('Livechat_history_monitor_type');

rcSettings.Livechat_Show_Connecting = this.showConnecting();

return rcSettings;
},

async saveRoomInfo(roomData, guestData, userId) {
Livechat.logger.debug(`Saving room information on room ${roomData._id}`);
const { livechatData = {} } = roomData;
Expand Down Expand Up @@ -280,35 +205,6 @@ export const Livechat = {
}
},

async closeOpenChats(userId, comment) {
Livechat.logger.debug(`Closing open chats for user ${userId}`);
const user = await Users.findOneById(userId);

const extraQuery = await callbacks.run('livechat.applyDepartmentRestrictions', {}, { userId });
const openChats = LivechatRooms.findOpenByAgent(userId, extraQuery);
const promises = [];
await openChats.forEach((room) => {
promises.push(LivechatTyped.closeRoom({ user, room, comment }));
});

await Promise.all(promises);
},

async forwardOpenChats(userId) {
Livechat.logger.debug(`Transferring open chats for user ${userId}`);
for await (const room of LivechatRooms.findOpenByAgent(userId)) {
const guest = await LivechatVisitors.findOneEnabledById(room.v._id);
const user = await Users.findOneById(userId);
const { _id, username, name } = user;
const transferredBy = normalizeTransferredByData({ _id, username, name }, room);
await this.transfer(room, guest, {
roomId: room._id,
transferredBy,
departmentId: guest.department,
});
}
},

async savePageHistory(token, roomId, pageInfo) {
Livechat.logger.debug(`Saving page movement history for visitor with token ${token}`);
if (pageInfo.change !== settings.get('Livechat_history_monitor_type')) {
Expand Down Expand Up @@ -387,23 +283,6 @@ export const Livechat = {
await sendMessage(transferredBy, transferMessage, room);
},

async transfer(room, guest, transferData) {
Livechat.logger.debug(`Transfering room ${room._id} [Transfered by: ${transferData?.transferredBy?._id}]`);
if (room.onHold) {
Livechat.logger.debug('Cannot transfer. Room is on hold');
throw new Error('error-room-onHold');
}

if (transferData.departmentId) {
transferData.department = await LivechatDepartmentRaw.findOneById(transferData.departmentId, {
projection: { name: 1 },
});
Livechat.logger.debug(`Transfering room ${room._id} to department ${transferData.department?._id}`);
}

return RoutingManager.transferRoom(room, guest, transferData);
},

async returnRoomAsInquiry(rid, departmentId, overrideTransferData = {}) {
Livechat.logger.debug(`Transfering room ${rid} to ${departmentId ? 'department' : ''} queue`);
const room = await LivechatRooms.findOneById(rid);
Expand Down Expand Up @@ -682,41 +561,6 @@ export const Livechat = {
return updateDepartmentAgents(_id, departmentAgents, department.enabled);
},

/*
* @deprecated - Use the equivalent from DepartmentHelpers class
*/
async removeDepartment(_id) {
check(_id, String);

const departmentRemovalEnabled = settings.get('Omnichannel_enable_department_removal');

if (!departmentRemovalEnabled) {
throw new Meteor.Error('department-removal-disabled', 'Department removal is disabled', {
method: 'livechat:removeDepartment',
});
}

const department = await LivechatDepartmentRaw.findOneById(_id, { projection: { _id: 1 } });

if (!department) {
throw new Meteor.Error('department-not-found', 'Department not found', {
method: 'livechat:removeDepartment',
});
}
const ret = (await LivechatDepartmentRaw.removeById(_id)).deletedCount;
const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id, { projection: { agentId: 1 } }).toArray()).map(
(agent) => agent.agentId,
);
await LivechatDepartmentAgents.removeByDepartmentId(_id);
await LivechatDepartmentRaw.unsetFallbackDepartmentByDepartmentId(_id);
if (ret) {
setImmediate(() => {
callbacks.run('livechat.afterRemoveDepartment', { department, agentsIds });
});
}
return ret;
},

showConnecting() {
const { showConnecting } = RoutingManager.getConfig();
return showConnecting;
Expand Down Expand Up @@ -778,63 +622,6 @@ export const Livechat = {
await LivechatRooms.updateVisitorStatus(token, status);
},

async sendOfflineMessage(data = {}) {
if (!settings.get('Livechat_display_offline_form')) {
throw new Error('error-offline-form-disabled');
}

const { message, name, email, department, host } = data;
const emailMessage = `${message}`.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1<br>$2');

let html = '<h1>New livechat message</h1>';
if (host && host !== '') {
html = html.concat(`<p><strong>Sent from:</strong><a href='${host}'> ${host}</a></p>`);
}
html = html.concat(`
<p><strong>Visitor name:</strong> ${name}</p>
<p><strong>Visitor email:</strong> ${email}</p>
<p><strong>Message:</strong><br>${emailMessage}</p>`);

let fromEmail = settings.get('From_Email').match(/\b[A-Z0-9._%+-]+@(?:[A-Z0-9-]+\.)+[A-Z]{2,4}\b/i);

if (fromEmail) {
fromEmail = fromEmail[0];
} else {
fromEmail = settings.get('From_Email');
}

if (settings.get('Livechat_validate_offline_email')) {
const emailDomain = email.substr(email.lastIndexOf('@') + 1);

try {
await dnsResolveMx(emailDomain);
} catch (e) {
throw new Meteor.Error('error-invalid-email-address', 'Invalid email address', {
method: 'livechat:sendOfflineMessage',
});
}
}

// TODO Block offline form if Livechat_offline_email is undefined
// (it does not make sense to have an offline form that does nothing)
// `this.sendEmail` will throw an error if the email is invalid
// thus this breaks livechat, since the "to" email is invalid, and that returns an [invalid email] error to the livechat client
let emailTo = settings.get('Livechat_offline_email');
if (department && department !== '') {
const dep = await LivechatDepartmentRaw.findOneByIdOrName(department);
emailTo = dep.email || emailTo;
}

const from = `${name} - ${email} <${fromEmail}>`;
const replyTo = `${name} <${email}>`;
const subject = `Livechat offline message from ${name}: ${`${emailMessage}`.substring(0, 20)}`;
await this.sendEmail(from, emailTo, replyTo, subject, html);

setImmediate(() => {
callbacks.run('livechat.offlineMessage', data);
});
},

async allowAgentChangeServiceStatus(statusLivechat, agentId) {
if (statusLivechat !== 'available') {
return true;
Expand Down
Loading

0 comments on commit d6fa895

Please sign in to comment.