UNPKG

gramio

Version:

Powerful, extensible and really type-safe Telegram Bot API framework

1,118 lines (1,106 loc) 41.7 kB
import { CallbackData } from '@gramio/callback-data'; export * from '@gramio/callback-data'; import { Context, UpdateName, ContextType, BotLike, Attachment } from '@gramio/contexts'; export * from '@gramio/contexts'; import { APIMethods, TelegramResponseParameters, TelegramAPIResponseError, TelegramUser, APIMethodParams, APIMethodReturn, SetWebhookParams, TelegramUpdate, TelegramReactionTypeEmojiEmoji } from '@gramio/types'; export * from '@gramio/types'; import * as middleware_io from 'middleware-io'; import { Composer as Composer$1, Middleware, CaughtMiddlewareHandler, NextMiddleware } from 'middleware-io'; export * from '@gramio/files'; export * from '@gramio/keyboards'; export * from '@gramio/format'; /** Symbol to determine which error kind is it */ declare const ErrorKind: symbol; /** Represent {@link TelegramAPIResponseError} and thrown in API calls */ declare class TelegramError<T extends keyof APIMethods> extends Error { /** Name of the API Method */ method: T; /** Params that were sent */ params: MaybeSuppressedParams<T>; /** See {@link TelegramAPIResponseError.error_code}*/ code: number; /** Describes why a request was unsuccessful. */ payload?: TelegramResponseParameters; /** Construct new TelegramError */ constructor(error: TelegramAPIResponseError, method: T, params: MaybeSuppressedParams<T>); } type MaybeArray<T> = T | T[] | ReadonlyArray<T>; /** Base-composer without chainable type-safety */ declare class Composer { protected composer: Composer$1<Context<AnyBot> & { [key: string]: unknown; }, Context<AnyBot> & { [key: string]: unknown; }>; length: number; composed: Middleware<Context<AnyBot>>; protected onError: CaughtMiddlewareHandler<Context<AnyBot>>; constructor(onError?: CaughtMiddlewareHandler<Context<any>>); /** Register handler to one or many Updates */ on<T extends UpdateName>(updateName: MaybeArray<T>, handler: Handler<any>): this; /** Register handler to any Update */ use(handler: Handler<any>): this; /** * Derive some data to handlers * * @example * ```ts * new Bot("token").derive((context) => { * return { * superSend: () => context.send("Derived method") * } * }) * ``` */ derive<Update extends UpdateName, Handler extends Hooks.Derive<ContextType<AnyBot, Update>>>(updateNameOrHandler: MaybeArray<Update> | Handler, handler?: Handler): this; protected recompose(): this; compose(context: Context<AnyBot>, next?: middleware_io.NextMiddleware): void; composeWait(context: Context<AnyBot>, next?: middleware_io.NextMiddleware): unknown; } /** * `Plugin` is an object from which you can extends in Bot instance and adopt types * * @example * ```ts * import { Plugin, Bot } from "gramio"; * * export class PluginError extends Error { * wow: "type" | "safe" = "type"; * } * * const plugin = new Plugin("gramio-example") * .error("PLUGIN", PluginError) * .derive(() => { * return { * some: ["derived", "props"] as const, * }; * }); * * const bot = new Bot(process.env.TOKEN!) * .extend(plugin) * .onError(({ context, kind, error }) => { * if (context.is("message") && kind === "PLUGIN") { * console.log(error.wow); * } * }) * .use((context) => { * console.log(context.some); * }); * ``` */ declare class Plugin<Errors extends ErrorDefinitions = {}, Derives extends DeriveDefinitions = DeriveDefinitions> { /** * @internal * Set of Plugin data * * */ _: { /** Name of plugin */ name: string; /** List of plugin dependencies. If user does't extend from listed there dependencies it throw a error */ dependencies: string[]; /** remap generic type. {} in runtime */ Errors: Errors; /** remap generic type. {} in runtime */ Derives: Derives; /** Composer */ composer: Composer; /** Store plugin preRequests hooks */ preRequests: [Hooks.PreRequest<any>, MaybeArray<keyof APIMethods> | undefined][]; /** Store plugin onResponses hooks */ onResponses: [Hooks.OnResponse<any>, MaybeArray<keyof APIMethods> | undefined][]; /** Store plugin onResponseErrors hooks */ onResponseErrors: [Hooks.OnResponseError<any>, MaybeArray<keyof APIMethods> | undefined][]; /** * Store plugin groups * * If you use `on` or `use` in group and on plugin-level groups handlers are registered after plugin-level handlers * */ groups: ((bot: AnyBot) => AnyBot)[]; /** Store plugin onStarts hooks */ onStarts: Hooks.OnStart[]; /** Store plugin onStops hooks */ onStops: Hooks.OnStop[]; /** Store plugin onErrors hooks */ onErrors: Hooks.OnError<any, any>[]; /** Map of plugin errors */ errorsDefinitions: Record<string, { new (...args: any): any; prototype: Error; }>; decorators: Record<string, unknown>; }; "~": { /** Name of plugin */ name: string; /** List of plugin dependencies. If user does't extend from listed there dependencies it throw a error */ dependencies: string[]; /** remap generic type. {} in runtime */ Errors: Errors; /** remap generic type. {} in runtime */ Derives: Derives; /** Composer */ composer: Composer; /** Store plugin preRequests hooks */ preRequests: [Hooks.PreRequest<any>, MaybeArray<keyof APIMethods> | undefined][]; /** Store plugin onResponses hooks */ onResponses: [Hooks.OnResponse<any>, MaybeArray<keyof APIMethods> | undefined][]; /** Store plugin onResponseErrors hooks */ onResponseErrors: [Hooks.OnResponseError<any>, MaybeArray<keyof APIMethods> | undefined][]; /** * Store plugin groups * * If you use `on` or `use` in group and on plugin-level groups handlers are registered after plugin-level handlers * */ groups: ((bot: AnyBot) => AnyBot)[]; /** Store plugin onStarts hooks */ onStarts: Hooks.OnStart[]; /** Store plugin onStops hooks */ onStops: Hooks.OnStop[]; /** Store plugin onErrors hooks */ onErrors: Hooks.OnError<any, any>[]; /** Map of plugin errors */ errorsDefinitions: Record<string, { new (...args: any): any; prototype: Error; }>; decorators: Record<string, unknown>; }; /** Create new Plugin. Please provide `name` */ constructor(name: string, { dependencies }?: { dependencies?: string[]; }); /** Currently not isolated!!! * * > [!WARNING] * > If you use `on` or `use` in a `group` and at the plugin level, the group handlers are registered **after** the handlers at the plugin level */ group(grouped: (bot: Bot<Errors, Derives>) => AnyBot): this; /** * Register custom class-error in plugin **/ error<Name extends string, NewError extends { new (...args: any): any; prototype: Error; }>(kind: Name, error: NewError): Plugin<Errors & { [name in Name]: InstanceType<NewError>; }, Derives>; /** * Derive some data to handlers * * @example * ```ts * new Bot("token").derive((context) => { * return { * superSend: () => context.send("Derived method") * } * }) * ``` */ derive<Handler extends Hooks.Derive<Context<BotLike> & Derives["global"]>>(handler: Handler): Plugin<Errors, Derives & { global: Awaited<ReturnType<Handler>>; }>; derive<Update extends UpdateName, Handler extends Hooks.Derive<ContextType<BotLike, Update> & Derives["global"] & Derives[Update]>>(updateName: MaybeArray<Update>, handler: Handler): Plugin<Errors, Derives & { [K in Update]: Awaited<ReturnType<Handler>>; }>; decorate<Value extends Record<string, any>>(value: Value): Plugin<Errors, Derives & { global: { [K in keyof Value]: Value[K]; }; }>; decorate<Name extends string, Value>(name: Name, value: Value): Plugin<Errors, Derives & { global: { [K in Name]: Value; }; }>; /** Register handler to one or many Updates */ on<T extends UpdateName>(updateName: MaybeArray<T>, handler: Handler<ContextType<BotLike, T> & Derives["global"] & Derives[T]>): this; /** Register handler to any Updates */ use(handler: Handler<Context<BotLike> & Derives["global"]>): this; /** * This hook called before sending a request to Telegram Bot API (allows us to impact the sent parameters). * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).preRequest((context) => { * if (context.method === "sendMessage") { * context.params.text = "mutate params"; * } * * return context; * }); * * bot.start(); * ``` * * [Documentation](https://gramio.dev/hooks/pre-request.html) * */ preRequest<Methods extends keyof APIMethods, Handler extends Hooks.PreRequest<Methods>>(methods: MaybeArray<Methods>, handler: Handler): this; preRequest(handler: Hooks.PreRequest): this; /** * This hook called when API return successful response * * [Documentation](https://gramio.dev/hooks/on-response.html) * */ onResponse<Methods extends keyof APIMethods, Handler extends Hooks.OnResponse<Methods>>(methods: MaybeArray<Methods>, handler: Handler): this; onResponse(handler: Hooks.OnResponse): this; /** * This hook called when API return an error * * [Documentation](https://gramio.dev/hooks/on-response-error.html) * */ onResponseError<Methods extends keyof APIMethods, Handler extends Hooks.OnResponseError<Methods>>(methods: MaybeArray<Methods>, handler: Handler): this; onResponseError(handler: Hooks.OnResponseError): this; /** * This hook called when the bot is `started`. * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).onStart( * ({ plugins, info, updatesFrom }) => { * console.log(`plugin list - ${plugins.join(", ")}`); * console.log(`bot username is @${info.username}`); * console.log(`updates from ${updatesFrom}`); * } * ); * * bot.start(); * ``` * * [Documentation](https://gramio.dev/hooks/on-start.html) * */ onStart(handler: Hooks.OnStart): this; /** * This hook called when the bot stops. * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).onStop( * ({ plugins, info, updatesFrom }) => { * console.log(`plugin list - ${plugins.join(", ")}`); * console.log(`bot username is @${info.username}`); * } * ); * * bot.start(); * bot.stop(); * ``` * * [Documentation](https://gramio.dev/hooks/on-stop.html) * */ onStop(handler: Hooks.OnStop): this; /** * Set error handler. * @example * ```ts * bot.onError("message", ({ context, kind, error }) => { * return context.send(`${kind}: ${error.message}`); * }) * ``` */ onError<T extends UpdateName>(updateName: MaybeArray<T>, handler: Hooks.OnError<Errors, ContextType<BotLike, T> & Derives["global"] & Derives[T]>): this; onError(handler: Hooks.OnError<Errors, Context<BotLike> & Derives["global"]>): this; /** * ! ** At the moment, it can only pick up types** */ extend<NewPlugin extends AnyPlugin>(plugin: MaybePromise<NewPlugin>): Plugin<Errors & NewPlugin["_"]["Errors"], Derives & NewPlugin["_"]["Derives"]>; } /** Bot options that you can provide to {@link Bot} constructor */ interface BotOptions { /** Bot token */ token: string; /** When the bot begins to listen for updates, `GramIO` retrieves information about the bot to verify if the **bot token is valid** * and to utilize some bot metadata. For example, this metadata will be used to strip bot mentions in commands. * * If you set it up, `GramIO` will not send a `getMe` request on startup. * * @important * **You should set this up when horizontally scaling your bot or working in serverless environments.** * */ info?: TelegramUser; /** List of plugins enabled by default */ plugins?: { /** Pass `false` to disable plugin. @default true */ format?: boolean; }; /** Options to configure how to send requests to the Telegram Bot API */ api: { /** Configure {@link fetch} parameters */ fetchOptions?: Parameters<typeof fetch>[1]; /** URL which will be used to send requests to. @default "https://api.telegram.org/bot" */ baseURL: string; /** * Should we send requests to `test` data center? * The test environment is completely separate from the main environment, so you will need to create a new user account and a new bot with `@BotFather`. * * [Documentation](https://core.telegram.org/bots/webapps#using-bots-in-the-test-environment) * @default false * */ useTest?: boolean; /** * Time in milliseconds before calling {@link APIMethods.getUpdates | getUpdates} again * @default 1000 */ retryGetUpdatesWait?: number; }; } /** * Handler is a function with context and next function arguments * * @example * ```ts * const handler: Handler<ContextType<Bot, "message">> = (context, _next) => context.send("HI!"); * * bot.on("message", handler) * ``` */ type Handler<T> = (context: T, next: NextMiddleware) => unknown; interface ErrorHandlerParams<Ctx extends Context<AnyBot>, Kind extends string, Err> { context: Ctx; kind: Kind; error: Err; } type AnyTelegramError<Methods extends keyof APIMethods = keyof APIMethods> = { [APIMethod in Methods]: TelegramError<APIMethod>; }[Methods]; type AnyTelegramMethod<Methods extends keyof APIMethods> = { [APIMethod in Methods]: { method: APIMethod; params: MaybeSuppressedParams<APIMethod>; }; }[Methods]; /** * Interface for add `suppress` param to params */ interface Suppress<IsSuppressed extends boolean | undefined = undefined> { /** * Pass `true` if you want to suppress throwing errors of this method. * * **But this does not undo getting into the `onResponseError` hook**. * * @example * ```ts * const response = await bot.api.sendMessage({ * suppress: true, * chat_id: "@not_found", * text: "Suppressed method" * }); * * if(response instanceof TelegramError) console.error("sendMessage returns an error...") * else console.log("Message has been sent successfully"); * ``` * * */ suppress?: IsSuppressed; } /** Type that assign API params with {@link Suppress} */ type MaybeSuppressedParams<Method extends keyof APIMethods, IsSuppressed extends boolean | undefined = undefined> = APIMethodParams<Method> & Suppress<IsSuppressed>; /** Return method params but with {@link Suppress} */ type SuppressedAPIMethodParams<Method extends keyof APIMethods> = undefined extends APIMethodParams<Method> ? Suppress<true> : MaybeSuppressedParams<Method, true>; /** Type that return MaybeSuppressed API method ReturnType */ type MaybeSuppressedReturn<Method extends keyof APIMethods, IsSuppressed extends boolean | undefined = undefined> = true extends IsSuppressed ? TelegramError<Method> | APIMethodReturn<Method> : APIMethodReturn<Method>; /** Type that return {@link Suppress | Suppressed} API method ReturnType */ type SuppressedAPIMethodReturn<Method extends keyof APIMethods> = MaybeSuppressedReturn<Method, true>; /** Map of APIMethods but with {@link Suppress} */ type SuppressedAPIMethods<Methods extends keyof APIMethods = keyof APIMethods> = { [APIMethod in Methods]: APIMethodParams<APIMethod> extends undefined ? <IsSuppressed extends boolean | undefined = undefined>(params?: Suppress<IsSuppressed>) => Promise<MaybeSuppressedReturn<APIMethod, IsSuppressed>> : undefined extends APIMethodParams<APIMethod> ? <IsSuppressed extends boolean | undefined = undefined>(params?: MaybeSuppressedParams<APIMethod, IsSuppressed>) => Promise<MaybeSuppressedReturn<APIMethod, IsSuppressed>> : <IsSuppressed extends boolean | undefined = undefined>(params: MaybeSuppressedParams<APIMethod, IsSuppressed>) => Promise<MaybeSuppressedReturn<APIMethod, IsSuppressed>>; }; type AnyTelegramMethodWithReturn<Methods extends keyof APIMethods> = { [APIMethod in Methods]: { method: APIMethod; params: APIMethodParams<APIMethod>; response: APIMethodReturn<APIMethod>; }; }[Methods]; /** Type for maybe {@link Promise} or may not */ type MaybePromise<T> = Promise<T> | T; /** * Namespace with GramIO hooks types * * [Documentation](https://gramio.dev/hooks/overview.html) * */ declare namespace Hooks { /** Derive */ type Derive<Ctx> = (context: Ctx) => MaybePromise<Record<string, unknown>>; /** Argument type for {@link PreRequest} */ type PreRequestContext<Methods extends keyof APIMethods> = AnyTelegramMethod<Methods>; /** * Type for `preRequest` hook * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).preRequest((context) => { * if (context.method === "sendMessage") { * context.params.text = "mutate params"; * } * * return context; * }); * * bot.start(); * ``` * * [Documentation](https://gramio.dev/hooks/pre-request.html) * */ type PreRequest<Methods extends keyof APIMethods = keyof APIMethods> = (ctx: PreRequestContext<Methods>) => MaybePromise<PreRequestContext<Methods>>; /** Argument type for {@link OnError} */ type OnErrorContext<Ctx extends Context<AnyBot>, T extends ErrorDefinitions> = ErrorHandlerParams<Ctx, "TELEGRAM", AnyTelegramError> | ErrorHandlerParams<Ctx, "UNKNOWN", Error> | { [K in keyof T]: ErrorHandlerParams<Ctx, K & string, T[K & string]>; }[keyof T]; /** * Type for `onError` hook * * @example * ```typescript * bot.on("message", () => { * bot.api.sendMessage({ * chat_id: "@not_found", * text: "Chat not exists....", * }); * }); * * bot.onError(({ context, kind, error }) => { * if (context.is("message")) return context.send(`${kind}: ${error.message}`); * }); * ``` * * [Documentation](https://gramio.dev/hooks/on-error.html) * */ type OnError<T extends ErrorDefinitions, Ctx extends Context<any> = Context<AnyBot>> = (options: OnErrorContext<Ctx, T>) => unknown; /** * Type for `onStart` hook * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).onStart( * ({ plugins, info, updatesFrom }) => { * console.log(`plugin list - ${plugins.join(", ")}`); * console.log(`bot username is @${info.username}`); * console.log(`updates from ${updatesFrom}`); * } * ); * * bot.start(); * ``` * * [Documentation](https://gramio.dev/hooks/on-start.html) * */ type OnStart = (context: { plugins: string[]; info: TelegramUser; updatesFrom: "webhook" | "long-polling"; }) => unknown; /** * Type for `onStop` hook * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).onStop( * ({ plugins, info, updatesFrom }) => { * console.log(`plugin list - ${plugins.join(", ")}`); * console.log(`bot username is @${info.username}`); * } * ); * * bot.start(); * bot.stop(); * ``` * * [Documentation](https://gramio.dev/hooks/on-stop.html) * */ type OnStop = (context: { plugins: string[]; info: TelegramUser; }) => unknown; /** * Type for `onResponseError` hook * * [Documentation](https://gramio.dev/hooks/on-response-error.html) * */ type OnResponseError<Methods extends keyof APIMethods = keyof APIMethods> = (context: AnyTelegramError<Methods>, api: Bot["api"]) => unknown; /** * Type for `onResponse` hook * * [Documentation](https://gramio.dev/hooks/on-response.html) * */ type OnResponse<Methods extends keyof APIMethods = keyof APIMethods> = (context: AnyTelegramMethodWithReturn<Methods>) => unknown; /** Store hooks */ interface Store<T extends ErrorDefinitions> { preRequest: PreRequest[]; onResponse: OnResponse[]; onResponseError: OnResponseError[]; onError: OnError<T>[]; onStart: OnStart[]; onStop: OnStop[]; } } /** Error map should be map of string: error */ type ErrorDefinitions = Record<string, Error>; /** Map of derives */ type DeriveDefinitions = Record<UpdateName | "global", {}>; type FilterDefinitions = Record<string, (...args: any[]) => (context: Context<Bot>) => boolean>; /** Type of Bot that accepts any generics */ type AnyBot = Bot<any, any>; /** Type of Bot that accepts any generics */ type AnyPlugin = Plugin<any, any>; type CallbackQueryShorthandContext<BotType extends BotLike, Trigger extends CallbackData | string | RegExp> = Omit<ContextType<BotType, "callback_query">, "data"> & BotType["__Derives"]["global"] & BotType["__Derives"]["callback_query"] & { queryData: Trigger extends CallbackData ? ReturnType<Trigger["unpack"]> : Trigger extends RegExp ? RegExpMatchArray : never; }; type BotStartOptionsLongPolling = Omit<NonNullable<APIMethodParams<"getUpdates">>, "allowed_updates" | "offset">; type BotStartOptionsWebhook = true | string | Omit<SetWebhookParams, "drop_pending_updates" | "allowed_updates">; type AllowedUpdates = Exclude<NonNullable<APIMethodParams<"getUpdates">>["allowed_updates"], "update_id">; interface BotStartOptions { webhook?: BotStartOptionsWebhook; longPolling?: BotStartOptionsLongPolling; dropPendingUpdates?: boolean; allowedUpdates?: AllowedUpdates; deleteWebhook?: boolean | "on-conflict-with-polling"; } interface PollingStartOptions { dropPendingUpdates?: boolean; deleteWebhookOnConflict?: boolean; } declare class UpdateQueue<Data = TelegramUpdate> { private updateQueue; private pendingUpdates; private handler; private onIdleResolver; private onIdlePromise; private isActive; constructor(handler: (update: Data) => Promise<unknown>); add(update: Data): void; private start; stop(timeout?: number): Promise<void>; } declare class Updates { private readonly bot; isStarted: boolean; isRequestActive: boolean; private offset; composer: Composer; queue: UpdateQueue<TelegramUpdate>; stopPollingPromiseResolve: ((value?: undefined) => void) | undefined; constructor(bot: AnyBot, onError: CaughtMiddlewareHandler<Context<any>>); handleUpdate(data: TelegramUpdate, mode?: "wait" | "lazy"): Promise<unknown>; /** @deprecated use bot.start instead @internal */ startPolling(params?: APIMethodParams<"getUpdates">, options?: PollingStartOptions): void; startFetchLoop(params?: APIMethodParams<"getUpdates">, options?: PollingStartOptions): Promise<void>; dropPendingUpdates(deleteWebhookOnConflict?: boolean): Promise<void>; /** * ! Soon waitPendingRequests param default will changed to true */ stopPolling(waitPendingRequests?: boolean): Promise<void>; } /** Bot instance * * @example * ```ts * import { Bot } from "gramio"; * * const bot = new Bot("") // put you token here * .command("start", (context) => context.send("Hi!")) * .onStart(console.log); * * bot.start(); * ``` */ declare class Bot<Errors extends ErrorDefinitions = {}, Derives extends DeriveDefinitions = DeriveDefinitions> { /** @deprecated use `~` instead*/ _: { /** @deprecated @internal. Remap generic */ derives: Derives; }; /** @deprecated use `~.derives` instead @internal. Remap generic */ __Derives: Derives; "~": { /** @deprecated @internal. Remap generic */ derives: Derives; }; private filters; /** Options provided to instance */ readonly options: BotOptions; /** Bot data (filled in when calling bot.init/bot.start) */ info: TelegramUser | undefined; /** * Send API Request to Telegram Bot API * * @example * ```ts * const response = await bot.api.sendMessage({ * chat_id: "@gramio_forum", * text: "some text", * }); * ``` * * [Documentation](https://gramio.dev/bot-api.html) */ readonly api: SuppressedAPIMethods; private lazyloadPlugins; private dependencies; private errorsDefinitions; private errorHandler; /** This instance handle updates */ updates: Updates; private hooks; constructor(token: string, options?: Omit<BotOptions, "token" | "api"> & { api?: Partial<BotOptions["api"]>; }); constructor(options: Omit<BotOptions, "api"> & { api?: Partial<BotOptions["api"]>; }); private runHooks; private runImmutableHooks; private _callApi; /** * Download file * * @example * ```ts * bot.on("message", async (context) => { * if (!context.document) return; * // download to ./file-name * await context.download(context.document.fileName || "file-name"); * // get ArrayBuffer * const buffer = await context.download(); * * return context.send("Thank you!"); * }); * ``` * [Documentation](https://gramio.dev/files/download.html) */ downloadFile(attachment: Attachment | { file_id: string; } | string): Promise<ArrayBuffer>; downloadFile(attachment: Attachment | { file_id: string; } | string, path: string): Promise<string>; /** * Register custom class-error for type-safe catch in `onError` hook * * @example * ```ts * export class NoRights extends Error { * needRole: "admin" | "moderator"; * * constructor(role: "admin" | "moderator") { * super(); * this.needRole = role; * } * } * * const bot = new Bot(process.env.TOKEN!) * .error("NO_RIGHTS", NoRights) * .onError(({ context, kind, error }) => { * if (context.is("message") && kind === "NO_RIGHTS") * return context.send( * format`You don't have enough rights! You need to have an «${bold( * error.needRole * )}» role.` * ); * }); * * bot.updates.on("message", (context) => { * if (context.text === "bun") throw new NoRights("admin"); * }); * ``` */ error<Name extends string, NewError extends { new (...args: any): any; prototype: Error; }>(kind: Name, error: NewError): Bot<Errors & { [name in Name]: InstanceType<NewError>; }, Derives>; /** * Set error handler. * @example * ```ts * bot.onError("message", ({ context, kind, error }) => { * return context.send(`${kind}: ${error.message}`); * }) * ``` */ onError<T extends UpdateName>(updateName: MaybeArray<T>, handler: Hooks.OnError<Errors, ContextType<typeof this, T>>): this; onError(handler: Hooks.OnError<Errors, Context<typeof this> & Derives["global"]>): this; /** * Derive some data to handlers * * @example * ```ts * new Bot("token").derive((context) => { * return { * superSend: () => context.send("Derived method") * } * }) * ``` */ derive<Handler extends Hooks.Derive<Context<typeof this> & Derives["global"]>>(handler: Handler): Bot<Errors, Derives & { global: Awaited<ReturnType<Handler>>; }>; derive<Update extends UpdateName, Handler extends Hooks.Derive<ContextType<typeof this, Update> & Derives["global"] & Derives[Update]>>(updateName: MaybeArray<Update>, handler: Handler): Bot<Errors, Derives & { [K in Update]: Awaited<ReturnType<Handler>>; }>; decorate<Value extends Record<string, any>>(value: Value): Bot<Errors, Derives & { global: { [K in keyof Value]: Value[K]; }; }>; decorate<Name extends string, Value>(name: Name, value: Value): Bot<Errors, Derives & { global: { [K in Name]: Value; }; }>; /** * This hook called when the bot is `started`. * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).onStart( * ({ plugins, info, updatesFrom }) => { * console.log(`plugin list - ${plugins.join(", ")}`); * console.log(`bot username is @${info.username}`); * console.log(`updates from ${updatesFrom}`); * } * ); * * bot.start(); * ``` * * [Documentation](https://gramio.dev/hooks/on-start.html) * */ onStart(handler: Hooks.OnStart): this; /** * This hook called when the bot stops. * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).onStop( * ({ plugins, info, updatesFrom }) => { * console.log(`plugin list - ${plugins.join(", ")}`); * console.log(`bot username is @${info.username}`); * } * ); * * bot.start(); * bot.stop(); * ``` * * [Documentation](https://gramio.dev/hooks/on-stop.html) * */ onStop(handler: Hooks.OnStop): this; /** * This hook called before sending a request to Telegram Bot API (allows us to impact the sent parameters). * * @example * ```typescript * import { Bot } from "gramio"; * * const bot = new Bot(process.env.TOKEN!).preRequest((context) => { * if (context.method === "sendMessage") { * context.params.text = "mutate params"; * } * * return context; * }); * * bot.start(); * ``` * * [Documentation](https://gramio.dev/hooks/pre-request.html) * */ preRequest<Methods extends keyof APIMethods, Handler extends Hooks.PreRequest<Methods>>(methods: MaybeArray<Methods>, handler: Handler): this; preRequest(handler: Hooks.PreRequest): this; /** * This hook called when API return successful response * * [Documentation](https://gramio.dev/hooks/on-response.html) * */ onResponse<Methods extends keyof APIMethods, Handler extends Hooks.OnResponse<Methods>>(methods: MaybeArray<Methods>, handler: Handler): this; onResponse(handler: Hooks.OnResponse): this; /** * This hook called when API return an error * * [Documentation](https://gramio.dev/hooks/on-response-error.html) * */ onResponseError<Methods extends keyof APIMethods, Handler extends Hooks.OnResponseError<Methods>>(methods: MaybeArray<Methods>, handler: Handler): this; onResponseError(handler: Hooks.OnResponseError): this; /** Register handler to one or many Updates */ on<T extends UpdateName>(updateName: MaybeArray<T>, handler: Handler<ContextType<typeof this, T>>): this; /** Register handler to any Updates */ use(handler: Handler<Context<typeof this> & Derives["global"]>): this; /** * Extend {@link Plugin} logic and types * * @example * ```ts * import { Plugin, Bot } from "gramio"; * * export class PluginError extends Error { * wow: "type" | "safe" = "type"; * } * * const plugin = new Plugin("gramio-example") * .error("PLUGIN", PluginError) * .derive(() => { * return { * some: ["derived", "props"] as const, * }; * }); * * const bot = new Bot(process.env.TOKEN!) * .extend(plugin) * .onError(({ context, kind, error }) => { * if (context.is("message") && kind === "PLUGIN") { * console.log(error.wow); * } * }) * .use((context) => { * console.log(context.some); * }); * ``` */ extend<NewPlugin extends AnyPlugin>(plugin: MaybePromise<NewPlugin>): Bot<Errors & NewPlugin["_"]["Errors"], Derives & NewPlugin["_"]["Derives"]>; /** * Register handler to reaction (`message_reaction` update) * * @example * ```ts * new Bot().reaction("👍", async (context) => { * await context.reply(`Thank you!`); * }); * ``` * */ reaction(trigger: MaybeArray<TelegramReactionTypeEmojiEmoji>, handler: (context: ContextType<typeof this, "message_reaction">) => unknown): this; /** * Register handler to `callback_query` event * * @example * ```ts * const someData = new CallbackData("example").number("id"); * * new Bot() * .command("start", (context) => * context.send("some", { * reply_markup: new InlineKeyboard().text( * "example", * someData.pack({ * id: 1, * }) * ), * }) * ) * .callbackQuery(someData, (context) => { * context.queryData; // is type-safe * }); * ``` */ callbackQuery<Trigger extends CallbackData | string | RegExp>(trigger: Trigger, handler: (context: CallbackQueryShorthandContext<typeof this, Trigger>) => unknown): this; /** Register handler to `chosen_inline_result` update */ chosenInlineResult<Ctx = ContextType<typeof this, "chosen_inline_result">>(trigger: RegExp | string | ((context: Ctx) => boolean), handler: (context: Ctx & { args: RegExpMatchArray | null; }) => unknown): this; /** * Register handler to `inline_query` update * * @example * ```ts * new Bot().inlineQuery( * /regular expression with (.*)/i, * async (context) => { * if (context.args) { * await context.answer( * [ * InlineQueryResult.article( * "id-1", * context.args[1], * InputMessageContent.text("some"), * { * reply_markup: new InlineKeyboard().text( * "some", * "callback-data" * ), * } * ), * ], * { * cache_time: 0, * } * ); * } * }, * { * onResult: (context) => context.editText("Message edited!"), * } * ); * ``` * */ inlineQuery<Ctx = ContextType<typeof this, "inline_query">>(trigger: RegExp | string | ((context: Ctx) => boolean), handler: (context: Ctx & { args: RegExpMatchArray | null; }) => unknown, options?: { onResult?: (context: ContextType<Bot, "chosen_inline_result"> & { args: RegExpMatchArray | null; }) => unknown; }): this; /** * Register handler to `message` and `business_message` event * * new Bot().hears(/regular expression with (.*)/i, async (context) => { * if (context.args) await context.send(`Params ${context.args[1]}`); * }); */ hears<Ctx = ContextType<typeof this, "message">, Trigger extends RegExp | MaybeArray<string> | ((context: Ctx) => boolean) = RegExp | MaybeArray<string> | ((context: Ctx) => boolean)>(trigger: Trigger, handler: (context: Ctx & { args: RegExpMatchArray | null; }) => unknown): this; /** * Register handler to `message` and `business_message` event when entities contains a command * * new Bot().command("start", async (context) => { * return context.send(`You message is /start ${context.args}`); * }); */ command(command: MaybeArray<string>, handler: (context: ContextType<typeof this, "message"> & { args: string | null; }) => unknown): this; /** Currently not isolated!!! */ group(grouped: (bot: typeof this) => AnyBot): typeof this; /** * Init bot. Call it manually only if you doesn't use {@link Bot.start} */ init(): Promise<void>; /** * Start receive updates via long-polling or webhook * * @example * ```ts * import { Bot } from "gramio"; * * const bot = new Bot("") // put you token here * .command("start", (context) => context.send("Hi!")) * .onStart(console.log); * * bot.start(); * ``` */ start({ webhook, longPolling, dropPendingUpdates, allowedUpdates, deleteWebhook: deleteWebhookRaw, }?: BotStartOptions): Promise<TelegramUser>; /** * Stops receiving events via long-polling or webhook * */ stop(timeout?: number): Promise<void>; } declare const frameworks: { elysia: ({ body, headers }: any) => { update: any; header: any; unauthorized: () => Response; response: () => Response; }; fastify: (request: any, reply: any) => { update: any; header: any; unauthorized: () => any; response: () => any; }; hono: (c: any) => { update: any; header: any; unauthorized: () => any; response: () => Response; }; express: (req: any, res: any) => { update: any; header: any; unauthorized: () => any; response: () => any; }; koa: (ctx: any) => { update: any; header: any; unauthorized: () => void; response: () => void; }; http: (req: any, res: any) => { update: Promise<TelegramUpdate>; header: any; unauthorized: () => any; response: () => any; }; "std/http": (req: any) => { update: any; header: any; response: () => Response; unauthorized: () => Response; }; "Bun.serve": (req: any) => { update: any; header: any; response: () => Response; unauthorized: () => Response; }; cloudflare: (req: any) => { update: any; header: any; response: () => Response; unauthorized: () => Response; }; Request: (req: any) => { update: any; header: any; response: () => Response; unauthorized: () => Response; }; }; /** Union type of webhook handlers name */ type WebhookHandlers = keyof typeof frameworks; interface WebhookHandlerOptionsShouldWait { /** Action to take when timeout occurs. @default "throw" */ onTimeout?: "throw" | "return"; /** Timeout in milliseconds. @default 10_000 */ timeout?: number; } interface WebhookHandlerOptions { secretToken?: string; shouldWait?: boolean | WebhookHandlerOptionsShouldWait; } /** * Setup handler with yours web-framework to receive updates via webhook * * @example * ```ts * import { Bot } from "gramio"; * import Fastify from "fastify"; * * const bot = new Bot(process.env.TOKEN as string).on( * "message", * (context) => { * return context.send("Fastify!"); * }, * ); * * const fastify = Fastify(); * * fastify.post("/telegram-webhook", webhookHandler(bot, "fastify")); * * fastify.listen({ port: 3445, host: "::" }); * * bot.start({ * webhook: { * url: "https://example.com:3445/telegram-webhook", * }, * }); * ``` */ declare function webhookHandler<Framework extends keyof typeof frameworks>(bot: Bot, framework: Framework, secretTokenOrOptions?: string | WebhookHandlerOptions): ReturnType<(typeof frameworks)[Framework]> extends { response: () => any; } ? (...args: Parameters<(typeof frameworks)[Framework]>) => ReturnType<ReturnType<(typeof frameworks)[Framework]>["response"]> : (...args: Parameters<(typeof frameworks)[Framework]>) => void; export { type AllowedUpdates, type AnyBot, type AnyPlugin, Bot, type BotOptions, type BotStartOptions, type BotStartOptionsLongPolling, type BotStartOptionsWebhook, type CallbackQueryShorthandContext, Composer, type DeriveDefinitions, type ErrorDefinitions, ErrorKind, type FilterDefinitions, type Handler, Hooks, type MaybePromise, type MaybeSuppressedParams, type MaybeSuppressedReturn, Plugin, type PollingStartOptions, type Suppress, type SuppressedAPIMethodParams, type SuppressedAPIMethodReturn, type SuppressedAPIMethods, TelegramError, Updates, type WebhookHandlerOptions, type WebhookHandlerOptionsShouldWait, type WebhookHandlers, webhookHandler };