From d465863ee6512ee90136f5860d010d3b99c0aa97 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Wed, 1 Oct 2025 19:43:46 -0400 Subject: [PATCH] Split commandPreprocessor in preperation for formatted private messaging. --- src/app/channel/commandPreprocessor.js | 145 ++------------------- src/app/chatPreprocessor.js | 171 +++++++++++++++++++++++++ src/schemas/channel/chatSchema.js | 3 +- 3 files changed, 182 insertions(+), 137 deletions(-) create mode 100644 src/app/chatPreprocessor.js diff --git a/src/app/channel/commandPreprocessor.js b/src/app/channel/commandPreprocessor.js index 8b75261..c08c653 100644 --- a/src/app/channel/commandPreprocessor.js +++ b/src/app/channel/commandPreprocessor.js @@ -18,6 +18,7 @@ along with this program. If not, see .*/ const validator = require('validator');//No express here, so regular validator it is! //Local Imports +const chatPreprocessor = require('../chatPreprocessor'); const tokebot = require('./tokebot'); const linkUtils = require('../../utils/linkUtils'); const permissionModel = require('../../schemas/permissionSchema'); @@ -26,148 +27,20 @@ const channelModel = require('../../schemas/channel/channelSchema'); /** * Class containing global server-side chat/command pre-processing logic */ -class commandPreprocessor{ +class commandPreprocessor extends chatPreprocessor{ /** * Instantiates a commandPreprocessor object * @param {channelManager} server - Parent Server Object * @param {chatHandler} chatHandler - Parent Chat Handler Object */ constructor(server, chatHandler){ - /** - * Parent Server Object - */ - this.server = server; - - /** - * Parent Chat Handler Object - */ - this.chatHandler = chatHandler; - - /** - * Child Command Processor Object - */ - this.commandProcessor = new commandProcessor(server, chatHandler); - - /** - * Child Tokebot Object - */ - this.tokebot = new tokebot(server, chatHandler); - } - - /** - * Ingests a command/chat request from Chat Handler and pre-processes and processes it accordingly - * @param {Socket} socket - Socket we're receiving the request from - * @param {Object} data - Event payload - */ - async preprocess(socket, data){ - //Set command object - const commandObj = { - socket, - sendFlag: true, - rawData: data, - chatType: 'chat' - } - - //If we don't pass sanatization/validation turn this car around - if(!this.sanatizeCommand(commandObj)){ - return; - } - - //split the command - this.splitCommand(commandObj); - - //Process the command - await this.processServerCommand(commandObj); - - //If we're going to relay this command as a message, continue on to chat processing - if(commandObj.sendFlag){ - //Prep the message - await this.prepMessage(commandObj); - - //Send the chat - this.sendChat(commandObj); - } - } - - /** - * Sanatizes and Validates a single user chat message/command - * @param {Object} commandObj - Object representing a single given command/chat request - * @returns {Boolean} false if Command/Message is too long to send - */ - sanatizeCommand(commandObj){ - //Trim and Sanatize for XSS - commandObj.command = validator.trim(validator.escape(commandObj.rawData.msg)); - - //Return whether or not the shit was long enough - return (validator.isLength(commandObj.rawData.msg, {min: 1, max: 255})); - } - - /** - * Splits raw chat/command data into seperate arrays, one by word-borders and words surrounded by word-borders - * These arrays are used to handle further command/chat processing - * @param {Object} commandObj - Object representing a single given command/chat request - */ - splitCommand(commandObj){ - //Split string by words - commandObj.commandArray = commandObj.command.split(/\b/g);//Split by word-borders - commandObj.argumentArray = commandObj.command.match(/\b\w+\b/g);//Match by words surrounded by borders - } - - /** - * Uses the server's Command Processor object to process the chat/command request. - * @param {Object} commandObj - Object representing a single given command/chat request - */ - async processServerCommand(commandObj){ - //If the raw message starts with '!' (skip commands that start with whitespace so people can send example commands in chat) - if(commandObj.rawData.msg[0] == '!'){ - //if it isn't just an exclimation point, and we have a real command - if(commandObj.argumentArray != null){ - //If the command processor knows what to do with whatever the fuck the user sent us - if(this.commandProcessor[commandObj.argumentArray[0].toLowerCase()] != null){ - //Process the command and use the return value to set the sendflag (true if command valid) - commandObj.sendFlag = await this.commandProcessor[commandObj.argumentArray[0].toLowerCase()](commandObj, this); - }else{ - //Process as toke command if we didnt get a match from the standard server-side command processor - commandObj.sendFlag = await this.tokebot.tokeProcessor(commandObj); - } - } - } - } - - /** - * Iterates through links in message and marks them by link type for later use by client-side post-processing - * @param {Object} commandObj - Object representing a single given command/chat request - */ - async markLinks(commandObj){ - //Setup the links array - commandObj.links = []; - - //For each link sent from the client - //this.rawData.links.forEach((link) => { - for (const link of commandObj.rawData.links){ - //Add a marked link object to our links array - commandObj.links.push(await linkUtils.markLink(link)); - } - } - - /** - * Re-creates message string from processed Command Array - * @param {Object} commandObj - Object representing a single given command/chat request - */ - async prepMessage(commandObj){ - //Create message from commandArray - commandObj.message = commandObj.commandArray.join('').trimStart(); - //Validate links and mark them by embed type - await this.markLinks(commandObj); - } - - /** - * Relays chat to channel via parent Chat Handler object - * @param {Object} commandObj - Object representing a single given command/chat request - */ - sendChat(commandObj){ - //FUCKIN' SEND IT! - this.chatHandler.relayUserChat(commandObj.socket, commandObj.message, commandObj.chatType, commandObj.links); + //Call derived constructor + super( + server, + chatHandler, + new commandProcessor(server, chatHandler), + new tokebot(server, chatHandler) + ); } } diff --git a/src/app/chatPreprocessor.js b/src/app/chatPreprocessor.js new file mode 100644 index 0000000..43e77fc --- /dev/null +++ b/src/app/chatPreprocessor.js @@ -0,0 +1,171 @@ +/*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 .*/ + +//NPM Imports +const validator = require('validator');//No express here, so regular validator it is! + +//Local Imports +const linkUtils = require('../utils/linkUtils'); + +/** + * Class containing global server-side chat/command pre-processing logic + */ +class chatPreprocessor{ + /** + * Instantiates a commandPreprocessor object + * @param {channelManager} server - Parent Server Object + * @param {chatHandler} chatHandler - Parent Chat Handler Object + */ + constructor(server, chatHandler, commandProcessor, tokebot){ + /** + * Parent Server Object + */ + this.server = server; + + /** + * Parent Chat Handler Object + */ + this.chatHandler = chatHandler; + + /** + * Child Command Processor Object. Contains functions named after commands. + */ + this.commandProcessor = commandProcessor; + + /** + * Child Tokebot Object + */ + this.tokebot = tokebot; + } + + /** + * Ingests a command/chat request from Chat Handler and pre-processes and processes it accordingly + * @param {Socket} socket - Socket we're receiving the request from + * @param {Object} data - Event payload + */ + async preprocess(socket, data){ + //Set command object + const commandObj = { + socket, + sendFlag: true, + rawData: data, + chatType: 'chat' + } + + //If we don't pass sanatization/validation turn this car around + if(!this.sanatizeCommand(commandObj)){ + return; + } + + //split the command + this.splitCommand(commandObj); + + //Process the command + await this.processServerCommand(commandObj); + + //If we're going to relay this command as a message, continue on to chat processing + if(commandObj.sendFlag){ + //Prep the message + await this.prepMessage(commandObj); + + //Send the chat + this.sendChat(commandObj); + } + } + + /** + * Sanatizes and Validates a single user chat message/command + * @param {Object} commandObj - Object representing a single given command/chat request + * @returns {Boolean} false if Command/Message is too long to send + */ + sanatizeCommand(commandObj){ + //Trim and Sanatize for XSS + commandObj.command = validator.trim(validator.escape(commandObj.rawData.msg)); + + //Return whether or not the shit was long enough + return (validator.isLength(commandObj.rawData.msg, {min: 1, max: 255})); + } + + /** + * Splits raw chat/command data into seperate arrays, one by word-borders and words surrounded by word-borders + * These arrays are used to handle further command/chat processing + * @param {Object} commandObj - Object representing a single given command/chat request + */ + splitCommand(commandObj){ + //Split string by words + commandObj.commandArray = commandObj.command.split(/\b/g);//Split by word-borders + commandObj.argumentArray = commandObj.command.match(/\b\w+\b/g);//Match by words surrounded by borders + } + + /** + * Uses the server's Command Processor object to process the chat/command request. + * @param {Object} commandObj - Object representing a single given command/chat request + */ + async processServerCommand(commandObj){ + //If the raw message starts with '!' (skip commands that start with whitespace so people can send example commands in chat) + if(commandObj.rawData.msg[0] == '!'){ + //if it isn't just an exclimation point, and we have a real command + if(commandObj.argumentArray != null){ + //If the command processor knows what to do with whatever the fuck the user sent us + if(this.commandProcessor[commandObj.argumentArray[0].toLowerCase()] != null){ + //Process the command and use the return value to set the sendflag (true if command valid) + commandObj.sendFlag = await this.commandProcessor[commandObj.argumentArray[0].toLowerCase()](commandObj, this); + }else{ + //Process as toke command if we didnt get a match from the standard server-side command processor + commandObj.sendFlag = await this.tokebot.tokeProcessor(commandObj); + } + } + } + } + + /** + * Iterates through links in message and marks them by link type for later use by client-side post-processing + * @param {Object} commandObj - Object representing a single given command/chat request + */ + async markLinks(commandObj){ + //Setup the links array + commandObj.links = []; + + //For each link sent from the client + //this.rawData.links.forEach((link) => { + for (const link of commandObj.rawData.links){ + //Add a marked link object to our links array + commandObj.links.push(await linkUtils.markLink(link)); + } + } + + /** + * Re-creates message string from processed Command Array + * @param {Object} commandObj - Object representing a single given command/chat request + */ + async prepMessage(commandObj){ + //Create message from commandArray + commandObj.message = commandObj.commandArray.join('').trimStart(); + //Validate links and mark them by embed type + await this.markLinks(commandObj); + } + + /** + * Relays chat to channel via parent Chat Handler object + * @param {Object} commandObj - Object representing a single given command/chat request + */ + sendChat(commandObj){ + //FUCKIN' SEND IT! + this.chatHandler.relayUserChat(commandObj.socket, commandObj.message, commandObj.chatType, commandObj.links); + } +} + +module.exports = chatPreprocessor; \ No newline at end of file diff --git a/src/schemas/channel/chatSchema.js b/src/schemas/channel/chatSchema.js index 537afd0..c0d81b0 100644 --- a/src/schemas/channel/chatSchema.js +++ b/src/schemas/channel/chatSchema.js @@ -32,10 +32,11 @@ const chatSchema = new mongoose.Schema({ }, flair: { type: mongoose.SchemaTypes.String, - required: true, + //Leave this as unreq'd for internal type chats that have no flair }, highLevel: { type: mongoose.SchemaTypes.Number, + default: 0, required: true, }, msg: {