diff --git a/website/server/controllers/api-v3/chat.js b/website/server/controllers/api-v3/chat.js index c6fe56d439..1a50a9601d 100644 --- a/website/server/controllers/api-v3/chat.js +++ b/website/server/controllers/api-v3/chat.js @@ -20,6 +20,7 @@ import { getMatchesByWordArray } from '../../libs/stringUtils'; import bannedSlurs from '../../libs/bannedSlurs'; import apiError from '../../libs/apiError'; import {highlightMentions} from '../../libs/highlightMentions'; +import {sendNotification} from '../../libs/pushNotifications'; const FLAG_REPORT_EMAILS = nconf.get('FLAG_REPORT_EMAIL').split(',').map((email) => { return { email, canSend: true }; @@ -183,7 +184,7 @@ api.postChat = { throw new NotAuthorized(res.t('messageGroupChatSpam')); } - const [message, mentions] = await highlightMentions(req.body.message); + const [message, mentions, mentionedMembers] = await highlightMentions(req.body.message); let client = req.headers['x-client'] || '3rd Party'; if (client) { client = client.replace('habitica-', ''); @@ -192,7 +193,6 @@ api.postChat = { let flagCount = 0; if (group.privacy === 'public' && user.flags.chatShadowMuted) { flagCount = common.constants.CHAT_FLAG_FROM_SHADOW_MUTE; - let message = req.body.message; // Email the mods let authorEmail = getUserInfo(user, ['email']).email; @@ -232,6 +232,29 @@ api.postChat = { toSave.push(user.save()); } + mentionedMembers.forEach((member) => { + if (member._id === user._id) return; + const pushNotifPrefs = member.preferences.pushNotifications; + if (group.type === 'party') { + if (pushNotifPrefs.mentionParty !== true) { + return; + } + } else if (member.guilds.contains(group._id)) { + if (pushNotifPrefs.mentionJoinedGuild !== true) { + return; + } + } else { + if (group.privacy !== 'public') { + return; + } + if (pushNotifPrefs.mentionUnjoinedGuild !== true) { + return; + } + } + sendNotification(member, {identifier: 'chatMention', title: `${user.profile.name} mentioned you in ${group.name}`, message: req.body.message}); + }); + + await Promise.all(toSave); let analyticsObject = { diff --git a/website/server/libs/highlightMentions.js b/website/server/libs/highlightMentions.js index d20735535e..48839bc38e 100644 --- a/website/server/libs/highlightMentions.js +++ b/website/server/libs/highlightMentions.js @@ -4,13 +4,14 @@ const mentionRegex = new RegExp('\\B@[-\\w]+', 'g'); export async function highlightMentions (text) { const mentions = text.match(mentionRegex); + let members = []; if (mentions !== null && mentions.length <= 5) { const usernames = mentions.map((mention) => { return mention.substr(1); }); - let members = await User + members = await User .find({'auth.local.username': {$in: usernames}, 'flags.verifiedUsername': true}) - .select(['auth.local.username', '_id']) + .select(['auth.local.username', '_id', 'preferences.pushNotifications', 'pushDevices']) .lean() .exec(); members.forEach((member) => { @@ -18,5 +19,5 @@ export async function highlightMentions (text) { text = text.replace(new RegExp(`@${username}(?![\\-\\w])`, 'g'), `[@${username}](/profile/${member._id})`); }); } - return [text, mentions]; + return [text, mentions, members]; } diff --git a/website/server/libs/pushNotifications.js b/website/server/libs/pushNotifications.js index 4cd4a1d82f..ad2a925bf0 100644 --- a/website/server/libs/pushNotifications.js +++ b/website/server/libs/pushNotifications.js @@ -56,7 +56,7 @@ function sendNotification (user, details = {}) { case 'ios': if (apnProvider) { const notification = new apn.Notification({ - alert: details.message, + alert: {title: details.title, body: details.message}, sound: 'default', category: details.category, topic: 'com.habitrpg.ios.Habitica', diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index f941f874b5..89cf5057ae 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -503,6 +503,9 @@ let schema = new Schema({ questStarted: {$type: Boolean, default: true}, invitedQuest: {$type: Boolean, default: true}, majorUpdates: {$type: Boolean, default: true}, + mentionParty: {$type: Boolean, default: true}, + mentionJoinedGuild: {$type: Boolean, default: true}, + mentionUnjoinedGuild: {$type: Boolean, default: true}, }, suppressModals: { levelUp: {$type: Boolean, default: false},