483 lines
22 KiB
HTML
483 lines
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>JSDoc: Source: app/channel/commandPreprocessor.js</title>
|
|
|
|
<script src="scripts/prettify/prettify.js"> </script>
|
|
<script src="scripts/prettify/lang-css.js"> </script>
|
|
<!--[if lt IE 9]>
|
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
<![endif]-->
|
|
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
|
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="main">
|
|
|
|
<h1 class="page-title">Source: app/channel/commandPreprocessor.js</h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
<article>
|
|
<pre class="prettyprint source linenums"><code>/*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 <https://www.gnu.org/licenses/>.*/
|
|
|
|
//NPM Imports
|
|
const validator = require('validator');//No express here, so regular validator it is!
|
|
|
|
//Local Imports
|
|
const tokebot = require('./tokebot');
|
|
const linkUtils = require('../../utils/linkUtils');
|
|
const permissionModel = require('../../schemas/permissionSchema');
|
|
const channelModel = require('../../schemas/channel/channelSchema');
|
|
|
|
/**
|
|
* Class containing global server-side chat/command pre-processing logic
|
|
*/
|
|
class commandPreprocessor{
|
|
/**
|
|
* Instantiates a commandPreprocessor object
|
|
* @param {channelManager} server - Parent Server Object
|
|
* @param {chatHandler} chatHandler - Parent Chat Handler Object
|
|
*/
|
|
constructor(server, chatHandler){
|
|
this.server = server;
|
|
this.chatHandler = chatHandler;
|
|
this.commandProcessor = new commandProcessor(server, chatHandler);
|
|
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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 = commandPreprocessor;</code></pre>
|
|
</article>
|
|
</section>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<nav>
|
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="activeChannel.html">activeChannel</a></li><li><a href="channelManager.html">channelManager</a></li><li><a href="chat.html">chat</a></li><li><a href="chatBuffer.html">chatBuffer</a></li><li><a href="chatHandler.html">chatHandler</a></li><li><a href="commandPreprocessor.html">commandPreprocessor</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="connectedUser.html">connectedUser</a></li><li><a href="media.html">media</a></li><li><a href="playlistHandler.html">playlistHandler</a></li><li><a href="queue.html">queue</a></li><li><a href="queuedMedia.html">queuedMedia</a></li><li><a href="tokebot.html">tokebot</a></li></ul><h3>Global</h3><ul><li><a href="global.html#authenticateSession">authenticateSession</a></li><li><a href="global.html#cache">cache</a></li><li><a href="global.html#channelBanSchema">channelBanSchema</a></li><li><a href="global.html#channelPermissionSchema">channelPermissionSchema</a></li><li><a href="global.html#channelSchema">channelSchema</a></li><li><a href="global.html#chatSchema">chatSchema</a></li><li><a href="global.html#comparePassword">comparePassword</a></li><li><a href="global.html#consoleWarn">consoleWarn</a></li><li><a href="global.html#daysToExpire">daysToExpire</a></li><li><a href="global.html#emailChangeSchema">emailChangeSchema</a></li><li><a href="global.html#emoteSchema">emoteSchema</a></li><li><a href="global.html#errorHandler">errorHandler</a></li><li><a href="global.html#errorMiddleware">errorMiddleware</a></li><li><a href="global.html#escapeRegex">escapeRegex</a></li><li><a href="global.html#exceptionHandler">exceptionHandler</a></li><li><a href="global.html#exceptionSmith">exceptionSmith</a></li><li><a href="global.html#failedAttempts">failedAttempts</a></li><li><a href="global.html#fetchMetadata">fetchMetadata</a></li><li><a href="global.html#fetchVideoMetadata">fetchVideoMetadata</a></li><li><a href="global.html#fetchYoutubeMetadata">fetchYoutubeMetadata</a></li><li><a href="global.html#fetchYoutubePlaylistMetadata">fetchYoutubePlaylistMetadata</a></li><li><a href="global.html#flairSchema">flairSchema</a></li><li><a href="global.html#genCaptcha">genCaptcha</a></li><li><a href="global.html#getLoginAttempts">getLoginAttempts</a></li><li><a href="global.html#getMediaType">getMediaType</a></li><li><a href="global.html#hashIP">hashIP</a></li><li><a href="global.html#hashPassword">hashPassword</a></li><li><a href="global.html#kickoff">kickoff</a></li><li><a href="global.html#killSession">killSession</a></li><li><a href="global.html#lifetime">lifetime</a></li><li><a href="global.html#localExceptionHandler">localExceptionHandler</a></li><li><a href="global.html#mailem">mailem</a></li><li><a href="global.html#markLink">markLink</a></li><li><a href="global.html#maxAttempts">maxAttempts</a></li><li><a href="global.html#mediaSchema">mediaSchema</a></li><li><a href="global.html#passwordResetSchema">passwordResetSchema</a></li><li><a href="global.html#permissionSchema">permissionSchema</a></li><li><a href="global.html#playlistMediaProperties">playlistMediaProperties</a></li><li><a href="global.html#playlistSchema">playlistSchema</a></li><li><a href="global.html#processExpiredAttempts">processExpiredAttempts</a></li><li><a href="global.html#queuedProperties">queuedProperties</a></li><li><a href="global.html#rankEnum">rankEnum</a></li><li><a href="global.html#refreshRawLink">refreshRawLink</a></li><li><a href="global.html#schedule">schedule</a></li><li><a href="global.html#securityCheck">securityCheck</a></li><li><a href="global.html#sendAddressVerification">sendAddressVerification</a></li><li><a href="global.html#socketCriticalExceptionHandler">socketCriticalExceptionHandler</a></li><li><a href="global.html#socketErrorHandler">socketErrorHandler</a></li><li><a href="global.html#socketExceptionHandler">socketExceptionHandler</a></li><li><a href="global.html#spent">spent</a></li><li><a href="global.html#statSchema">statSchema</a></li><li><a href="global.html#throttleAttempts">throttleAttempts</a></li><li><a href="global.html#tokeCommandSchema">tokeCommandSchema</a></li><li><a href="global.html#transporter">transporter</a></li><li><a href="global.html#typeEnum">typeEnum</a></li><li><a href="global.html#userBanSchema">userBanSchema</a></li><li><a href="global.html#userSchema">userSchema</a></li><li><a href="global.html#verify">verify</a></li><li><a href="global.html#yankMedia">yankMedia</a></li><li><a href="global.html#ytdlpFetch">ytdlpFetch</a></li></ul>
|
|
</nav>
|
|
|
|
<br class="clear">
|
|
|
|
<footer>
|
|
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Tue Sep 02 2025 07:43:33 GMT-0400 (Eastern Daylight Time)
|
|
</footer>
|
|
|
|
<script> prettyPrint(); </script>
|
|
<script src="scripts/linenumber.js"> </script>
|
|
</body>
|
|
</html>
|