import type { Session, Events } from '@biscuitland/core'; import { EventEmitter } from 'node:events'; export interface CollectorOptions { event: E; filter?(...args: Parameters): unknown; max?: number; time?: number; idle?: number; } export class Collector extends EventEmitter { collected = new Set[0]>(); ended = false; private timeout: NodeJS.Timeout; constructor(readonly session: Session, public options: CollectorOptions) { super(); if (!('filter' in this.options)) { this.options.filter = (() => true); } if (!('max' in this.options)) { this.options.max = -1; } this.session.events.setMaxListeners(this.session.events.getMaxListeners() + 1); this.session.events.on(this.options.event, (...args: unknown[]) => this.collect(...args as Parameters)); this.timeout = setTimeout(() => this.stop('time'), this.options.idle ?? this.options.time); } private collect(...args: Parameters) { if (this.options.filter?.(...args)) { this.collected.add(args[0]); this.emit('collect', ...args); } if (this.options.idle) { clearTimeout(this.timeout); this.timeout = setTimeout(() => this.stop('time'), this.options.idle); } if (this.collected.size >= this.options.max!) { this.stop('max'); } } stop(reason?: string) { if (this.ended) { return; } clearTimeout(this.timeout); this.session.events.removeListener(this.options.event, (...args: unknown[]) => this.collect(...args as Parameters)); this.session.events.setMaxListeners(this.session.events.getMaxListeners() - 1); this.ended = true; this.emit('end', reason, this.collected); } on(event: 'collect', listener: (...args: Parameters) => unknown): this; on(event: 'end', listener: (reason: string | null | undefined, collected: Set[0]>) => void): this; on(event: string, listener: unknown): this { return super.on(event, listener as (() => unknown)); } once(event: 'collect', listener: (...args: Parameters) => unknown): this; once(event: 'end', listener: (reason: string | null | undefined, collected: Set[0]>) => void): this; once(event: string, listener: unknown): this { return super.once(event, listener as (() => unknown)); } }