'use strict'; const winston = require('winston'); const batch = require('../batch'); const db = require('../database'); const notifications = require('../notifications'); const user = require('../user'); const io = require('../socket.io'); const plugins = require('../plugins'); const meta = require('../meta'); module.exports = function (Messaging) { // Only used to notify a user of a new chat message Messaging.notifyQueue = {}; Messaging.setUserNotificationSetting = async (uid, roomId, value) => { if (parseInt(value, 10) === -1) { // go back to default return await db.deleteObjectField(`chat:room:${roomId}:notification:settings`, uid); } await db.setObjectField(`chat:room:${roomId}:notification:settings`, uid, parseInt(value, 10)); }; Messaging.getUidsNotificationSetting = async (uids, roomId) => { const [settings, roomData] = await Promise.all([ db.getObjectFields(`chat:room:${roomId}:notification:settings`, uids), Messaging.getRoomData(roomId, ['notificationSetting']), ]); return uids.map(uid => parseInt(settings[uid] || roomData.notificationSetting, 10)); }; Messaging.markRoomNotificationsRead = async (uid, roomId) => { const chatNids = await db.getSortedSetScan({ key: `uid:${uid}:notifications:unread`, match: `chat_${roomId}_*`, }); if (chatNids.length) { await notifications.markReadMultiple(chatNids, uid); await user.notifications.pushCount(uid); } }; Messaging.notifyUsersInRoom = async (fromUid, roomId, messageObj) => { const isPublic = parseInt(await db.getObjectField(`chat:room:${roomId}`, 'public'), 10) === 1; let data = { roomId: roomId, fromUid: fromUid, message: messageObj, public: isPublic, }; data = await plugins.hooks.fire('filter:messaging.notify', data); if (!data) { return; } // delivers full message to all online users in roomId io.in(`chat_room_${roomId}`).emit('event:chats.receive', data); const unreadData = { roomId, fromUid, public: isPublic }; if (isPublic && !messageObj.system) { // delivers unread public msg to all online users on the chats page io.in(`chat_room_public_${roomId}`).emit('event:chats.public.unread', unreadData); } if (messageObj.system) { return; } // push unread count only for private rooms if (!isPublic) { const uids = await Messaging.getAllUidsInRoomFromSet(`chat:room:${roomId}:uids:online`); Messaging.pushUnreadCount(uids, unreadData); } // Delayed notifications let queueObj = Messaging.notifyQueue[`${fromUid}:${roomId}`]; if (queueObj) { queueObj.message.content += `\n${messageObj.content}`; clearTimeout(queueObj.timeout); } else { queueObj = { message: messageObj, }; Messaging.notifyQueue[`${fromUid}:${roomId}`] = queueObj; } queueObj.timeout = setTimeout(async () => { try { await sendNotification(fromUid, roomId, queueObj.message); delete Messaging.notifyQueue[`${fromUid}:${roomId}`]; } catch (err) { winston.error(`[messaging/notifications] Unabled to send notification\n${err.stack}`); } }, meta.config.notificationSendDelay * 1000); }; async function sendNotification(fromUid, roomId, messageObj) { fromUid = parseInt(fromUid, 10); const [settings, roomData] = await Promise.all([ db.getObject(`chat:room:${roomId}:notification:settings`), Messaging.getRoomData(roomId, ['notificationSetting']), ]); const roomDefault = roomData.notificationSetting; const uidsToNotify = []; const { ALLMESSAGES } = Messaging.notificationSettings; await batch.processSortedSet(`chat:room:${roomId}:uids:online`, async (uids) => { uids = uids.filter( uid => (parseInt((settings && settings[uid]) || roomDefault, 10) === ALLMESSAGES) && fromUid !== parseInt(uid, 10) ); const hasRead = await Messaging.hasRead(uids, roomId); uidsToNotify.push(...uids.filter((uid, index) => !hasRead[index])); }, { reverse: true, batch: 500, interval: 100, }); if (uidsToNotify.length) { const { displayname } = messageObj.fromUser; const isGroupChat = await Messaging.isGroupChat(roomId); const notification = await notifications.create({ type: isGroupChat ? 'new-group-chat' : 'new-chat', subject: `[[email:notif.chat.subject, ${displayname}]]`, bodyShort: `[[notifications:new_message_from, ${displayname}]]`, bodyLong: messageObj.content, nid: `chat_${roomId}_${fromUid}`, from: fromUid, path: `/chats/${messageObj.roomId}`, }); await notifications.push(notification, uidsToNotify); } } };