Skip to content

Commit

Permalink
[BREAK] Improvements to notifications logic (#10686)
Browse files Browse the repository at this point in the history
[NEW] Improvements to notifications logic
  • Loading branch information
sampaiodiego authored and rodrigok committed May 9, 2018
1 parent f58c185 commit 9f70755
Show file tree
Hide file tree
Showing 21 changed files with 1,035 additions and 779 deletions.
28 changes: 28 additions & 0 deletions packages/rocketchat-lib/lib/getUserNotificationPreference.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
RocketChat.getUserNotificationPreference = function _getUserNotificationPreference(user, pref) {
if (typeof user === 'string') {
user = RocketChat.models.Users.findOneById(user);
}

let preferenceKey;
switch (pref) {
case 'desktop': preferenceKey = 'desktopNotifications'; break;
case 'mobile': preferenceKey = 'mobileNotifications'; break;
case 'email': preferenceKey = 'emailNotificationMode'; break;
}

if (user && user.settings && user.settings.preferences && user.settings.preferences[preferenceKey] !== 'default') {
return {
value: user.settings.preferences[preferenceKey],
origin: 'user'
};
}
const serverValue = RocketChat.settings.get(`Accounts_Default_User_Preferences_${ preferenceKey }`);
if (serverValue) {
return {
value: serverValue,
origin: 'server'
};
}

return null;
};
2 changes: 1 addition & 1 deletion packages/rocketchat-lib/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ Package.onUse(function(api) {
api.addFiles('lib/MessageTypes.js');
api.addFiles('lib/templateVarHandler.js');

api.addFiles('lib/getUserNotificationPreference.js');
api.addFiles('lib/getUserPreference.js');

api.addFiles('server/lib/bugsnag.js', 'server');
Expand Down Expand Up @@ -112,7 +113,6 @@ Package.onUse(function(api) {
api.addFiles('server/lib/notifyUsersOnMessage.js', 'server');
api.addFiles('server/lib/processDirectEmail.js', 'server');
api.addFiles('server/lib/roomTypes.js', 'server');
api.addFiles('server/lib/sendEmailOnMessage.js', 'server');
api.addFiles('server/lib/sendNotificationsOnMessage.js', 'server');
api.addFiles('server/lib/validateEmailDomain.js', 'server');

Expand Down
2 changes: 1 addition & 1 deletion packages/rocketchat-lib/server/functions/createRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ RocketChat.createRoom = function(type, name, owner, members, readOnly, extraData
room = RocketChat.models.Rooms.createWithFullRoomData(room);

for (const username of members) {
const member = RocketChat.models.Users.findOneByUsername(username, { fields: { username: 1 }});
const member = RocketChat.models.Users.findOneByUsername(username, { fields: { username: 1, 'settings.preferences': 1 }});
const isTheOwner = username === owner.username;
if (!member) {
continue;
Expand Down
35 changes: 35 additions & 0 deletions packages/rocketchat-lib/server/functions/notifications/audio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export function shouldNotifyAudio({
disableAllMessageNotifications,
status,
audioNotifications,
hasMentionToAll,
hasMentionToHere,
isHighlighted,
hasMentionToUser
}) {
if (disableAllMessageNotifications && audioNotifications == null) {
return false;
}

if (status === 'busy' || audioNotifications === 'nothing') {
return false;
}

if (!audioNotifications && RocketChat.settings.get('Accounts_Default_User_Preferences_audioNotifications') === 'all') {
return true;
}

return (!disableAllMessageNotifications && (hasMentionToAll || hasMentionToHere)) || isHighlighted || audioNotifications === 'all' || hasMentionToUser;
}

export function notifyAudioUser(userId, message, room) {
RocketChat.Notifications.notifyUser(userId, 'audioNotification', {
payload: {
_id: message._id,
rid: message.rid,
sender: message.u,
type: room.t,
name: room.name
}
});
}
94 changes: 94 additions & 0 deletions packages/rocketchat-lib/server/functions/notifications/desktop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { parseMessageText } from './index';

/**
* 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;
}

/**
* 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
*/
export 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 = '';
let text = '';
if (room.t === 'd') {
title = UI_Use_Real_Name ? user.name : `@${ user.username }`;
text = message.msg;
} else if (room.name) {
title = `#${ room.name }`;
text = `${ UI_Use_Real_Name ? user.name : user.username }: ${ message.msg }`;
} else {
return;
}

RocketChat.Notifications.notifyUser(userId, 'notification', {
title,
text,
duration,
payload: {
_id: message._id,
rid: message.rid,
sender: message.u,
type: room.t,
name: room.name
}
});
}

export function shouldNotifyDesktop({
disableAllMessageNotifications,
status,
desktopNotifications,
hasMentionToAll,
hasMentionToHere,
isHighlighted,
hasMentionToUser
}) {
if (disableAllMessageNotifications && desktopNotifications == null) {
return false;
}

if (status === 'busy' || desktopNotifications === 'nothing') {
return false;
}

if (!desktopNotifications) {
if (RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotifications') === 'all') {
return true;
}
if (RocketChat.settings.get('Accounts_Default_User_Preferences_desktopNotifications') === 'nothing') {
return false;
}
}

return (!disableAllMessageNotifications && (hasMentionToAll || hasMentionToHere)) || isHighlighted || desktopNotifications === 'all' || hasMentionToUser;
}
176 changes: 176 additions & 0 deletions packages/rocketchat-lib/server/functions/notifications/email.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import s from 'underscore.string';

let contentHeader;
RocketChat.settings.get('Email_Header', (key, value) => {
contentHeader = RocketChat.placeholders.replace(value || '');
});

let contentFooter;
RocketChat.settings.get('Email_Footer', (key, value) => {
contentFooter = RocketChat.placeholders.replace(value || '');
});

const divisorMessage = '<hr style="margin: 20px auto; border: none; border-bottom: 1px solid #dddddd;">';

function getEmailContent({ message, user, room }) {
const lng = user && user.language || RocketChat.settings.get('language') || 'en';

const roomName = s.escapeHTML(`#${ RocketChat.roomTypes.getRoomName(room.t, room) }`);
const userName = s.escapeHTML(RocketChat.settings.get('UI_Use_Real_Name') ? message.u.name || message.u.username : message.u.username);

const header = TAPi18n.__(room.t === 'd' ? 'User_sent_a_message_to_you' : 'User_sent_a_message_on_channel', {
username: userName,
channel: roomName,
lng
});

if (message.msg !== '') {
let messageContent = s.escapeHTML(message.msg);
message = RocketChat.callbacks.run('renderMessage', message);
if (message.tokens && message.tokens.length > 0) {
message.tokens.forEach((token) => {
token.text = token.text.replace(/([^\$])(\$[^\$])/gm, '$1$$$2');
messageContent = messageContent.replace(token.token, token.text);
});
}
return `${ header }<br/><br/>${ messageContent.replace(/\n/gm, '<br/>') }`;
}

if (message.file) {
const fileHeader = TAPi18n.__(room.t === 'd' ? 'User_uploaded_a_file_to_you' : 'User_uploaded_a_file_on_channel', {
username: userName,
channel: roomName,
lng
});

let content = `${ TAPi18n.__('Attachment_File_Uploaded') }: ${ s.escapeHTML(message.file.name) }`;

if (message.attachments && message.attachments.length === 1 && message.attachments[0].description !== '') {
content += `<br/><br/>${ s.escapeHTML(message.attachments[0].description) }`;
}

return `${ fileHeader }<br/><br/>${ content }`;
}

if (message.attachments.length > 0) {
const [ attachment ] = message.attachments;

let content = '';

if (attachment.title) {
content += `${ s.escapeHTML(attachment.title) }<br/>`;
}
if (attachment.text) {
content += `${ s.escapeHTML(attachment.text) }<br/>`;
}

return `${ header }<br/><br/>${ content }`;
}

return header;
}

function getMessageLink(room, sub) {
const roomPath = RocketChat.roomTypes.getRouteLink(room.t, sub);
const path = Meteor.absoluteUrl(roomPath ? roomPath.replace(/^\//, '') : '');
const style = [
'color: #fff;',
'padding: 9px 12px;',
'border-radius: 4px;',
'background-color: #04436a;',
'text-decoration: none;'
].join(' ');
const message = TAPi18n.__('Offline_Link_Message');
return `<p style="text-align:center;margin-bottom:8px;"><a style="${ style }" href="${ path }">${ message }</a>`;
}

export function sendEmail({ message, user, subscription, room, emailAddress, toAll }) {
let emailSubject;
const username = RocketChat.settings.get('UI_Use_Real_Name') ? message.u.name : message.u.username;

if (room.t === 'd') {
emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_DM_Email'), {
user: username,
room: RocketChat.roomTypes.getRoomName(room.t, room)
});
} else if (toAll) {
emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_Mention_All_Email'), {
user: username,
room: RocketChat.roomTypes.getRoomName(room.t, room)
});
} else {
emailSubject = RocketChat.placeholders.replace(RocketChat.settings.get('Offline_Mention_Email'), {
user: username,
room: RocketChat.roomTypes.getRoomName(room.t, room)
});
}
const content = getEmailContent({
message,
user,
room
});

const link = getMessageLink(room, subscription);

if (RocketChat.settings.get('Direct_Reply_Enable')) {
contentFooter = RocketChat.placeholders.replace(RocketChat.settings.get('Email_Footer_Direct_Reply') || '');
}

const email = {
to: emailAddress,
subject: emailSubject,
html: contentHeader + content + divisorMessage + link + contentFooter
};

// using user full-name/channel name in from address
if (room.t === 'd') {
email.from = `${ String(message.u.name).replace(/@/g, '%40').replace(/[<>,]/g, '') } <${ RocketChat.settings.get('From_Email') }>`;
} else {
email.from = `${ String(room.name).replace(/@/g, '%40').replace(/[<>,]/g, '') } <${ RocketChat.settings.get('From_Email') }>`;
}
// If direct reply enabled, email content with headers
if (RocketChat.settings.get('Direct_Reply_Enable')) {
email.headers = {
// Reply-To header with format "username+messageId@domain"
'Reply-To': `${ RocketChat.settings.get('Direct_Reply_Username').split('@')[0].split(RocketChat.settings.get('Direct_Reply_Separator'))[0] }${ RocketChat.settings.get('Direct_Reply_Separator') }${ message._id }@${ RocketChat.settings.get('Direct_Reply_Username').split('@')[1] }`
};
}

Meteor.defer(() => {
Email.send(email);
});
}

export function shouldNotifyEmail({
disableAllMessageNotifications,
statusConnection,
emailNotifications,
isHighlighted,
hasMentionToUser,
hasMentionToAll
}) {

// use connected (don't need to send him an email)
if (statusConnection === 'online') {
return false;
}

// user/room preference to nothing
if (emailNotifications === 'nothing') {
return false;
}

// no user or room preference
if (emailNotifications == null) {
if (disableAllMessageNotifications) {
return false;
}

// default server preference is disabled
if (RocketChat.settings.get('Accounts_Default_User_Preferences_emailNotificationMode') === 'disabled') {
return false;
}
}

return isHighlighted || emailNotifications === 'all' || hasMentionToUser || (!disableAllMessageNotifications && hasMentionToAll);
}
Loading

0 comments on commit 9f70755

Please sign in to comment.