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, * Send this message as a copy (i.e. send the same message,
* but do not forward it). * but do not forward it).

View file

@ -31,6 +31,7 @@ import {
PollVoteHandler, PollVoteHandler,
UserStatusUpdateHandler, UserStatusUpdateHandler,
UserTypingHandler, UserTypingHandler,
UpdateInfoForError,
} from './handler' } from './handler'
// end-codegen-imports // end-codegen-imports
import { filters, UpdateFilter } from './filters' import { filters, UpdateFilter } from './filters'
@ -169,6 +170,14 @@ export class Dispatcher<State = never, SceneName extends string = string> {
private _handlersCount: Record<string, number> = {} 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, * Create a new dispatcher, that will be used as a child,
* optionally providing a custom key delegate * optionally providing a custom key delegate
@ -400,6 +409,7 @@ export class Dispatcher<State = never, SceneName extends string = string> {
RawUpdateHandler RawUpdateHandler
>[] >[]
try {
for (const h of handlers) { for (const h of handlers) {
let result: void | PropagationSymbol let result: void | PropagationSymbol
@ -407,7 +417,10 @@ export class Dispatcher<State = never, SceneName extends string = string> {
!h.check || !h.check ||
(await h.check(parsed, parsedState as never)) (await h.check(parsed, parsedState as never))
) { ) {
result = await h.callback(parsed, parsedState as never) result = await h.callback(
parsed,
parsedState as never
)
} else continue } else continue
if (result === ContinuePropagation) continue if (result === ContinuePropagation) continue
@ -417,6 +430,17 @@ export class Dispatcher<State = never, SceneName extends string = string> {
tryRaw = false tryRaw = false
break break
} }
} catch (e) {
if (this._errorHandler) {
await this._errorHandler(
e,
{ type: parsedType, data: parsed },
parsedState as never
)
} else {
throw e
}
}
} }
if (tryRaw && 'raw' in group) { if (tryRaw && 'raw' in group) {
@ -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 // // children //
/** /**

View file

@ -30,6 +30,16 @@ type ParsedUpdateHandler<Type, Update, State = never> = BaseUpdateHandler<
(update: Update, state: State) => MaybeAsync<boolean> (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< export type RawUpdateHandler = BaseUpdateHandler<
'raw', 'raw',
( (

View file

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

View file

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