Implement basic tag system
This commit is contained in:
parent
828443cb93
commit
9b48d41bd8
18
README.md
18
README.md
|
@ -20,10 +20,12 @@ Brandon's server originally used MongoDB for storing user data, but there are to
|
||||||
|
|
||||||
- Chat
|
- Chat
|
||||||
- Original chat filter by chacha and Brandon Lockaby
|
- Original chat filter by chacha and Brandon Lockaby
|
||||||
|
- Commands for debugging or administrative purposes
|
||||||
- Piano Notes
|
- Piano Notes
|
||||||
- Uses the same `NoteQuota` implementation from the client
|
- Uses the same `NoteQuota` implementation from the client
|
||||||
- Usernames/colors
|
- 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
|
- Channels
|
||||||
- Channel list
|
- Channel list
|
||||||
- Channel settings
|
- Channel settings
|
||||||
|
@ -37,20 +39,32 @@ Brandon's server originally used MongoDB for storing user data, but there are to
|
||||||
- Chat muting
|
- Chat muting
|
||||||
- Rate limit bypasses
|
- Rate limit bypasses
|
||||||
- Channel/User-targeted notifications
|
- Channel/User-targeted notifications
|
||||||
|
- Server-wide/channel-specific/user-specific notifications
|
||||||
- New admin messages
|
- New admin messages
|
||||||
- Restart message
|
- Restart message
|
||||||
- Triggers notification on every connected socket, then shuts down after 20 seconds
|
- 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
|
- Ability to change tags
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
- Fully implement and test tags
|
- 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
|
- Redo all of the validations with Zod
|
||||||
- This probably means making Zod schemas for every single message type
|
- This probably means making Zod schemas for every single message type
|
||||||
- Also user and channel data
|
- Also user and channel data
|
||||||
- Test every frontend
|
- Test every frontend
|
||||||
- Test fishing bot
|
- Test fishing bot
|
||||||
|
- Remote console
|
||||||
|
|
||||||
## Backlog/Notes
|
## Backlog/Notes
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,17 @@ defaultFlags:
|
||||||
|
|
||||||
# Whether or not to allow users to change their color.
|
# 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.
|
# 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.
|
# Allows custom data inside note messages.
|
||||||
# This was in the original server, but not in MPP.net's server.
|
# 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.
|
# This only exists for backwards compatibility with scripts like nagalun's drarwing script.
|
||||||
enableCustomNoteData: true
|
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.
|
# This is the user data that the server will use to send admin chat messages with.
|
||||||
adminParticipant:
|
adminParticipant:
|
||||||
_id: "0"
|
_id: "0"
|
||||||
|
@ -27,11 +31,11 @@ enableAdminEval: true
|
||||||
|
|
||||||
# The token validation scheme. Valid values are "none", "jwt" and "uuid".
|
# 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".
|
# 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".
|
# The browser challenge scheme. Valid values are "none", "obf" and "basic".
|
||||||
# This is to change what is sent in the "b" message.
|
# This is to change what is sent in the "b" message.
|
||||||
# "none" will disable the browser challenge,
|
# "none" will disable the browser challenge,
|
||||||
# "obf" will sent an obfuscated function to the client,
|
# "obf" will sent an obfuscated function to the client,
|
||||||
# and "basic" will just send a simple function that expects a boolean.
|
# and "basic" will just send a simple function that expects a boolean.
|
||||||
browserChallenge: none
|
browserChallenge: basic
|
||||||
|
|
|
@ -88,7 +88,7 @@ export class Channel extends EventEmitter {
|
||||||
(typeof set.color2 == "undefined" ||
|
(typeof set.color2 == "undefined" ||
|
||||||
set.color2 === this.settings.color2)
|
set.color2 === this.settings.color2)
|
||||||
) {
|
) {
|
||||||
this.logger.debug("color 2 darken triggered");
|
//this.logger.debug("color 2 darken triggered");
|
||||||
set.color2 = darken(set.color);
|
set.color2 = darken(set.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ export class Channel extends EventEmitter {
|
||||||
|
|
||||||
// Set the verified settings
|
// Set the verified settings
|
||||||
for (const key of Object.keys(validatedSet)) {
|
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;
|
if ((validatedSet as any)[key] === false) continue;
|
||||||
(this.settings as any)[key] = (set as any)[key];
|
(this.settings as any)[key] = (set as any)[key];
|
||||||
}
|
}
|
||||||
|
@ -480,7 +480,7 @@ export class Channel extends EventEmitter {
|
||||||
if (this.isTrueLobby()) {
|
if (this.isTrueLobby()) {
|
||||||
// Get the next lobby number
|
// Get the next lobby number
|
||||||
const nextID = this.getNextLobbyID();
|
const nextID = this.getNextLobbyID();
|
||||||
this.logger.debug("New ID:", nextID);
|
//this.logger.debug("New ID:", nextID);
|
||||||
// Move them to the next lobby
|
// Move them to the next lobby
|
||||||
return socket.setChannel(nextID);
|
return socket.setChannel(nextID);
|
||||||
}
|
}
|
||||||
|
@ -691,7 +691,7 @@ export class Channel extends EventEmitter {
|
||||||
name: p.name,
|
name: p.name,
|
||||||
color: p.color,
|
color: p.color,
|
||||||
id: p.id,
|
id: p.id,
|
||||||
tag: config.sendTags ? p.tag : undefined
|
tag: usersConfig.enableTags ? p.tag : undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
25
src/index.ts
25
src/index.ts
|
@ -20,31 +20,6 @@ loadForcedStartupChannels();
|
||||||
|
|
||||||
// This literally breaks editors and they stick all the imports here instead of at the top
|
// This literally breaks editors and they stick all the imports here instead of at the top
|
||||||
import "./util/readline";
|
import "./util/readline";
|
||||||
import { Socket, socketsBySocketID } from "./ws/Socket";
|
|
||||||
import { createSocketID } from "./util/id";
|
|
||||||
|
|
||||||
// Nevermind we use it twice
|
// Nevermind we use it twice
|
||||||
logger.info("Ready");
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
|
import EventEmitter from "events";
|
||||||
import { padNum, unimportant } from "./helpers";
|
import { padNum, unimportant } from "./helpers";
|
||||||
|
|
||||||
|
export const logEvents = new EventEmitter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A logger that doesn't fuck with the readline prompt
|
* A logger that doesn't fuck with the readline prompt
|
||||||
* timestamps are likely wrong because of js timezones
|
* timestamps are likely wrong because of js timezones
|
||||||
**/
|
**/
|
||||||
export class Logger {
|
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[]) {
|
private static log(method: string, ...args: any[]) {
|
||||||
// Un-fuck the readline prompt
|
// Un-fuck the readline prompt
|
||||||
process.stdout.write("\x1b[2K\r");
|
process.stdout.write("\x1b[2K\r");
|
||||||
|
@ -21,8 +30,15 @@ export class Logger {
|
||||||
// Re-print the readline prompt (spooky cringe global variable)
|
// Re-print the readline prompt (spooky cringe global variable)
|
||||||
if ((globalThis as unknown as any).rl)
|
if ((globalThis as unknown as any).rl)
|
||||||
(globalThis as unknown as any).rl.prompt();
|
(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() {
|
public static getHHMMSSMS() {
|
||||||
const ms = Date.now();
|
const ms = Date.now();
|
||||||
|
|
||||||
|
@ -38,24 +54,44 @@ export class Logger {
|
||||||
return `${hh}:${mm}:${ss}.${ll}`;
|
return `${hh}:${mm}:${ss}.${ll}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current date in ISO format
|
||||||
|
* @returns The current date in ISO format
|
||||||
|
**/
|
||||||
public static getDate() {
|
public static getDate() {
|
||||||
return new Date().toISOString().split("T")[0];
|
return new Date().toISOString().split("T")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(public id: string) { }
|
constructor(public id: string) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print an info message
|
||||||
|
* @param args The data to print
|
||||||
|
**/
|
||||||
public info(...args: any[]) {
|
public info(...args: any[]) {
|
||||||
Logger.log("log", `[${this.id}]`, `\x1b[34m[info]\x1b[0m`, ...args);
|
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[]) {
|
public error(...args: any[]) {
|
||||||
Logger.log("error", `[${this.id}]`, `\x1b[31m[error]\x1b[0m`, ...args);
|
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[]) {
|
public warn(...args: any[]) {
|
||||||
Logger.log("warn", `[${this.id}]`, `\x1b[33m[warn]\x1b[0m`, ...args);
|
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[]) {
|
public debug(...args: any[]) {
|
||||||
Logger.log("debug", `[${this.id}]`, `\x1b[32m[debug]\x1b[0m`, ...args);
|
Logger.log("debug", `[${this.id}]`, `\x1b[32m[debug]\x1b[0m`, ...args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ import {
|
||||||
ServerEvents,
|
ServerEvents,
|
||||||
UserFlags,
|
UserFlags,
|
||||||
Vector2,
|
Vector2,
|
||||||
Notification
|
Notification,
|
||||||
|
Tag
|
||||||
} from "../util/types";
|
} from "../util/types";
|
||||||
import type { User } from "@prisma/client";
|
import type { User } from "@prisma/client";
|
||||||
import { createUser, readUser, updateUser } from "../data/user";
|
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 {
|
return {
|
||||||
_id: facadeID,
|
_id: facadeID,
|
||||||
name: this.user.name,
|
name: this.user.name,
|
||||||
color: this.user.color,
|
color: this.user.color,
|
||||||
id: this.getParticipantID()
|
id: this.getParticipantID(),
|
||||||
|
tag: config.enableTags ? tag : undefined
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
|
@ -744,6 +754,7 @@ export class Socket extends EventEmitter {
|
||||||
* @param color Color of the tag
|
* @param color Color of the tag
|
||||||
**/
|
**/
|
||||||
public setTag(text: string, color: string) {
|
public setTag(text: string, color: string) {
|
||||||
|
//logger.debug("Setting tag:", text, color);
|
||||||
const user = this.getUser();
|
const user = this.getUser();
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
user.tag = JSON.stringify({ text, color });
|
user.tag = JSON.stringify({ text, color });
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { name } from "./handlers/name";
|
||||||
import { notification } from "./handlers/notification";
|
import { notification } from "./handlers/notification";
|
||||||
import { rename_channel } from "./handlers/rename_channel";
|
import { rename_channel } from "./handlers/rename_channel";
|
||||||
import { restart } from "./handlers/restart";
|
import { restart } from "./handlers/restart";
|
||||||
|
import { tag } from "./handlers/tag";
|
||||||
import { user_flag } from "./handlers/user_flag";
|
import { user_flag } from "./handlers/user_flag";
|
||||||
|
|
||||||
// EVENT_GROUP_ADMIN.add(color);
|
// EVENT_GROUP_ADMIN.add(color);
|
||||||
|
@ -27,7 +28,8 @@ EVENT_GROUP_ADMIN.addMany(
|
||||||
move,
|
move,
|
||||||
rename_channel,
|
rename_channel,
|
||||||
admin_chat,
|
admin_chat,
|
||||||
eval_msg
|
eval_msg,
|
||||||
|
tag
|
||||||
);
|
);
|
||||||
|
|
||||||
eventGroups.push(EVENT_GROUP_ADMIN);
|
eventGroups.push(EVENT_GROUP_ADMIN);
|
||||||
|
|
|
@ -65,10 +65,13 @@ export const hi: ServerEventListener<"hi"> = {
|
||||||
_id: socket.getUserID(),
|
_id: socket.getUserID(),
|
||||||
name: "Anonymous",
|
name: "Anonymous",
|
||||||
color: "#777",
|
color: "#777",
|
||||||
id: ""
|
id: "",
|
||||||
|
tag: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug("Tag:", part.tag);
|
||||||
|
|
||||||
socket.sendArray([{
|
socket.sendArray([{
|
||||||
m: "hi",
|
m: "hi",
|
||||||
accountInfo: undefined,
|
accountInfo: undefined,
|
||||||
|
@ -77,7 +80,8 @@ export const hi: ServerEventListener<"hi"> = {
|
||||||
u: {
|
u: {
|
||||||
_id: part._id,
|
_id: part._id,
|
||||||
color: part.color,
|
color: part.color,
|
||||||
name: part.name
|
name: part.name,
|
||||||
|
tag: part.tag
|
||||||
},
|
},
|
||||||
motd: getMOTD(),
|
motd: getMOTD(),
|
||||||
token
|
token
|
||||||
|
|
|
@ -6,6 +6,7 @@ export interface UsersConfig {
|
||||||
defaultFlags: UserFlags;
|
defaultFlags: UserFlags;
|
||||||
enableColorChanging: boolean;
|
enableColorChanging: boolean;
|
||||||
enableCustomNoteData: boolean;
|
enableCustomNoteData: boolean;
|
||||||
|
enableTags: boolean;
|
||||||
adminParticipant: Participant;
|
adminParticipant: Participant;
|
||||||
enableAdminEval: boolean;
|
enableAdminEval: boolean;
|
||||||
tokenAuth: "jwt" | "uuid" | "none";
|
tokenAuth: "jwt" | "uuid" | "none";
|
||||||
|
@ -21,6 +22,7 @@ export const defaultUsersConfig: UsersConfig = {
|
||||||
},
|
},
|
||||||
enableColorChanging: false,
|
enableColorChanging: false,
|
||||||
enableCustomNoteData: true,
|
enableCustomNoteData: true,
|
||||||
|
enableTags: false,
|
||||||
adminParticipant: {
|
adminParticipant: {
|
||||||
_id: "0",
|
_id: "0",
|
||||||
name: "mpp",
|
name: "mpp",
|
||||||
|
|
Loading…
Reference in New Issue