diff --git a/README.md b/README.md index c24b999..141def6 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,12 @@ Brandon's server originally used MongoDB for storing user data, but there are to - Chat - Original chat filter by chacha and Brandon Lockaby + - Commands for debugging or administrative purposes - Piano Notes - Uses the same `NoteQuota` implementation from the client - Usernames/colors - - Allowing color changing can be toggled in the config + - Allowing color changing can be toggled in the config, similar to MPP.com + - Default user parameters can be set - Channels - Channel list - Channel settings @@ -37,20 +39,32 @@ Brandon's server originally used MongoDB for storing user data, but there are to - Chat muting - Rate limit bypasses - Channel/User-targeted notifications + - Server-wide/channel-specific/user-specific notifications - New admin messages - Restart message - Triggers notification on every connected socket, then shuts down after 20 seconds - - Server must be setup as a pm2/docker/systemd process + - Server must be setup as a pm2/docker/systemd process for automatic restarting - Ability to change tags ## TODO - Fully implement and test tags + - Tags are sent to clients now + - Check if tags are sent to everyone +- Permission groups and permissions + - Probable permission groups: owner, admin, mod, trialmod, default + - Setup tags for each permission group +- Full server-wide event bus + - Channel events + - Socket events + - User data events + - Permission-related events - Redo all of the validations with Zod - This probably means making Zod schemas for every single message type - Also user and channel data - Test every frontend - Test fishing bot +- Remote console ## Backlog/Notes diff --git a/config/users.yml b/config/users.yml index 31ec330..3218941 100644 --- a/config/users.yml +++ b/config/users.yml @@ -7,13 +7,17 @@ defaultFlags: # Whether or not to allow users to change their color. # Brandon's server has this set to false, but multiple users have reported it to be on before 2016. -enableColorChanging: false +enableColorChanging: true # Allows custom data inside note messages. # This was in the original server, but not in MPP.net's server. # This only exists for backwards compatibility with scripts like nagalun's drarwing script. enableCustomNoteData: true +# Whether or not to enable tags that are sent publicly. +# This won't prevent admins from changing tags internally, but it will not be sent to clients. +enableTags: true + # This is the user data that the server will use to send admin chat messages with. adminParticipant: _id: "0" @@ -27,11 +31,11 @@ enableAdminEval: true # The token validation scheme. Valid values are "none", "jwt" and "uuid". # This server will still validate existing tokens generated with other schemes if not set to "none". -tokenAuth: none +tokenAuth: jwt # The browser challenge scheme. Valid values are "none", "obf" and "basic". # This is to change what is sent in the "b" message. # "none" will disable the browser challenge, # "obf" will sent an obfuscated function to the client, # and "basic" will just send a simple function that expects a boolean. -browserChallenge: none +browserChallenge: basic diff --git a/src/channel/Channel.ts b/src/channel/Channel.ts index beb797f..9d0c6a7 100644 --- a/src/channel/Channel.ts +++ b/src/channel/Channel.ts @@ -88,7 +88,7 @@ export class Channel extends EventEmitter { (typeof set.color2 == "undefined" || set.color2 === this.settings.color2) ) { - this.logger.debug("color 2 darken triggered"); + //this.logger.debug("color 2 darken triggered"); set.color2 = darken(set.color); } @@ -97,7 +97,7 @@ export class Channel extends EventEmitter { // Set the verified settings for (const key of Object.keys(validatedSet)) { - this.logger.debug(`${key}: ${(validatedSet as any)[key]}`); + //this.logger.debug(`${key}: ${(validatedSet as any)[key]}`); if ((validatedSet as any)[key] === false) continue; (this.settings as any)[key] = (set as any)[key]; } @@ -480,7 +480,7 @@ export class Channel extends EventEmitter { if (this.isTrueLobby()) { // Get the next lobby number const nextID = this.getNextLobbyID(); - this.logger.debug("New ID:", nextID); + //this.logger.debug("New ID:", nextID); // Move them to the next lobby return socket.setChannel(nextID); } @@ -691,7 +691,7 @@ export class Channel extends EventEmitter { name: p.name, color: p.color, id: p.id, - tag: config.sendTags ? p.tag : undefined + tag: usersConfig.enableTags ? p.tag : undefined }); } diff --git a/src/index.ts b/src/index.ts index 7ee320f..de0060b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -20,31 +20,6 @@ loadForcedStartupChannels(); // This literally breaks editors and they stick all the imports here instead of at the top import "./util/readline"; -import { Socket, socketsBySocketID } from "./ws/Socket"; -import { createSocketID } from "./util/id"; // Nevermind we use it twice logger.info("Ready"); - -// Connect 5 sockets -const sockets = []; - -for (let i = 0; i < 5; i++) { - setTimeout(() => { - const socket = new Socket(undefined, createSocketID()); - - socket.on("ready", () => { - logger.info(`Socket ${i} is ready`); - }); - - socket.setChannel("lobby"); - - sockets.push(socket); - - if (socket.socketID == undefined) { - socket.socketID = createSocketID(); - } - - socketsBySocketID.set(socket.socketID, socket); - }, i * 5000); -} diff --git a/src/util/Logger.ts b/src/util/Logger.ts index d8044e2..d07c7e5 100644 --- a/src/util/Logger.ts +++ b/src/util/Logger.ts @@ -1,10 +1,19 @@ +import EventEmitter from "events"; import { padNum, unimportant } from "./helpers"; +export const logEvents = new EventEmitter(); + /** * A logger that doesn't fuck with the readline prompt * timestamps are likely wrong because of js timezones **/ export class Logger { + /** + * Log a message + * This does weird prompt things + * @param method The method from `console` to use + * @param args The data to print + **/ private static log(method: string, ...args: any[]) { // Un-fuck the readline prompt process.stdout.write("\x1b[2K\r"); @@ -21,8 +30,15 @@ export class Logger { // Re-print the readline prompt (spooky cringe global variable) if ((globalThis as unknown as any).rl) (globalThis as unknown as any).rl.prompt(); + + // Emit the log event for remote consoles + logEvents.emit("log", method, unimportant(this.getDate()), unimportant(this.getHHMMSSMS()), args); } + /** + * Get the current time in HH:MM:SS.MS format + * @returns The current time in HH:MM:SS.MS format + **/ public static getHHMMSSMS() { const ms = Date.now(); @@ -38,24 +54,44 @@ export class Logger { return `${hh}:${mm}:${ss}.${ll}`; } + /** + * Get the current date in ISO format + * @returns The current date in ISO format + **/ public static getDate() { return new Date().toISOString().split("T")[0]; } constructor(public id: string) { } + /** + * Print an info message + * @param args The data to print + **/ public info(...args: any[]) { Logger.log("log", `[${this.id}]`, `\x1b[34m[info]\x1b[0m`, ...args); } + /** + * Print an error message + * @param args The data to print + **/ public error(...args: any[]) { Logger.log("error", `[${this.id}]`, `\x1b[31m[error]\x1b[0m`, ...args); } + /** + * Print a warning message + * @param args The data to print + **/ public warn(...args: any[]) { Logger.log("warn", `[${this.id}]`, `\x1b[33m[warn]\x1b[0m`, ...args); } + /** + * Print a debug message + * @param args The data to print + **/ public debug(...args: any[]) { Logger.log("debug", `[${this.id}]`, `\x1b[32m[debug]\x1b[0m`, ...args); } diff --git a/src/ws/Socket.ts b/src/ws/Socket.ts index 3a7d3a2..7de985d 100644 --- a/src/ws/Socket.ts +++ b/src/ws/Socket.ts @@ -14,7 +14,8 @@ import { ServerEvents, UserFlags, Vector2, - Notification + Notification, + Tag } from "../util/types"; import type { User } from "@prisma/client"; import { createUser, readUser, updateUser } from "../data/user"; @@ -355,11 +356,20 @@ export class Socket extends EventEmitter { } } + let tag: Tag | undefined; + + try { + tag = JSON.parse(this.user.tag) as Tag; + } catch (err) { + logger.warn("Unable to parse tag:", err); + } + return { _id: facadeID, name: this.user.name, color: this.user.color, - id: this.getParticipantID() + id: this.getParticipantID(), + tag: config.enableTags ? tag : undefined }; } else { return null; @@ -744,6 +754,7 @@ export class Socket extends EventEmitter { * @param color Color of the tag **/ public setTag(text: string, color: string) { + //logger.debug("Setting tag:", text, color); const user = this.getUser(); if (!user) return; user.tag = JSON.stringify({ text, color }); diff --git a/src/ws/events/admin/index.ts b/src/ws/events/admin/index.ts index 383d7f0..a09632c 100644 --- a/src/ws/events/admin/index.ts +++ b/src/ws/events/admin/index.ts @@ -11,6 +11,7 @@ import { name } from "./handlers/name"; import { notification } from "./handlers/notification"; import { rename_channel } from "./handlers/rename_channel"; import { restart } from "./handlers/restart"; +import { tag } from "./handlers/tag"; import { user_flag } from "./handlers/user_flag"; // EVENT_GROUP_ADMIN.add(color); @@ -27,7 +28,8 @@ EVENT_GROUP_ADMIN.addMany( move, rename_channel, admin_chat, - eval_msg + eval_msg, + tag ); eventGroups.push(EVENT_GROUP_ADMIN); diff --git a/src/ws/events/user/handlers/hi.ts b/src/ws/events/user/handlers/hi.ts index 964c2cb..694d851 100644 --- a/src/ws/events/user/handlers/hi.ts +++ b/src/ws/events/user/handlers/hi.ts @@ -65,10 +65,13 @@ export const hi: ServerEventListener<"hi"> = { _id: socket.getUserID(), name: "Anonymous", color: "#777", - id: "" + id: "", + tag: undefined }; } + logger.debug("Tag:", part.tag); + socket.sendArray([{ m: "hi", accountInfo: undefined, @@ -77,7 +80,8 @@ export const hi: ServerEventListener<"hi"> = { u: { _id: part._id, color: part.color, - name: part.name + name: part.name, + tag: part.tag }, motd: getMOTD(), token diff --git a/src/ws/usersConfig.ts b/src/ws/usersConfig.ts index dd64545..707c145 100644 --- a/src/ws/usersConfig.ts +++ b/src/ws/usersConfig.ts @@ -6,6 +6,7 @@ export interface UsersConfig { defaultFlags: UserFlags; enableColorChanging: boolean; enableCustomNoteData: boolean; + enableTags: boolean; adminParticipant: Participant; enableAdminEval: boolean; tokenAuth: "jwt" | "uuid" | "none"; @@ -21,6 +22,7 @@ export const defaultUsersConfig: UsersConfig = { }, enableColorChanging: false, enableCustomNoteData: true, + enableTags: false, adminParticipant: { _id: "0", name: "mpp",