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.
498 lines
16 KiB
498 lines
16 KiB
/* globals Push */
|
|
import moment from 'moment';
|
|
|
|
/**
|
|
* Replaces @username with full name
|
|
*
|
|
* @param {string} message The message to replace
|
|
* @param {object[]} mentions Array of mentions used to make replacements
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
function replaceMentionedUsernamesWithFullNames(message, mentions) {
|
|
if (!mentions || !mentions.length) {
|
|
return message;
|
|
}
|
|
mentions.forEach((mention) => {
|
|
const user = RocketChat.models.Users.findOneById(mention._id);
|
|
if (user && user.name) {
|
|
message = message.replace(`@${ mention.username }`, user.name);
|
|
}
|
|
});
|
|
return message;
|
|
}
|
|
|
|
/**
|
|
* This function returns a string ready to be shown in the notification
|
|
*
|
|
* @param {object} message the message to be parsed
|
|
*/
|
|
function parseMessageText(message, userId) {
|
|
const user = RocketChat.models.Users.findOneById(userId);
|
|
const lng = user && user.language || RocketChat.settings.get('language') || 'en';
|
|
|
|
if (!message.msg && message.attachments[0]) {
|
|
message.msg = message.attachments[0].image_type ? TAPi18n.__('User_uploaded_image', {lng}) : TAPi18n.__('User_uploaded_file', {lng});
|
|
}
|
|
message.msg = RocketChat.callbacks.run('beforeNotifyUser', message.msg);
|
|
|
|
return message.msg;
|
|
}
|
|
/**
|
|
* Send notification to user
|
|
*
|
|
* @param {string} userId The user to notify
|
|
* @param {object} user The sender
|
|
* @param {object} room The room send from
|
|
* @param {number} duration Duration of notification
|
|
*/
|
|
function notifyDesktopUser(userId, user, message, room, duration) {
|
|
|
|
const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true;
|
|
message.msg = parseMessageText(message, userId);
|
|
|
|
if (UI_Use_Real_Name) {
|
|
message.msg = replaceMentionedUsernamesWithFullNames(message.msg, message.mentions);
|
|
}
|
|
let title = UI_Use_Real_Name ? user.name : `@${ user.username }`;
|
|
if (room.t !== 'd' && room.name) {
|
|
title += ` @ #${ room.name }`;
|
|
}
|
|
RocketChat.Notifications.notifyUser(userId, 'desktopNotification', {
|
|
title,
|
|
text: message.msg,
|
|
duration,
|
|
payload: {
|
|
_id: message._id,
|
|
rid: message.rid,
|
|
sender: message.u,
|
|
type: room.t,
|
|
name: room.name
|
|
}
|
|
});
|
|
}
|
|
|
|
function notifyAudioUser(userId, user, message, room) {
|
|
|
|
const UI_Use_Real_Name = RocketChat.settings.get('UI_Use_Real_Name') === true;
|
|
message.msg = parseMessageText(message, userId);
|
|
|
|
if (UI_Use_Real_Name) {
|
|
message.msg = replaceMentionedUsernamesWithFullNames(message.msg, message.mentions);
|
|
}
|
|
let title = UI_Use_Real_Name ? user.name : `@${ user.username }`;
|
|
if (room.t !== 'd' && room.name) {
|
|
title += ` @ #${ room.name }`;
|
|
}
|
|
RocketChat.Notifications.notifyUser(userId, 'audioNotification', {
|
|
title,
|
|
text: message.msg,
|
|
payload: {
|
|
_id: message._id,
|
|
rid: message.rid,
|
|
sender: message.u,
|
|
type: room.t,
|
|
name: room.name
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Checks if a message contains a user highlight
|
|
*
|
|
* @param {string} message
|
|
* @param {array|undefined} highlights
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
function messageContainsHighlight(message, highlights) {
|
|
if (! highlights || highlights.length === 0) { return false; }
|
|
|
|
let has = false;
|
|
highlights.some(function(highlight) {
|
|
const regexp = new RegExp(s.escapeRegExp(highlight), 'i');
|
|
if (regexp.test(message.msg)) {
|
|
has = true;
|
|
return true;
|
|
}
|
|
});
|
|
|
|
return has;
|
|
}
|
|
|
|
function getBadgeCount(userId) {
|
|
const subscriptions = RocketChat.models.Subscriptions.findUnreadByUserId(userId).fetch();
|
|
|
|
return subscriptions.reduce((unread, sub) => {
|
|
return sub.unread + unread;
|
|
}, 0);
|
|
}
|
|
|
|
RocketChat.callbacks.add('afterSaveMessage', function(message, room, userId) {
|
|
|
|
// 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;
|
|
}
|
|
|
|
const user = RocketChat.models.Users.findOneById(message.u._id);
|
|
|
|
/*
|
|
Increment unread couter if direct messages
|
|
*/
|
|
const settings = {
|
|
alwaysNotifyDesktopUsers: [],
|
|
dontNotifyDesktopUsers: [],
|
|
alwaysNotifyMobileUsers: [],
|
|
dontNotifyMobileUsers: [],
|
|
desktopNotificationDurations: {},
|
|
alwaysNotifyAudioUsers: [],
|
|
dontNotifyAudioUsers: [],
|
|
audioNotificationValues: {}
|
|
};
|
|
|
|
/**
|
|
* Checks if a given user can be notified
|
|
*
|
|
* @param {string} id
|
|
* @param {string} type - mobile|desktop
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
function canBeNotified(id, type) {
|
|
const types = {
|
|
mobile: [ 'dontNotifyDesktopUsers', 'alwaysNotifyDesktopUsers' ],
|
|
desktop: [ 'dontNotifyMobileUsers', 'alwaysNotifyMobileUsers' ],
|
|
audio: [ 'dontNotifyAudioUsers', 'alwaysNotifyAudioUsers' ]
|
|
};
|
|
|
|
return (settings[types[type][0]].indexOf(id) === -1 || settings[types[type][1]].indexOf(id) !== -1);
|
|
}
|
|
|
|
// Don't fetch all users if room exceeds max members
|
|
const maxMembersForNotification = RocketChat.settings.get('Notifications_Max_Room_Members');
|
|
const disableAllMessageNotifications = room.usernames.length > maxMembersForNotification && maxMembersForNotification !== 0;
|
|
const subscriptions = RocketChat.models.Subscriptions.findNotificationPreferencesByRoom(room._id, disableAllMessageNotifications);
|
|
const userIds = [];
|
|
subscriptions.forEach((s) => {
|
|
userIds.push(s.u._id);
|
|
});
|
|
const userSettings = {};
|
|
RocketChat.models.Users.findUsersByIds(userIds, { fields: { 'settings.preferences.audioNotifications': 1, 'settings.preferences.desktopNotifications': 1, 'settings.preferences.mobileNotifications': 1 } }).forEach((user) => {
|
|
userSettings[user._id] = user.settings;
|
|
});
|
|
|
|
subscriptions.forEach((subscription) => {
|
|
if (subscription.disableNotifications) {
|
|
settings.dontNotifyDesktopUsers.push(subscription.u._id);
|
|
settings.dontNotifyMobileUsers.push(subscription.u._id);
|
|
settings.dontNotifyAudioUsers.push(subscription.u._id);
|
|
} else {
|
|
const preferences = userSettings[subscription.u._id] ? userSettings[subscription.u._id].preferences || {} : {};
|
|
const userAudioNotificationPreference = preferences.audioNotifications !== 'default' ? preferences.audioNotifications : undefined;
|
|
const userDesktopNotificationPreference = preferences.desktopNotifications !== 'default' ? preferences.desktopNotifications : undefined;
|
|
const userMobileNotificationPreference = preferences.mobileNotifications !== 'default' ? preferences.mobileNotifications : undefined;
|
|
// Set defaults if they don't exist
|
|
const {
|
|
audioNotifications = userAudioNotificationPreference || RocketChat.settings.get('Audio_Notifications_Default_Alert'),
|
|
desktopNotifications = userDesktopNotificationPreference || RocketChat.settings.get('Desktop_Notifications_Default_Alert'),
|
|
mobilePushNotifications = userMobileNotificationPreference || RocketChat.settings.get('Mobile_Notifications_Default_Alert')
|
|
} = subscription;
|
|
|
|
if (audioNotifications === 'all' && !disableAllMessageNotifications) {
|
|
settings.alwaysNotifyAudioUsers.push(subscription.u._id);
|
|
}
|
|
if (desktopNotifications === 'all' && !disableAllMessageNotifications) {
|
|
settings.alwaysNotifyDesktopUsers.push(subscription.u._id);
|
|
} else if (desktopNotifications === 'nothing') {
|
|
settings.dontNotifyDesktopUsers.push(subscription.u._id);
|
|
}
|
|
if (mobilePushNotifications === 'all' && !disableAllMessageNotifications) {
|
|
settings.alwaysNotifyMobileUsers.push(subscription.u._id);
|
|
} else if (mobilePushNotifications === 'nothing') {
|
|
settings.dontNotifyMobileUsers.push(subscription.u._id);
|
|
}
|
|
}
|
|
settings.audioNotificationValues[subscription.u._id] = subscription.audioNotificationValue;
|
|
settings.desktopNotificationDurations[subscription.u._id] = subscription.desktopNotificationDuration;
|
|
});
|
|
let userIdsForAudio = [];
|
|
let userIdsToNotify = [];
|
|
let userIdsToPushNotify = [];
|
|
|
|
const usersWithHighlights = [];
|
|
|
|
const highlights = RocketChat.models.Users.findUsersByUsernamesWithHighlights(room.usernames, { fields: { '_id': 1, 'settings.preferences.highlights': 1 }}).fetch();
|
|
|
|
highlights.forEach(function(user) {
|
|
if (messageContainsHighlight(message, user.settings.preferences.highlights)) {
|
|
usersWithHighlights.push(user);
|
|
}
|
|
});
|
|
|
|
let push_message = ' ';
|
|
//Set variables depending on Push Notification settings
|
|
if (RocketChat.settings.get('Push_show_message')) {
|
|
push_message = parseMessageText(message, userId);
|
|
}
|
|
|
|
let push_username = '';
|
|
let push_room = '';
|
|
if (RocketChat.settings.get('Push_show_username_room')) {
|
|
push_username = user.username;
|
|
push_room = `#${ room.name }`;
|
|
}
|
|
|
|
if (room.t == null || room.t === 'd') {
|
|
const userOfMentionId = message.rid.replace(message.u._id, '');
|
|
const userOfMention = RocketChat.models.Users.findOne({
|
|
_id: userOfMentionId
|
|
}, {
|
|
fields: {
|
|
username: 1,
|
|
statusConnection: 1
|
|
}
|
|
});
|
|
|
|
// Always notify Sandstorm
|
|
if (userOfMention != null) {
|
|
RocketChat.Sandstorm.notify(message, [userOfMention._id],
|
|
`@${ user.username }: ${ message.msg }`, 'privateMessage');
|
|
|
|
}
|
|
if ((userOfMention != null) && canBeNotified(userOfMentionId, 'mobile')) {
|
|
const duration = settings.desktopNotificationDurations[userOfMention._id];
|
|
notifyDesktopUser(userOfMention._id, user, message, room, duration);
|
|
}
|
|
|
|
if ((userOfMention != null) && canBeNotified(userOfMentionId, 'desktop')) {
|
|
if (Push.enabled === true && userOfMention.statusConnection !== 'online') {
|
|
RocketChat.PushNotification.send({
|
|
roomId: message.rid,
|
|
username: push_username,
|
|
message: push_message,
|
|
badge: getBadgeCount(userOfMention._id),
|
|
payload: {
|
|
host: Meteor.absoluteUrl(),
|
|
rid: message.rid,
|
|
sender: message.u,
|
|
type: room.t,
|
|
name: room.name
|
|
},
|
|
usersTo: {
|
|
userId: userOfMention._id
|
|
}
|
|
});
|
|
return message;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
const mentionIds = [];
|
|
if (message.mentions != null) {
|
|
message.mentions.forEach(function(mention) {
|
|
return mentionIds.push(mention._id);
|
|
});
|
|
}
|
|
const toAll = mentionIds.indexOf('all') > -1;
|
|
const toHere = mentionIds.indexOf('here') > -1;
|
|
if (mentionIds.length > 0 || settings.alwaysNotifyDesktopUsers.length > 0) {
|
|
let desktopMentionIds = _.union(mentionIds, settings.alwaysNotifyDesktopUsers);
|
|
desktopMentionIds = _.difference(desktopMentionIds, settings.dontNotifyDesktopUsers);
|
|
|
|
let usersOfDesktopMentions = RocketChat.models.Users.find({
|
|
_id: {
|
|
$in: desktopMentionIds
|
|
}
|
|
}, {
|
|
fields: {
|
|
_id: 1,
|
|
username: 1,
|
|
active: 1
|
|
}
|
|
}).fetch();
|
|
if (room.t === 'c' && !toAll) {
|
|
const callJoin = function(usersOfMentionItem) {
|
|
if (usersOfMentionItem.active) {
|
|
Meteor.runAsUser(usersOfMentionItem._id, function() {
|
|
return Meteor.call('joinRoom', room._id);
|
|
});
|
|
}
|
|
};
|
|
for (const usersOfMentionItem of usersOfDesktopMentions) {
|
|
if (room.usernames.indexOf(usersOfMentionItem.username) === -1) {
|
|
callJoin(usersOfMentionItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (room.t !== 'c') {
|
|
usersOfDesktopMentions = _.reject(usersOfDesktopMentions, (usersOfMentionItem) => {
|
|
return room.usernames.indexOf(usersOfMentionItem.username) === -1;
|
|
});
|
|
}
|
|
|
|
userIdsToNotify = _.pluck(usersOfDesktopMentions, '_id');
|
|
}
|
|
|
|
if (mentionIds.length > 0 || settings.alwaysNotifyMobileUsers.length > 0) {
|
|
let mobileMentionIds = _.union(mentionIds, settings.alwaysNotifyMobileUsers);
|
|
mobileMentionIds = _.difference(mobileMentionIds, settings.dontNotifyMobileUsers);
|
|
|
|
let usersOfMobileMentions = RocketChat.models.Users.find({
|
|
_id: {
|
|
$in: mobileMentionIds
|
|
}
|
|
}, {
|
|
fields: {
|
|
_id: 1,
|
|
username: 1,
|
|
statusConnection: 1
|
|
}
|
|
}).fetch();
|
|
|
|
if (room.t !== 'c') {
|
|
usersOfMobileMentions = _.reject(usersOfMobileMentions, (usersOfMentionItem) => {
|
|
return room.usernames.indexOf(usersOfMentionItem.username) === -1;
|
|
});
|
|
}
|
|
|
|
userIdsToPushNotify = _.pluck(_.filter(usersOfMobileMentions, function(user) {
|
|
return user.statusConnection !== 'online';
|
|
}), '_id');
|
|
}
|
|
|
|
if (mentionIds.length > 0 || settings.alwaysNotifyAudioUsers.length > 0) {
|
|
let audioMentionIds = _.union(mentionIds, settings.alwaysNotifyAudioUsers);
|
|
audioMentionIds = _.difference(audioMentionIds, userIdsToNotify);
|
|
|
|
let usersOfAudioMentions = RocketChat.models.Users.find({
|
|
_id: {
|
|
$in: audioMentionIds
|
|
}
|
|
}, {
|
|
fields: {
|
|
_id: 1,
|
|
username: 1,
|
|
active: 1
|
|
}
|
|
}).fetch();
|
|
if (room.t === 'c' && !toAll) {
|
|
const callJoin = function(usersOfMentionItem) {
|
|
if (usersOfMentionItem.active) {
|
|
Meteor.runAsUser(usersOfMentionItem._id, function() {
|
|
return Meteor.call('joinRoom', room._id);
|
|
});
|
|
}
|
|
};
|
|
for (const usersOfMentionItem of usersOfAudioMentions) {
|
|
if (room.usernames.indexOf(usersOfMentionItem.username) === -1) {
|
|
callJoin(usersOfMentionItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (room.t !== 'c') {
|
|
usersOfAudioMentions = _.reject(usersOfAudioMentions, (usersOfMentionItem) => {
|
|
return room.usernames.indexOf(usersOfMentionItem.username) === -1;
|
|
});
|
|
}
|
|
|
|
userIdsForAudio = _.pluck(usersOfAudioMentions, '_id');
|
|
}
|
|
|
|
if ((toAll || toHere) && room.usernames && room.usernames.length > 0) {
|
|
RocketChat.models.Users.find({
|
|
username: {
|
|
$in: room.usernames
|
|
},
|
|
_id: {
|
|
$ne: user._id
|
|
}
|
|
}, {
|
|
fields: {
|
|
_id: 1,
|
|
username: 1,
|
|
status: 1,
|
|
statusConnection: 1
|
|
}
|
|
}).forEach(function(user) {
|
|
if (['online', 'away', 'busy'].includes(user.status) && (settings.dontNotifyDesktopUsers || []).includes(user._id) === false) {
|
|
userIdsToNotify.push(user._id);
|
|
userIdsForAudio.push(user._id);
|
|
}
|
|
if (toAll && user.statusConnection !== 'online' && (settings.dontNotifyMobileUsers || []).includes(user._id) === false) {
|
|
return userIdsToPushNotify.push(user._id);
|
|
}
|
|
if (toAll && user.statusConnection !== 'online') {
|
|
userIdsForAudio.push(user._id);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (usersWithHighlights.length > 0) {
|
|
const highlightsIds = _.pluck(usersWithHighlights, '_id');
|
|
userIdsForAudio = userIdsForAudio.concat(highlightsIds);
|
|
userIdsToNotify = userIdsToNotify.concat(highlightsIds);
|
|
userIdsToPushNotify = userIdsToPushNotify.concat(highlightsIds);
|
|
}
|
|
|
|
userIdsToNotify = _.without(_.compact(_.unique(userIdsToNotify)), message.u._id);
|
|
userIdsToPushNotify = _.without(_.compact(_.unique(userIdsToPushNotify)), message.u._id);
|
|
userIdsForAudio = _.without(_.compact(_.unique(userIdsForAudio)), message.u._id);
|
|
|
|
if (userIdsToNotify.length > 0) {
|
|
for (const usersOfMentionId of userIdsToNotify) {
|
|
const duration = settings.desktopNotificationDurations[usersOfMentionId];
|
|
notifyDesktopUser(usersOfMentionId, user, message, room, duration);
|
|
}
|
|
}
|
|
|
|
if (userIdsForAudio.length > 0) {
|
|
for (const usersOfMentionId of userIdsForAudio) {
|
|
notifyAudioUser(usersOfMentionId, user, message, room);
|
|
}
|
|
}
|
|
|
|
if (userIdsToPushNotify.length > 0) {
|
|
if (Push.enabled === true) {
|
|
// send a push notification for each user individually (to get his/her badge count)
|
|
userIdsToPushNotify.forEach((userIdToNotify) => {
|
|
RocketChat.PushNotification.send({
|
|
roomId: message.rid,
|
|
roomName: push_room,
|
|
username: push_username,
|
|
message: push_message,
|
|
badge: getBadgeCount(userIdToNotify),
|
|
payload: {
|
|
host: Meteor.absoluteUrl(),
|
|
rid: message.rid,
|
|
sender: message.u,
|
|
type: room.t,
|
|
name: room.name
|
|
},
|
|
usersTo: {
|
|
userId: userIdToNotify
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
const allUserIdsToNotify = _.unique(userIdsToNotify.concat(userIdsToPushNotify));
|
|
if (room.t === 'p') {
|
|
RocketChat.Sandstorm.notify(message, allUserIdsToNotify,
|
|
`@${ user.username }: ${ message.msg }`, 'privateMessage');
|
|
} else {
|
|
RocketChat.Sandstorm.notify(message, allUserIdsToNotify,
|
|
`@${ user.username }: ${ message.msg }`, 'message');
|
|
}
|
|
}
|
|
|
|
return message;
|
|
|
|
}, RocketChat.callbacks.priority.LOW, 'sendNotificationOnMessage');
|
|
|