forked from Hri7566/mpp-server-dev2
Update everything
This commit is contained in:
parent
8644f3787e
commit
275ad3823f
|
@ -16,3 +16,8 @@ model User {
|
||||||
color String @default("#ffffff")
|
color String @default("#ffffff")
|
||||||
flags String @default("{}") // JSON flags object
|
flags String @default("{}") // JSON flags object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model ChatHistory {
|
||||||
|
id String @id @unique @map("_id")
|
||||||
|
messages String @default("[]") // JSON messages
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
ServerEvents,
|
ServerEvents,
|
||||||
IChannelInfo
|
IChannelInfo
|
||||||
} from "../util/types";
|
} from "../util/types";
|
||||||
import { Socket } from "../ws/Socket";
|
import type { Socket } from "../ws/Socket";
|
||||||
import { validateChannelSettings } from "./settings";
|
import { validateChannelSettings } from "./settings";
|
||||||
import { findSocketByPartID, socketsBySocketID } from "../ws/Socket";
|
import { findSocketByPartID, socketsBySocketID } from "../ws/Socket";
|
||||||
import Crown from "./Crown";
|
import Crown from "./Crown";
|
||||||
|
@ -185,8 +185,6 @@ 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("Setting color 2 from first color:", set.color);
|
|
||||||
this.logger.debug("Red:", parseInt(set.color.substring(1, 2), 16));
|
|
||||||
const r = Math.max(
|
const r = Math.max(
|
||||||
0,
|
0,
|
||||||
parseInt(set.color.substring(1, 3), 16) - 0x40
|
parseInt(set.color.substring(1, 3), 16) - 0x40
|
||||||
|
@ -201,7 +199,6 @@ export class Channel extends EventEmitter {
|
||||||
);
|
);
|
||||||
|
|
||||||
set.color2 = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
set.color2 = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
||||||
this.logger.debug("Color 2 is now:", set.color2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isLobby() && !admin) return;
|
if (this.isLobby() && !admin) return;
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { type Socket, findSocketByPartID } from "../ws/Socket";
|
||||||
import type Channel from "./Channel";
|
import type Channel from "./Channel";
|
||||||
|
|
||||||
const onChannelUpdate = (channel: Channel) => {
|
const onChannelUpdate = (channel: Channel) => {
|
||||||
|
// If this shit ever manages to handle over 10 people I'd be impressed
|
||||||
const info = channel.getInfo();
|
const info = channel.getInfo();
|
||||||
// const ppl = channel.getParticipantList();
|
// const ppl = channel.getParticipantList();
|
||||||
if (info.settings.visible !== true) return;
|
if (info.settings.visible !== true) return;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Participant, Vector2 } from "../util/types";
|
import { Participant, Vector2 } from "../util/types";
|
||||||
import { Socket } from "../ws/Socket";
|
import { Socket } from "../ws/Socket";
|
||||||
|
|
||||||
|
// shiny hat
|
||||||
export class Crown {
|
export class Crown {
|
||||||
public userId: string | undefined;
|
public userId: string | undefined;
|
||||||
public participantId: string | undefined;
|
public participantId: string | undefined;
|
||||||
|
|
|
@ -27,6 +27,7 @@ export const config = loadConfig<ChannelConfig>("config/channels.yml", {
|
||||||
color2: "#001014",
|
color2: "#001014",
|
||||||
visible: true
|
visible: true
|
||||||
},
|
},
|
||||||
|
// Here's a terrifying fact: Brandon used parseInt to check lobby names in the OG server code
|
||||||
lobbyRegexes: ["^lobby[0-9][0-9]$", "^lobby[1-9]$", "^test/.+$"],
|
lobbyRegexes: ["^lobby[0-9][0-9]$", "^lobby[1-9]$", "^test/.+$"],
|
||||||
lobbyBackdoor: "lolwutsecretlobbybackdoor",
|
lobbyBackdoor: "lolwutsecretlobbybackdoor",
|
||||||
fullChannel: "test/awkward"
|
fullChannel: "test/awkward"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { Channel } from "./Channel";
|
import { Channel } from "./Channel";
|
||||||
import { config } from "./config";
|
import { config } from "./config";
|
||||||
|
|
||||||
|
// This shit was moved here to try to fix the unit tests segfaulting but it didn't work
|
||||||
|
|
||||||
// Channel forceloader (cringe)
|
// Channel forceloader (cringe)
|
||||||
let hasFullChannel = false;
|
let hasFullChannel = false;
|
||||||
|
|
||||||
|
|
|
@ -4,4 +4,6 @@ import validateChannelSettings, {
|
||||||
validate as validateChannelSetting
|
validate as validateChannelSetting
|
||||||
} from "./settings";
|
} from "./settings";
|
||||||
|
|
||||||
|
// I don't even know if I bothered to use this file
|
||||||
|
|
||||||
export { Channel, Crown, validateChannelSettings, validateChannelSetting };
|
export { Channel, Crown, validateChannelSettings, validateChannelSetting };
|
||||||
|
|
|
@ -3,6 +3,8 @@ import { IChannelSettings } from "../util/types";
|
||||||
|
|
||||||
type Validator = "boolean" | "string" | "number" | ((val: unknown) => boolean);
|
type Validator = "boolean" | "string" | "number" | ((val: unknown) => boolean);
|
||||||
|
|
||||||
|
// This record contains the exact data Brandon used to check channel settings, down to the regex.
|
||||||
|
// It also contains things that might be useful to other people in the future (things that make me vomit)
|
||||||
const validationRecord: Record<keyof IChannelSettings, Validator> = {
|
const validationRecord: Record<keyof IChannelSettings, Validator> = {
|
||||||
// Brandon
|
// Brandon
|
||||||
lobby: "boolean",
|
lobby: "boolean",
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* by Hri7566
|
* by Hri7566
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// If you don't load the server first, bun will literally segfault
|
||||||
import "./ws/server";
|
import "./ws/server";
|
||||||
import "./channel/forceLoad";
|
import "./channel/forceLoad";
|
||||||
import { Logger } from "./util/Logger";
|
import { Logger } from "./util/Logger";
|
||||||
|
@ -12,4 +13,5 @@ const logger = new Logger("Main");
|
||||||
|
|
||||||
import "./util/readline";
|
import "./util/readline";
|
||||||
|
|
||||||
|
// Does this really even need to be here?
|
||||||
logger.info("Ready");
|
logger.info("Ready");
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class Logger {
|
||||||
...args
|
...args
|
||||||
);
|
);
|
||||||
|
|
||||||
// Fix the readline prompt (spooky code)
|
// Fix the readline prompt (spooky cringe code)
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,16 @@ import { existsSync, readFileSync, writeFileSync } from "fs";
|
||||||
import { parse, stringify } from "yaml";
|
import { parse, stringify } from "yaml";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file uses the synchronous functions from the fs
|
||||||
|
* module because these functions should only be used
|
||||||
|
* to load configs at the beginning of runtime
|
||||||
|
*
|
||||||
|
* Hint: This means you shouldn't load configs in the
|
||||||
|
* middle of doing other shit, only when you start the
|
||||||
|
* program.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a YAML config file and set default values if config path is nonexistent
|
* Load a YAML config file and set default values if config path is nonexistent
|
||||||
*
|
*
|
||||||
|
@ -64,6 +74,7 @@ export function loadConfig<T>(configPath: string, defaultConfig: T): T {
|
||||||
* @param config
|
* @param config
|
||||||
*/
|
*/
|
||||||
export function writeConfig<T>(configPath: string, config: T) {
|
export function writeConfig<T>(configPath: string, config: T) {
|
||||||
|
// Write config to disk unconditionally
|
||||||
writeFileSync(
|
writeFileSync(
|
||||||
configPath,
|
configPath,
|
||||||
stringify(config, {
|
stringify(config, {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { createEnv } from "@t3-oss/env-core";
|
import { createEnv } from "@t3-oss/env-core";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// Best way to do env ever
|
||||||
export const env = createEnv({
|
export const env = createEnv({
|
||||||
server: {
|
server: {
|
||||||
PORT: z.coerce.number(),
|
PORT: z.coerce.number(),
|
||||||
|
@ -9,6 +10,7 @@ export const env = createEnv({
|
||||||
ADMIN_PASS: z.string()
|
ADMIN_PASS: z.string()
|
||||||
},
|
},
|
||||||
isServer: true,
|
isServer: true,
|
||||||
|
// Bun loads process.env automatically so we don't have to use modules
|
||||||
runtimeEnv: process.env
|
runtimeEnv: process.env
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -40,3 +40,20 @@ export function darken(hex: string) {
|
||||||
.padStart(2, "0")}${newB.toString(16).padStart(2, "0")}`;
|
.padStart(2, "0")}${newB.toString(16).padStart(2, "0")}`;
|
||||||
} catch (err) {}
|
} catch (err) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Brandon made this literally eons ago and it's fucking hilarious
|
||||||
|
export function spoop_text(message: string) {
|
||||||
|
var old = message;
|
||||||
|
message = "";
|
||||||
|
for (var i = 0; i < old.length; i++) {
|
||||||
|
if (Math.random() < 0.9) {
|
||||||
|
message += String.fromCharCode(
|
||||||
|
old.charCodeAt(i) + Math.floor(Math.random() * 20 - 10)
|
||||||
|
);
|
||||||
|
//message[i] = String.fromCharCode(Math.floor(Math.random() * 255));
|
||||||
|
} else {
|
||||||
|
message += old[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
import { createHash, randomBytes } from "crypto";
|
import { createHash, randomBytes } from "crypto";
|
||||||
import env from "./env";
|
import env from "./env";
|
||||||
|
import { spoop_text } from "./helpers";
|
||||||
|
|
||||||
export function createID() {
|
export function createID() {
|
||||||
return randomBytes(12).toString("hex");
|
// Maybe I could make this funnier than it needs to be...
|
||||||
|
// return randomBytes(12).toString("hex");
|
||||||
|
|
||||||
|
let weirdness = "";
|
||||||
|
while (weirdness.length < 24) {
|
||||||
|
const time = new Date().toString();
|
||||||
|
const randomShit = spoop_text(time); // looks like this: We]%Cau&:,\u0018403*"32>8,B15&GP[2)='7\u0019-@etyhlw\u0017QqXqiime&Khhe-
|
||||||
|
|
||||||
|
let index1 = Math.floor(Math.random() * randomShit.length);
|
||||||
|
let index2 = Math.floor(Math.random() * randomShit.length);
|
||||||
|
|
||||||
|
weirdness += randomShit.substring(index1, index2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 12 bytes
|
||||||
|
return Buffer.from(weirdness.substring(0, 12)).toString("hex");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createUserID(ip: string) {
|
export function createUserID(ip: string) {
|
||||||
|
@ -25,6 +41,6 @@ export function createColor(ip: string) {
|
||||||
.update(env.SALT)
|
.update(env.SALT)
|
||||||
.update("color")
|
.update("color")
|
||||||
.digest("hex")
|
.digest("hex")
|
||||||
.substring(0, 6)
|
.substring(0, 24 + 6)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// bruh
|
||||||
export function getMOTD() {
|
export function getMOTD() {
|
||||||
return "This site makes a lot of sound! You may want to adjust the volume before continuing.";
|
return "This site makes a lot of sound! You may want to adjust the volume before continuing.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,5 @@ rl.on("SIGINT", () => {
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fucking cringe but it works
|
||||||
(globalThis as unknown as any).rl = rl;
|
(globalThis as unknown as any).rl = rl;
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
|
/**
|
||||||
|
* Typedefs
|
||||||
|
*/
|
||||||
|
|
||||||
import { Socket } from "../ws/Socket";
|
import { Socket } from "../ws/Socket";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Halfway through this file, the same types have appeared again
|
||||||
|
*
|
||||||
|
* I am not a decent enough person to go looking down there, so
|
||||||
|
* good luck when you get there. Somehow I forgot what is even
|
||||||
|
* in this file, so don't come and ask me why something isn't
|
||||||
|
* defined correctly here.
|
||||||
|
*/
|
||||||
|
|
||||||
declare type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
declare type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||||
|
|
||||||
declare type UserFlags = Partial<{
|
declare type UserFlags = Partial<{
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
/**
|
||||||
|
* I made this thing to keep track of what sockets
|
||||||
|
* have and haven't done yet so we know if they
|
||||||
|
* should be doing certain things.
|
||||||
|
*
|
||||||
|
* For instance, being logged in in the first place,
|
||||||
|
* or if they're on shitty McDonalds WiFi and they
|
||||||
|
* lost connection for over a minute, or if they
|
||||||
|
* decided that they're going to put their browser
|
||||||
|
* in a chokehold and force it to load weird shit...
|
||||||
|
* or, you know, maybe I could log their user agent
|
||||||
|
* and IP address instead sometime in the future.
|
||||||
|
*/
|
||||||
export class Gateway {
|
export class Gateway {
|
||||||
public hasProcessedHi: boolean = false;
|
public hasProcessedHi: boolean = false;
|
||||||
public hasSentDevices: boolean = false;
|
public hasSentDevices: boolean = false;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
UserFlags,
|
UserFlags,
|
||||||
Vector2
|
Vector2
|
||||||
} from "../util/types";
|
} from "../util/types";
|
||||||
import { User } from "@prisma/client";
|
import type { User } from "@prisma/client";
|
||||||
import { createUser, readUser, updateUser } from "../data/user";
|
import { createUser, readUser, updateUser } from "../data/user";
|
||||||
import { eventGroups } from "./events";
|
import { eventGroups } from "./events";
|
||||||
import { Gateway } from "./Gateway";
|
import { Gateway } from "./Gateway";
|
||||||
|
@ -55,10 +55,7 @@ export class Socket extends EventEmitter {
|
||||||
public currentChannelID: string | undefined;
|
public currentChannelID: string | undefined;
|
||||||
private cursorPos: Vector2<CursorValue> = { x: 200, y: 100 };
|
private cursorPos: Vector2<CursorValue> = { x: 200, y: 100 };
|
||||||
|
|
||||||
constructor(
|
constructor(private ws: ServerWebSocket<unknown>, public socketID: string) {
|
||||||
private ws: ServerWebSocket<unknown>,
|
|
||||||
public socketID: string
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
this.ip = ws.remoteAddress; // Participant ID
|
this.ip = ws.remoteAddress; // Participant ID
|
||||||
|
|
||||||
|
@ -432,6 +429,22 @@ export class Socket extends EventEmitter {
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isOwner() {
|
||||||
|
const channel = this.getCurrentChannel();
|
||||||
|
const part = this.getParticipant();
|
||||||
|
|
||||||
|
// this looks cool
|
||||||
|
if (!channel) return false;
|
||||||
|
if (!channel.crown) return false;
|
||||||
|
if (!channel.crown.userId) return false;
|
||||||
|
if (!channel.crown.participantId) return false;
|
||||||
|
if (!part) return;
|
||||||
|
if (!part.id) return;
|
||||||
|
if (channel.crown.participantId !== part.id) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const socketsBySocketID = new Map<string, Socket>();
|
export const socketsBySocketID = new Map<string, Socket>();
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
// Bun hoists import so we are kinda forced to use require here...
|
// Bun hoists import so we are kinda forced to use require here...
|
||||||
// Maybe bun should have a setting for that :/
|
// Maybe bun should have a setting for that :/
|
||||||
|
|
||||||
|
// This feels really dirty and months have passed... guess who's fixing it?
|
||||||
|
// Nobody is. It's going to stay like this.
|
||||||
|
|
||||||
require("./events/user");
|
require("./events/user");
|
||||||
require("./events/admin");
|
require("./events/admin");
|
||||||
|
|
|
@ -8,6 +8,10 @@ export class EventGroup {
|
||||||
this.eventList.push(listener);
|
this.eventList.push(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addMany(...listeners: ServerEventListener<any>[]) {
|
||||||
|
listeners.forEach(l => this.add(l));
|
||||||
|
}
|
||||||
|
|
||||||
public remove(listener: ServerEventListener<any>) {
|
public remove(listener: ServerEventListener<any>) {
|
||||||
this.eventList.splice(this.eventList.indexOf(listener), 1);
|
this.eventList.splice(this.eventList.indexOf(listener), 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { findSocketsByUserID } from "../../../Socket";
|
||||||
export const color: ServerEventListener<"color"> = {
|
export const color: ServerEventListener<"color"> = {
|
||||||
id: "color",
|
id: "color",
|
||||||
callback: async (msg, socket) => {
|
callback: async (msg, socket) => {
|
||||||
// TODO color
|
// I'm not allowed to use this feature on MPP.net for a really stupid reason
|
||||||
const id = msg._id;
|
const id = msg._id;
|
||||||
const color = msg.color;
|
const color = msg.color;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { findSocketsByUserID } from "../../../Socket";
|
||||||
export const name: ServerEventListener<"name"> = {
|
export const name: ServerEventListener<"name"> = {
|
||||||
id: "name",
|
id: "name",
|
||||||
callback: async (msg, socket) => {
|
callback: async (msg, socket) => {
|
||||||
|
// Change someone else's name but it's an annoying admin feature
|
||||||
const id = msg._id;
|
const id = msg._id;
|
||||||
const name = msg.name;
|
const name = msg.name;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { findSocketsByUserID } from "../../../Socket";
|
||||||
export const user_flag: ServerEventListener<"user_flag"> = {
|
export const user_flag: ServerEventListener<"user_flag"> = {
|
||||||
id: "user_flag",
|
id: "user_flag",
|
||||||
callback: async (msg, socket) => {
|
callback: async (msg, socket) => {
|
||||||
|
// User flag modification (changing some real specific shit)
|
||||||
if (typeof msg._id !== "string") return;
|
if (typeof msg._id !== "string") return;
|
||||||
if (typeof msg.key !== "string") return;
|
if (typeof msg.key !== "string") return;
|
||||||
if (typeof msg.value == "undefined") return;
|
if (typeof msg.value == "undefined") return;
|
||||||
|
|
|
@ -6,8 +6,10 @@ import { color } from "./handlers/color";
|
||||||
import { name } from "./handlers/name";
|
import { name } from "./handlers/name";
|
||||||
import { user_flag } from "./handlers/user_flag";
|
import { user_flag } from "./handlers/user_flag";
|
||||||
|
|
||||||
EVENT_GROUP_ADMIN.add(color);
|
// EVENT_GROUP_ADMIN.add(color);
|
||||||
EVENT_GROUP_ADMIN.add(name);
|
// EVENT_GROUP_ADMIN.add(name);
|
||||||
EVENT_GROUP_ADMIN.add(user_flag);
|
// EVENT_GROUP_ADMIN.add(user_flag);
|
||||||
|
|
||||||
|
EVENT_GROUP_ADMIN.addMany(color, name, user_flag);
|
||||||
|
|
||||||
eventGroups.push(EVENT_GROUP_ADMIN);
|
eventGroups.push(EVENT_GROUP_ADMIN);
|
||||||
|
|
|
@ -3,6 +3,15 @@ import { ServerEventListener } from "../../../../util/types";
|
||||||
export const plus_ls: ServerEventListener<"+ls"> = {
|
export const plus_ls: ServerEventListener<"+ls"> = {
|
||||||
id: "+ls",
|
id: "+ls",
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
|
// Give us the latest news on literally everything
|
||||||
|
// that's happening in the server. In fact, I want
|
||||||
|
// to know when someone clicks a button instantly,
|
||||||
|
// so I can stalk other users by watching the room
|
||||||
|
// count go up somewhere else when I watch someone
|
||||||
|
// leave the channel I'm reading their messages in
|
||||||
|
// and when I see their cursor disappear I'll know
|
||||||
|
// precisely where they went to follow them and to
|
||||||
|
// annoy them in chat when I see them again.
|
||||||
socket.subscribeToChannelList();
|
socket.subscribeToChannelList();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { ServerEventListener } from "../../../../util/types";
|
||||||
export const minus_ls: ServerEventListener<"-ls"> = {
|
export const minus_ls: ServerEventListener<"-ls"> = {
|
||||||
id: "-ls",
|
id: "-ls",
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
|
// Stop giving us the latest server forecast
|
||||||
socket.unsubscribeFromChannelList();
|
socket.unsubscribeFromChannelList();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,11 +7,15 @@ export const a: ServerEventListener<"a"> = {
|
||||||
const flags = socket.getUserFlags();
|
const flags = socket.getUserFlags();
|
||||||
if (!flags) return;
|
if (!flags) return;
|
||||||
|
|
||||||
|
// Why did I write this statement so weird
|
||||||
if (!flags["no chat rate limit"] || flags["no chat rate limit"] == 0)
|
if (!flags["no chat rate limit"] || flags["no chat rate limit"] == 0)
|
||||||
if (!socket.rateLimits?.normal.a.attempt()) return;
|
if (!socket.rateLimits?.normal.a.attempt()) return;
|
||||||
const ch = socket.getCurrentChannel();
|
const ch = socket.getCurrentChannel();
|
||||||
if (!ch) return;
|
if (!ch) return;
|
||||||
|
|
||||||
|
// msg.m
|
||||||
|
// Permission denied: msg.m
|
||||||
|
// sudo msg.m
|
||||||
ch.emit("message", msg, socket);
|
ch.emit("message", msg, socket);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,8 @@ export const admin_message: ServerEventListener<"admin message"> = {
|
||||||
if (typeof msg.password !== "string") return;
|
if (typeof msg.password !== "string") return;
|
||||||
if (msg.password !== env.ADMIN_PASS) return;
|
if (msg.password !== env.ADMIN_PASS) return;
|
||||||
|
|
||||||
|
// Probably shouldn't be using password auth in 2024
|
||||||
|
// Maybe I'll setup a dashboard instead some day
|
||||||
socket.admin.emit(msg.msg.m, msg.msg, socket, true);
|
socket.admin.emit(msg.msg.m, msg.msg, socket, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const ch: ServerEventListener<"ch"> = {
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
// Switch channel
|
// Switch channel
|
||||||
if (!msg._id) return;
|
if (!msg._id) return;
|
||||||
|
|
||||||
|
// So technical and convoluted...
|
||||||
socket.setChannel(msg._id, msg.set);
|
socket.setChannel(msg._id, msg.set);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,8 +5,11 @@ export const chset: ServerEventListener<"chset"> = {
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
// Change channel settings
|
// Change channel settings
|
||||||
if (typeof msg.set == "undefined") return;
|
if (typeof msg.set == "undefined") return;
|
||||||
|
|
||||||
const ch = socket.getCurrentChannel();
|
const ch = socket.getCurrentChannel();
|
||||||
if (!ch) return;
|
if (!ch) return;
|
||||||
|
|
||||||
|
// Edit room now
|
||||||
ch.changeSettings(msg.set, false);
|
ch.changeSettings(msg.set, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { ServerEventListener } from "../../../../util/types";
|
||||||
export const devices: ServerEventListener<"devices"> = {
|
export const devices: ServerEventListener<"devices"> = {
|
||||||
id: "devices",
|
id: "devices",
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
// List of MIDI Devices
|
// List of MIDI Devices (or software ports, because nobody can tell the difference)
|
||||||
if (socket.gateway.hasSentDevices) return;
|
if (socket.gateway.hasSentDevices) return;
|
||||||
socket.gateway.hasSentDevices = true;
|
socket.gateway.hasSentDevices = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,9 @@ export const hi: ServerEventListener<"hi"> = {
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
// Handshake message
|
// Handshake message
|
||||||
// TODO Hi message tokens
|
// TODO Hi message tokens
|
||||||
|
// I'm not actually sure if I'm up for doing tokens,
|
||||||
|
// but if someone wants to submit a pull request, I
|
||||||
|
// look forward to watching you do all the work
|
||||||
if (socket.gateway.hasProcessedHi) return;
|
if (socket.gateway.hasProcessedHi) return;
|
||||||
let part = socket.getParticipant();
|
let part = socket.getParticipant();
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,18 @@ export const m: ServerEventListener<"m"> = {
|
||||||
id: "m",
|
id: "m",
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
// Cursor movement
|
// Cursor movement
|
||||||
if (!socket.rateLimits?.normal.m.attempt()) return;
|
if (!socket.rateLimits) return;
|
||||||
|
if (!socket.rateLimits.normal.m.attempt()) return;
|
||||||
if (!msg.x || !msg.y) return;
|
if (!msg.x || !msg.y) return;
|
||||||
|
|
||||||
let x = msg.x;
|
let x = msg.x;
|
||||||
let y = msg.y;
|
let y = msg.y;
|
||||||
|
|
||||||
|
// Make it numbers
|
||||||
if (typeof msg.x == "string") x = parseFloat(msg.x);
|
if (typeof msg.x == "string") x = parseFloat(msg.x);
|
||||||
if (typeof msg.y == "string") y = parseFloat(msg.y);
|
if (typeof msg.y == "string") y = parseFloat(msg.y);
|
||||||
|
|
||||||
|
// Move the laggy piece of shit
|
||||||
socket.setCursorPos(x, y);
|
socket.setCursorPos(x, y);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,10 @@ export const n: ServerEventListener<"n"> = {
|
||||||
if (!Array.isArray(msg.n)) return;
|
if (!Array.isArray(msg.n)) return;
|
||||||
if (typeof msg.t !== "number") return;
|
if (typeof msg.t !== "number") return;
|
||||||
|
|
||||||
|
// This should've been here months ago
|
||||||
|
const channel = socket.getCurrentChannel();
|
||||||
|
if (!channel) return;
|
||||||
|
|
||||||
// Check note properties
|
// Check note properties
|
||||||
for (const n of msg.n) {
|
for (const n of msg.n) {
|
||||||
if (typeof n.n != "string") return;
|
if (typeof n.n != "string") return;
|
||||||
|
@ -29,9 +33,15 @@ export const n: ServerEventListener<"n"> = {
|
||||||
|
|
||||||
let amount = msg.n.length;
|
let amount = msg.n.length;
|
||||||
|
|
||||||
// TODO Check crownsolo
|
const crownsolo = channel.getSetting("crownsolo");
|
||||||
|
|
||||||
|
if ((crownsolo && socket.isOwner()) || !crownsolo) {
|
||||||
|
// Shiny hat exists and we have shiny hat
|
||||||
|
// or there is no shiny hat
|
||||||
if (socket.noteQuota.spend(amount)) {
|
if (socket.noteQuota.spend(amount)) {
|
||||||
|
// make noise
|
||||||
socket.playNotes(msg);
|
socket.playNotes(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,6 +8,7 @@ export const t: ServerEventListener<"t"> = {
|
||||||
if (typeof msg.e !== "number") return;
|
if (typeof msg.e !== "number") return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pong!
|
||||||
socket.sendArray([
|
socket.sendArray([
|
||||||
{
|
{
|
||||||
m: "t",
|
m: "t",
|
||||||
|
|
|
@ -5,6 +5,15 @@ export const userset: ServerEventListener<"userset"> = {
|
||||||
callback: (msg, socket) => {
|
callback: (msg, socket) => {
|
||||||
// Change username/color
|
// Change username/color
|
||||||
if (!socket.rateLimits?.chains.userset.attempt()) return;
|
if (!socket.rateLimits?.chains.userset.attempt()) return;
|
||||||
|
// You can disable color in the config because
|
||||||
|
// Brandon's/jacored's server doesn't allow color changes,
|
||||||
|
// and that's the OG server, but folks over at MPP.net
|
||||||
|
// said otherwise because they're dumb roleplayers
|
||||||
|
// or something and don't understand the unique value
|
||||||
|
// of the fishing bot and how it allows you to change colors
|
||||||
|
// without much control, giving it the feeling of value...
|
||||||
|
// Kinda reminds me of crypto.
|
||||||
|
// Also, Brandon's server had color changing on before.
|
||||||
if (!msg.set.name && !msg.set.color) return;
|
if (!msg.set.name && !msg.set.color) return;
|
||||||
socket.userset(msg.set.name, msg.set.color);
|
socket.userset(msg.set.name, msg.set.color);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,16 +14,33 @@ import { minus_ls } from "./handlers/-ls";
|
||||||
import { admin_message } from "./handlers/admin_message";
|
import { admin_message } from "./handlers/admin_message";
|
||||||
import { chset } from "./handlers/chset";
|
import { chset } from "./handlers/chset";
|
||||||
|
|
||||||
EVENTGROUP_USER.add(hi);
|
// Imagine not having an "addMany" function...
|
||||||
EVENTGROUP_USER.add(devices);
|
|
||||||
EVENTGROUP_USER.add(ch);
|
// EVENTGROUP_USER.add(hi);
|
||||||
EVENTGROUP_USER.add(m);
|
// EVENTGROUP_USER.add(devices);
|
||||||
EVENTGROUP_USER.add(a);
|
// EVENTGROUP_USER.add(ch);
|
||||||
EVENTGROUP_USER.add(userset);
|
// EVENTGROUP_USER.add(m);
|
||||||
EVENTGROUP_USER.add(n);
|
// EVENTGROUP_USER.add(a);
|
||||||
EVENTGROUP_USER.add(plus_ls);
|
// EVENTGROUP_USER.add(userset);
|
||||||
EVENTGROUP_USER.add(minus_ls);
|
// EVENTGROUP_USER.add(n);
|
||||||
EVENTGROUP_USER.add(admin_message);
|
// EVENTGROUP_USER.add(plus_ls);
|
||||||
EVENTGROUP_USER.add(chset);
|
// EVENTGROUP_USER.add(minus_ls);
|
||||||
|
// EVENTGROUP_USER.add(admin_message);
|
||||||
|
// EVENTGROUP_USER.add(chset);
|
||||||
|
|
||||||
|
// Imagine it looks exactly the same and calls the same function underneath
|
||||||
|
EVENTGROUP_USER.addMany(
|
||||||
|
hi,
|
||||||
|
devices,
|
||||||
|
ch,
|
||||||
|
m,
|
||||||
|
a,
|
||||||
|
userset,
|
||||||
|
n,
|
||||||
|
plus_ls,
|
||||||
|
minus_ls,
|
||||||
|
admin_message,
|
||||||
|
chset
|
||||||
|
);
|
||||||
|
|
||||||
eventGroups.push(EVENTGROUP_USER);
|
eventGroups.push(EVENTGROUP_USER);
|
||||||
|
|
|
@ -2,7 +2,9 @@ import { Logger } from "../util/Logger";
|
||||||
import { Socket } from "./Socket";
|
import { Socket } from "./Socket";
|
||||||
import { hasOwn } from "../util/helpers";
|
import { hasOwn } from "../util/helpers";
|
||||||
|
|
||||||
const logger = new Logger("Message Handler");
|
// const logger = new Logger("Message Handler");
|
||||||
|
// this one sounds cooler
|
||||||
|
const logger = new Logger("The Messenger");
|
||||||
|
|
||||||
export function handleMessage(socket: Socket, text: string) {
|
export function handleMessage(socket: Socket, text: string) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This is some convoluted dark magic I copied from some old mpp server I wrote
|
||||||
|
// No fucking clue where it came from or how it works internally, but I typedefized it
|
||||||
|
// It's just a bunch of rate limits in a chain... like a RateLimitChain...... hmmmm.......
|
||||||
export class NoteQuota {
|
export class NoteQuota {
|
||||||
public allowance = 8000;
|
public allowance = 8000;
|
||||||
public max = 24000;
|
public max = 24000;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Thank you Brandon for this thing
|
||||||
export class RateLimit {
|
export class RateLimit {
|
||||||
public after: number = 0;
|
public after: number = 0;
|
||||||
constructor(private interval_ms: number = 0) {}
|
constructor(private interval_ms: number = 0) {}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { RateLimit } from "./RateLimit";
|
import { RateLimit } from "./RateLimit";
|
||||||
|
|
||||||
|
// Thank you Brandon for this other thing
|
||||||
export class RateLimitChain {
|
export class RateLimitChain {
|
||||||
public chain: RateLimit[] = [];
|
public chain: RateLimit[] = [];
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,8 @@ export const config = loadConfig<RateLimitsConfig>("config/ratelimits.yml", {
|
||||||
crown: {
|
crown: {
|
||||||
normal: {
|
normal: {
|
||||||
a: 6000 / 10,
|
a: 6000 / 10,
|
||||||
m: 1000 / 20
|
m: 1000 / 20,
|
||||||
|
ch: 1000 / 1
|
||||||
},
|
},
|
||||||
chains: {
|
chains: {
|
||||||
userset: {
|
userset: {
|
||||||
|
@ -59,7 +60,8 @@ export const config = loadConfig<RateLimitsConfig>("config/ratelimits.yml", {
|
||||||
admin: {
|
admin: {
|
||||||
normal: {
|
normal: {
|
||||||
a: 6000 / 50,
|
a: 6000 / 50,
|
||||||
m: 1000 / 60
|
m: 1000 / 60,
|
||||||
|
ch: 1000 / 10
|
||||||
},
|
},
|
||||||
chains: {
|
chains: {
|
||||||
userset: {
|
userset: {
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { RateLimitConstructorList, config } from "../config";
|
||||||
export const adminLimits: RateLimitConstructorList = {
|
export const adminLimits: RateLimitConstructorList = {
|
||||||
normal: {
|
normal: {
|
||||||
a: () => new RateLimit(config.admin.normal.a),
|
a: () => new RateLimit(config.admin.normal.a),
|
||||||
m: () => new RateLimit(config.admin.normal.m)
|
m: () => new RateLimit(config.admin.normal.m),
|
||||||
|
ch: () => new RateLimit(config.admin.normal.ch)
|
||||||
},
|
},
|
||||||
chains: {
|
chains: {
|
||||||
userset: () =>
|
userset: () =>
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { RateLimitConstructorList, config } from "../config";
|
||||||
export const crownLimits: RateLimitConstructorList = {
|
export const crownLimits: RateLimitConstructorList = {
|
||||||
normal: {
|
normal: {
|
||||||
a: () => new RateLimit(config.crown.normal.a),
|
a: () => new RateLimit(config.crown.normal.a),
|
||||||
m: () => new RateLimit(config.crown.normal.m)
|
m: () => new RateLimit(config.crown.normal.m),
|
||||||
|
ch: () => new RateLimit(config.crown.normal.ch)
|
||||||
},
|
},
|
||||||
chains: {
|
chains: {
|
||||||
userset: () =>
|
userset: () =>
|
||||||
|
|
|
@ -2,6 +2,9 @@ import { RateLimit } from "../RateLimit";
|
||||||
import { RateLimitChain } from "../RateLimitChain";
|
import { RateLimitChain } from "../RateLimitChain";
|
||||||
import { RateLimitConstructorList, config } from "../config";
|
import { RateLimitConstructorList, config } from "../config";
|
||||||
|
|
||||||
|
// I have no idea why these things exist but I made it apparently
|
||||||
|
// All it does it construct the rate limits from the config instead
|
||||||
|
// of using random numbers I found on the internet
|
||||||
export const userLimits: RateLimitConstructorList = {
|
export const userLimits: RateLimitConstructorList = {
|
||||||
normal: {
|
normal: {
|
||||||
a: () => new RateLimit(config.user.normal.a),
|
a: () => new RateLimit(config.user.normal.a),
|
||||||
|
|
|
@ -10,7 +10,15 @@ import nunjucks from "nunjucks";
|
||||||
|
|
||||||
const logger = new Logger("WebSocket Server");
|
const logger = new Logger("WebSocket Server");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a rendered version of the index file
|
||||||
|
* @returns Response with html in it
|
||||||
|
*/
|
||||||
async function getIndex() {
|
async function getIndex() {
|
||||||
|
// This tiny function took like an hour to write because
|
||||||
|
// nobody realistically uses templates in 2024 and documents
|
||||||
|
// it well enough to say what library they used
|
||||||
|
|
||||||
const index = Bun.file("./public/index.html");
|
const index = Bun.file("./public/index.html");
|
||||||
|
|
||||||
const rendered = nunjucks.renderString(await index.text(), {
|
const rendered = nunjucks.renderString(await index.text(), {
|
||||||
|
@ -19,6 +27,7 @@ async function getIndex() {
|
||||||
|
|
||||||
const response = new Response(rendered);
|
const response = new Response(rendered);
|
||||||
response.headers.set("Content-Type", "text/html");
|
response.headers.set("Content-Type", "text/html");
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +39,21 @@ export const app = Bun.serve({
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
const url = new URL(req.url).pathname;
|
const url = new URL(req.url).pathname;
|
||||||
|
|
||||||
|
// lol
|
||||||
// const ip = decoder.decode(res.getRemoteAddressAsText());
|
// const ip = decoder.decode(res.getRemoteAddressAsText());
|
||||||
// logger.debug(`${req.getMethod()} ${url} ${ip}`);
|
// logger.debug(`${req.getMethod()} ${url} ${ip}`);
|
||||||
// res.writeStatus(`200 OK`).end("HI!");
|
// res.writeStatus(`200 OK`).end("HI!");
|
||||||
|
|
||||||
|
// I have no clue if this is even safe...
|
||||||
|
// wtf do I do when the user types "/../.env" in the URL?
|
||||||
|
// From my testing, nothing out of the ordinary happens...
|
||||||
|
// but just in case, if you find something wrong with URLs,
|
||||||
|
// this is the most likely culprit
|
||||||
|
|
||||||
const file = path.join("./public/", url);
|
const file = path.join("./public/", url);
|
||||||
|
|
||||||
|
// Time for unreadable blocks of confusion
|
||||||
try {
|
try {
|
||||||
if (fs.lstatSync(file).isFile()) {
|
if (fs.lstatSync(file).isFile()) {
|
||||||
const data = Bun.file(file);
|
const data = Bun.file(file);
|
||||||
|
@ -54,20 +73,33 @@ export const app = Bun.serve({
|
||||||
},
|
},
|
||||||
websocket: {
|
websocket: {
|
||||||
open: ws => {
|
open: ws => {
|
||||||
|
// We got one!
|
||||||
const socket = new Socket(ws, createSocketID());
|
const socket = new Socket(ws, createSocketID());
|
||||||
|
|
||||||
|
// Reel 'em in...
|
||||||
(ws as unknown as any).socket = socket;
|
(ws as unknown as any).socket = socket;
|
||||||
// logger.debug("Connection at " + socket.getIP());
|
// logger.debug("Connection at " + socket.getIP());
|
||||||
|
|
||||||
|
// Let's put it in the dinner bucket.
|
||||||
socketsBySocketID.set(socket.socketID, socket);
|
socketsBySocketID.set(socket.socketID, socket);
|
||||||
},
|
},
|
||||||
|
|
||||||
message: (ws, message) => {
|
message: (ws, message) => {
|
||||||
|
// "Let's make it binary" said all websocket developers for some reason
|
||||||
const msg = message.toString();
|
const msg = message.toString();
|
||||||
|
|
||||||
|
// Let's find out wtf they even sent
|
||||||
handleMessage((ws as unknown as any).socket, msg);
|
handleMessage((ws as unknown as any).socket, msg);
|
||||||
},
|
},
|
||||||
|
|
||||||
close: (ws, code, message) => {
|
close: (ws, code, message) => {
|
||||||
// logger.debug("Close called");
|
// logger.debug("Close called");
|
||||||
|
|
||||||
|
// This usually gets called when someone leaves,
|
||||||
|
// but it's also used internally just in case
|
||||||
|
// some dickhead can't close their tab like a
|
||||||
|
// normal person.
|
||||||
|
|
||||||
const socket = (ws as unknown as any).socket as Socket;
|
const socket = (ws as unknown as any).socket as Socket;
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
|
|
|
@ -20,6 +20,12 @@ export const defaultUsersConfig = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Importing this elsewhere causes bun to segfault
|
// Importing this elsewhere causes bun to segfault
|
||||||
|
|
||||||
|
// Now that I look back at this, using this elsewhere
|
||||||
|
// before calling other things tends to make bun segfault?
|
||||||
|
// Not dealing with it. The code somehow runs, and I'm not
|
||||||
|
// going to fuck with the order of loading things until bun
|
||||||
|
// pushes an update and fucks all this stuff up.
|
||||||
export const config = loadConfig<UsersConfig>(
|
export const config = loadConfig<UsersConfig>(
|
||||||
usersConfigPath,
|
usersConfigPath,
|
||||||
defaultUsersConfig
|
defaultUsersConfig
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { test, expect } from "bun:test";
|
|
||||||
import { Channel } from "../../src/channel/Channel";
|
|
||||||
|
|
||||||
test("Channel is created correctly", () => {
|
|
||||||
const channel = new Channel("my room");
|
|
||||||
expect(channel.getID()).toBe("my room");
|
|
||||||
|
|
||||||
const info = channel.getInfo();
|
|
||||||
expect(info.id).toBe("my room");
|
|
||||||
expect(info._id).toBe("my room");
|
|
||||||
expect(info.count).toBe(0);
|
|
||||||
|
|
||||||
const ppl = channel.getParticipantList();
|
|
||||||
expect(ppl).toBeEmpty();
|
|
||||||
});
|
|
|
@ -1 +0,0 @@
|
||||||
export class FakeSocket {}
|
|
Loading…
Reference in New Issue