163 lines
4.4 KiB
TypeScript
163 lines
4.4 KiB
TypeScript
|
import { Collection } from '../utils/collection.ts'
|
||
|
import { EventEmitter } from '../../deps.ts'
|
||
|
import type { Client } from './client.ts'
|
||
|
|
||
|
export type CollectorFilter = (...args: any[]) => boolean | Promise<boolean>
|
||
|
|
||
|
export interface CollectorOptions {
|
||
|
/** Event name to listen for */
|
||
|
event: string
|
||
|
/** Optionally Client object for deinitOnEnd functionality */
|
||
|
client?: Client
|
||
|
/** Filter function */
|
||
|
filter?: CollectorFilter
|
||
|
/** Max entries to collect */
|
||
|
max?: number
|
||
|
/** Whether or not to de-initialize on end */
|
||
|
deinitOnEnd?: boolean
|
||
|
/** Timeout to end the Collector if not fulfilled if any filter or max */
|
||
|
timeout?: number
|
||
|
}
|
||
|
|
||
|
export class Collector extends EventEmitter {
|
||
|
client?: Client
|
||
|
private _started: boolean = false
|
||
|
event: string
|
||
|
filter: CollectorFilter = () => true
|
||
|
collected: Collection<string, any[]> = 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
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Start collecting */
|
||
|
collect(): Collector {
|
||
|
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 collecting */
|
||
|
end(): Collector {
|
||
|
this.started = false
|
||
|
if (this._timer !== undefined) clearTimeout(this._timer)
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
/** Reset collector and start again */
|
||
|
reset(): Collector {
|
||
|
this.collected = new Collection()
|
||
|
this.collect()
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
/** Init the Collector on Client */
|
||
|
init(client: Client): Collector {
|
||
|
this.client = client
|
||
|
client.addCollector(this)
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
/** De initialize the Collector i.e. remove cleanly */
|
||
|
deinit(client: Client): Collector {
|
||
|
client.removeCollector(this)
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
/** Checks we may want to perform on an extended version of Collector */
|
||
|
protected check(..._args: any[]): boolean | Promise<boolean> {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
/** Fire the Collector */
|
||
|
async _fire(...args: any[]): 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 &&
|
||
|
// linter: send help
|
||
|
this.max < Number(this.collected.size) + 1
|
||
|
) {
|
||
|
this.end()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Set filter of the Collector */
|
||
|
when(filter: CollectorFilter): Collector {
|
||
|
this.filter = filter
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
/** Add a new listener for 'collect' event */
|
||
|
each(handler: CallableFunction): Collector {
|
||
|
this.on('collect', () => handler())
|
||
|
return this
|
||
|
}
|
||
|
|
||
|
/** Returns a Promise resolved when Collector ends or a timeout occurs */
|
||
|
async wait(timeout: number = this.timeout ?? 0): Promise<Collector> {
|
||
|
return await new Promise((resolve, reject) => {
|
||
|
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||
|
if (!timeout)
|
||
|
throw new Error(
|
||
|
'Timeout is required parameter if not given in CollectorOptions'
|
||
|
)
|
||
|
|
||
|
let done = false
|
||
|
const onend = (): void => {
|
||
|
done = true
|
||
|
this.removeListener('end', onend)
|
||
|
resolve(this)
|
||
|
}
|
||
|
|
||
|
this.on('end', onend)
|
||
|
setTimeout(() => {
|
||
|
if (!done) {
|
||
|
this.removeListener('end', onend)
|
||
|
reject(new Error('Timeout'))
|
||
|
}
|
||
|
}, timeout)
|
||
|
})
|
||
|
}
|
||
|
}
|