/*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 Imports const permissionModel = require('../../schemas/permissionSchema'); const channelModel = require('../../schemas/channel/channelSchema'); /** * Class representing global server-side chat/command processing logic */ class commandProcessor{ /** * Instantiates a commandProcessor object * @param {channelManager} server - Parent Server Object * @param {chatHandler} chatHandler - Parent Chat Handler Object */ constructor(server, chatHandler){ this.server = server; this.chatHandler = chatHandler; } //Command keywords get run through .toLowerCase(), so we should use lowercase method names for command methods /** * Command Processor method to handle the '!whisper' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag */ whisper(commandObj){ //splice out our command commandObj.commandArray.splice(0,2); //Mark out the current message as a whisper commandObj.chatType = 'whisper'; //Make sure to throw the send flag return true } /** * Command Processor method to handle the '!spoiler' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag */ spoiler(commandObj){ //splice out our command commandObj.commandArray.splice(0,2); //Mark out the current message as a spoiler commandObj.chatType = 'spoiler'; //Make sure to throw the send flag return true } /** * Command Processor method to handle the '!strikethrough' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag */ strikethrough(commandObj){ //splice out our command commandObj.commandArray.splice(0,2); //Mark out the current message as a spoiler commandObj.chatType = 'strikethrough'; //Make sure to throw the send flag return true } /** * Command Processor method to handle the '!bold' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag */ bold(commandObj){ //splice out our command commandObj.commandArray.splice(0,2); //Mark out the current message as a spoiler commandObj.chatType = 'bold'; //Make sure to throw the send flag return true } /** * Command Processor method to handle the '!italics' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag */ italics(commandObj){ //splice out our command commandObj.commandArray.splice(0,2); //Mark out the current message as a spoiler commandObj.chatType = 'italics'; //Make sure to throw the send flag return true } /** * Command Processor method to handle the '!announce' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag on un-authorized call to shame the user */ async announce(commandObj, preprocessor){ //Get the current channel from the database const chanDB = await channelModel.findOne({name: commandObj.socket.chan}); //Check if the user has permission, and publicly shame them if they don't (lmao) if(chanDB != null && await chanDB.permCheck(commandObj.socket.user, 'announce')){ //splice out our command commandObj.commandArray.splice(0,2); //Prep the message using pre-processor functions chat-handling await preprocessor.prepMessage(commandObj); //send it this.chatHandler.relayChannelAnnouncement(commandObj.socket.chan, commandObj.message, commandObj.links); //throw send flag return false; } //throw send flag return true; } /** * Command Processor method to handle the '!serverannounce' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag on un-authorized call to shame the user */ async serverannounce(commandObj, preprocessor){ //Check if the user has permission, and publicly shame them if they don't (lmao) if(await permissionModel.permCheck(commandObj.socket.user, 'announce')){ //splice out our command commandObj.commandArray.splice(0,2); //Prep the message using pre-processor functions for chat-handling await preprocessor.prepMessage(commandObj); //send it this.chatHandler.relayServerAnnouncement(commandObj.message, commandObj.links); //disble send flag return false; } //throw send flag return true; } /** * Command Processor method to handle the '!resettoke' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag on un-authorized call to shame the user */ async resettoke(commandObj, preprocessor){ //Check if the user has permission, and publicly shame them if they don't (lmao) if(await permissionModel.permCheck(commandObj.socket.user, 'resetToke')){ //Acknowledge command this.chatHandler.relayTokeWhisper(commandObj.socket, 'Toke cooldown reset.'); //Tell tokebot to reset the toke preprocessor.tokebot.resetToke(); //disable send flag return false; } //throw send flag return true; } /** * Command Processor method to handle the '!clear' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag on un-authorized call to shame the user */ async clear(commandObj){ //Get the current channel from the database const chanDB = await channelModel.findOne({name: commandObj.socket.chan}); //Check if the user has permission, and publicly shame them if they don't (lmao) if(await chanDB.permCheck(commandObj.socket.user, 'clearChat')){ //Send off the command this.chatHandler.clearChat(commandObj.socket.chan, commandObj.argumentArray[1]); //disable send flag return false; } //throw send flag return true; } /** * Command Processor method to handle the '!kick' command * @param {Object} commandObj - Object representing a single given command/chat request * @returns {Boolean} True to enable send flag on un-authorized call to shame the user */ async kick(commandObj){ //Get the current channel from the database const chanDB = await channelModel.findOne({name: commandObj.socket.chan}); //Check if the user has permission, and publicly shame them if they don't (lmao) if(await chanDB.permCheck(commandObj.socket.user, 'kickUser')){ //Get username from argument array const username = commandObj.argumentArray[1]; //Get channel const channel = this.server.activeChannels.get(commandObj.socket.chan); //get initiator and target user objects const initiator = channel.userList.get(commandObj.socket.user.user); const target = channel.userList.get(username); //get initiator and target override abilities const override = await permissionModel.overrideCheck(commandObj.socket.user, 'kickUser'); const targetOverride = await permissionModel.overrideCheck(target, 'kickUser'); //If there is no user if(target == null){ //silently drop the command return false; } //If the user is capable of overriding this permission based on site permissions if(override || targetOverride){ //If the site rank is equal if(permissionModel.rankToNum(initiator.rank) == permissionModel.rankToNum(target.rank)){ //compare chan rank if(permissionModel.rankToNum(initiator.chanRank) <= permissionModel.rankToNum(target.chanRank)){ //shame the person running it return true; } //otherwise }else{ //compare site rank if(permissionModel.rankToNum(initiator.rank) <= permissionModel.rankToNum(target.rank)){ //shame the person running it return true; } } }else{ //If the target has a higher chan rank than the initiator if(permissionModel.rankToNum(initiator.chanRank) <= permissionModel.rankToNum(target.chanRank)){ //shame the person running it return true; } } //Splice out kick commandObj.commandArray.splice(0,4) //Get collect reason var reason = commandObj.commandArray.join(''); //If no reason was given if(reason == ''){ //Fill in a generic reason reason = "You have been kicked from the channel!"; } //Kick the user target.disconnect(reason); //throw send flag return false; } //throw send flag return true; } } module.exports = commandProcessor;