-
Notifications
You must be signed in to change notification settings - Fork 10.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BREAK] Improvements to notifications logic (#10686)
[NEW] Improvements to notifications logic
- Loading branch information
1 parent
f58c185
commit 9f70755
Showing
21 changed files
with
1,035 additions
and
779 deletions.
There are no files selected for viewing
28 changes: 28 additions & 0 deletions
28
packages/rocketchat-lib/lib/getUserNotificationPreference.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
packages/rocketchat-lib/server/functions/notifications/audio.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
94
packages/rocketchat-lib/server/functions/notifications/desktop.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
176
packages/rocketchat-lib/server/functions/notifications/email.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Oops, something went wrong.