canopy/www/js/channel/commandPreprocessor.js

243 lines
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, add invisible whitespace to the end to keep next character from mushing into the link
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: [
['toke', true]
].concat(injectPerms(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 ", true],
["announce ", client.user.permMap.chan.get('announce')],
["serverannounce ", client.user.permMap.site.get('announce')],
["clear ", client.user.permMap.chan.get('clearChat')],
["kick ", client.user.permMap.chan.get('kickUser')],
]
},
localCMD:{
prefix: '/',
postfix: '',
cmds: [
["high ", true]
]
},
usernames:{
prefix: '',
postfix: '',
cmds: injectPerms(Array.from(client.userList.colorMap.keys()))
},
emotes:{
prefix:'[',
postfix:']',
cmds: injectPerms(this.getEmoteNames())
}
};
//return our dictionary object
return dictionary;
function injectPerms(cmds, perm = true){
//Create empty array to hold cmds
let cmdSet = [];
//For each cmd
for(let cmd of cmds){
//Add the cmd with its perm to the cmdset
cmdSet.push([cmd, perm]);
}
//return the cmd set
return cmdSet;
}
}
}
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]});
}
}
}