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/lib/sendNotificationsOnMessage.js

263 lines
7.5 KiB

import moment from 'moment';
import { callJoinRoom, messageContainsHighlight, parseMessageTextPerUser, replaceMentionedUsernamesWithFullNames } from '../functions/notifications/';
import { sendEmail, shouldNotifyEmail } from '../functions/notifications/email';
import { sendSinglePush, shouldNotifyMobile } from '../functions/notifications/mobile';
import { notifyDesktopUser, shouldNotifyDesktop } from '../functions/notifications/desktop';
import { notifyAudioUser, shouldNotifyAudio } from '../functions/notifications/audio';
const sendNotification = ({
subscription,
sender,
hasMentionToAll,
hasMentionToHere,
message,
notificationMessage,
room,
mentionIds,
disableAllMessageNotifications
}) => {
// don't notify the sender
if (subscription.u._id === sender._id) {
return;
}
// notifications disabled
if (subscription.disableNotifications) {
return;
}
// dont send notification to users who ignored the sender
if (Array.isArray(subscription.ignored) && subscription.ignored.includes(sender._id)) {
return;
}
const hasMentionToUser = mentionIds.includes(subscription.u._id);
// mute group notifications (@here and @all) if not directly mentioned as well
if (!hasMentionToUser && subscription.muteGroupMentions && (hasMentionToAll || hasMentionToHere)) {
return;
}
const receiver = RocketChat.models.Users.findOneById(subscription.u._id);
if (!receiver || !receiver.active) {
return;
}
8 years ago
notificationMessage = parseMessageTextPerUser(notificationMessage, message, receiver);
const isHighlighted = messageContainsHighlight(message, subscription.userHighlights);
const roomType = room.t;
const {
audioNotifications,
desktopNotifications,
mobilePushNotifications,
emailNotifications
} = subscription;
let notificationSent = false;
// busy users don't receive audio notification
if (shouldNotifyAudio({
disableAllMessageNotifications,
status: receiver.status,
audioNotifications,
hasMentionToAll,
hasMentionToHere,
isHighlighted,
hasMentionToUser,
roomType
})) {
notifyAudioUser(subscription.u._id, message, room);
}
// busy users don't receive desktop notification
if (shouldNotifyDesktop({
disableAllMessageNotifications,
status: receiver.status,
desktopNotifications,
hasMentionToAll,
hasMentionToHere,
isHighlighted,
hasMentionToUser,
roomType
})) {
notificationSent = true;
notifyDesktopUser({
notificationMessage,
userId: subscription.u._id,
user: sender,
message,
room,
duration: subscription.desktopNotificationDuration
});
}
if (shouldNotifyMobile({
disableAllMessageNotifications,
mobilePushNotifications,
hasMentionToAll,
isHighlighted,
hasMentionToUser,
statusConnection: receiver.statusConnection,
roomType
})) {
notificationSent = true;
sendSinglePush({
notificationMessage,
room,
message,
userId: subscription.u._id,
senderUsername: sender.username,
senderName: sender.name,
receiverUsername: receiver.username
});
}
if (receiver.emails && shouldNotifyEmail({
disableAllMessageNotifications,
statusConnection: receiver.statusConnection,
emailNotifications,
isHighlighted,
hasMentionToUser,
hasMentionToAll,
roomType
})) {
receiver.emails.some((email) => {
if (email.verified) {
sendEmail({ message, receiver, subscription, room, emailAddress: email.address, hasMentionToUser });
return true;
}
8 years ago
});
}
if (notificationSent) {
RocketChat.Sandstorm.notify(message, [subscription.u._id], `@${ sender.username }: ${ message.msg }`, room.t === 'p' ? 'privateMessage' : 'message');
}
8 years ago
};
function sendAllNotifications(message, room) {
// skips this callback if the message was edited
if (message.editedAt) {
return message;
}
if (message.ts && Math.abs(moment(message.ts).diff()) > 60000) {
return message;
}
if (!room || room.t == null) {
return message;
}
const sender = RocketChat.roomTypes.getConfig(room.t).getMsgSender(message.u._id);
if (!sender) {
return message;
}
const mentionIds = (message.mentions || []).map(({_id}) => _id);
const mentionIdsWithoutGroups = mentionIds.filter((_id) => _id !== 'all' && _id !== 'here');
const hasMentionToAll = mentionIds.includes('all');
const hasMentionToHere = mentionIds.includes('here');
let notificationMessage = RocketChat.callbacks.run('beforeSendMessageNotifications', message.msg);
if (mentionIds.length > 0 && RocketChat.settings.get('UI_Use_Real_Name')) {
notificationMessage = replaceMentionedUsernamesWithFullNames(message.msg, message.mentions);
}
9 years ago
// Don't fetch all users if room exceeds max members
const maxMembersForNotification = RocketChat.settings.get('Notifications_Max_Room_Members');
const roomMembersCount = RocketChat.models.Subscriptions.findByRoomId(room._id).count();
const disableAllMessageNotifications = roomMembersCount > maxMembersForNotification && maxMembersForNotification !== 0;
const query = {
rid: room._id,
$or: [{
'userHighlights.0': { $exists: 1 }
}]
};
['audio', 'desktop', 'mobile', 'email'].map((kind) => {
const notificationField = `${ kind === 'mobile' ? 'mobilePush' : kind }Notifications`;
const filter = { [notificationField]: 'all' };
if (disableAllMessageNotifications) {
filter[`${ kind }PrefOrigin`] = { $ne: 'user' };
}
query.$or.push(filter);
if (mentionIdsWithoutGroups.length > 0) {
query.$or.push({
[notificationField]: 'mentions',
'u._id': { $in: mentionIdsWithoutGroups }
});
} else if (!disableAllMessageNotifications && (hasMentionToAll || hasMentionToHere)) {
query.$or.push({
[notificationField]: 'mentions'
});
}
const serverField = kind === 'email' ? 'emailNotificationMode' : `${ kind }Notifications`;
const serverPreference = RocketChat.settings.get(`Accounts_Default_User_Preferences_${ serverField }`);
if ((room.t === 'd' && serverPreference !== 'nothing') || (!disableAllMessageNotifications && (serverPreference === 'all' || hasMentionToAll || hasMentionToHere))) {
query.$or.push({
[notificationField]: { $exists: false }
});
} else if (serverPreference === 'mentions' && mentionIdsWithoutGroups.length) {
query.$or.push({
[notificationField]: { $exists: false },
'u._id': { $in: mentionIdsWithoutGroups }
});
}
});
// the find bellow is crucial. all subscription records returned will receive at least one kind of notification.
// the query is defined by the server's default values and Notifications_Max_Room_Members setting.
const subscriptions = RocketChat.models.Subscriptions.findNotificationPreferencesByRoom(query);
subscriptions.forEach((subscription) => sendNotification({
subscription,
sender,
hasMentionToAll,
hasMentionToHere,
message,
notificationMessage,
room,
mentionIds,
disableAllMessageNotifications
}));
// on public channels, if a mentioned user is not member of the channel yet, he will first join the channel and then be notified based on his preferences.
if (room.t === 'c') {
const mentions = message.mentions.filter(({ _id }) => _id !== 'here' && _id !== 'all').map(({ _id }) => _id);
Promise.all(RocketChat.models.Subscriptions.findByRoomIdAndUserIds(room._id, mentions)
.fetch()
.map(async subscription => {
await callJoinRoom(subscription.u, room._id);
return subscription;
})).then(subscriptions => subscriptions.forEach(subscription =>
sendNotification({
subscription,
sender,
hasMentionToAll,
hasMentionToHere,
message,
notificationMessage,
room,
mentionIds
})));
}
return message;
}
RocketChat.callbacks.add('afterSaveMessage', sendAllNotifications, RocketChat.callbacks.priority.LOW, 'sendNotificationsOnMessage');