Add rate limits for every message, fix lobby regex, implement tags into database, reword comments, add channel flags
This commit is contained in:
parent
bb0516b367
commit
71960104eb
|
@ -1,7 +1,6 @@
|
|||
forceLoad:
|
||||
- lobby
|
||||
- test/awkward
|
||||
|
||||
lobbySettings:
|
||||
lobby: true
|
||||
chat: true
|
||||
|
@ -9,17 +8,18 @@ lobbySettings:
|
|||
visible: true
|
||||
color: "#73b3cc"
|
||||
color2: "#273546"
|
||||
|
||||
defaultSettings:
|
||||
chat: true
|
||||
crownsolo: false
|
||||
color: "#3b5054"
|
||||
color2: "#001014"
|
||||
visible: true
|
||||
|
||||
lobbyRegexes:
|
||||
- "^lobby[1-9]?[1-9]?$"
|
||||
- "^test/.+$"
|
||||
|
||||
lobbyBackdoor: "lolwutsecretlobbybackdoor"
|
||||
fullChannel: "test/awkward"
|
||||
- ^lobby[0-9][0-9]$
|
||||
- ^lobby[0-9]$
|
||||
- ^lobby$
|
||||
- ^test/.+$
|
||||
lobbyBackdoor: lolwutsecretlobbybackdoor
|
||||
fullChannel: test/awkward
|
||||
sendLimit: false
|
||||
sendTags: false
|
||||
|
|
|
@ -3,28 +3,73 @@ user:
|
|||
a: 1500
|
||||
m: 50
|
||||
ch: 1000
|
||||
kickban: 250
|
||||
kickban: 125
|
||||
t: 7.8125
|
||||
+ls: 16.666666666666668
|
||||
-ls: 16.666666666666668
|
||||
chown: 2000
|
||||
hi: 50
|
||||
bye: 50
|
||||
devices: 50
|
||||
admin_message: 50
|
||||
admin message: 50
|
||||
chains:
|
||||
userset:
|
||||
interval: 1800000
|
||||
num: 1000
|
||||
chset:
|
||||
interval: 1800000
|
||||
num: 1024
|
||||
n:
|
||||
interval: 1000
|
||||
num: 512
|
||||
crown:
|
||||
normal:
|
||||
a: 600
|
||||
m: 50
|
||||
ch: 1000
|
||||
kickban: 250
|
||||
kickban: 125
|
||||
t: 7.8125
|
||||
+ls: 16.666666666666668
|
||||
-ls: 16.666666666666668
|
||||
chown: 2000
|
||||
hi: 50
|
||||
bye: 50
|
||||
devices: 50
|
||||
admin_message: 50
|
||||
admin message: 50
|
||||
chains:
|
||||
userset:
|
||||
interval: 1800000
|
||||
num: 1000
|
||||
chset:
|
||||
interval: 1800000
|
||||
num: 1024
|
||||
n:
|
||||
interval: 1000
|
||||
num: 512
|
||||
admin:
|
||||
normal:
|
||||
a: 120
|
||||
m: 16.666666666666668
|
||||
ch: 100
|
||||
kickban: 31.25
|
||||
kickban: 16.666666666666668
|
||||
t: 3.90625
|
||||
+ls: 16.666666666666668
|
||||
-ls: 16.666666666666668
|
||||
chown: 500
|
||||
hi: 50
|
||||
bye: 50
|
||||
devices: 50
|
||||
admin_message: 16.666666666666668
|
||||
admin message: 16.666666666666668
|
||||
chains:
|
||||
userset:
|
||||
interval: 1800000
|
||||
interval: 500
|
||||
num: 1000
|
||||
chset:
|
||||
interval: 1800000
|
||||
num: 1024
|
||||
n:
|
||||
interval: 50
|
||||
num: 512
|
||||
|
|
|
@ -16,6 +16,7 @@ model User {
|
|||
name String @default("Anonymous")
|
||||
color String @default("#ffffff")
|
||||
flags String @default("{}") // JSON flags object
|
||||
tag String // JSON tag
|
||||
}
|
||||
|
||||
model ChatHistory {
|
||||
|
|
|
@ -7,7 +7,8 @@ import {
|
|||
Participant,
|
||||
ServerEvents,
|
||||
IChannelInfo,
|
||||
Notification
|
||||
Notification,
|
||||
UserFlags
|
||||
} from "../util/types";
|
||||
import type { Socket } from "../ws/Socket";
|
||||
import { validateChannelSettings } from "./settings";
|
||||
|
@ -17,7 +18,7 @@ import { ChannelList } from "./ChannelList";
|
|||
import { config } from "./config";
|
||||
import { saveChatHistory, getChatHistory } from "../data/history";
|
||||
import { mixin } from "../util/helpers";
|
||||
import { NoteQuota } from "../ws/ratelimit/NoteQuota";
|
||||
import { readUser } from "../data/user";
|
||||
|
||||
interface CachedKickban {
|
||||
userId: string;
|
||||
|
@ -25,9 +26,17 @@ interface CachedKickban {
|
|||
endTime: number;
|
||||
}
|
||||
|
||||
interface ExtraPartData {
|
||||
uuids: string[];
|
||||
flags: Partial<UserFlags>;
|
||||
}
|
||||
|
||||
type ExtraPart = Participant & ExtraPartData;
|
||||
|
||||
export class Channel extends EventEmitter {
|
||||
private settings: Partial<IChannelSettings>;
|
||||
private ppl = new Array<Participant & { uuids: string[] }>();
|
||||
private ppl = new Array<ExtraPart>();
|
||||
|
||||
public chatHistory = new Array<ClientEvents["a"]>();
|
||||
|
||||
private async loadChatHistory() {
|
||||
|
@ -39,6 +48,8 @@ export class Channel extends EventEmitter {
|
|||
|
||||
public crown?: Crown;
|
||||
|
||||
private flags: Record<string, any> = {};
|
||||
|
||||
constructor(
|
||||
private _id: string,
|
||||
set?: Partial<IChannelSettings>,
|
||||
|
@ -55,6 +66,8 @@ export class Channel extends EventEmitter {
|
|||
// Validate settings in set
|
||||
// Set the verified settings
|
||||
|
||||
this.logger.debug("lobby me?", this.isLobby());
|
||||
|
||||
if (!this.isLobby()) {
|
||||
if (set) {
|
||||
//this.logger.debug("Passed settings:", set);
|
||||
|
@ -185,6 +198,15 @@ export class Channel extends EventEmitter {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether this channel is a lobby with the name "lobby" in it
|
||||
*/
|
||||
public isTrueLobby() {
|
||||
if (this.getID().match("^lobby[0-9][0-9]$") && this.getID().match("^lobby[1-9]$")) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change this channel's settings
|
||||
* @param set Channel settings
|
||||
|
@ -265,7 +287,7 @@ export class Channel extends EventEmitter {
|
|||
* @param socket Socket that is joining
|
||||
* @returns undefined
|
||||
*/
|
||||
public join(socket: Socket): void {
|
||||
public join(socket: Socket, force: boolean = false): void {
|
||||
//! /!\ Players are forced to join the same channel on two different tabs!
|
||||
//? TODO Should this be a bug or a feature?
|
||||
//this.logger.debug("join triggered");
|
||||
|
@ -274,8 +296,8 @@ export class Channel extends EventEmitter {
|
|||
const part = socket.getParticipant() as Participant;
|
||||
|
||||
let hasChangedChannel = false;
|
||||
let oldChannelID = socket.currentChannelID;
|
||||
|
||||
if (!force) {
|
||||
// Is user banned?
|
||||
if (this.isBanned(part._id)) {
|
||||
// Send user to ban channel instead
|
||||
|
@ -289,6 +311,17 @@ export class Channel extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
// Is the channel full?
|
||||
if (this.isFull()) {
|
||||
// Is this a genuine lobby (not a test/ room)?
|
||||
if (this.isTrueLobby()) {
|
||||
const nextID = this.getNextIncrementationFromID();
|
||||
this.logger.debug("New ID:", nextID);
|
||||
return socket.setChannel(nextID, undefined, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is user in this channel?
|
||||
if (this.hasUser(part._id)) {
|
||||
// Already in channel, don't add to list, but tell them they're here
|
||||
|
@ -311,7 +344,8 @@ export class Channel extends EventEmitter {
|
|||
color: part.color,
|
||||
id: part.id,
|
||||
tag: part.tag,
|
||||
uuids: [socket.getUUID()]
|
||||
uuids: [socket.getUUID()],
|
||||
flags: socket.getUserFlags() || {}
|
||||
});
|
||||
} else {
|
||||
if (socket.currentChannelID !== config.fullChannel) {
|
||||
|
@ -331,7 +365,7 @@ export class Channel extends EventEmitter {
|
|||
if (hasChangedChannel) {
|
||||
// Were they in a channel before?
|
||||
if (socket.currentChannelID) {
|
||||
// Find the channel they were in
|
||||
// Find the other channel they were in
|
||||
const ch = ChannelList.getList().find(
|
||||
ch => ch._id == socket.currentChannelID
|
||||
);
|
||||
|
@ -340,7 +374,7 @@ export class Channel extends EventEmitter {
|
|||
if (ch) ch.leave(socket);
|
||||
}
|
||||
|
||||
// Change the thing we checked to point to us now
|
||||
// You belong here now
|
||||
socket.currentChannelID = this.getID();
|
||||
}
|
||||
|
||||
|
@ -443,9 +477,9 @@ export class Channel extends EventEmitter {
|
|||
public isFull() {
|
||||
// TODO Use limit setting
|
||||
|
||||
// if (this.isLobby() && this.ppl.length >= 20) {
|
||||
// return true;
|
||||
// }
|
||||
if (this.isTrueLobby() && this.ppl.length >= 20) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -472,13 +506,20 @@ export class Channel extends EventEmitter {
|
|||
* @returns List of people
|
||||
*/
|
||||
public getParticipantList() {
|
||||
return this.ppl.map(p => ({
|
||||
id: p.id,
|
||||
const ppl = [];
|
||||
|
||||
for (const p of this.ppl) {
|
||||
if (p.flags.vanish) continue;
|
||||
ppl.push({
|
||||
_id: p._id,
|
||||
name: p.name,
|
||||
color: p.color,
|
||||
tag: p.tag
|
||||
}));
|
||||
id: p.id,
|
||||
tag: config.sendTags ? p.tag : undefined
|
||||
});
|
||||
}
|
||||
|
||||
return ppl;
|
||||
}
|
||||
|
||||
public getParticipantListUnsanitized() {
|
||||
|
@ -626,7 +667,7 @@ export class Channel extends EventEmitter {
|
|||
}
|
||||
|
||||
/**
|
||||
* Drop the crown (remove from user)
|
||||
* Drop the crown (reset timer, and, if applicable, remove from user's head)
|
||||
*/
|
||||
public dropCrown() {
|
||||
if (this.crown) {
|
||||
|
@ -780,6 +821,11 @@ export class Channel extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user is banned here right now
|
||||
* @param _id User ID
|
||||
* @returns True if the user is banned, otherwise false
|
||||
**/
|
||||
public isBanned(_id: string) {
|
||||
const now = Date.now();
|
||||
|
||||
|
@ -799,6 +845,9 @@ export class Channel extends EventEmitter {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the chat and chat history
|
||||
**/
|
||||
public async clearChat() {
|
||||
this.chatHistory = [];
|
||||
await saveChatHistory(this.getID(), this.chatHistory);
|
||||
|
@ -826,6 +875,11 @@ export class Channel extends EventEmitter {
|
|||
}]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message in chat
|
||||
* @param msg Chat message event to send
|
||||
* @param p Participant who is "sending the message"
|
||||
**/
|
||||
public async sendChat(msg: ServerEvents["a"], p: Participant) {
|
||||
if (!msg.message) return;
|
||||
|
||||
|
@ -848,6 +902,38 @@ export class Channel extends EventEmitter {
|
|||
this.chatHistory.push(outgoing);
|
||||
await saveChatHistory(this.getID(), this.chatHistory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a flag on this channel
|
||||
* @param key Flag ID
|
||||
* @param val Value of which the flag will be set to
|
||||
**/
|
||||
public setFlag(key: string, val: any) {
|
||||
this.flags[key] = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a flag on this channel
|
||||
* @param key Flag ID
|
||||
* @returns Value of flag
|
||||
*/
|
||||
public getFlag(key: string) {
|
||||
return this.flags[key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the name of this channel where the number at the end is one higher than this one, given it ends with a number
|
||||
**/
|
||||
public getNextIncrementationFromID() {
|
||||
try {
|
||||
const id = this.getID();
|
||||
const num = parseInt((id.match(/\d+$/) as string[])[0]);
|
||||
return `${id.substring(0, id.length - num.toString().length)}${num + 1}`;
|
||||
} catch (err) {
|
||||
return config.fullChannel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Channel;
|
||||
|
|
|
@ -8,6 +8,8 @@ interface ChannelConfig {
|
|||
lobbyRegexes: string[];
|
||||
lobbyBackdoor: string;
|
||||
fullChannel: string;
|
||||
sendLimit: boolean;
|
||||
sendTags: boolean;
|
||||
}
|
||||
|
||||
export const config = loadConfig<ChannelConfig>("config/channels.yml", {
|
||||
|
@ -27,8 +29,10 @@ export const config = loadConfig<ChannelConfig>("config/channels.yml", {
|
|||
color2: "#001014",
|
||||
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/.+$"],
|
||||
// Here's a terrifying fact: Brandon used parseInt to check lobby names
|
||||
lobbyRegexes: ["^lobby[0-9][0-9]$", "^lobby[0-9]$", "^lobby$", "^test/.+$"],
|
||||
lobbyBackdoor: "lolwutsecretlobbybackdoor",
|
||||
fullChannel: "test/awkward"
|
||||
fullChannel: "test/awkward",
|
||||
sendLimit: false,
|
||||
sendTags: false,
|
||||
});
|
||||
|
|
|
@ -1,16 +1,41 @@
|
|||
import { Channel } from "./Channel";
|
||||
import { config } from "./config";
|
||||
|
||||
// This shit was moved here to try to fix the unit tests segfaulting but it didn't work
|
||||
import { Logger } from "../util/Logger";
|
||||
import { ChannelList } from "./ChannelList";
|
||||
|
||||
// Channel forceloader (cringe)
|
||||
let hasFullChannel = false;
|
||||
|
||||
for (const id of config.forceLoad) {
|
||||
const logger = new Logger("Channel Forceloader");
|
||||
|
||||
export function forceloadChannel(id: string) {
|
||||
try {
|
||||
logger.info("Forceloading", id);
|
||||
new Channel(id, undefined, undefined, undefined, true);
|
||||
if (id == config.fullChannel) hasFullChannel = true;
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasFullChannel) {
|
||||
new Channel(config.fullChannel, undefined, undefined, undefined, true);
|
||||
export function unforceloadChannel(id: string) {
|
||||
const ch = ChannelList.getList().find(ch => ch.getID() == id);
|
||||
if (!ch) return false;
|
||||
|
||||
logger.info("Unloading", id);
|
||||
ch.destroy();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function loadDefaultForcedChannels() {
|
||||
let hasFullChannel = false;
|
||||
|
||||
for (const id of config.forceLoad) {
|
||||
forceloadChannel(id);
|
||||
if (id == config.fullChannel) hasFullChannel = true;
|
||||
}
|
||||
|
||||
if (!hasFullChannel) {
|
||||
new Channel(config.fullChannel, undefined, undefined, undefined, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
import { User } from "@prisma/client";
|
||||
import { prisma } from "./prisma";
|
||||
import { UserFlags } from "../util/types";
|
||||
import { Tag, UserFlags } from "../util/types";
|
||||
|
||||
export async function createUser(
|
||||
_id: string,
|
||||
name?: string,
|
||||
color?: string,
|
||||
flags?: UserFlags
|
||||
flags?: UserFlags,
|
||||
tag?: Tag
|
||||
) {
|
||||
return await prisma.user.create({
|
||||
data: { id: _id, name, color, flags: JSON.stringify(flags) }
|
||||
data: {
|
||||
id: _id,
|
||||
name,
|
||||
color,
|
||||
flags: JSON.stringify(flags) || "",
|
||||
tag: JSON.stringify(tag) || ""
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function getUsers() {
|
||||
return await {
|
||||
return {
|
||||
users: await prisma.user.findMany(),
|
||||
count: await prisma.user.count()
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
|
||||
// If you don't load the server first, bun will literally segfault
|
||||
import "./ws/server";
|
||||
import "./channel/forceLoad";
|
||||
import { loadDefaultForcedChannels } from "./channel/forceLoad";
|
||||
import { Logger } from "./util/Logger";
|
||||
|
||||
const logger = new Logger("Main");
|
||||
logger.info("Loading default channels...");
|
||||
loadDefaultForcedChannels();
|
||||
|
||||
import "./util/readline";
|
||||
|
||||
|
|
|
@ -1,18 +1,5 @@
|
|||
/**
|
||||
* Typedefs
|
||||
*/
|
||||
|
||||
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 UserFlags = Partial<{
|
||||
|
@ -27,9 +14,17 @@ declare type UserFlags = Partial<{
|
|||
cansetcrowns: number;
|
||||
|
||||
// new
|
||||
"no note quota": number;
|
||||
"no note rate limit": number;
|
||||
"no cursor rate limit": number;
|
||||
"no userset rate limit": number;
|
||||
mod: number;
|
||||
admin: number;
|
||||
vanish: number;
|
||||
}>;
|
||||
|
||||
type ChannelFlags = Partial<{
|
||||
limit: number;
|
||||
}>;
|
||||
|
||||
declare interface Tag {
|
||||
|
@ -110,7 +105,6 @@ declare interface Crown {
|
|||
endPos: Vector2;
|
||||
}
|
||||
|
||||
// Events copied from Hri7566/mppclone-client typedefs
|
||||
declare interface ServerEvents {
|
||||
a: {
|
||||
m: "a";
|
||||
|
@ -238,19 +232,40 @@ declare interface ServerEvents {
|
|||
remove?: true;
|
||||
};
|
||||
|
||||
tag: {
|
||||
m: "tag";
|
||||
_id: string;
|
||||
tag: {
|
||||
text: string;
|
||||
color: string;
|
||||
};
|
||||
}
|
||||
|
||||
clear_chat: {
|
||||
m: "clear_chat"
|
||||
m: "clear_chat";
|
||||
};
|
||||
|
||||
notification: {
|
||||
m: "notification"
|
||||
m: "notification";
|
||||
targetChannel?: string;
|
||||
targetUser?: string;
|
||||
} & Notification;
|
||||
|
||||
restart: {
|
||||
m: "restart"
|
||||
}
|
||||
m: "restart";
|
||||
};
|
||||
|
||||
forceload: {
|
||||
m: "forceload";
|
||||
_id: string;
|
||||
};
|
||||
|
||||
ch_flag: {
|
||||
m: "ch_flag";
|
||||
_id?: string;
|
||||
key: string;
|
||||
value: any;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface ClientEvents {
|
||||
|
|
|
@ -123,7 +123,7 @@ export class Socket extends EventEmitter {
|
|||
return this.id;
|
||||
}
|
||||
|
||||
public setChannel(_id: string, set?: Partial<IChannelSettings>) {
|
||||
public setChannel(_id: string, set?: Partial<IChannelSettings>, force: boolean = false) {
|
||||
if (this.isDestroyed()) return;
|
||||
if (this.currentChannelID === _id) {
|
||||
logger.debug("Guy in channel was already in");
|
||||
|
@ -155,7 +155,7 @@ export class Socket extends EventEmitter {
|
|||
this
|
||||
);
|
||||
|
||||
channel.join(this);
|
||||
channel.join(this, force);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,17 +443,25 @@ export class Socket extends EventEmitter {
|
|||
ch.playNotes(msg, this);
|
||||
}
|
||||
|
||||
private isSubscribedToChannelList = false;
|
||||
|
||||
public subscribeToChannelList() {
|
||||
if (this.isSubscribedToChannelList) return;
|
||||
|
||||
ChannelList.subscribe(this.id);
|
||||
|
||||
const firstList = ChannelList.getPublicList().map(v =>
|
||||
v.getInfo(this._id)
|
||||
);
|
||||
this.sendChannelList(firstList);
|
||||
|
||||
this.isSubscribedToChannelList = true;
|
||||
}
|
||||
|
||||
public unsubscribeFromChannelList() {
|
||||
if (!this.isSubscribedToChannelList) return;
|
||||
ChannelList.unsubscribe(this.id);
|
||||
this.isSubscribedToChannelList = false;
|
||||
}
|
||||
|
||||
public sendChannelList(list: IChannelInfo[], complete: boolean = true) {
|
||||
|
@ -476,7 +484,6 @@ export class Socket extends EventEmitter {
|
|||
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;
|
||||
|
@ -514,6 +521,13 @@ export class Socket extends EventEmitter {
|
|||
html: notif.html
|
||||
}]);
|
||||
}
|
||||
|
||||
public setTag(text: string, color: string) {
|
||||
const user = this.getUser();
|
||||
if (!user) return;
|
||||
user.tag = JSON.stringify({ text, color });
|
||||
updateUser(this.getUserID(), user);
|
||||
}
|
||||
}
|
||||
|
||||
export const socketsBySocketID = new Map<string, Socket>();
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { ChannelList } from "../../../../channel/ChannelList";
|
||||
import { ServerEventListener } from "../../../../util/types";
|
||||
|
||||
export const ch_flag: ServerEventListener<"ch_flag"> = {
|
||||
id: "ch_flag",
|
||||
callback: async (msg, socket) => {
|
||||
// Edit channel flag
|
||||
let chid = msg._id;
|
||||
|
||||
if (typeof chid !== "string") {
|
||||
const ch = socket.getCurrentChannel();
|
||||
if (!ch) return;
|
||||
|
||||
chid = ch.getID();
|
||||
}
|
||||
|
||||
const ch = ChannelList.getList().find(c => c.getID() == chid);
|
||||
if (!ch) return;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
import { forceloadChannel } from "../../../../channel/forceLoad";
|
||||
import { ServerEventListener } from "../../../../util/types";
|
||||
|
||||
export const forceload: ServerEventListener<"forceload"> = {
|
||||
id: "forceload",
|
||||
callback: async (msg, socket) => {
|
||||
// Forceload channel
|
||||
if (typeof msg._id !== "string") return;
|
||||
|
||||
forceloadChannel(msg._id);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
import { readUser, updateUser } from "../../../../data/user";
|
||||
import { ServerEventListener } from "../../../../util/types";
|
||||
import { findSocketsByUserID } from "../../../Socket";
|
||||
|
||||
export const tag: ServerEventListener<"tag"> = {
|
||||
id: "tag",
|
||||
callback: async (msg, socket) => {
|
||||
// Change someone else's tag
|
||||
const id = msg._id;
|
||||
const tag = msg.tag;
|
||||
|
||||
if (typeof id !== "string") return;
|
||||
if (typeof tag !== "object") return;
|
||||
if (typeof tag["text"] !== "string") return;
|
||||
if (typeof tag.color !== "string") return;
|
||||
if (!tag.color.match(/^#[0-9a-f]{6}$/i)) return;
|
||||
|
||||
const user = await readUser(msg._id);
|
||||
if (!user) return;
|
||||
|
||||
user.tag = JSON.stringify(tag);
|
||||
await updateUser(id, user);
|
||||
|
||||
const toUpdate = findSocketsByUserID(id);
|
||||
toUpdate.forEach(s => {
|
||||
s.setTag(msg.tag.text, msg.tag.color);
|
||||
});
|
||||
}
|
||||
};
|
|
@ -12,6 +12,10 @@ export const plus_ls: ServerEventListener<"+ls"> = {
|
|||
// 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.
|
||||
if (socket.rateLimits) {
|
||||
if (!socket.rateLimits.normal["+ls"].attempt()) return;
|
||||
}
|
||||
|
||||
socket.subscribeToChannelList();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,10 @@ export const minus_ls: ServerEventListener<"-ls"> = {
|
|||
id: "-ls",
|
||||
callback: (msg, socket) => {
|
||||
// Stop giving us the latest server forecast
|
||||
if (socket.rateLimits) {
|
||||
if (!socket.rateLimits.normal["-ls"].attempt()) return;
|
||||
}
|
||||
|
||||
socket.unsubscribeFromChannelList();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { Logger } from "../../../../util/Logger";
|
||||
import env from "../../../../util/env";
|
||||
import { ServerEventListener } from "../../../../util/types";
|
||||
import { config } from "../../../usersConfig";
|
||||
|
||||
const logger = new Logger("Admin Message Handler");
|
||||
|
||||
export const admin_message: ServerEventListener<"admin message"> = {
|
||||
id: "admin message",
|
||||
callback: (msg, socket) => {
|
||||
// Administrator control message
|
||||
if (socket.rateLimits)
|
||||
if (!socket.rateLimits.normal["admin message"].attempt()) return;
|
||||
|
||||
if (typeof msg.password !== "string") return;
|
||||
if (msg.password !== env.ADMIN_PASS) return;
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ export const bye: ServerEventListener<"bye"> = {
|
|||
id: "bye",
|
||||
callback: (msg, socket) => {
|
||||
// Leave server
|
||||
if (socket.rateLimits)
|
||||
if (!socket.rateLimits.normal.bye.attempt()) return;
|
||||
|
||||
socket.destroy();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -7,6 +7,9 @@ export const chown: ServerEventListener<"chown"> = {
|
|||
id: "chown",
|
||||
callback: (msg, socket) => {
|
||||
// Change channel ownership
|
||||
if (socket.rateLimits)
|
||||
if (!socket.rateLimits.normal["chown"].attempt()) return;
|
||||
|
||||
const ch = socket.getCurrentChannel();
|
||||
if (!ch) return;
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ export const chset: ServerEventListener<"chset"> = {
|
|||
id: "chset",
|
||||
callback: (msg, socket) => {
|
||||
// Change channel settings
|
||||
if (socket.rateLimits)
|
||||
if (!socket.rateLimits.chains.chset.attempt()) return;
|
||||
|
||||
if (typeof msg.set == "undefined") return;
|
||||
|
||||
const ch = socket.getCurrentChannel();
|
||||
|
|
|
@ -4,6 +4,9 @@ export const devices: ServerEventListener<"devices"> = {
|
|||
id: "devices",
|
||||
callback: (msg, socket) => {
|
||||
// List of MIDI Devices (or software ports, because nobody can tell the difference)
|
||||
if (socket.rateLimits)
|
||||
if (!socket.rateLimits.normal.devices.attempt()) return;
|
||||
|
||||
if (socket.gateway.hasSentDevices) return;
|
||||
socket.gateway.hasSentDevices = true;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,10 @@ export const hi: ServerEventListener<"hi"> = {
|
|||
callback: (msg, socket) => {
|
||||
// Handshake message
|
||||
// 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.rateLimits)
|
||||
if (!socket.rateLimits.normal.hi.attempt()) return;
|
||||
|
||||
if (socket.gateway.hasProcessedHi) return;
|
||||
let part = socket.getParticipant();
|
||||
|
||||
|
|
|
@ -4,8 +4,10 @@ export const m: ServerEventListener<"m"> = {
|
|||
id: "m",
|
||||
callback: (msg, socket) => {
|
||||
// Cursor movement
|
||||
if (!socket.rateLimits) return;
|
||||
if (socket.rateLimits) {
|
||||
if (!socket.rateLimits.normal.m.attempt()) return;
|
||||
}
|
||||
|
||||
if (!msg.x || !msg.y) return;
|
||||
|
||||
let x = msg.x;
|
||||
|
|
|
@ -5,6 +5,9 @@ export const n: ServerEventListener<"n"> = {
|
|||
id: "n",
|
||||
callback: (msg, socket) => {
|
||||
// Piano note
|
||||
if (socket.rateLimits)
|
||||
if (!socket.rateLimits.chains.n.attempt()) return;
|
||||
|
||||
if (!Array.isArray(msg.n)) return;
|
||||
if (typeof msg.t !== "number") return;
|
||||
|
||||
|
|
|
@ -4,11 +4,15 @@ export const t: ServerEventListener<"t"> = {
|
|||
id: "t",
|
||||
callback: (msg, socket) => {
|
||||
// Ping
|
||||
|
||||
if (socket.rateLimits)
|
||||
if (!socket.rateLimits.normal.t.attempt()) return
|
||||
|
||||
if (msg.e) {
|
||||
if (typeof msg.e !== "number") return;
|
||||
}
|
||||
|
||||
// Pong!
|
||||
// Pong
|
||||
socket.sendArray([
|
||||
{
|
||||
m: "t",
|
||||
|
|
|
@ -7,14 +7,26 @@ export interface RateLimitConfigList<
|
|||
RLC = { num: number; interval: number }
|
||||
> {
|
||||
normal: {
|
||||
m: RL;
|
||||
a: RL;
|
||||
m: RL;
|
||||
ch: RL;
|
||||
kickban: RL;
|
||||
t: RL;
|
||||
"+ls": RL;
|
||||
"-ls": RL;
|
||||
chown: RL;
|
||||
|
||||
// weird limits
|
||||
hi: RL;
|
||||
bye: RL;
|
||||
devices: RL;
|
||||
"admin message": RL;
|
||||
};
|
||||
|
||||
chains: {
|
||||
userset: RLC;
|
||||
chset: RLC;
|
||||
n: RLC; // not to be confused with NoteQuota
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -37,12 +49,29 @@ export const config = loadConfig<RateLimitsConfig>("config/ratelimits.yml", {
|
|||
a: 6000 / 4,
|
||||
m: 1000 / 20,
|
||||
ch: 1000 / 1,
|
||||
kickban: 1000 / 4
|
||||
kickban: 1000 / 8,
|
||||
t: 1000 / 128,
|
||||
"+ls": 1000 / 60,
|
||||
"-ls": 1000 / 60,
|
||||
chown: 2000,
|
||||
|
||||
hi: 1000 / 20,
|
||||
bye: 1000 / 20,
|
||||
devices: 1000 / 20,
|
||||
"admin message": 1000 / 20
|
||||
},
|
||||
chains: {
|
||||
userset: {
|
||||
interval: 1000 * 60 * 30,
|
||||
num: 1000
|
||||
},
|
||||
chset: {
|
||||
interval: 1000 * 60 * 30,
|
||||
num: 1024
|
||||
},
|
||||
n: {
|
||||
interval: 1000,
|
||||
num: 512
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -51,12 +80,29 @@ export const config = loadConfig<RateLimitsConfig>("config/ratelimits.yml", {
|
|||
a: 6000 / 10,
|
||||
m: 1000 / 20,
|
||||
ch: 1000 / 1,
|
||||
kickban: 1000 / 4
|
||||
kickban: 1000 / 8,
|
||||
t: 1000 / 128,
|
||||
"+ls": 1000 / 60,
|
||||
"-ls": 1000 / 60,
|
||||
chown: 2000,
|
||||
|
||||
hi: 1000 / 20,
|
||||
bye: 1000 / 20,
|
||||
devices: 1000 / 20,
|
||||
"admin message": 1000 / 20
|
||||
},
|
||||
chains: {
|
||||
userset: {
|
||||
interval: 1000 * 60 * 30,
|
||||
num: 1000
|
||||
},
|
||||
chset: {
|
||||
interval: 1000 * 60 * 30,
|
||||
num: 1024
|
||||
},
|
||||
n: {
|
||||
interval: 1000,
|
||||
num: 512
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -65,12 +111,29 @@ export const config = loadConfig<RateLimitsConfig>("config/ratelimits.yml", {
|
|||
a: 6000 / 50,
|
||||
m: 1000 / 60,
|
||||
ch: 1000 / 10,
|
||||
kickban: 1000 / 32
|
||||
kickban: 1000 / 60,
|
||||
t: 1000 / 256,
|
||||
"+ls": 1000 / 60,
|
||||
"-ls": 1000 / 60,
|
||||
chown: 500,
|
||||
|
||||
hi: 1000 / 20,
|
||||
bye: 1000 / 20,
|
||||
devices: 1000 / 20,
|
||||
"admin message": 1000 / 60
|
||||
},
|
||||
chains: {
|
||||
userset: {
|
||||
interval: 1000 * 60 * 30,
|
||||
interval: 500,
|
||||
num: 1000
|
||||
},
|
||||
chset: {
|
||||
interval: 1000 * 60 * 30,
|
||||
num: 1024
|
||||
},
|
||||
n: {
|
||||
interval: 50,
|
||||
num: 512
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,32 @@ export const adminLimits: RateLimitConstructorList = {
|
|||
a: () => new RateLimit(config.admin.normal.a),
|
||||
m: () => new RateLimit(config.admin.normal.m),
|
||||
ch: () => new RateLimit(config.admin.normal.ch),
|
||||
kickban: () => new RateLimit(config.admin.normal.kickban)
|
||||
kickban: () => new RateLimit(config.crown.normal.kickban),
|
||||
t: () => new RateLimit(config.crown.normal.t),
|
||||
"+ls": () => new RateLimit(config.crown.normal["+ls"]),
|
||||
"-ls": () => new RateLimit(config.crown.normal["-ls"]),
|
||||
chown: () => new RateLimit(config.crown.normal.chown),
|
||||
|
||||
hi: () => new RateLimit(config.crown.normal.hi),
|
||||
bye: () => new RateLimit(config.crown.normal.bye),
|
||||
devices: () => new RateLimit(config.crown.normal.devices),
|
||||
"admin message": () => new RateLimit(config.crown.normal["admin message"])
|
||||
},
|
||||
chains: {
|
||||
userset: () =>
|
||||
new RateLimitChain(
|
||||
config.admin.chains.userset.interval,
|
||||
config.admin.chains.userset.num
|
||||
),
|
||||
chset: () =>
|
||||
new RateLimitChain(
|
||||
config.crown.chains.chset.interval,
|
||||
config.crown.chains.userset.num
|
||||
),
|
||||
n: () =>
|
||||
new RateLimitChain(
|
||||
config.crown.chains.n.interval,
|
||||
config.crown.chains.userset.num
|
||||
)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,13 +6,33 @@ export const crownLimits: RateLimitConstructorList = {
|
|||
normal: {
|
||||
a: () => new RateLimit(config.crown.normal.a),
|
||||
m: () => new RateLimit(config.crown.normal.m),
|
||||
ch: () => new RateLimit(config.crown.normal.ch)
|
||||
ch: () => new RateLimit(config.crown.normal.ch),
|
||||
kickban: () => new RateLimit(config.crown.normal.kickban),
|
||||
t: () => new RateLimit(config.crown.normal.t),
|
||||
"+ls": () => new RateLimit(config.crown.normal["+ls"]),
|
||||
"-ls": () => new RateLimit(config.crown.normal["-ls"]),
|
||||
chown: () => new RateLimit(config.crown.normal.chown),
|
||||
|
||||
hi: () => new RateLimit(config.crown.normal.hi),
|
||||
bye: () => new RateLimit(config.crown.normal.bye),
|
||||
devices: () => new RateLimit(config.crown.normal.devices),
|
||||
"admin message": () => new RateLimit(config.crown.normal["admin message"])
|
||||
},
|
||||
chains: {
|
||||
userset: () =>
|
||||
new RateLimitChain(
|
||||
config.crown.chains.userset.interval,
|
||||
config.crown.chains.userset.num
|
||||
),
|
||||
chset: () =>
|
||||
new RateLimitChain(
|
||||
config.crown.chains.chset.interval,
|
||||
config.crown.chains.userset.num
|
||||
),
|
||||
n: () =>
|
||||
new RateLimitChain(
|
||||
config.crown.chains.n.interval,
|
||||
config.crown.chains.userset.num
|
||||
)
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,19 +2,37 @@ import { RateLimit } from "../RateLimit";
|
|||
import { RateLimitChain } from "../RateLimitChain";
|
||||
import { RateLimitConstructorList, config } from "../config";
|
||||
|
||||
// All this does is instantiate rate limits from the config
|
||||
export const userLimits: RateLimitConstructorList = {
|
||||
normal: {
|
||||
a: () => new RateLimit(config.user.normal.a),
|
||||
m: () => new RateLimit(config.user.normal.m),
|
||||
ch: () => new RateLimit(config.user.normal.ch),
|
||||
kickban: () => new RateLimit(config.user.normal.kickban)
|
||||
kickban: () => new RateLimit(config.user.normal.kickban),
|
||||
t: () => new RateLimit(config.user.normal.t),
|
||||
"+ls": () => new RateLimit(config.user.normal["+ls"]),
|
||||
"-ls": () => new RateLimit(config.user.normal["-ls"]),
|
||||
chown: () => new RateLimit(config.user.normal.chown),
|
||||
|
||||
hi: () => new RateLimit(config.user.normal.hi),
|
||||
bye: () => new RateLimit(config.user.normal.bye),
|
||||
devices: () => new RateLimit(config.user.normal.devices),
|
||||
"admin message": () => new RateLimit(config.user.normal["admin message"])
|
||||
},
|
||||
chains: {
|
||||
userset: () =>
|
||||
new RateLimitChain(
|
||||
config.user.chains.userset.interval,
|
||||
config.user.chains.userset.num
|
||||
),
|
||||
chset: () =>
|
||||
new RateLimitChain(
|
||||
config.user.chains.chset.interval,
|
||||
config.user.chains.userset.num
|
||||
),
|
||||
n: () =>
|
||||
new RateLimitChain(
|
||||
config.user.chains.n.interval,
|
||||
config.user.chains.userset.num
|
||||
)
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue