import { Collection } from '../utils/collection.ts'import type { Client } from '../client/client.ts'import { HarmonyEventEmitter } from '../utils/events.ts'
export type CollectorFilter = (...args: any[]) => boolean | Promise<boolean>
export interface CollectorOptions { event: string client?: Client filter?: CollectorFilter max?: number deinitOnEnd?: boolean timeout?: number}
export type CollectorEvents = { start: [] end: [] collect: any[]}
export class Collector< T extends unknown[] = any[]> extends HarmonyEventEmitter<CollectorEvents> { client?: Client private _started: boolean = false event: string filter: CollectorFilter = () => true collected: Collection<string, T> = new Collection() max?: number deinitOnEnd: boolean = false timeout?: number private _timer?: number
get started(): boolean { return this._started }
set started(d: boolean) { if (d !== this._started) { this._started = d if (d) this.emit('start') else { if (this.deinitOnEnd && this.client !== undefined) this.deinit(this.client) this.emit('end') } } }
constructor(options: CollectorOptions | string) { super() if (typeof options === 'string') this.event = options else { this.event = options.event this.client = options.client this.filter = options.filter ?? (() => true) this.max = options.max this.deinitOnEnd = options.deinitOnEnd ?? false this.timeout = options.timeout } }
collect(): this { this.started = true if (this.client !== undefined) this.init(this.client) if (this._timer !== undefined) clearTimeout(this._timer) if (this.timeout !== undefined) { this._timer = setTimeout(() => { this.end() }, this.timeout) } return this }
end(): this { this.started = false if (this._timer !== undefined) clearTimeout(this._timer) return this }
reset(): this { this.collected = new Collection() this.collect() return this }
init(client: Client): this { this.client = client client.addCollector(this) return this }
deinit(client: Client): this { client.removeCollector(this) return this }
protected check(..._args: T): boolean | Promise<boolean> { return true }
async _fire(...args: T): Promise<void> { if (!this.started) return const check = await this.check(...args) if (!check) return const filter = await this.filter(...args) if (!filter) return this.collected.set((Number(this.collected.size) + 1).toString(), args) this.emit('collect', ...args) if ( this.max !== undefined && this.max < Number(this.collected.size) + 1 ) { this.end() } }
when(filter: CollectorFilter): this { this.filter = filter return this }
each(handler: CallableFunction): this { this.on('collect', () => handler()) return this }
async wait(timeout?: number): Promise<this> { if (timeout === undefined) timeout = this.timeout ?? 0 return await new Promise((resolve, reject) => { if (!timeout) throw new Error( 'Timeout is required parameter if not given in CollectorOptions' )
let done = false const onend = (): void => { done = true this.off('end', onend) resolve(this) }
this.on('end', onend) setTimeout(() => { if (!done) { this.off('end', onend) reject(new Error('Timeout')) } }, timeout) }) }}