feat(dispatcher): local error handling

This commit is contained in:
teidesu 2021-06-07 23:59:17 +03:00
parent 8cbd6e14c8
commit 7a0de134ba
5 changed files with 98 additions and 17 deletions

View file

@ -786,6 +786,20 @@ export class Message {
})
}
/**
* Forward this message to some chat
*
* @param peer Chat where to forward this message
* @param params
* @returns Forwarded message
*/
forwardTo(
peer: InputPeerLike,
params?: Parameters<TelegramClient['forwardMessages']>[3]
): Promise<Message> {
return this.client.forwardMessages(peer, this.chat.inputPeer, this.id, params)
}
/**
* Send this message as a copy (i.e. send the same message,
* but do not forward it).

View file

@ -31,6 +31,7 @@ import {
PollVoteHandler,
UserStatusUpdateHandler,
UserTypingHandler,
UpdateInfoForError,
} from './handler'
// end-codegen-imports
import { filters, UpdateFilter } from './filters'
@ -169,6 +170,14 @@ export class Dispatcher<State = never, SceneName extends string = string> {
private _handlersCount: Record<string, number> = {}
private _errorHandler?: <
T extends Exclude<UpdateHandler, RawUpdateHandler>
>(
err: Error,
update: UpdateInfoForError<T>,
state?: UpdateState<State, SceneName>
) => MaybeAsync<void>
/**
* Create a new dispatcher, that will be used as a child,
* optionally providing a custom key delegate
@ -400,22 +409,37 @@ export class Dispatcher<State = never, SceneName extends string = string> {
RawUpdateHandler
>[]
for (const h of handlers) {
let result: void | PropagationSymbol
try {
for (const h of handlers) {
let result: void | PropagationSymbol
if (
!h.check ||
(await h.check(parsed, parsedState as never))
) {
result = await h.callback(parsed, parsedState as never)
} else continue
if (
!h.check ||
(await h.check(parsed, parsedState as never))
) {
result = await h.callback(
parsed,
parsedState as never
)
} else continue
if (result === ContinuePropagation) continue
if (result === StopPropagation) break outer
if (result === StopChildrenPropagation) return
if (result === ContinuePropagation) continue
if (result === StopPropagation) break outer
if (result === StopChildrenPropagation) return
tryRaw = false
break
tryRaw = false
break
}
} catch (e) {
if (this._errorHandler) {
await this._errorHandler(
e,
{ type: parsedType, data: parsed },
parsedState as never
)
} else {
throw e
}
}
}
@ -542,6 +566,35 @@ export class Dispatcher<State = never, SceneName extends string = string> {
}
}
/**
* Register an error handler.
*
* This is used locally within this dispatcher
* (does not affect children/parent) whenever
* an error is thrown inside an update handler.
* Not used for raw update handlers
*
* When an error is thrown, but there is no error
* handler, it is propagated to `TelegramClient`.
*
* There can be at most one error handler.
* Pass `null` to remove it.
*
* @param handler Error handler
*/
onError(
handler:
| ((
err: Error,
update: UpdateInfoForError<UpdateHandler>,
state?: UpdateState<State, SceneName>
) => MaybeAsync<void>)
| null
): void {
if (handler) this._errorHandler = handler
else this._errorHandler = undefined
}
// children //
/**

View file

@ -30,6 +30,16 @@ type ParsedUpdateHandler<Type, Update, State = never> = BaseUpdateHandler<
(update: Update, state: State) => MaybeAsync<boolean>
>
export type UpdateInfoForError<T> = T extends ParsedUpdateHandler<
infer K,
infer Q
>
? {
type: K
data: Q
}
: never
export type RawUpdateHandler = BaseUpdateHandler<
'raw',
(

View file

@ -1,6 +1,6 @@
import { TelegramClient, User } from '@mtcute/client'
import { BaseTelegramClient } from '@mtcute/core'
import { NodeNativeCryptoProvider } from '@mtcute/crypto-node'
import type { NodeNativeCryptoProvider } from '@mtcute/crypto-node'
import { HtmlMessageEntityParser } from '@mtcute/html-parser'
import { MarkdownMessageEntityParser } from '@mtcute/markdown-parser'
import { SqliteStorage } from '@mtcute/sqlite'
@ -9,6 +9,11 @@ import { createInterface, Interface as RlInterface } from 'readline'
export * from '@mtcute/dispatcher'
export { SqliteStorage }
let nativeCrypto: typeof NodeNativeCryptoProvider | null
try {
nativeCrypto = require('@mtcute/crypto-node').NodeNativeCryptoProvider
} catch (e) {}
export namespace NodeTelegramClient {
export interface Options
extends Omit<BaseTelegramClient.Options, 'storage'> {
@ -63,7 +68,7 @@ export const input = (text: string): Promise<string> => {
export class NodeTelegramClient extends TelegramClient {
constructor(opts: NodeTelegramClient.Options) {
super({
crypto: () => new NodeNativeCryptoProvider(),
crypto: nativeCrypto ? () => new nativeCrypto!() : undefined,
...opts,
storage:
typeof opts.storage === 'string'

View file

@ -16,7 +16,6 @@
"@mtcute/sqlite": "^1.0.0",
"@mtcute/markdown-parser": "^1.0.0",
"@mtcute/html-parser": "^1.0.0",
"@mtcute/dispatcher": "^1.0.0",
"@mtcute/crypto-node": "^1.0.0"
"@mtcute/dispatcher": "^1.0.0"
}
}