Skip to content

Commit

Permalink
sendOfflineMessage
Browse files Browse the repository at this point in the history
  • Loading branch information
KevLehman committed Oct 16, 2023
1 parent 5a8f429 commit 0100558
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 65 deletions.
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
63 changes: 0 additions & 63 deletions apps/meteor/app/livechat/server/lib/Livechat.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
// 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 {
Expand Down Expand Up @@ -42,8 +38,6 @@ import { RoutingManager } from './RoutingManager';

const logger = new Logger('Livechat');

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

export const Livechat = {
Analytics,

Expand Down Expand Up @@ -628,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
76 changes: 76 additions & 0 deletions apps/meteor/app/livechat/server/lib/LivechatTyped.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import dns from 'dns';
import * as util from 'util';

import { Message, VideoConf, api } from '@rocket.chat/core-services';
import type {
IOmnichannelRoom,
Expand Down Expand Up @@ -78,6 +81,16 @@ export type CloseRoomParamsByVisitor = {

export type CloseRoomParams = CloseRoomParamsByUser | CloseRoomParamsByVisitor;

type OfflineMessageData = {
message: string;
name: string;
email: string;
department?: string;
host?: string;
};

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

class LivechatClass {
logger: Logger;

Expand Down Expand Up @@ -1047,6 +1060,69 @@ class LivechatClass {

return rcSettings;
}

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

const { message, name, email, department, host } = data;

if (!email) {
throw new Error('error-invalid-email');
}

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>`);

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

let from: string;
if (fromEmail) {
from = fromEmail[0];
} else {
from = settings.get<string>('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');
}
}

// 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<string>('Livechat_offline_email');
if (department && department !== '') {
const dep = await LivechatDepartment.findOneByIdOrName(department, { projection: { email: 1 } });
if (dep) {
emailTo = dep.email || emailTo;
}
}

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

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

export const Livechat = new LivechatClass();
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DDPRateLimiter } from 'meteor/ddp-rate-limiter';
import { Meteor } from 'meteor/meteor';

import { methodDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger';
import { Livechat } from '../lib/Livechat';
import { Livechat } from '../lib/LivechatTyped';

declare module '@rocket.chat/ui-contexts' {
// eslint-disable-next-line @typescript-eslint/naming-convention
Expand Down

0 comments on commit 0100558

Please sign in to comment.