effective rate limiting
This commit is contained in:
parent
ccbf487725
commit
d11f697dec
@ -29,16 +29,71 @@ interface CommandSpec {
|
|||||||
execute(msg: IMessage, command: CommandSpec, prefix: string, ...args: any[]): Promise<boolean>;
|
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({
|
@Configurable({
|
||||||
prefix: {
|
prefix: {
|
||||||
'*': '!'
|
'*': '!'
|
||||||
},
|
},
|
||||||
keywords: ['squeebot']
|
keywords: ['squeebot'],
|
||||||
|
rateLimits: [],
|
||||||
})
|
})
|
||||||
class SqueebotCommandsAPIPlugin extends Plugin {
|
class SqueebotCommandsAPIPlugin extends Plugin {
|
||||||
private commands: CommandSpec[] = [];
|
private commands: CommandSpec[] = [];
|
||||||
private permissions: any = null;
|
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[] {
|
private roomMatcher(msg: IMessage, specList: CommandSpec[]): CommandSpec[] {
|
||||||
const roomMatches = [];
|
const roomMatches = [];
|
||||||
|
|
||||||
@ -143,6 +198,14 @@ class SqueebotCommandsAPIPlugin extends Plugin {
|
|||||||
// Iteration 4: Match permissions for user
|
// Iteration 4: Match permissions for user
|
||||||
const permitted = this.permissionMatcher(msg, sorted);
|
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
|
// Start executing
|
||||||
for (const spec of permitted) {
|
for (const spec of permitted) {
|
||||||
const success = await spec.execute(msg, spec, prefix, ...separate.slice(1));
|
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
|
// Iteration 3: Match permissions for user
|
||||||
const permitted = this.permissionMatcher(msg, sorted);
|
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
|
// Start executing
|
||||||
for (const spec of permitted) {
|
for (const spec of permitted) {
|
||||||
const success = await spec.execute(msg, spec, keyword);
|
const success = await spec.execute(msg, spec, keyword);
|
||||||
|
Loading…
Reference in New Issue
Block a user