effective rate limiting

This commit is contained in:
Evert Prants 2020-12-05 12:26:38 +02:00
parent ccbf487725
commit d11f697dec
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
1 changed files with 72 additions and 1 deletions

View File

@ -29,16 +29,71 @@ interface CommandSpec {
execute(msg: IMessage, command: CommandSpec, prefix: string, ...args: any[]): Promise<boolean>;
}
interface RateLimit {
rooms: string[];
perSecond: number;
cooldown: number;
}
interface Rate {
lastMessage: number;
messages: number;
}
const rates: {[key: string]: Rate} = {};
@Configurable({
prefix: {
'*': '!'
},
keywords: ['squeebot']
keywords: ['squeebot'],
rateLimits: [],
})
class SqueebotCommandsAPIPlugin extends Plugin {
private commands: CommandSpec[] = [];
private permissions: any = null;
public getRateLimit(room: string): RateLimit | null {
for (const rm of this.config.get('rateLimits', [])) {
if (rm.rooms && (rm.rooms.indexOf(room) !== -1 || rm.rooms.indexOf('*') !== -1)) {
return rm;
}
}
return null;
}
public doLimiting(room: string, sender: string): boolean {
const rl = this.getRateLimit(room);
if (!rl) {
return false;
}
// Hasn't been limited yet
if (!rates[sender]) {
rates[sender] = {
lastMessage: Date.now(),
messages: 1,
};
return false;
}
const r = rates[sender];
if (r.lastMessage > Date.now() - rl.cooldown) {
if (r.messages >= rl.perSecond) {
// Rate limited
return true;
}
r.lastMessage = Date.now();
r.messages++;
return false;
}
r.lastMessage = Date.now();
r.messages = 1;
return false;
}
private roomMatcher(msg: IMessage, specList: CommandSpec[]): CommandSpec[] {
const roomMatches = [];
@ -143,6 +198,14 @@ class SqueebotCommandsAPIPlugin extends Plugin {
// Iteration 4: Match permissions for user
const permitted = this.permissionMatcher(msg, sorted);
// Rate limit check
if (permitted.length &&
this.config.get('rateLimits', []).length &&
this.doLimiting(msg.fullRoomID!, msg.fullSenderID!)) {
logger.warn('[%s] User %s rate limited', this.name, msg.fullSenderID);
return;
}
// Start executing
for (const spec of permitted) {
const success = await spec.execute(msg, spec, prefix, ...separate.slice(1));
@ -199,6 +262,14 @@ class SqueebotCommandsAPIPlugin extends Plugin {
// Iteration 3: Match permissions for user
const permitted = this.permissionMatcher(msg, sorted);
// Rate limit check
if (permitted.length &&
this.config.get('rateLimits', []).length &&
this.doLimiting(msg.fullRoomID!, msg.fullSenderID!)) {
logger.warn('[%s] User %s rate limited', this.name, msg.fullSenderID);
return;
}
// Start executing
for (const spec of permitted) {
const success = await spec.execute(msg, spec, keyword);