/*Canopy - The next generation of stoner streaming software Copyright (C) 2024-2025 Rainbownapkin and the TTN Community This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see .*/ //local includes const auxServer = require('../auxServer'); const chatPreprocessor = require('../chatPreprocessor'); const loggerUtils = require("../../utils/loggerUtils"); const message = require("./message"); const socketUtils = require("../../utils/socketUtils"); /** * Class containg global server-side private message relay logic */ class pmHandler extends auxServer{ /** * Instantiates object containing global server-side private message relay logic * @param {Socket.io} io - Socket.io server instanced passed down from server.js * @param {channelManager} chanServer - Sister channel management server object */ constructor(io, chanServer){ super(io, chanServer, "/pm"); this.chatPreprocessor = new chatPreprocessor(null, null); } /** * Handles global server-side initialization for new connections to the private messaging system * @param {Socket} socket - Requesting Socket */ async handleConnection(socket){ //Check if we're properly authorized const authorized = await super.handleConnection(socket); //If we're authorized if(authorized != null){ //Throw the user into their own unique channel socket.join(socket.user.user); //Define listeners this.defineListeners(socket); } } defineListeners(socket){ super.defineListeners(socket); socket.on("pm", (data)=>{this.handlePM(data, socket)}); } async handlePM(data, socket){ try{ //Check recipients const recipients = this.checkRecipients(data.recipients, socket); //If we don't have any valid recipients if(recipients.length <= 0){ //Drop that shit return false; } //If this is a sesh starter if(data.msg == '' || data.msg == null){ //Skip pre-processing and send a cooked message return this.relayPMObj(new message( socket.user.user, recipients, '', 'chat', [] )); } //preprocess message const preprocessedMessage = await this.chatPreprocessor.preprocess(socket, data); //If the send flag wasnt thrown false if(preprocessedMessage != false){ //Pull an active user profile from the first channel that gives it in the chan server const senderProfile = this.chanServer.activeUsers.get(socket.user.user); //If user isn't actively connected to a channel if(senderProfile == null || senderProfile.length == 0){ //They don't get to send shit lol return; } //Create message object and relay it off to the recipients this.relayPMObj(new message( socket.user.user, recipients, senderProfile[0].flair, senderProfile[0].highLevel, preprocessedMessage.message, preprocessedMessage.chatType, preprocessedMessage.links )); } //If something fucked up }catch(err){ //Bitch and moan return loggerUtils.socketExceptionHandler(socket, err); } } relayPMObj(msg){ //For each recipient for(let user of msg.recipients){ //Send the message this.namespace.to(user).emit("message", msg); } //Acknowledge the sent message this.namespace.to(msg.user).emit("sent", msg); } /** * Basic function for checking presence * This could be done using Channel Presence, but running off of bare Socket.io functionality makes this easier to implement outside the channel if need be * @param {String} user - Username to check presence of * @returns {Boolean} Whether or not the user is currently able to accept messages */ checkPresence(user){ //Pull room map from the guts of socket.io and run a null check against the given username return this.namespace.adapter.rooms.get(user) != null; } checkRecipients(input, socket){ //Create empty recipients array let recipients = []; //For each requested recipient for(let user of input){ //If the given user is online and didn't send the message if(this.checkPresence(user) && user != socket.user.user){ //Add the recipient to the list recipients.push(user); } } //return recipients return recipients; } } module.exports = pmHandler;