From 7a0de134bae7a4b5df196304bb6d2708511aae42 Mon Sep 17 00:00:00 2001 From: teidesu Date: Mon, 7 Jun 2021 23:59:17 +0300 Subject: [PATCH] feat(dispatcher): local error handling --- packages/client/src/types/messages/message.ts | 14 ++++ packages/dispatcher/src/dispatcher.ts | 79 ++++++++++++++++--- packages/dispatcher/src/handler.ts | 10 +++ packages/node/index.ts | 9 ++- packages/node/package.json | 3 +- 5 files changed, 98 insertions(+), 17 deletions(-) diff --git a/packages/client/src/types/messages/message.ts b/packages/client/src/types/messages/message.ts index 7e51e993..08360a16 100644 --- a/packages/client/src/types/messages/message.ts +++ b/packages/client/src/types/messages/message.ts @@ -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[3] + ): Promise { + 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). diff --git a/packages/dispatcher/src/dispatcher.ts b/packages/dispatcher/src/dispatcher.ts index f8927f65..9a4da773 100644 --- a/packages/dispatcher/src/dispatcher.ts +++ b/packages/dispatcher/src/dispatcher.ts @@ -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 { private _handlersCount: Record = {} + private _errorHandler?: < + T extends Exclude + >( + err: Error, + update: UpdateInfoForError, + state?: UpdateState + ) => MaybeAsync + /** * Create a new dispatcher, that will be used as a child, * optionally providing a custom key delegate @@ -400,22 +409,37 @@ export class Dispatcher { 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 { } } + /** + * 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, + state?: UpdateState + ) => MaybeAsync) + | null + ): void { + if (handler) this._errorHandler = handler + else this._errorHandler = undefined + } + // children // /** diff --git a/packages/dispatcher/src/handler.ts b/packages/dispatcher/src/handler.ts index dd69ab92..76df4097 100644 --- a/packages/dispatcher/src/handler.ts +++ b/packages/dispatcher/src/handler.ts @@ -30,6 +30,16 @@ type ParsedUpdateHandler = BaseUpdateHandler< (update: Update, state: State) => MaybeAsync > +export type UpdateInfoForError = T extends ParsedUpdateHandler< + infer K, + infer Q +> + ? { + type: K + data: Q + } + : never + export type RawUpdateHandler = BaseUpdateHandler< 'raw', ( diff --git a/packages/node/index.ts b/packages/node/index.ts index 1fcac47f..1740f6f5 100644 --- a/packages/node/index.ts +++ b/packages/node/index.ts @@ -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 { @@ -63,7 +68,7 @@ export const input = (text: string): Promise => { export class NodeTelegramClient extends TelegramClient { constructor(opts: NodeTelegramClient.Options) { super({ - crypto: () => new NodeNativeCryptoProvider(), + crypto: nativeCrypto ? () => new nativeCrypto!() : undefined, ...opts, storage: typeof opts.storage === 'string' diff --git a/packages/node/package.json b/packages/node/package.json index 2f384ba1..e8037481 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -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" } }