refactor(dispatcher): store and lookup handlers by their type

avoids redundant loops over all registered handlers
This commit is contained in:
teidesu 2021-05-23 14:43:40 +03:00
parent 2d335af78e
commit b926178b9d

View file

@ -82,7 +82,7 @@ const userTypingParser: UpdateParser = [
] ]
const deleteMessageParser: UpdateParser = [ const deleteMessageParser: UpdateParser = [
'delete_message', 'delete_message',
(client, upd) => new DeleteMessageUpdate(client, upd as any) (client, upd) => new DeleteMessageUpdate(client, upd as any),
] ]
const PARSERS: Partial< const PARSERS: Partial<
@ -132,7 +132,10 @@ const PARSERS: Partial<
* Updates dispatcher * Updates dispatcher
*/ */
export class Dispatcher { export class Dispatcher {
private _groups: Record<number, UpdateHandler[]> = {} private _groups: Record<
number,
Record<UpdateHandler['type'], UpdateHandler[]>
> = {}
private _groupsOrder: number[] = [] private _groupsOrder: number[] = []
private _client?: TelegramClient private _client?: TelegramClient
@ -220,7 +223,7 @@ export class Dispatcher {
async dispatchUpdateNow( async dispatchUpdateNow(
update: tl.TypeUpdate | tl.TypeMessage, update: tl.TypeUpdate | tl.TypeMessage,
users: UsersIndex, users: UsersIndex,
chats: ChatsIndex, chats: ChatsIndex
): Promise<void> { ): Promise<void> {
return this._dispatchUpdateNowImpl(update, users, chats) return this._dispatchUpdateNowImpl(update, users, chats)
} }
@ -246,33 +249,54 @@ export class Dispatcher {
} }
outer: for (const grp of this._groupsOrder) { outer: for (const grp of this._groupsOrder) {
for (const handler of this._groups[grp]) { const group = this._groups[grp]
let tryRaw = !isRawMessage
if (parsedType && parsedType in group) {
// raw is not handled here, so we can safely assume this
const handlers = group[parsedType] as Exclude<
UpdateHandler,
RawUpdateHandler
>[]
for (const h of handlers) {
let result: void | PropagationSymbol
if (!h.check || (await h.check(parsed, this._client))) {
result = await h.callback(parsed, this._client)
} else continue
if (result === ContinuePropagation) continue
if (result === StopPropagation) break outer
if (result === StopChildrenPropagation) return
tryRaw = false
break
}
}
if (tryRaw && 'raw' in group) {
const handlers = group['raw'] as RawUpdateHandler[]
for (const h of handlers) {
let result: void | PropagationSymbol let result: void | PropagationSymbol
if ( if (
handler.type === 'raw' && !h.check ||
!isRawMessage && (await h.check(
(!handler.check ||
(await handler.check(
this._client, this._client,
update as any, update as any,
users, users,
chats chats
))) ))
) { ) {
result = await handler.callback( result = await h.callback(
this._client, this._client,
update as any, update as any,
users, users,
chats chats
) )
} else if (
parsedType &&
handler.type === parsedType &&
(!handler.check ||
(await handler.check(parsed, this._client)))
) {
result = await handler.callback(parsed, this._client)
} else continue } else continue
if (result === ContinuePropagation) continue if (result === ContinuePropagation) continue
@ -282,9 +306,16 @@ export class Dispatcher {
break break
} }
} }
}
for (const child of this._children) { for (const child of this._children) {
await child._dispatchUpdateNowImpl(update, users, chats, parsed, parsedType) await child._dispatchUpdateNowImpl(
update,
users,
chats,
parsed,
parsedType
)
} }
} }
@ -296,12 +327,16 @@ export class Dispatcher {
*/ */
addUpdateHandler(handler: UpdateHandler, group = 0): void { addUpdateHandler(handler: UpdateHandler, group = 0): void {
if (!(group in this._groups)) { if (!(group in this._groups)) {
this._groups[group] = [] this._groups[group] = {} as any
this._groupsOrder.push(group) this._groupsOrder.push(group)
this._groupsOrder.sort((a, b) => a - b) this._groupsOrder.sort((a, b) => a - b)
} }
this._groups[group].push(handler) if (!(handler.type in this._groups[group])) {
this._groups[group][handler.type] = []
}
this._groups[group][handler.type].push(handler)
} }
/** /**
@ -309,24 +344,26 @@ export class Dispatcher {
* handler group. * handler group.
* *
* @param handler Update handler to remove, its type or `'all'` to remove all * @param handler Update handler to remove, its type or `'all'` to remove all
* @param group Handler group index * @param group Handler group index (-1 to affect all groups)
* @internal * @internal
*/ */
removeUpdateHandler( removeUpdateHandler(
handler: UpdateHandler | UpdateHandler['type'] | 'all', handler: UpdateHandler | UpdateHandler['type'] | 'all',
group = 0 group = 0
): void { ): void {
if (!(group in this._groups)) { if (group !== -1 && !(group in this._groups)) {
return return
} }
if (typeof handler === 'string') { if (typeof handler === 'string') {
if (handler === 'all') { if (handler === 'all') {
delete this._groups[group] if (group === -1) {
this._groups = {}
} else { } else {
this._groups[group] = this._groups[group].filter( delete this._groups[group]
(h) => h.type !== handler }
) } else {
delete this._groups[group][handler]
} }
return return
} }
@ -335,9 +372,9 @@ export class Dispatcher {
return return
} }
const idx = this._groups[group].indexOf(handler) const idx = this._groups[group][handler.type].indexOf(handler)
if (idx > 0) { if (idx > 0) {
this._groups[group].splice(idx, 1) this._groups[group][handler.type].splice(idx, 1)
} }
} }
@ -418,10 +455,19 @@ export class Dispatcher {
extend(other: Dispatcher): void { extend(other: Dispatcher): void {
other._groupsOrder.forEach((group) => { other._groupsOrder.forEach((group) => {
if (!(group in this._groups)) { if (!(group in this._groups)) {
this._groups[group] = [] this._groups[group] = other._groups[group]
this._groupsOrder.push(group) this._groupsOrder.push(group)
} else {
const otherGrp = other._groups[group] as any
const selfGrp = this._groups[group] as any
Object.keys(otherGrp).forEach((typ) => {
if (!(typ in selfGrp)) {
selfGrp[typ] = otherGrp[typ]
} else {
selfGrp[typ].push(...otherGrp[typ])
}
})
} }
this._groups[group].push(...other._groups[group])
}) })
this._groupsOrder.sort((a, b) => a - b) this._groupsOrder.sort((a, b) => a - b)