diff --git a/src/app/channel/commandProcessor.js b/src/app/channel/commandProcessor.js index 0a157a3..34959a1 100644 --- a/src/app/channel/commandProcessor.js +++ b/src/app/channel/commandProcessor.js @@ -17,6 +17,7 @@ along with this program. If not, see .*/ //Local Imports const permissionModel = require('../../schemas/permissionSchema'); const channelModel = require('../../schemas/channel/channelSchema'); +const {userModel} = require('../../schemas/user/userSchema'); /** * Class representing global server-side chat/command processing logic @@ -113,6 +114,62 @@ class commandProcessor{ return true } + async ignore(commandObj){ + //If we wherent given a user to ignore + if(commandObj.argumentArray[1] == null){ + //Silently return + return; + } + + //Attempt to find user DB doc object + const userDB = await userModel.findOne({user: commandObj.socket.user.user}); + + //Prevent missing users + if(userDB == null){ + throw loggerUtils.exceptionSmith("Called from invalid user", "validation"); + } + + //ignore user + await userDB.ignoreByUser(commandObj.argumentArray[1]); + + //Update all users sockets on every channel + this.server.crawlConnections(commandObj.socket.user.user, (user)=>{ + //syncrhonously fire and forget ignored user list + user.sendIgnoredList(userDB); + }); + + //disable send flag + return false; + } + + async unignore(commandObj){ + //If we wherent given a user to ignore + if(commandObj.argumentArray[1] == null){ + //Silently return + return; + } + + //Attempt to find user DB doc object + const userDB = await userModel.findOne({user: commandObj.socket.user.user}); + + //Prevent missing users + if(userDB == null){ + throw loggerUtils.exceptionSmith("Called from invalid user", "validation"); + } + + //ignore user + await userDB.unignoreByUser(commandObj.argumentArray[1]); + + //Update all users sockets on every channel + this.server.crawlConnections(commandObj.socket.user.user, (user)=>{ + //syncrhonously fire and forget ignored user list + user.sendIgnoredList(userDB); + }); + + //disable send flag + return false; + } + /** * Command Processor method to handle the '!announce' command * @param {Object} commandObj - Object representing a single given command/chat request diff --git a/src/app/channel/connectedUser.js b/src/app/channel/connectedUser.js index d0b09ae..e1c5e47 100644 --- a/src/app/channel/connectedUser.js +++ b/src/app/channel/connectedUser.js @@ -216,6 +216,7 @@ class connectedUser{ chan: Array.from(this.permMap.chan), }, flair: this.flair, + ignored: (await userDB.getIgnoredUsers()).user } //For each flair listed in the Database @@ -332,6 +333,21 @@ class connectedUser{ this.channel.broadcastUserList(); this.sendClientMetadata(); } + + /** + * Sends updated ignored list to a client + * @param {Mongoose.Document} userDB - Users DB Document + */ + async sendIgnoredList(userDB){ + //if we wherent handed a user document + if(userDB == null){ + //Pull it based on user name + userDB = await userModel.findOne({user: this.user}); + } + + //Retrieve and send updated ignored users list to clients socket + this.emit("ignore", (await userDB.getIgnoredUsers()).user); + } } module.exports = connectedUser; \ No newline at end of file diff --git a/src/schemas/user/userSchema.js b/src/schemas/user/userSchema.js index f013a07..5139e99 100644 --- a/src/schemas/user/userSchema.js +++ b/src/schemas/user/userSchema.js @@ -153,6 +153,10 @@ const userSchema = new mongoose.Schema({ type: mongoose.SchemaTypes.ObjectID, ref: "user" }], + ignored:[{ + type: mongoose.SchemaTypes.ObjectID, + ref: "user", + }], playlists: [playlistSchema] }); @@ -662,6 +666,136 @@ userSchema.methods.deleteEmote = async function(name){ } } +/** + * Ignores a given user by their username + * @param {Mongoose.Document} userDB - DB Document of user to ignore + */ +userSchema.methods.ignoreByUser = async function(user){ + //Pull user doc from DB + const userDB = await this.constructor.findOne({user}); + //Do a ignorem user by username + await this.ignoreByUserDoc(userDB); +} + +/** + * Ignores a given user by their DB Document + * @param {Mongoose.Document} userDB - DB Document of user to ignore + */ +userSchema.methods.ignoreByUserDoc = async function(userDB){ + //Prevent missing users + if(userDB == null){ + throw loggerUtils.exceptionSmith("User not found", "validation"); + } + + //if someone is trying to self-ignore + if(userDB._id.equals(this._id)){ + //Remind them of the brutal truth that is reality + throw loggerUtils.exceptionSmith("YOU CANNOT ESCAPE THE PRISON THAT IS YOUR MIND!!!", "validation"); + } + + if(this.ignored.includes(userDB._id)){ + throw loggerUtils.exceptionSmith("User already ignored", "validation"); + } + + //Push user into ignored array + this.ignored.push(userDB._id); + + //Save the DB document + await this.save(); + + console.log(await this.getIgnoredUsers()); +} + +/** + * un-ignores a given user by their username + * @param {Mongoose.Document} userDB - DB Document of user to ignore + */ +userSchema.methods.unignoreByUser = async function(user){ + //Pull user doc from DB + const userDB = await this.constructor.findOne({user}); + //Do an unignorem user by username + await this.unignoreByUserDoc(userDB); +} + +/** + * un-ignores a given user by their DB Document + * @param {Mongoose.Document} userDB - DB Document of user to ignore + */ +userSchema.methods.unignoreByUserDoc = async function(userDB){ + //Prevent missing users + if(userDB == null){ + throw loggerUtils.exceptionSmith("User not found", "validation"); + } + + //prevent double un-ignoring + if(!this.ignored.includes(userDB._id)){ + throw loggerUtils.exceptionSmith("User not ignored", "validation"); + } + + //Filter user out of ignored array + this.ignored = this.ignored.filter((id)=>{return !id.equals(userDB._id)}); + + //Save the DB document + await this.save(); + + console.log(await this.getIgnoredUsers()); +} + +userSchema.methods.getIgnoredUsers = async function(fetchAlts = true){ + //Not me commiting ancient fucking sin + const _this = this; + + //Populate all ignored users + await this.populate('ignored'); + + //If we're fetching alts + if(fetchAlts){ + //Populate ignored users alts + await this.populate('ignored.alts'); + } + + //Create return object to hold list of DB ids and public user IDs/names + //Code using this method typically only need either/or, so seperating them from da get-go was the easiest option. + const ignored = { + _id: [], + user: [] + } + + //Iterate through all ignored users + for(const ignoredUser of this.ignored){ + //Push user to ignored list + pushToList(ignoredUser); + + //If we're fetching alts + if(fetchAlts){ + //Iterate through ignored users alts + for(const ignoredAlt of ignoredUser.alts){ + //Push alt to ignored list + pushToList(ignoredAlt); + } + } + } + + //Return list of ignored users + return ignored; + + function pushToList(ignoredUser){ + //If we haven't already added this user, and it isn't us + //ignoring our own alts seem silly at first, but remember 1 IP =/= one user. + //VPNs, CGNAT, and even household disagreements all happen in da real world. + if(!ignored._id.includes(ignoredUser._id) && !ignoredUser._id.equals(_this._id)){ + //Push DB _id into list for server-side use + ignored._id.push(ignoredUser._id); + //Push ignored username/outward id combo for client usage + ignored.user.push({ + user: ignoredUser.user, + id: ignoredUser.id + }); + } + } + +} + /** * Salts, Hashes and Tattoo's IP address to user document in a privacy respecting manner * @param {String} ip - Plaintext IP Address to Salt, Hash and Tattoo diff --git a/www/js/channel/channel.js b/www/js/channel/channel.js index f519cbe..1682269 100644 --- a/www/js/channel/channel.js +++ b/www/js/channel/channel.js @@ -129,7 +129,9 @@ class channel{ this.socket.on("archive", (data) =>{ this.sortQueue(data.archive); - }) + }); + + this.socket.on("ignore", this.handleIgnoredList.bind(this)); } /** @@ -169,6 +171,21 @@ class channel{ this.streamType = data.streamType; } + /** + * Updated ignore list + * @param {Array} data - Ignore list received from server + */ + handleIgnoredList(data){ + //Update ignored users list + this.user.ignored = data; + + //If we have the chatbox loaded in + if(this.chatBox != null){ + //Re-render buffer + this.chatBox.rerenderBuffer(); + } + } + /** * Sorts queue, optionally adding new items along the way * @param {object} queueData - Contains list of entries to add to the queue diff --git a/www/js/channel/chat.js b/www/js/channel/chat.js index c05ec8c..867776d 100644 --- a/www/js/channel/chat.js +++ b/www/js/channel/chat.js @@ -229,6 +229,16 @@ class chatBox{ * @param {Object} data De-hydrated chat object from server */ displayChat(data){ + + //For every douchebag in the ignored users list + for(const douchebag of this.client.user.ignored){ + //If this message is from the douchebag in question + if(data.user == douchebag.user){ + //tell it to fuck off and die + return; + } + } + //Append the post-processed chat-body to the chat buffer this.chatBuffer.appendChild(this.chatPostprocessor.postprocess(data)); diff --git a/www/js/channel/userlist.js b/www/js/channel/userlist.js index 89abe90..4b372ff 100644 --- a/www/js/channel/userlist.js +++ b/www/js/channel/userlist.js @@ -196,6 +196,26 @@ class userList{ ]); if(user.user != "Tokebot" && user.user != this.client.user.user){ + let ignored = false; + + //For each douchebag in the ignored users list + for(const douchebag of this.client.user.ignored){ + if(douchebag.user == user.user){ + /* Normally I abhor making if statements to set booleans, but this only need to happen when its true + Plus I also needed to put the break statement somewhere + The fucked thing is the break statement isnt necissary with the if statement, but without it it becomes necissary + But it cant be used without the if statement, making the task of reducing linecount for this definition a fucking sisyphean nightmare */ + ignored = true; + break; + } + } + + if(ignored){ + menuMap.set("Unignore", ()=>{this.client.chatBox.transmit(`!unignore ${user.user}`);}); + }else{ + menuMap.set("Ignore", ()=>{this.client.chatBox.transmit(`!ignore ${user.user}`);}); + } + if(this.client.user.permMap.chan.get("kickUser")){ menuMap.set("Kick", ()=>{this.client.chatBox.transmit(`!kick ${user.user}`);}); }