canopy/www/js/channel/commandPreprocessor.js

243 lines
7.8 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*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/>.*/
class commandPreprocessor{
constructor(client){
this.client = client;
this.commandProcessor = new commandProcessor(client);
this.emotes = {
site: [],
chan: [],
personal: []
}
//define listeners
this.defineListeners();
}
defineListeners(){
//When we receive site-wide emote list
this.client.socket.on("siteEmotes", this.setSiteEmotes.bind(this));
this.client.socket.on("chanEmotes", this.setChanEmotes.bind(this));
this.client.socket.on("personalEmotes", this.setPersonalEmotes.bind(this));
this.client.socket.on("usedTokes", this.setUsedTokes.bind(this));
}
preprocess(command){
//Set command and sendFlag
this.command = command;
this.sendFlag = true;
this.processLocalCommand();
if(this.sendFlag){
this.message = command;
this.processEmotes();
this.processLinks();
this.sendRemoteCommand();
}
}
processLocalCommand(){
//Create an empty array to hold the command
this.commandArray = [];
//Split string by words
this.commandArray = this.command.split(/\b/g);//Split by word-borders
this.argumentArray = this.command.match(/\b\w+\b/g);//Match by words surrounded by borders
//If this is a local command
if(this.commandArray[0] == '/'){
//If the command exists
if(this.argumentArray != null && this.commandProcessor[this.argumentArray[0].toLowerCase()] != null){
//Don't send it to the server
this.sendFlag = false;
//Call the command with the argument array
this.commandProcessor[this.argumentArray[0].toLowerCase()](this.argumentArray, this.commandArray);
}
}
}
processEmotes(){
//inject invisible whitespace in-between emotes to prevent from mushing links together
this.message = this.message.replaceAll('][','][');
//For each list of emotes
Object.keys(this.emotes).forEach((key) => {
//For each emote in the current list
this.emotes[key].forEach((emote) => {
//Inject emote links into the message
this.message = this.message.replaceAll(`[${emote.name}]`, emote.link);
});
});
}
processLinks(){
//Strip out file seperators in-case the user is being a smart-ass
this.message = this.message.replaceAll('␜','');
//Split message by links
var splitMessage = this.message.split(/(https?:\/\/[^\s]+)/g);
//Create an empty array to hold links
this.links = [];
splitMessage.forEach((chunk, chunkIndex) => {
//For each chunk that is a link
if(chunk.match(/(https?:\/\/[^\s]+)/g)){
//I looked online for obscure characters that no one would use to prevent people from chatting embed placeholders
//Then I found this fucker, turns out it's literally made for the job lmao (even if it was originally intended for paper/magnetic tape)
//Replace link with indexed placeholder
splitMessage[chunkIndex] = `${this.links.length}`
//push current chunk as link
this.links.push(chunk);
}
});
//Join the message back together
this.message = splitMessage.join('');
}
sendRemoteCommand(){
this.client.socket.emit("chatMessage",{msg: this.message, links: this.links});
}
setSiteEmotes(data){
this.emotes.site = data;
}
setChanEmotes(data){
this.emotes.chan = data;
}
setPersonalEmotes(data){
this.emotes.personal = data;
}
setUsedTokes(data){
this.usedTokes = data.tokes;
}
getEmoteByLink(link){
//Create an empty variable to hold the found emote
var foundEmote = null;
//For each list of emotes
Object.keys(this.emotes).forEach((key) => {
//For each emote in the current list
this.emotes[key].forEach((emote) => {
//if we found a match
if(emote.link == link){
//return the match
foundEmote = emote;
}
});
});
return foundEmote;
}
getEmoteNames(){
//Create an empty array to hold names
let names = [];
//For every set of emotes
for(let set of Object.keys(this.emotes)){
//for every emote in the current set of emotes
for(let emote of this.emotes[set]){
//push the name of the emote to the name list
names.push(emote.name);
}
}
//return our list of names
return names;
}
buildAutocompleteDictionary(){
let dictionary = {
tokes: {
prefix: '!',
postfix: '',
cmds: this.usedTokes
},
//Make sure to add spaces at the end for commands that take arguments
//Not necissary but definitely nice to have
serverCMD: {
prefix: '!',
postfix: '',
cmds: [
"whisper ",
"announce ",
"serverannounce ",
"clear ",
"kick "
]
},
localCMD:{
prefix: '/',
postfix: '',
cmds: [
"high "
]
},
usernames:{
prefix: '',
postfix: '',
cmds: Array.from(client.userList.colorMap.keys())
},
emotes:{
prefix:'[',
postfix:']',
cmds: this.getEmoteNames()
}
};
//Ensure default toke command
//Check if 'toke' is the first registered toke
if(dictionary.tokes.cmds[0] != 'toke'){
//Find the current place of the 'toke' command, if any
const tokeIndex = dictionary.tokes.cmds.indexOf('toke');
//If the default command is present but is out of order
if(tokeIndex != -1){
//Splice it out
dictionary.tokes.cmds.splice(tokeIndex,1);
}
//Throw it into the beggining of the array
dictionary.tokes.cmds.unshift('toke');
}
//return our dictionary object
return dictionary;
}
}
class commandProcessor{
constructor(client){
this.client = client
}
high(argumentArray){
//If we have an argument
if(argumentArray[1]){
//Use it to set our high level
//Technically this is less of a local command than it would be if it where telling the select to do this
//but TTN used to treat this as a local command so fuck it
this.client.socket.emit("setHighLevel", {highLevel: argumentArray[1]});
}
}
}