diff --git a/Clave_SSH b/Clave_SSH new file mode 100644 index 0000000000..9775dbe13c --- /dev/null +++ b/Clave_SSH @@ -0,0 +1,49 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +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 +-----END OPENSSH PRIVATE KEY----- diff --git a/Clave_SSH.pub b/Clave_SSH.pub new file mode 100644 index 0000000000..40f0183a93 --- /dev/null +++ b/Clave_SSH.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCz/ZyNof5pkgyt2BdmrwWXH/vIqjTw3/z0L1CEskLXZi2vu0JQN+ioIz3BuRRHjDdfCP5cRGnE0zcd1FAlSkB9Q4LUg3gXx1l+JVlK7yHtrYONIDEAkqJNNCSa0l3PXzMwMkObtbM+sKiU2kbkSm+u7nPkiyMzhcH8O65ZV+WEDSRhObGVVehuxxfBKG9uvcgW5XEtpQ+eoDck71aDyuO2HzAHqQldJEHVltTqG9flHmnb0k577ULjphszsS0RRzgim3bzjdlRQn4m8z6f0+z0NVBv7iOMWpIaXjPImuBnKVBU+C5eby/6XzMtHU82ZUrmtfDOsyEgI6DQfqInkNkSFN9fH8nfaTiDBFdPOB24gw19qLctyL9VrEWZVD0GaDyscpFR+ulyK4wKkF8Pgfjb2ZRzno8UJuiUbj+9CRLk6UPjezXjQ+hc3a/tRN8BpVvwNM0S0P7paK1iXCVQL4Dpo/LMtfoyOduz9XJ5mXU82FM10pfLAya4WBuyfkz2JcQM6gGEwRyISqyDaMF+/veDCP3/CYSKl5KIPpsNjJUjpJK+WAko9mYk57uAgWD6E8MGunXpdiPuJTU0WL2Dow3RBw0Kj53+oRQj5Yjwd51+d/q/xXmJkteVV8mjzUHV/NigE8q60SARl+IGej0kpN1L29978X52d1/3hUAmQmGiYQ== 15-11669@usb.ve diff --git a/src/messaging/notifications.js b/src/messaging/notifications.js index 503382cf01..1f70926d1c 100644 --- a/src/messaging/notifications.js +++ b/src/messaging/notifications.js @@ -1,134 +1,280 @@ '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'); - -module.exports = function (Messaging) { - 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); +const __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P((resolve) => { resolve(value); }); } + return new (P || (P = Promise))((resolve, reject) => { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +const __generator = (this && this.__generator) || function (thisArg, body) { + let _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }; let f; let y; let t; let + g; + return g = { next: verb(0), throw: verb(1), return: verb(2) }, typeof Symbol === 'function' && (g[Symbol.iterator] = function () { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError('Generator is already executing.'); + while (g && (g = 0, op[0] && (_ = 0)), _) { + try { + if (f = 1, y && (t = op[0] & 2 ? y.return : op[0] ? y.throw || ((t = y.return) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } } - 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)); + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.default = default_1; +const winston = require('winston'); // Importa la biblioteca 'winston' para el manejo de logs y errores +const batch_1 = require('../batch'); // Importa utilidades para procesamiento por lotes +const database_1 = require('../database'); // Importa funciones para interactuar con la base de datos +const notifications_1 = require('../notifications'); // Importa funcionalidades para manejar notificaciones +const user_1 = require('../user'); // Importa funcionalidades relacionadas con usuarios +const socket_io_1 = require('../socket.io'); // Importa la funcionalidad de WebSockets (para comunicación en tiempo real) +const plugins_1 = require('../plugins'); +// Importa funcionalidades de extensiones y plugins +function default_1(Messaging) { + const _this = this; + // Establece la configuración de notificaciones de un usuario en una sala + Messaging.setUserNotificationSetting = function (uid, roomId, value) { + return __awaiter(_this, void 0, void 0, function () { + return __generator(this, (_a) => { + switch (_a.label) { + case 0: + if (!(value === -1)) return [3 /* break */, 2]; + // Si el valor es -1, restablecer la configuración a los valores predeterminados + return [4 /* yield */, database_1.default.deleteObjectField('chat:room:'.concat(roomId, ':notification:settings'), uid)]; + case 1: + // Si el valor es -1, restablecer la configuración a los valores predeterminados + _a.sent(); + return [3 /* break */, 4]; + case 2: + // Establece el valor personalizado para el usuario en la sala + return [4 /* yield */, database_1.default.setObjectField('chat:room:'.concat(roomId, ':notification:settings'), uid, value)]; + case 3: + // Establece el valor personalizado para el usuario en la sala + _a.sent(); + _a.label = 4; + case 4: return [2]; + } + }); + }); }; - - Messaging.markRoomNotificationsRead = async (uid, roomId) => { - const chatNids = await db.getSortedSetScan({ - key: `uid:${uid}:notifications:unread`, - match: `chat_${roomId}_*`, + // Obtiene las configuraciones de notificación de un grupo de usuarios en una sala + Messaging.getUidsNotificationSetting = function (uids, roomId) { + return __awaiter(_this, void 0, void 0, function () { + let _a; let settings; let + roomData; + return __generator(this, (_b) => { + switch (_b.label) { + case 0: return [4 /* yield */, Promise.all([ + // Obtiene los ajustes personalizados de cada usuario + database_1.default.getObjectFields('chat:room:'.concat(roomId, ':notification:settings'), uids), + // Obtiene la configuración de notificaciones predeterminada de la sala + Messaging.getRoomData(roomId, ['notificationSetting']), + ])]; + case 1: + _a = _b.sent(), settings = _a[0], roomData = _a[1]; + // Combina los ajustes personalizados con la configuración predeterminada + return [2 /* return */, uids.map(uid => parseInt(settings[uid] || roomData.notificationSetting.toString(), 10))]; + } + }); }); - 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); - } - - try { - await sendNotification(fromUid, roomId, messageObj); - } catch (err) { - winston.error(`[messaging/notifications] Unabled to send notification\n${err.stack}`); - } + // Marca las notificaciones de una sala como leídas para un usuario específico + Messaging.markRoomNotificationsRead = function (uid, roomId) { + return __awaiter(_this, void 0, void 0, function () { + let chatNids; + return __generator(this, (_a) => { + switch (_a.label) { + case 0: return [4 /* yield */, database_1.default.getSortedSetScan({ + key: 'uid:'.concat(uid, ':notifications:unread'), // Clave donde se almacenan las notificaciones no leídas del usuario + match: 'chat_'.concat(roomId, '_*'), // Busca notificaciones relacionadas con la sala especificada + })]; + case 1: + chatNids = _a.sent(); + if (!chatNids.length) return [3 /* break */, 4]; + // Marca las notificaciones como leídas + return [4 /* yield */, notifications_1.default.markReadMultiple(chatNids, uid)]; + case 2: + // Marca las notificaciones como leídas + _a.sent(); + // Actualiza el recuento de notificaciones para el usuario + return [4 /* yield */, user_1.default.notifications.pushCount(uid)]; + case 3: + // Actualiza el recuento de notificaciones para el usuario + _a.sent(); + _a.label = 4; + case 4: return [2]; + } + }); + }); }; - - async function sendNotification(fromUid, roomId, messageObj) { - fromUid = parseInt(fromUid, 10); - - const [settings, roomData, realtimeUids] = await Promise.all([ - db.getObject(`chat:room:${roomId}:notification:settings`), - Messaging.getRoomData(roomId), - io.getUidsInRoom(`chat_room_${roomId}`), - ]); - 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) && - !realtimeUids.includes(parseInt(uid, 10)) - ); - const hasRead = await Messaging.hasRead(uids, roomId); - uidsToNotify.push(...uids.filter((uid, index) => !hasRead[index])); - }, { - reverse: true, - batch: 500, - interval: 100, + // Notifica a los usuarios de una sala sobre un nuevo mensaje + Messaging.notifyUsersInRoom = function (fromUid, roomId, messageObj) { + return __awaiter(_this, void 0, void 0, function () { + let isPublic; let _a; let data; let unreadData; let uids; let + err_1; + return __generator(this, (_b) => { + switch (_b.label) { + case 0: + _a = parseInt; + return [4 /* yield */, database_1.default.getObjectField('chat:room:'.concat(roomId), 'public')]; + case 1: + isPublic = _a.apply(void 0, [_b.sent(), 10]) === 1; + data = { + roomId: roomId, + fromUid: fromUid, + message: messageObj, + public: isPublic, + }; + return [4 /* yield */, plugins_1.default.hooks.fire('filter:messaging.notify', data)]; + case 2: + // Permite a los plugins modificar los datos de la notificación + data = _b.sent(); + if (!data) { + // Si los datos fueron anulados por un plugin, no continuar + return [2]; + } + // Envía el mensaje a todos los usuarios conectados a la sala + socket_io_1.default.in('chat_room_'.concat(roomId)).emit('event:chats.receive', data); + unreadData = { roomId: roomId, fromUid: fromUid, public: isPublic }; + if (isPublic && !messageObj.system) { + // Notifica a los usuarios en la página de chats públicos sobre un nuevo mensaje no leído + socket_io_1.default.in('chat_room_public_'.concat(roomId)).emit('event:chats.public.unread', unreadData); + } + if (messageObj.system) { + return [2]; // No se necesita continuar si es un mensaje del sistema + } + if (isPublic) return [3 /* break */, 4]; + return [4 /* yield */, Messaging.getAllUidsInRoomFromSet('chat:room:'.concat(roomId, ':uids:online'))]; + case 3: + uids = _b.sent(); + Messaging.pushUnreadCount(uids, unreadData); + _b.label = 4; + case 4: + _b.trys.push([4, 6, , 7]); + // Envía una notificación al usuario que recibió el mensaje + return [4 /* yield */, sendNotification(Messaging, fromUid, roomId, messageObj)]; + case 5: + // Envía una notificación al usuario que recibió el mensaje + _b.sent(); + return [3 /* break */, 7]; + case 6: + err_1 = _b.sent(); + // Manejo de errores en caso de fallo al enviar la notificación + winston.error('[messaging/notifications] Unable to send notification\n'.concat(err_1.stack)); + return [3 /* break */, 7]; + case 7: return [2]; + } + }); }); + }; +} - if (uidsToNotify.length) { - const { displayname } = messageObj.fromUser; - const isGroupChat = await Messaging.isGroupChat(roomId); - const roomName = roomData.roomName || `[[modules:chat.room-id, ${roomId}]]`; - const notifData = { - type: isGroupChat ? 'new-group-chat' : 'new-chat', - subject: roomData.roomName ? - `[[email:notif.chat.new-message-from-user-in-room, ${displayname}, ${roomName}]]` : - `[[email:notif.chat.new-message-from-user, ${displayname}]]`, - bodyShort: isGroupChat || roomData.roomName ? `[[notifications:new-message-in, ${roomName}]]` : `[[notifications:new-message-from, ${displayname}]]`, - bodyLong: messageObj.content, - nid: `chat_${roomId}_${fromUid}_${Date.now()}`, - mergeId: `new-chat|${roomId}`, // as roomId is the differentiator, no distinction between direct vs. group req'd. - from: fromUid, - roomId, - roomName, - path: `/chats/${messageObj.roomId}`, - }; - if (roomData.public) { - const icon = Messaging.getRoomIcon(roomData); - notifData.type = 'new-public-chat'; - notifData.roomIcon = icon; - notifData.subject = `[[email:notif.chat.new-message-from-user-in-room, ${displayname}, ${roomName}]]`; - notifData.bodyShort = `[[notifications:user-posted-in-public-room, ${displayname}, ${icon}, ${roomName}]]`; - notifData.mergeId = `notifications:user-posted-in-public-room|${roomId}`; +function sendNotification(Messaging, fromUid, roomId, messageObj) { + return __awaiter(this, void 0, void 0, function () { + let _a; let settings; let roomData; let realtimeUids; let roomDefault; let uidsToNotify; let ALLMESSAGES; let displayname; let isGroupChat; let roomName; let notifData; let icon; let + notification; + const _this = this; + return __generator(this, (_b) => { + switch (_b.label) { + case 0: return [4 /* yield */, Promise.all([ + database_1.default.getObject('chat:room:'.concat(roomId, ':notification:settings')), + Messaging.getRoomData(roomId, ['notificationSetting', 'roomName', 'public']), // Se especifican los campos que necesitamos + socket_io_1.default.getUidsInRoom('chat_room_'.concat(roomId)), + ]), + // Configuración de notificación por defecto para la sala + ]; + case 1: + _a = _b.sent(), settings = _a[0], roomData = _a[1], realtimeUids = _a[2]; + roomDefault = roomData.notificationSetting; + uidsToNotify = []; + ALLMESSAGES = Messaging.notificationSettings.ALLMESSAGES; + // Procesa a los usuarios en el conjunto de IDs en línea en la sala + return [4 /* yield */, batch_1.default.processSortedSet('chat:room:'.concat(roomId, ':uids:online'), uids => __awaiter(_this, void 0, void 0, function () { + let hasRead; + return __generator(this, (_a) => { + switch (_a.label) { + case 0: + // Filtra los usuarios que deben ser notificados + uids = uids.filter(uid => + // Verifica si el usuario tiene habilitada la opción de recibir notificaciones para todos los mensajes + parseInt((settings && settings[uid]) || roomDefault.toString(), 10) === ALLMESSAGES && + // Evita notificar al remitente del mensaje + fromUid !== parseInt(uid, 10) && + // Evita notificar a los usuarios que ya están en tiempo real (conectados) + !realtimeUids.includes(uid)); + return [4 /* yield */, Messaging.hasRead(uids, roomId)]; + case 1: + hasRead = _a.sent(); + // Añade a la lista de notificaciones aquellos que no hayan leído el mensaje + uidsToNotify.push.apply(uidsToNotify, uids.filter((uid, index) => !hasRead[index])); + return [2]; + } + }); + }), { + reverse: true, // Procesa los IDs de usuarios en orden inverso + batch: 500, // Tamaño del lote a procesar + interval: 100, // Intervalo de procesamiento entre lotes + })]; + case 2: + // Procesa a los usuarios en el conjunto de IDs en línea en la sala + _b.sent(); + if (!uidsToNotify.length) return [3 /* break */, 6]; + displayname = messageObj.fromUser.displayname; + return [4 /* yield */, Messaging.isGroupChat(roomId)]; + case 3: + isGroupChat = _b.sent(); + roomName = roomData.roomName || '[[modules:chat.room-id, '.concat(roomId, ']]'); + notifData = { + type: isGroupChat ? 'new-group-chat' : 'new-chat', // Tipo de notificación según si es un chat grupal o privado + subject: roomData.roomName ? + '[[email:notif.chat.new-message-from-user-in-room, '.concat(displayname, ', ').concat(roomName, ']]') : + '[[email:notif.chat.new-message-from-user, '.concat(displayname, ']]'), // Asunto del email dependiendo si hay nombre de la sala o no + bodyShort: isGroupChat || roomData.roomName ? '[[notifications:new-message-in, '.concat(roomName, ']]') : '[[notifications:new-message-from, '.concat(displayname, ']]'), // Cuerpo corto de la notificación + bodyLong: messageObj.content, // Contenido del mensaje + nid: 'chat_'.concat(roomId, '_').concat(fromUid, '_').concat(Date.now()), // ID único de la notificación + mergeId: 'new-chat|'.concat(roomId), // ID para combinar notificaciones similares + from: fromUid, // ID del remitente + roomId: roomId, // ID de la sala + roomName: roomName, // Nombre de la sala + path: '/chats/'.concat(messageObj.roomId), // Ruta a la sala de chat en la interfaz + }; + // Si la sala es pública, modifica el tipo de notificación y añade icono de la sala + if (roomData.public) { + icon = Messaging.getRoomIcon(roomData); + notifData.type = 'new-public-chat'; + notifData.roomIcon = icon; // Añade el icono de la sala a la notificación + notifData.subject = '[[email:notif.chat.new-message-from-user-in-room, '.concat(displayname, ', ').concat(roomName, ']]'); + notifData.bodyShort = '[[notifications:user-posted-in-public-room, '.concat(displayname, ', ').concat(icon, ', ').concat(roomName, ']]'); + notifData.mergeId = 'notifications:user-posted-in-public-room|'.concat(roomId); // ID para combinar notificaciones en salas públicas + } + return [4 /* yield */, notifications_1.default.create(notifData)]; + case 4: + notification = _b.sent(); + return [4 /* yield */, notifications_1.default.push(notification, uidsToNotify)]; + case 5: + _b.sent(); // Envía la notificación a los usuarios que deben ser notificados + _b.label = 6; + case 6: return [2]; } - const notification = await notifications.create(notifData); - await notifications.push(notification, uidsToNotify); - } - } -}; + }); + }); +} diff --git a/src/messaging/notifications.ts b/src/messaging/notifications.ts new file mode 100644 index 0000000000..9066d9550e --- /dev/null +++ b/src/messaging/notifications.ts @@ -0,0 +1,217 @@ +import * as winston from 'winston'; // Importa la biblioteca 'winston' para el manejo de logs y errores +import batch from '../batch'; // Importa utilidades para procesamiento por lotes +import db from '../database'; // Importa funciones para interactuar con la base de datos +import notifications from '../notifications'; // Importa funcionalidades para manejar notificaciones +import user from '../user'; // Importa funcionalidades relacionadas con usuarios +import io from '../socket.io'; // Importa la funcionalidad de WebSockets (para comunicación en tiempo real) +import plugins from '../plugins'; // Importa funcionalidades de extensiones y plugins +// Define la estructura de un objeto de mensaje (MessageObj) +interface MessageObj { + system?: boolean; + content: string; + fromUser: { + displayname: string; + }; + roomId: string; +} +// Define la estructura de los datos de una sala de chat (RoomData) +interface RoomData { + roomName?: string; + notificationSetting: number; + public?: boolean; +} +// Define la estructura de los datos de una notificación (NotificationData) +interface NotificationData { + type: string; + subject: string; + bodyShort: string; + bodyLong: string; + nid: string; + mergeId: string; + from: number; + roomId: string; + roomName: string; + path: string; + roomIcon?: string; +} +interface UnreadData { + roomId: string; // ID de la sala + fromUid: number; // ID del usuario que envió el mensaje + public: boolean; // Indica si la sala es pública o privada + } +// Definición de la interfaz para el sistema de mensajería +interface IMessaging { + setUserNotificationSetting(uid: number, roomId: string, value: number): Promise; + getUidsNotificationSetting(uids: string[], roomId: string): Promise; + markRoomNotificationsRead(uid: string, roomId: string): Promise; + notifyUsersInRoom(fromUid: number, roomId: string, messageObj: MessageObj): Promise; + getAllUidsInRoomFromSet(setKey: string): Promise; + pushUnreadCount(uids: string[], unreadData: UnreadData): void; + getRoomData(roomId: string, fields: string[]): Promise; + hasRead(uids: string[], roomId: string): Promise; + isGroupChat(roomId: string): Promise; + getRoomIcon(roomData: RoomData): string; + notificationSettings: { + ALLMESSAGES: number; + MENTIONS: number; + NONE: number; + }; +} +export default function (Messaging: IMessaging) { + // Establece la configuración de notificaciones de un usuario en una sala + Messaging.setUserNotificationSetting = async (uid: number, roomId: string, value: number): Promise => { + if (value === -1) { + // Si el valor es -1, restablecer la configuración a los valores predeterminados + await db.deleteObjectField(`chat:room:${roomId}:notification:settings`, uid); + } else { + // Establece el valor personalizado para el usuario en la sala + await db.setObjectField(`chat:room:${roomId}:notification:settings`, uid, value); + } + }; + // Obtiene las configuraciones de notificación de un grupo de usuarios en una sala + Messaging.getUidsNotificationSetting = async (uids: string[], roomId: string): Promise => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const [settings, roomData]: [Record, RoomData] = await Promise.all([ + // Obtiene los ajustes personalizados de cada usuario + db.getObjectFields(`chat:room:${roomId}:notification:settings`, uids), + // Obtiene la configuración de notificaciones predeterminada de la sala + Messaging.getRoomData(roomId, ['notificationSetting']), + ]); + // Combina los ajustes personalizados con la configuración predeterminada + return uids.map(uid => parseInt(settings[uid] || roomData.notificationSetting.toString(), 10)); + }; + // Marca las notificaciones de una sala como leídas para un usuario específico + Messaging.markRoomNotificationsRead = async (uid: string, roomId: string): Promise => { + const chatNids: string[] = await db.getSortedSetScan({ + key: `uid:${uid}:notifications:unread`, // Clave donde se almacenan las notificaciones no leídas del usuario + match: `chat_${roomId}_*`, // Busca notificaciones relacionadas con la sala especificada + }); + if (chatNids.length) { + // Marca las notificaciones como leídas + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + await notifications.markReadMultiple(chatNids, uid); + // Actualiza el recuento de notificaciones para el usuario + await user.notifications.pushCount(uid); + } + }; + // Notifica a los usuarios de una sala sobre un nuevo mensaje + Messaging.notifyUsersInRoom = async (fromUid: number, roomId: string, messageObj: MessageObj): Promise => { + // Comprueba si la sala es pública + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + const isPublic = parseInt(await db.getObjectField(`chat:room:${roomId}`, 'public'), 10) === 1; + let data = { + roomId, + fromUid, + message: messageObj, + public: isPublic, + }; + // Permite a los plugins modificar los datos de la notificación + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + data = await plugins.hooks.fire('filter:messaging.notify', data);; + if (!data) { + // Si los datos fueron anulados por un plugin, no continuar + return; + } + // Envía el mensaje a todos los usuarios conectados a la sala + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + io.in(`chat_room_${roomId}`).emit('event:chats.receive', data); + + const unreadData = { roomId, fromUid, public: isPublic }; + if (isPublic && !messageObj.system) { + // Notifica a los usuarios en la página de chats públicos sobre un nuevo mensaje no leído + io.in(`chat_room_public_${roomId}`).emit('event:chats.public.unread', unreadData); + } + if (messageObj.system) { + return; // No se necesita continuar si es un mensaje del sistema + } + // Si la sala es privada, actualiza el recuento de mensajes no leídos + if (!isPublic) { + const uids = await Messaging.getAllUidsInRoomFromSet(`chat:room:${roomId}:uids:online`); + Messaging.pushUnreadCount(uids, unreadData); + } + try { + // Envía una notificación al usuario que recibió el mensaje + await sendNotification(Messaging,fromUid, roomId, messageObj); + } catch (err) { + // Manejo de errores en caso de fallo al enviar la notificación + winston.error(`[messaging/notifications] Unable to send notification\n${err.stack}`); + } + }; +}; +async function sendNotification( + Messaging: IMessaging, + fromUid: number, + roomId: string, + messageObj: MessageObj +): Promise { + const [settings, roomData, realtimeUids]: [Record, RoomData, string[]] = await Promise.all([ + db.getObject(`chat:room:${roomId}:notification:settings`), + Messaging.getRoomData(roomId, ['notificationSetting', 'roomName', 'public']), // Se especifican los campos que necesitamos + io.getUidsInRoom(`chat_room_${roomId}`), + ]) + // Configuración de notificación por defecto para la sala + const roomDefault = roomData.notificationSetting; + // Arreglo donde se almacenarán los IDs de usuarios que deben ser notificados + const uidsToNotify: string[] = []; + // Se asume que ALLMESSAGES es un valor constante que indica que se debe notificar por todos los mensajes + const { ALLMESSAGES } = Messaging.notificationSettings; + // Procesa a los usuarios en el conjunto de IDs en línea en la sala + await batch.processSortedSet( + `chat:room:${roomId}:uids:online`, + async (uids: string[]) => { + // Filtra los usuarios que deben ser notificados + uids = uids.filter( + uid => + // Verifica si el usuario tiene habilitada la opción de recibir notificaciones para todos los mensajes + parseInt((settings && settings[uid]) || roomDefault.toString(), 10) === ALLMESSAGES && + // Evita notificar al remitente del mensaje + fromUid !== parseInt(uid, 10) && + // Evita notificar a los usuarios que ya están en tiempo real (conectados) + !realtimeUids.includes(uid) + ); + // Verifica si los usuarios ya han leído los mensajes en esta sala + const hasRead = await Messaging.hasRead(uids, roomId); + // Añade a la lista de notificaciones aquellos que no hayan leído el mensaje + uidsToNotify.push(...uids.filter((uid, index) => !hasRead[index])); + }, { + reverse: true, // Procesa los IDs de usuarios en orden inverso + batch: 500, // Tamaño del lote a procesar + interval: 100, // Intervalo de procesamiento entre lotes + }); + // Si hay usuarios a los que se les debe notificar + if (uidsToNotify.length) { + // Obtiene el nombre del usuario remitente + const { displayname } = messageObj.fromUser; + // Verifica si la sala es un chat grupal + const isGroupChat = await Messaging.isGroupChat(roomId); + // Nombre de la sala (si no tiene nombre, se usa el ID de la sala) + const roomName = roomData.roomName || `[[modules:chat.room-id, ${roomId}]]`; + // Crea los datos para la notificación + const notifData: NotificationData = { + type: isGroupChat ? 'new-group-chat' : 'new-chat', // Tipo de notificación según si es un chat grupal o privado + subject: roomData.roomName ? + `[[email:notif.chat.new-message-from-user-in-room, ${displayname}, ${roomName}]]` : + `[[email:notif.chat.new-message-from-user, ${displayname}]]`, // Asunto del email dependiendo si hay nombre de la sala o no + bodyShort: isGroupChat || roomData.roomName ? `[[notifications:new-message-in, ${roomName}]]` : `[[notifications:new-message-from, ${displayname}]]`, // Cuerpo corto de la notificación + bodyLong: messageObj.content, // Contenido del mensaje + nid: `chat_${roomId}_${fromUid}_${Date.now()}`, // ID único de la notificación + mergeId: `new-chat|${roomId}`, // ID para combinar notificaciones similares + from: fromUid, // ID del remitente + roomId, // ID de la sala + roomName, // Nombre de la sala + path: `/chats/${messageObj.roomId}`, // Ruta a la sala de chat en la interfaz + }; + // Si la sala es pública, modifica el tipo de notificación y añade icono de la sala + if (roomData.public) { + const icon = Messaging.getRoomIcon(roomData); // Obtiene el icono de la sala + notifData.type = 'new-public-chat'; + notifData.roomIcon = icon; // Añade el icono de la sala a la notificación + notifData.subject = `[[email:notif.chat.new-message-from-user-in-room, ${displayname}, ${roomName}]]`; + notifData.bodyShort = `[[notifications:user-posted-in-public-room, ${displayname}, ${icon}, ${roomName}]]`; + notifData.mergeId = `notifications:user-posted-in-public-room|${roomId}`; // ID para combinar notificaciones en salas públicas + } + // Crea y envía la notificación + const notification = await notifications.create(notifData); + await notifications.push(notification, uidsToNotify); // Envía la notificación a los usuarios que deben ser notificados + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..10aeeef7e6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "allowJs": false, + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "esModuleInterop": true, + }, + "include": [ + "public/src/**/*", + "src/**/*", + "test/**/*", + ], + "exclude":[ + "node_modules", + ] +} \ No newline at end of file