snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
153
apps/meteor/server/lib/omnichannel/messages.ts
Normal file
153
apps/meteor/server/lib/omnichannel/messages.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import dns from 'node:dns';
|
||||
import * as util from 'node:util';
|
||||
|
||||
import type { ILivechatVisitor, AtLeast, IMessage, IUser, IOmnichannelRoomInfo, SelectedAgent } from '@rocket.chat/core-typings';
|
||||
import { LivechatDepartment, Messages } from '@rocket.chat/models';
|
||||
import { check, Match } from 'meteor/check';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import type { ILivechatMessage } from './localTypes';
|
||||
import { getRoom } from './rooms';
|
||||
import { showConnecting } from './utils';
|
||||
import { settings } from '../../settings';
|
||||
import { callbacks } from '../callbacks';
|
||||
import { deleteMessage as deleteMessageFunc } from '../messages/deleteMessage';
|
||||
import { sendMessage as sendMessageFunc } from '../messages/sendMessage';
|
||||
import { updateMessage as updateMessageFunc } from '../messages/updateMessage';
|
||||
import * as Mailer from '../notifications/email/api';
|
||||
|
||||
const dnsResolveMx = util.promisify(dns.resolveMx);
|
||||
|
||||
type OfflineMessageData = {
|
||||
message: string;
|
||||
name: string;
|
||||
email: string;
|
||||
department?: string;
|
||||
host?: string;
|
||||
};
|
||||
|
||||
export async function 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 Mailer.send({
|
||||
to: emailTo,
|
||||
from: fromText,
|
||||
replyTo,
|
||||
subject,
|
||||
html,
|
||||
});
|
||||
|
||||
setImmediate(() => {
|
||||
void callbacks.run('livechat.offlineMessage', data);
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateMessage({ guest, message }: { guest: ILivechatVisitor; message: AtLeast<IMessage, '_id' | 'msg' | 'rid'> }) {
|
||||
// TODO: Remove check
|
||||
check(message, Match.ObjectIncluding({ _id: String }));
|
||||
|
||||
const originalMessage = await Messages.findOneById<Pick<IMessage, 'u' | '_id'>>(message._id, { projection: { u: 1 } });
|
||||
if (!originalMessage?._id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: shouldn't this happen inside updateMessageFunc?
|
||||
const editAllowed = settings.get('Message_AllowEditing');
|
||||
const editOwn = originalMessage.u && originalMessage.u._id === guest._id;
|
||||
|
||||
if (!editAllowed || !editOwn) {
|
||||
throw new Error('error-action-not-allowed');
|
||||
}
|
||||
|
||||
// TODO: Apps sends an `any` object and apparently we just check for _id being present
|
||||
// while updateMessage expects AtLeast<id, msg, rid>
|
||||
await updateMessageFunc(message, guest as unknown as IUser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function deleteMessage({ guest, message }: { guest: ILivechatVisitor; message: IMessage }) {
|
||||
const deleteAllowed = settings.get<boolean>('Message_AllowDeleting');
|
||||
const editOwn = message.u && message.u._id === guest._id;
|
||||
|
||||
if (!deleteAllowed || !editOwn) {
|
||||
throw new Error('error-action-not-allowed');
|
||||
}
|
||||
|
||||
// TODO: we shouldn't do this :(
|
||||
await deleteMessageFunc(message, guest as unknown as IUser);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function sendMessage({
|
||||
guest,
|
||||
message,
|
||||
roomInfo,
|
||||
agent,
|
||||
}: {
|
||||
guest: ILivechatVisitor;
|
||||
message: ILivechatMessage;
|
||||
roomInfo: IOmnichannelRoomInfo;
|
||||
agent?: SelectedAgent;
|
||||
}) {
|
||||
const { room, newRoom } = await getRoom(guest, message, roomInfo, agent);
|
||||
return {
|
||||
...(await sendMessageFunc(guest, { ...message, token: guest.token, ...(guest.name && { alias: guest.name }) }, room)),
|
||||
newRoom,
|
||||
showConnecting: showConnecting(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user