diff --git a/src/Queue.ts b/src/Queue.ts new file mode 100644 index 00000000..19969792 --- /dev/null +++ b/src/Queue.ts @@ -0,0 +1,36 @@ +type QueueFn = (...args: any[]) => Promise; + +export class Queue { + protected running: boolean = false; + protected queue: QueueFn[] = []; + protected timeout: number = 10 * 1000; + + public add(fn) { + const promise = new Promise(resolve => { + this.queue.push(async () => { + await fn(); + resolve(); + }); + + if (!this.running) this.next(); + }); + + return promise; + } + + public next() { + this.running = true; + + if (this.queue.length === 0) { + this.running = false; + return; + } + + const fn = this.queue.shift(); + new Promise(resolve => { + // Either fn() completes or the timeout is reached + fn().then(resolve); + setTimeout(resolve, this.timeout); + }).then(() => this.next()); + } +} diff --git a/src/QueuedEventEmitter.ts b/src/QueuedEventEmitter.ts new file mode 100644 index 00000000..f854c59a --- /dev/null +++ b/src/QueuedEventEmitter.ts @@ -0,0 +1,38 @@ +import { Queue } from "./Queue"; + +type Listener = (...args: any[]) => void; + +export class QueuedEventEmitter { + protected listeners: Map; + protected queue: Queue; + + constructor() { + this.listeners = new Map(); + this.queue = new Queue(); + } + + on(eventName: string, listener: Listener) { + if (!this.listeners.has(eventName)) { + this.listeners.set(eventName, []); + } + + this.listeners.get(eventName).push(listener); + } + + off(eventName: string, listener: Listener) { + if (!this.listeners.has(eventName)) { + return; + } + + const listeners = this.listeners.get(eventName); + listeners.splice(listeners.indexOf(listener), 1); + } + + emit(eventName: string, args: any[] = []) { + const listeners = [...(this.listeners.get(eventName) || []), ...(this.listeners.get("*") || [])]; + + listeners.forEach(listener => { + this.queue.add(listener.bind(null, args)); + }); + } +} diff --git a/src/data/GuildSavedMessages.ts b/src/data/GuildSavedMessages.ts index f2ca5554..c84b0985 100644 --- a/src/data/GuildSavedMessages.ts +++ b/src/data/GuildSavedMessages.ts @@ -1,7 +1,7 @@ import { Brackets, getRepository, Repository } from "typeorm"; import { BaseRepository } from "./BaseRepository"; import { ISavedMessageData, SavedMessage } from "./entities/SavedMessage"; -import EventEmitter from "events"; +import { QueuedEventEmitter } from "../QueuedEventEmitter"; import { GuildChannel, Message } from "eris"; import moment from "moment-timezone"; @@ -11,12 +11,12 @@ const RETENTION_PERIOD = 7 * 24 * 60 * 60 * 1000; // 1 week export class GuildSavedMessages extends BaseRepository { private messages: Repository; - public events: EventEmitter; + public events: QueuedEventEmitter; constructor(guildId) { super(guildId); this.messages = getRepository(SavedMessage); - this.events = new EventEmitter(); + this.events = new QueuedEventEmitter(); this.cleanup(); setInterval(() => this.cleanup(), CLEANUP_INTERVAL);