The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/packages/rocketchat-lib/server/functions/sendMessage.js

202 lines
5.1 KiB

import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import { settings } from 'meteor/rocketchat:settings';
import { callbacks } from 'meteor/rocketchat:callbacks';
import { Messages } from 'meteor/rocketchat:models';
import { Apps } from 'meteor/rocketchat:apps';
const objectMaybeIncluding = (types) => Match.Where((value) => {
Object.keys(types).forEach((field) => {
if (value[field] != null) {
try {
check(value[field], types[field]);
} catch (error) {
error.path = field;
throw error;
}
}
});
return true;
});
const validateAttachmentsFields = (attachmentField) => {
check(attachmentField, objectMaybeIncluding({
short: Boolean,
title: String,
value: Match.OneOf(String, Match.Integer, Boolean),
}));
if (typeof attachmentField.value !== 'undefined') {
attachmentField.value = String(attachmentField.value);
}
};
const validateAttachmentsActions = (attachmentActions) => {
check(attachmentActions, objectMaybeIncluding({
type: String,
text: String,
url: String,
image_url: String,
is_webview: Boolean,
webview_height_ratio: String,
msg: String,
msg_in_chat_window: Boolean,
}));
};
const validateAttachment = (attachment) => {
check(attachment, objectMaybeIncluding({
color: String,
text: String,
ts: Match.OneOf(String, Match.Integer),
thumb_url: String,
button_alignment: String,
actions: [Match.Any],
message_link: String,
collapsed: Boolean,
author_name: String,
author_link: String,
author_icon: String,
title: String,
title_link: String,
title_link_download: Boolean,
image_dimensions: Object,
image_url: String,
image_preview: String,
image_type: String,
image_size: Number,
audio_url: String,
audio_type: String,
audio_size: Number,
video_url: String,
video_type: String,
video_size: Number,
fields: [Match.Any],
}));
if (attachment.fields && attachment.fields.length) {
attachment.fields.map(validateAttachmentsFields);
}
if (attachment.actions && attachment.actions.length) {
attachment.actions.map(validateAttachmentsActions);
}
};
const validateBodyAttachments = (attachments) => attachments.map(validateAttachment);
export const sendMessage = function(user, message, room, upsert = false) {
if (!user || !message || !room._id) {
return false;
}
check(message, objectMaybeIncluding({
_id: String,
msg: String,
text: String,
alias: String,
emoji: String,
avatar: String,
attachments: [Match.Any],
}));
if (Array.isArray(message.attachments) && message.attachments.length) {
validateBodyAttachments(message.attachments);
}
if (!message.ts) {
message.ts = new Date();
}
const { _id, username, name } = user;
message.u = {
_id,
username,
name,
};
message.rid = room._id;
if (!Match.test(message.msg, String)) {
message.msg = '';
}
if (message.ts == null) {
message.ts = new Date();
}
if (settings.get('Message_Read_Receipt_Enabled')) {
message.unread = true;
}
// For the Rocket.Chat Apps :)
if (message && Apps && Apps.isLoaded()) {
const prevent = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentPrevent', message));
if (prevent) {
throw new Meteor.Error('error-app-prevented-sending', 'A Rocket.Chat App prevented the message sending.');
}
let result;
result = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentExtend', message));
result = Promise.await(Apps.getBridges().getListenerBridge().messageEvent('IPreMessageSentModify', result));
if (typeof result === 'object') {
message = Object.assign(message, result);
}
}
if (message.parseUrls !== false) {
message.html = message.msg;
message = RocketChat.Markdown.code(message);
const urls = message.html.match(/([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\(\)\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g);
if (urls) {
message.urls = urls.map((url) => ({ url }));
}
message = RocketChat.Markdown.mountTokensBack(message, false);
message.msg = message.html;
delete message.html;
delete message.tokens;
}
message = callbacks.run('beforeSaveMessage', message);
if (message) {
// Avoid saving sandstormSessionId to the database
let sandstormSessionId = null;
if (message.sandstormSessionId) {
sandstormSessionId = message.sandstormSessionId;
delete message.sandstormSessionId;
}
if (message._id && upsert) {
const { _id } = message;
delete message._id;
Messages.upsert({
_id,
'u._id': message.u._id,
}, message);
message._id = _id;
} else {
message._id = Messages.insert(message);
}
if (Apps && Apps.isLoaded()) {
// This returns a promise, but it won't mutate anything about the message
// so, we don't really care if it is successful or fails
Apps.getBridges().getListenerBridge().messageEvent('IPostMessageSent', message);
}
/*
Defer other updates as their return is not interesting to the user
*/
Meteor.defer(() => {
// Execute all callbacks
message.sandstormSessionId = sandstormSessionId;
return callbacks.run('afterSaveMessage', message, room, user._id);
});
return message;
}
};
RocketChat.sendMessage = sendMessage;