From 2f3311be1ff92b1b830536477373a206d5e08290 Mon Sep 17 00:00:00 2001 From: Hri7566 Date: Thu, 1 Feb 2024 08:25:24 -0500 Subject: [PATCH] Add kickban, improve command line --- config/ratelimits.yml | 2 + src/channel/Channel.ts | 77 +++++++++++++++++++++++++- src/channel/ChannelList.ts | 8 +++ src/util/id.ts | 3 +- src/util/readline/commands.ts | 44 +++++++++++++-- src/ws/Socket.ts | 17 +++++- src/ws/events/user/handlers/kickban.ts | 11 ++++ src/ws/events/user/index.ts | 4 +- 8 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 src/ws/events/user/handlers/kickban.ts diff --git a/config/ratelimits.yml b/config/ratelimits.yml index 3dcbf17..b46174f 100644 --- a/config/ratelimits.yml +++ b/config/ratelimits.yml @@ -11,6 +11,7 @@ crown: normal: a: 600 m: 50 + ch: 1000 chains: userset: interval: 1800000, @@ -19,6 +20,7 @@ admin: normal: a: 120 m: 16.6666666667 + ch: 100 chains: userset: interval: 1800000, diff --git a/src/channel/Channel.ts b/src/channel/Channel.ts index 1668294..b5136d8 100644 --- a/src/channel/Channel.ts +++ b/src/channel/Channel.ts @@ -1,4 +1,4 @@ -import EventEmitter from "events"; +import EventEmitter, { on } from "events"; import { Logger } from "../util/Logger"; import { ChannelSettingValue, @@ -15,12 +15,19 @@ import Crown from "./Crown"; import { ChannelList } from "./ChannelList"; import { config } from "./config"; +interface CachedKickban { + userId: string; + startTime: number; + endTime: number; +} + export class Channel extends EventEmitter { private settings: Partial = config.defaultSettings; private ppl = new Array(); public logger: Logger; public chatHistory = new Array(); + public bans = new Array(); public crown?: Crown; @@ -233,7 +240,7 @@ export class Channel extends EventEmitter { * @param socket Socket that is joining * @returns undefined */ - public join(socket: Socket) { + public join(socket: Socket): void { //! /!\ Players are forced to join the same channel on two different tabs! //? TODO Should this be a bug or a feature? @@ -243,6 +250,18 @@ export class Channel extends EventEmitter { let hasChangedChannel = false; let oldChannelID = socket.currentChannelID; + // Is user banned? + if (this.isBanned(part._id)) { + // Send user to ban channel instead + // TODO Send notification for ban + const chs = ChannelList.getList(); + for (const ch of chs) { + if (ch.getID() == config.fullChannel) { + return ch.join(socket); + } + } + } + // Is user in this channel? if (this.hasUser(part._id)) { // Alreay in channel, don't add to list, but tell them they're here @@ -370,10 +389,11 @@ export class Channel extends EventEmitter { * Get this channel's information * @returns Channel info object (includes ID, number of users, settings, and the crown) */ - public getInfo() { + public getInfo(_id?: string) { return { _id: this.getID(), id: this.getID(), + banned: _id ? this.isBanned(_id) : false, count: this.ppl.length, settings: this.settings, crown: this.crown @@ -561,6 +581,57 @@ export class Channel extends EventEmitter { this.emit("update", this); } } + + /** + * Kickban a poor soul for t milliseconds. + * @param _id User ID to ban + * @param t Time in millseconds to ban for + **/ + public kickban(_id: string, t: number = 1000 * 60 * 30) { + const now = Date.now(); + + if (!this.hasUser(_id)) return; + + const part = this.ppl.find(p => p._id == _id); + if (!part) return; + + this.bans.push({ + userId: _id, + startTime: now, + endTime: now + t + }); + + const socket = findSocketByPartID(part.id); + if (!socket) return; + + const banChannel = ChannelList.getList().find( + ch => ch.getID() == config.fullChannel + ); + + if (!banChannel) return; + banChannel.join(socket); + + this.emit("update", this); + } + + public isBanned(_id: string) { + const now = Date.now(); + + for (const ban of this.bans) { + if (ban.endTime <= now) { + // Remove old ban and skip + this.bans.splice(this.bans.indexOf(ban), 1); + continue; + } + + // Check if they are banned + if (ban.userId == _id) { + return true; + } + } + + return false; + } } export default Channel; diff --git a/src/channel/ChannelList.ts b/src/channel/ChannelList.ts index 86dbbfb..dd1c36c 100644 --- a/src/channel/ChannelList.ts +++ b/src/channel/ChannelList.ts @@ -18,6 +18,14 @@ const onChannelUpdate = (channel: Channel) => { return; } + const part = channel.getParticipantList().find(p => p.id == partId); + + if (part) { + info.banned = channel.isBanned(part._id); + } else { + info.banned = false; + } + socket.sendChannelList([info], false); } }; diff --git a/src/util/id.ts b/src/util/id.ts index 7af38a4..cfda859 100644 --- a/src/util/id.ts +++ b/src/util/id.ts @@ -39,8 +39,7 @@ export function createColor(ip: string) { createHash("sha256") .update(ip) .update(env.SALT) - .update("color") .digest("hex") - .substring(0, 24 + 6) + .substring(24, 24 + 6) ); } diff --git a/src/util/readline/commands.ts b/src/util/readline/commands.ts index cfda747..7433dae 100644 --- a/src/util/readline/commands.ts +++ b/src/util/readline/commands.ts @@ -1,16 +1,33 @@ +import { ChannelList } from "../../channel/ChannelList"; +import { deleteUser } from "../../data/user"; import Command from "./Command"; Command.addCommand( new Command(["help", "h", "commands", "cmds"], "help", msg => { - return ( - "Commands: " + - Command.commands.map(cmd => cmd.aliases[0]).join(" | ") - ); + if (!msg.args[1]) { + return ( + "Commands: " + + Command.commands.map(cmd => cmd.aliases[0]).join(" | ") + ); + } else { + let foundCommand: Command | undefined; + + for (const command of Command.commands) { + for (const alias of command.aliases) { + if (msg.args[1] == alias) { + foundCommand = command; + } + } + } + + if (!foundCommand) return `No such command "${msg.args[1]}"`; + return "Usage: " + foundCommand.usage; + } }) ); Command.addCommand( - new Command(["memory", "mem"], "mem", msg => { + new Command(["memory", "mem"], "memory", msg => { const mem = process.memoryUsage(); return `Memory: ${(mem.heapUsed / 1000 / 1000).toFixed(2)} MB used / ${( mem.heapTotal / @@ -25,3 +42,20 @@ Command.addCommand( process.exit(); }) ); + +Command.addCommand( + new Command(["userdel", "deluser"], "userdel ", async msg => { + await deleteUser(msg.args[1]); + }) +); + +Command.addCommand( + new Command(["list", "ls"], "list", async msg => { + return ( + "Channels:\n- " + + ChannelList.getList() + .map(ch => ch.getID()) + .join("\n- ") + ); + }) +); diff --git a/src/ws/Socket.ts b/src/ws/Socket.ts index 5516e58..d290adb 100644 --- a/src/ws/Socket.ts +++ b/src/ws/Socket.ts @@ -55,7 +55,10 @@ export class Socket extends EventEmitter { public currentChannelID: string | undefined; private cursorPos: Vector2 = { x: 200, y: 100 }; - constructor(private ws: ServerWebSocket, public socketID: string) { + constructor( + private ws: ServerWebSocket, + public socketID: string + ) { super(); this.ip = ws.remoteAddress; // Participant ID @@ -406,7 +409,7 @@ export class Socket extends EventEmitter { public subscribeToChannelList() { ChannelList.subscribe(this.id); - const firstList = ChannelList.getPublicList().map(v => v.getInfo()); + const firstList = ChannelList.getPublicList().map(v => v.getInfo(this._id)); this.sendChannelList(firstList); } @@ -445,6 +448,16 @@ export class Socket extends EventEmitter { return true; } + + public kickban(_id: string, ms: number) { + const channel = this.getCurrentChannel(); + + if (!channel) return; + + if (this.isOwner()) { + channel.kickban(_id, ms); + } + } } export const socketsBySocketID = new Map(); diff --git a/src/ws/events/user/handlers/kickban.ts b/src/ws/events/user/handlers/kickban.ts new file mode 100644 index 0000000..331399a --- /dev/null +++ b/src/ws/events/user/handlers/kickban.ts @@ -0,0 +1,11 @@ +import { ServerEventListener } from "../../../../util/types"; + +export const kickban: ServerEventListener<"kickban"> = { + id: "kickban", + callback: (msg, socket) => { + // Kickban asshole from channel + if (!msg._id) return; + if (!msg.ms) return; + socket.kickban(msg._id, msg.ms); + } +}; diff --git a/src/ws/events/user/index.ts b/src/ws/events/user/index.ts index 2bd6cce..1589f9a 100644 --- a/src/ws/events/user/index.ts +++ b/src/ws/events/user/index.ts @@ -13,6 +13,7 @@ import { plus_ls } from "./handlers/+ls"; import { minus_ls } from "./handlers/-ls"; import { admin_message } from "./handlers/admin_message"; import { chset } from "./handlers/chset"; +import { kickban } from "./handlers/kickban"; // Imagine not having an "addMany" function... @@ -40,7 +41,8 @@ EVENTGROUP_USER.addMany( plus_ls, minus_ls, admin_message, - chset + chset, + kickban ); eventGroups.push(EVENTGROUP_USER);