Comparing version 0.0.51 to 0.1.0
1022
dist/index.d.ts
@@ -0,16 +1,1012 @@ | ||
import { CallbackData } from '@gramio/callback-data'; | ||
export * from '@gramio/callback-data'; | ||
import { Context, UpdateName, MaybeArray, ContextType, Attachment } from '@gramio/contexts'; | ||
export * from '@gramio/contexts'; | ||
import { APIMethods, TelegramResponseParameters, TelegramAPIResponseError, TelegramUser, APIMethodParams, APIMethodReturn, TelegramUpdate, TelegramReactionTypeEmojiEmoji, SetMyCommandsParams, TelegramBotCommand } 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>); | ||
} | ||
/** 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; | ||
} | ||
/** | ||
* @module | ||
* `Plugin` is an object from which you can extends in Bot instance and adopt types | ||
* | ||
* Powerful Telegram Bot API framework | ||
* @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); | ||
* }); | ||
* ``` | ||
*/ | ||
export * from "./bot"; | ||
export * from "./errors"; | ||
export * from "./types"; | ||
export * from "./plugin"; | ||
export * from "./webhook/index"; | ||
export * from "@gramio/contexts"; | ||
export * from "@gramio/files"; | ||
export * from "@gramio/keyboards"; | ||
export type * from "@gramio/types"; | ||
export * from "@gramio/format"; | ||
export * from "@gramio/callback-data"; | ||
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>; | ||
}; | ||
/** 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<AnyBot> & Derives["global"]>>(handler: Handler): Plugin<Errors, Derives & { | ||
global: Awaited<ReturnType<Handler>>; | ||
}>; | ||
derive<Update extends UpdateName, Handler extends Hooks.Derive<ContextType<AnyBot, 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<AnyBot, T> & Derives["global"] & Derives[T]>): this; | ||
/** Register handler to any Updates */ | ||
use(handler: Handler<Context<AnyBot> & 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<Bot, T> & Derives["global"] & Derives[T]>): this; | ||
onError(handler: Hooks.OnError<Errors, Context<AnyBot> & Derives["global"]>): this; | ||
} | ||
/** 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 instance, this metadata can 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>; | ||
declare class Updates { | ||
private readonly bot; | ||
isStarted: boolean; | ||
private offset; | ||
composer: Composer; | ||
constructor(bot: AnyBot, onError: CaughtMiddlewareHandler<Context<any>>); | ||
handleUpdate(data: TelegramUpdate): Promise<void>; | ||
/** @deprecated use bot.start instead @internal */ | ||
startPolling(params?: APIMethodParams<"getUpdates">): Promise<void>; | ||
startFetchLoop(params?: APIMethodParams<"getUpdates">): Promise<void>; | ||
stopPolling(): 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> { | ||
_: { | ||
/** @internal. Remap generic */ | ||
derives: Derives; | ||
}; | ||
/** @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> & Derives["global"] & Derives[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> & Derives["global"] & Derives[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"> & Derives["global"] & Derives["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: Omit<ContextType<typeof this, "callback_query">, "data"> & Derives["global"] & Derives["callback_query"] & { | ||
queryData: Trigger extends CallbackData ? ReturnType<Trigger["unpack"]> : RegExpMatchArray | null; | ||
}) => unknown): this; | ||
/** Register handler to `chosen_inline_result` update */ | ||
chosenInlineResult<Ctx = ContextType<typeof this, "chosen_inline_result"> & Derives["global"] & Derives["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"> & Derives["global"] & Derives["inline_query"]>(trigger: RegExp | string | ((context: Ctx) => boolean), handler: (context: Ctx & { | ||
args: RegExpMatchArray | null; | ||
}) => unknown, options?: { | ||
onResult?: (context: ContextType<Bot, "chosen_inline_result"> & Derives["global"] & Derives["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"> & Derives["global"] & Derives["message"]>(trigger: RegExp | string | ((context: Ctx) => boolean), 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: string, handler: (context: ContextType<typeof this, "message"> & Derives["global"] & Derives["message"] & { | ||
args: string | null; | ||
}) => unknown, options?: Omit<SetMyCommandsParams, "commands"> & Omit<TelegramBotCommand, "command">): 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, dropPendingUpdates, allowedUpdates, }?: { | ||
webhook?: Omit<APIMethodParams<"setWebhook">, "drop_pending_updates" | "allowed_updates">; | ||
dropPendingUpdates?: boolean; | ||
allowedUpdates?: NonNullable<APIMethodParams<"getUpdates">>["allowed_updates"]; | ||
}): Promise<TelegramUser | undefined>; | ||
/** | ||
* Stops receiving events via long-polling or webhook | ||
* Currently does not implement graceful shutdown | ||
* */ | ||
stop(): Promise<void>; | ||
} | ||
declare const frameworks: { | ||
elysia: ({ body, headers }: any) => { | ||
update: any; | ||
header: any; | ||
unauthorized: () => Response; | ||
}; | ||
fastify: (request: any, reply: any) => { | ||
update: any; | ||
header: any; | ||
unauthorized: () => any; | ||
}; | ||
hono: (c: any) => { | ||
update: any; | ||
header: any; | ||
unauthorized: () => any; | ||
}; | ||
express: (req: any, res: any) => { | ||
update: any; | ||
header: any; | ||
unauthorized: () => any; | ||
}; | ||
koa: (ctx: any) => { | ||
update: any; | ||
header: any; | ||
unauthorized: () => void; | ||
}; | ||
http: (req: any, res: any) => { | ||
update: Promise<TelegramUpdate>; | ||
header: any; | ||
unauthorized: () => 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; | ||
}; | ||
}; | ||
/** Union type of webhook handlers name */ | ||
type WebhookHandlers = keyof typeof frameworks; | ||
/** | ||
* 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, secretToken?: string): 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 AnyBot, type AnyPlugin, Bot, type BotOptions, type DeriveDefinitions, type ErrorDefinitions, ErrorKind, type FilterDefinitions, type Handler, Hooks, type MaybePromise, type MaybeSuppressedParams, Plugin, type Suppress, type SuppressedAPIMethodParams, type SuppressedAPIMethodReturn, type SuppressedAPIMethods, TelegramError, type WebhookHandlers, webhookHandler }; |
1186
dist/index.js
@@ -1,33 +0,1159 @@ | ||
"use strict"; | ||
/** | ||
* @module | ||
* | ||
* Powerful Telegram Bot API framework | ||
*/ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
import fs from 'node:fs/promises'; | ||
import { Readable } from 'node:stream'; | ||
import { CallbackData } from '@gramio/callback-data'; | ||
export * from '@gramio/callback-data'; | ||
import { contextsMappings, PhotoAttachment } from '@gramio/contexts'; | ||
export * from '@gramio/contexts'; | ||
import { isMediaUpload, convertJsonToFormData } from '@gramio/files'; | ||
export * from '@gramio/files'; | ||
import { FormattableMap } from '@gramio/format'; | ||
export * from '@gramio/format'; | ||
import debug from 'debug'; | ||
import { Inspectable } from 'inspectable'; | ||
import { Composer as Composer$1, noopNext } from 'middleware-io'; | ||
export * from '@gramio/keyboards'; | ||
const ErrorKind = Symbol("ErrorKind"); | ||
class TelegramError extends Error { | ||
/** Name of the API Method */ | ||
method; | ||
/** Params that were sent */ | ||
params; | ||
/** See {@link TelegramAPIResponseError.error_code}*/ | ||
code; | ||
/** Describes why a request was unsuccessful. */ | ||
payload; | ||
/** Construct new TelegramError */ | ||
constructor(error, method, params) { | ||
super(error.description); | ||
this.name = method; | ||
this.method = method; | ||
this.params = params; | ||
this.code = error.error_code; | ||
if (error.parameters) this.payload = error.parameters; | ||
} | ||
} | ||
TelegramError.constructor[ErrorKind] = "TELEGRAM"; | ||
class Composer { | ||
composer = Composer$1.builder(); | ||
length = 0; | ||
composed; | ||
onError; | ||
constructor(onError) { | ||
this.onError = onError || ((_, error) => { | ||
throw error; | ||
}); | ||
this.recompose(); | ||
} | ||
/** Register handler to one or many Updates */ | ||
on(updateName, handler) { | ||
return this.use(async (context, next) => { | ||
if (context.is(updateName)) return await handler(context, next); | ||
return await next(); | ||
}); | ||
} | ||
/** Register handler to any Update */ | ||
use(handler) { | ||
this.composer.caught(this.onError).use(handler); | ||
return this.recompose(); | ||
} | ||
/** | ||
* Derive some data to handlers | ||
* | ||
* @example | ||
* ```ts | ||
* new Bot("token").derive((context) => { | ||
* return { | ||
* superSend: () => context.send("Derived method") | ||
* } | ||
* }) | ||
* ``` | ||
*/ | ||
derive(updateNameOrHandler, handler) { | ||
if (typeof updateNameOrHandler === "function") | ||
this.use(async (context, next) => { | ||
for (const [key, value] of Object.entries( | ||
await updateNameOrHandler(context) | ||
)) { | ||
context[key] = value; | ||
} | ||
next(); | ||
}); | ||
else if (handler) | ||
this.on(updateNameOrHandler, async (context, next) => { | ||
for (const [key, value] of Object.entries(await handler(context))) { | ||
context[key] = value; | ||
} | ||
next(); | ||
}); | ||
return this; | ||
} | ||
recompose() { | ||
this.composed = this.composer.compose(); | ||
this.length = this.composer.length; | ||
return this; | ||
} | ||
compose(context, next = noopNext) { | ||
this.composed(context, next); | ||
} | ||
} | ||
var __create$1 = Object.create; | ||
var __defProp$1 = Object.defineProperty; | ||
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor; | ||
var __knownSymbol$1 = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name); | ||
var __typeError$1 = (msg) => { | ||
throw TypeError(msg); | ||
}; | ||
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __name$1 = (target, value) => __defProp$1(target, "name", { value, configurable: true }); | ||
var __decoratorStart$1 = (base) => [, , , __create$1(null)]; | ||
var __decoratorStrings$1 = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; | ||
var __expectFn$1 = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError$1("Function expected") : fn; | ||
var __decoratorContext$1 = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings$1[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError$1("Already initialized") : fns.push(__expectFn$1(fn || null)) }); | ||
var __decoratorMetadata$1 = (array, target) => __defNormalProp$1(target, __knownSymbol$1("metadata"), array[3]); | ||
var __runInitializers$1 = (array, flags, self, value) => { | ||
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) fns[i].call(self) ; | ||
return value; | ||
}; | ||
var __decorateElement$1 = (array, flags, name, decorators, target, extra) => { | ||
var it, done, ctx, k = flags & 7, p = !!(flags & 16); | ||
var j = 0; | ||
var extraInitializers = array[j] || (array[j] = []); | ||
var desc = k && ((target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc$1(target , name)); | ||
__name$1(target, name); | ||
for (var i = decorators.length - 1; i >= 0; i--) { | ||
ctx = __decoratorContext$1(k, name, done = {}, array[3], extraInitializers); | ||
it = (0, decorators[i])(target, ctx), done._ = 1; | ||
__expectFn$1(it) && (target = it); | ||
} | ||
return __decoratorMetadata$1(array, target), desc && __defProp$1(target, name, desc), p ? k ^ 4 ? extra : desc : target; | ||
}; | ||
var _Plugin_decorators, _init$1; | ||
_Plugin_decorators = [Inspectable({ | ||
serialize: (plugin) => ({ | ||
name: plugin._.name, | ||
dependencies: plugin._.dependencies | ||
}) | ||
})]; | ||
class Plugin { | ||
/** | ||
* @internal | ||
* Set of Plugin data | ||
* | ||
* | ||
*/ | ||
_ = { | ||
/** Name of plugin */ | ||
name: "", | ||
/** List of plugin dependencies. If user does't extend from listed there dependencies it throw a error */ | ||
dependencies: [], | ||
/** remap generic type. {} in runtime */ | ||
Errors: {}, | ||
/** remap generic type. {} in runtime */ | ||
Derives: {}, | ||
/** Composer */ | ||
composer: new Composer(), | ||
/** Store plugin preRequests hooks */ | ||
preRequests: [], | ||
/** Store plugin onResponses hooks */ | ||
onResponses: [], | ||
/** Store plugin onResponseErrors hooks */ | ||
onResponseErrors: [], | ||
/** | ||
* Store plugin groups | ||
* | ||
* If you use `on` or `use` in group and on plugin-level groups handlers are registered after plugin-level handlers | ||
* */ | ||
groups: [], | ||
/** Store plugin onStarts hooks */ | ||
onStarts: [], | ||
/** Store plugin onStops hooks */ | ||
onStops: [], | ||
/** Store plugin onErrors hooks */ | ||
onErrors: [], | ||
/** Map of plugin errors */ | ||
errorsDefinitions: {}, | ||
decorators: {} | ||
}; | ||
/** Create new Plugin. Please provide `name` */ | ||
constructor(name, { dependencies } = {}) { | ||
this._.name = name; | ||
if (dependencies) this._.dependencies = dependencies; | ||
} | ||
/** 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) { | ||
this._.groups.push(grouped); | ||
return this; | ||
} | ||
/** | ||
* Register custom class-error in plugin | ||
**/ | ||
error(kind, error) { | ||
error[ErrorKind] = kind; | ||
this._.errorsDefinitions[kind] = error; | ||
return this; | ||
} | ||
derive(updateNameOrHandler, handler) { | ||
this._.composer.derive(updateNameOrHandler, handler); | ||
return this; | ||
} | ||
decorate(nameOrValue, value) { | ||
if (typeof nameOrValue === "string") this._.decorators[nameOrValue] = value; | ||
else { | ||
for (const [name, value2] of Object.entries(nameOrValue)) { | ||
this._.decorators[name] = value2; | ||
} | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
return this; | ||
} | ||
/** Register handler to one or many Updates */ | ||
on(updateName, handler) { | ||
this._.composer.on(updateName, handler); | ||
return this; | ||
} | ||
/** Register handler to any Updates */ | ||
use(handler) { | ||
this._.composer.use(handler); | ||
return this; | ||
} | ||
preRequest(methodsOrHandler, handler) { | ||
if ((typeof methodsOrHandler === "string" || Array.isArray(methodsOrHandler)) && handler) | ||
this._.preRequests.push([handler, methodsOrHandler]); | ||
else if (typeof methodsOrHandler === "function") | ||
this._.preRequests.push([methodsOrHandler, void 0]); | ||
return this; | ||
} | ||
onResponse(methodsOrHandler, handler) { | ||
if ((typeof methodsOrHandler === "string" || Array.isArray(methodsOrHandler)) && handler) | ||
this._.onResponses.push([handler, methodsOrHandler]); | ||
else if (typeof methodsOrHandler === "function") | ||
this._.onResponses.push([methodsOrHandler, void 0]); | ||
return this; | ||
} | ||
onResponseError(methodsOrHandler, handler) { | ||
if ((typeof methodsOrHandler === "string" || Array.isArray(methodsOrHandler)) && handler) | ||
this._.onResponseErrors.push([handler, methodsOrHandler]); | ||
else if (typeof methodsOrHandler === "function") | ||
this._.onResponseErrors.push([methodsOrHandler, void 0]); | ||
return 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) { | ||
this._.onStarts.push(handler); | ||
return 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) { | ||
this._.onStops.push(handler); | ||
return this; | ||
} | ||
onError(updateNameOrHandler, handler) { | ||
if (typeof updateNameOrHandler === "function") { | ||
this._.onErrors.push(updateNameOrHandler); | ||
return this; | ||
} | ||
if (handler) { | ||
this._.onErrors.push(async (errContext) => { | ||
if (errContext.context.is(updateNameOrHandler)) | ||
await handler(errContext); | ||
}); | ||
} | ||
return this; | ||
} | ||
} | ||
_init$1 = __decoratorStart$1(); | ||
Plugin = __decorateElement$1(_init$1, 0, "Plugin", _Plugin_decorators, Plugin); | ||
__runInitializers$1(_init$1, 1, Plugin); | ||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); | ||
class Updates { | ||
bot; | ||
isStarted = false; | ||
offset = 0; | ||
composer; | ||
constructor(bot, onError) { | ||
this.bot = bot; | ||
this.composer = new Composer(onError); | ||
} | ||
async handleUpdate(data) { | ||
const updateType = Object.keys(data).at(1); | ||
this.offset = data.update_id + 1; | ||
const UpdateContext = contextsMappings[updateType]; | ||
if (!UpdateContext) throw new Error(updateType); | ||
const updatePayload = data[updateType]; | ||
if (!updatePayload) throw new Error(""); | ||
try { | ||
let context = new UpdateContext({ | ||
bot: this.bot, | ||
update: data, | ||
// @ts-expect-error | ||
payload: updatePayload, | ||
type: updateType, | ||
updateId: data.update_id | ||
}); | ||
if ("isEvent" in context && context.isEvent() && context.eventType) { | ||
const payload = data.message ?? data.edited_message ?? data.channel_post ?? data.edited_channel_post ?? data.business_message; | ||
if (!payload) throw new Error("Unsupported event??"); | ||
context = new contextsMappings[context.eventType]({ | ||
bot: this.bot, | ||
update: data, | ||
payload, | ||
// @ts-expect-error | ||
type: context.eventType, | ||
updateId: data.update_id | ||
}); | ||
} | ||
this.composer.compose(context); | ||
} catch (error) { | ||
throw new Error(`[UPDATES] Update type ${updateType} not supported.`); | ||
} | ||
} | ||
/** @deprecated use bot.start instead @internal */ | ||
async startPolling(params = {}) { | ||
if (this.isStarted) throw new Error("[UPDATES] Polling already started!"); | ||
this.isStarted = true; | ||
this.startFetchLoop(params); | ||
return; | ||
} | ||
async startFetchLoop(params = {}) { | ||
while (this.isStarted) { | ||
try { | ||
const updates = await this.bot.api.getUpdates({ | ||
...params, | ||
offset: this.offset | ||
}); | ||
for await (const update of updates) { | ||
await this.handleUpdate(update).catch(console.error); | ||
} | ||
} catch (error) { | ||
console.error("Error received when fetching updates", error); | ||
await sleep(this.bot.options.api.retryGetUpdatesWait ?? 1e3); | ||
} | ||
} | ||
} | ||
stopPolling() { | ||
this.isStarted = false; | ||
} | ||
} | ||
var __create = Object.create; | ||
var __defProp = Object.defineProperty; | ||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
var __knownSymbol = (name2, symbol) => (symbol = Symbol[name2]) ? symbol : Symbol.for("Symbol." + name2); | ||
var __typeError = (msg) => { | ||
throw TypeError(msg); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// INFO: Temp polyfill, more info https://github.com/microsoft/TypeScript/issues/55453#issuecomment-1687496648 | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); | ||
var __decoratorStart = (base) => [, , , __create(null)]; | ||
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"]; | ||
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn; | ||
var __decoratorContext = (kind, name2, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name: name2, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) }); | ||
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]); | ||
var __runInitializers = (array, flags, self, value) => { | ||
for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) fns[i].call(self) ; | ||
return value; | ||
}; | ||
var __decorateElement = (array, flags, name2, decorators, target, extra) => { | ||
var it, done, ctx, k = flags & 7, p = !!(flags & 16); | ||
var j = 0; | ||
var extraInitializers = array[j] || (array[j] = []); | ||
var desc = k && ((target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(target , name2)); | ||
__name(target, name2); | ||
for (var i = decorators.length - 1; i >= 0; i--) { | ||
ctx = __decoratorContext(k, name2, done = {}, array[3], extraInitializers); | ||
it = (0, decorators[i])(target, ctx), done._ = 1; | ||
__expectFn(it) && (target = it); | ||
} | ||
return __decoratorMetadata(array, target), desc && __defProp(target, name2, desc), p ? k ^ 4 ? extra : desc : target; | ||
}; | ||
var _Bot_decorators, _init; | ||
const $debugger = debug("gramio"); | ||
const debug$api = $debugger.extend("api"); | ||
_Bot_decorators = [Inspectable({ | ||
serialize: () => ({}) | ||
})]; | ||
class Bot { | ||
_ = { | ||
/** @internal. Remap generic */ | ||
derives: {} | ||
}; | ||
/** @internal. Remap generic */ | ||
__Derives; | ||
filters = { | ||
context: (name2) => (context) => context.is(name2) | ||
}; | ||
/** Options provided to instance */ | ||
options; | ||
/** Bot data (filled in when calling bot.init/bot.start) */ | ||
info; | ||
/** | ||
* 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) | ||
*/ | ||
api = new Proxy({}, { | ||
get: (_target, method) => ( | ||
// @ts-expect-error | ||
// biome-ignore lint/suspicious/noAssignInExpressions: <explanation> | ||
_target[method] ??= (args) => this._callApi(method, args) | ||
) | ||
}); | ||
lazyloadPlugins = []; | ||
dependencies = []; | ||
errorsDefinitions = { | ||
TELEGRAM: TelegramError | ||
}; | ||
errorHandler(context, error) { | ||
if (!this.hooks.onError.length) | ||
return console.error("[Default Error Handler]", context, error); | ||
return this.runImmutableHooks("onError", { | ||
context, | ||
//@ts-expect-error ErrorKind exists if user register error-class with .error("kind", SomeError); | ||
kind: error.constructor[ErrorKind] ?? "UNKNOWN", | ||
error | ||
}); | ||
} | ||
/** This instance handle updates */ | ||
updates = new Updates(this, this.errorHandler.bind(this)); | ||
hooks = { | ||
preRequest: [], | ||
onResponse: [], | ||
onResponseError: [], | ||
onError: [], | ||
onStart: [], | ||
onStop: [] | ||
}; | ||
constructor(tokenOrOptions, optionsRaw) { | ||
const token = typeof tokenOrOptions === "string" ? tokenOrOptions : tokenOrOptions?.token; | ||
const options = typeof tokenOrOptions === "object" ? tokenOrOptions : optionsRaw; | ||
if (!token || typeof token !== "string") | ||
throw new Error(`Token is ${typeof token} but it should be a string!`); | ||
this.options = { | ||
...options, | ||
token, | ||
api: { | ||
baseURL: "https://api.telegram.org/bot", | ||
retryGetUpdatesWait: 1e3, | ||
...options?.api | ||
} | ||
}; | ||
if (options?.info) this.info = options.info; | ||
if (!(optionsRaw?.plugins && "format" in optionsRaw.plugins && !optionsRaw.plugins.format)) | ||
this.extend( | ||
new Plugin("@gramio/format").preRequest((context) => { | ||
if (!context.params) return context; | ||
const formattable = FormattableMap[context.method]; | ||
if (formattable) context.params = formattable(context.params); | ||
return context; | ||
}) | ||
); | ||
} | ||
async runHooks(type, context) { | ||
let data = context; | ||
for await (const hook of this.hooks[type]) { | ||
data = await hook(data); | ||
} | ||
return data; | ||
} | ||
async runImmutableHooks(type, ...context) { | ||
for await (const hook of this.hooks[type]) { | ||
await hook(...context); | ||
} | ||
} | ||
async _callApi(method, params = {}) { | ||
const debug$api$method = debug$api.extend(method); | ||
const url = `${this.options.api.baseURL}${this.options.token}/${method}`; | ||
const reqOptions = { | ||
method: "POST", | ||
...this.options.api.fetchOptions, | ||
// @ts-ignore types node/bun and global missmatch | ||
headers: new Headers(this.options.api.fetchOptions?.headers) | ||
}; | ||
const context = await this.runHooks( | ||
"preRequest", | ||
// TODO: fix type error | ||
// @ts-expect-error | ||
{ | ||
method, | ||
params | ||
} | ||
); | ||
params = context.params; | ||
if (params && isMediaUpload(method, params)) { | ||
const formData = await convertJsonToFormData(method, params); | ||
reqOptions.body = formData; | ||
} else { | ||
reqOptions.headers.set("Content-Type", "application/json"); | ||
reqOptions.body = JSON.stringify(params); | ||
} | ||
debug$api$method("options: %j", reqOptions); | ||
const response = await fetch(url, reqOptions); | ||
const data = await response.json(); | ||
debug$api$method("response: %j", data); | ||
if (!data.ok) { | ||
const err = new TelegramError(data, method, params); | ||
this.runImmutableHooks("onResponseError", err, this.api); | ||
if (!params?.suppress) throw err; | ||
return err; | ||
} | ||
this.runImmutableHooks( | ||
"onResponse", | ||
// TODO: fix type error | ||
// @ts-expect-error | ||
{ | ||
method, | ||
params, | ||
response: data.result | ||
} | ||
); | ||
return data.result; | ||
} | ||
async downloadFile(attachment, path) { | ||
function getFileId(attachment2) { | ||
if (attachment2 instanceof PhotoAttachment) { | ||
return attachment2.bigSize.fileId; | ||
} | ||
if ("fileId" in attachment2 && typeof attachment2.fileId === "string") | ||
return attachment2.fileId; | ||
if ("file_id" in attachment2) return attachment2.file_id; | ||
throw new Error("Invalid attachment"); | ||
} | ||
const fileId = typeof attachment === "string" ? attachment : getFileId(attachment); | ||
const file = await this.api.getFile({ file_id: fileId }); | ||
const url = `${this.options.api.baseURL.replace("/bot", "/file/bot")}${this.options.token}/${file.file_path}`; | ||
const res = await fetch(url); | ||
if (path) { | ||
if (!res.body) | ||
throw new Error("Response without body (should be never throw)"); | ||
await fs.writeFile(path, Readable.fromWeb(res.body)); | ||
return path; | ||
} | ||
const buffer = await res.arrayBuffer(); | ||
return buffer; | ||
} | ||
/** | ||
* 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(kind, error) { | ||
error[ErrorKind] = kind; | ||
this.errorsDefinitions[kind] = error; | ||
return this; | ||
} | ||
onError(updateNameOrHandler, handler) { | ||
if (typeof updateNameOrHandler === "function") { | ||
this.hooks.onError.push(updateNameOrHandler); | ||
return this; | ||
} | ||
if (handler) { | ||
this.hooks.onError.push(async (errContext) => { | ||
if (errContext.context.is(updateNameOrHandler)) | ||
await handler(errContext); | ||
}); | ||
} | ||
return this; | ||
} | ||
derive(updateNameOrHandler, handler) { | ||
this.updates.composer.derive(updateNameOrHandler, handler); | ||
return this; | ||
} | ||
decorate(nameOrRecordValue, value) { | ||
for (const contextName of Object.keys(contextsMappings)) { | ||
if (typeof nameOrRecordValue === "string") | ||
Object.defineProperty(contextsMappings[contextName].prototype, name, { | ||
value | ||
}); | ||
else | ||
Object.defineProperties( | ||
// @ts-expect-error | ||
contextsMappings[contextName].prototype, | ||
Object.keys(nameOrRecordValue).reduce( | ||
(acc, key) => { | ||
acc[key] = { value: nameOrRecordValue[key] }; | ||
return acc; | ||
}, | ||
{} | ||
) | ||
); | ||
} | ||
return 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) { | ||
this.hooks.onStart.push(handler); | ||
return 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) { | ||
this.hooks.onStop.push(handler); | ||
return this; | ||
} | ||
preRequest(methodsOrHandler, handler) { | ||
if (typeof methodsOrHandler === "string" || Array.isArray(methodsOrHandler)) { | ||
if (!handler) throw new Error("TODO:"); | ||
const methods = typeof methodsOrHandler === "string" ? [methodsOrHandler] : methodsOrHandler; | ||
this.hooks.preRequest.push(async (context) => { | ||
if (methods.includes(context.method)) return handler(context); | ||
return context; | ||
}); | ||
} else this.hooks.preRequest.push(methodsOrHandler); | ||
return this; | ||
} | ||
onResponse(methodsOrHandler, handler) { | ||
if (typeof methodsOrHandler === "string" || Array.isArray(methodsOrHandler)) { | ||
if (!handler) throw new Error("TODO:"); | ||
const methods = typeof methodsOrHandler === "string" ? [methodsOrHandler] : methodsOrHandler; | ||
this.hooks.onResponse.push(async (context) => { | ||
if (methods.includes(context.method)) return handler(context); | ||
return context; | ||
}); | ||
} else this.hooks.onResponse.push(methodsOrHandler); | ||
return this; | ||
} | ||
onResponseError(methodsOrHandler, handler) { | ||
if (typeof methodsOrHandler === "string" || Array.isArray(methodsOrHandler)) { | ||
if (!handler) throw new Error("TODO:"); | ||
const methods = typeof methodsOrHandler === "string" ? [methodsOrHandler] : methodsOrHandler; | ||
this.hooks.onResponseError.push(async (context) => { | ||
if (methods.includes(context.method)) return handler(context); | ||
return context; | ||
}); | ||
} else this.hooks.onResponseError.push(methodsOrHandler); | ||
return this; | ||
} | ||
// onExperimental( | ||
// // filter: Filters, | ||
// filter: ( | ||
// f: Filters< | ||
// Context<typeof this> & Derives["global"], | ||
// [{ equal: { prop: number }; addition: { some: () => 2 } }] | ||
// >, | ||
// ) => Filters, | ||
// handler: Handler<Context<typeof this> & Derives["global"]>, | ||
// ) {} | ||
/** Register handler to one or many Updates */ | ||
on(updateName, handler) { | ||
this.updates.composer.on(updateName, handler); | ||
return this; | ||
} | ||
/** Register handler to any Updates */ | ||
use(handler) { | ||
this.updates.composer.use(handler); | ||
return 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(plugin) { | ||
if (plugin instanceof Promise) { | ||
this.lazyloadPlugins.push(plugin); | ||
return this; | ||
} | ||
if (plugin._.dependencies.some((dep) => !this.dependencies.includes(dep))) | ||
throw new Error( | ||
`The \xAB${plugin._.name}\xBB plugin needs dependencies registered before: ${plugin._.dependencies.filter((dep) => !this.dependencies.includes(dep)).join(", ")}` | ||
); | ||
if (plugin._.composer.length) { | ||
this.use(plugin._.composer.composed); | ||
} | ||
this.decorate(plugin._.decorators); | ||
for (const [key, value] of Object.entries(plugin._.errorsDefinitions)) { | ||
if (this.errorsDefinitions[key]) this.errorsDefinitions[key] = value; | ||
} | ||
for (const value of plugin._.preRequests) { | ||
const [preRequest, updateName] = value; | ||
if (!updateName) this.preRequest(preRequest); | ||
else this.preRequest(updateName, preRequest); | ||
} | ||
for (const value of plugin._.onResponses) { | ||
const [onResponse, updateName] = value; | ||
if (!updateName) this.onResponse(onResponse); | ||
else this.onResponse(updateName, onResponse); | ||
} | ||
for (const value of plugin._.onResponseErrors) { | ||
const [onResponseError, updateName] = value; | ||
if (!updateName) this.onResponseError(onResponseError); | ||
else this.onResponseError(updateName, onResponseError); | ||
} | ||
for (const handler of plugin._.groups) { | ||
this.group(handler); | ||
} | ||
this.dependencies.push(plugin._.name); | ||
return this; | ||
} | ||
/** | ||
* Register handler to reaction (`message_reaction` update) | ||
* | ||
* @example | ||
* ```ts | ||
* new Bot().reaction("👍", async (context) => { | ||
* await context.reply(`Thank you!`); | ||
* }); | ||
* ``` | ||
* */ | ||
reaction(trigger, handler) { | ||
const reactions = Array.isArray(trigger) ? trigger : [trigger]; | ||
return this.on("message_reaction", (context, next) => { | ||
const newReactions = []; | ||
for (const reaction of context.newReactions) { | ||
if (reaction.type !== "emoji") continue; | ||
const foundIndex = context.oldReactions.findIndex( | ||
(oldReaction) => oldReaction.type === "emoji" && oldReaction.emoji === reaction.emoji | ||
); | ||
if (foundIndex === -1) { | ||
newReactions.push(reaction); | ||
} else { | ||
context.oldReactions.splice(foundIndex, 1); | ||
} | ||
} | ||
if (!newReactions.some( | ||
(x) => x.type === "emoji" && reactions.includes(x.emoji) | ||
)) | ||
return next(); | ||
return handler(context); | ||
}); | ||
} | ||
/** | ||
* 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, handler) { | ||
return this.on("callback_query", (context, next) => { | ||
if (!context.data) return next(); | ||
if (typeof trigger === "string" && context.data !== trigger) | ||
return next(); | ||
if (trigger instanceof CallbackData && !trigger.regexp().test(context.data)) | ||
return next(); | ||
if (trigger instanceof RegExp && !trigger.test(context.data)) | ||
return next(); | ||
if (trigger instanceof CallbackData) | ||
context.queryData = trigger.unpack(context.data); | ||
return handler(context); | ||
}); | ||
} | ||
/** Register handler to `chosen_inline_result` update */ | ||
chosenInlineResult(trigger, handler) { | ||
return this.on("chosen_inline_result", (context, next) => { | ||
if (typeof trigger === "string" && context.query === trigger || // @ts-expect-error | ||
typeof trigger === "function" && trigger(context) || trigger instanceof RegExp && trigger.test(context.query)) { | ||
context.args = trigger instanceof RegExp ? context.query?.match(trigger) : null; | ||
return handler(context); | ||
} | ||
return next(); | ||
}); | ||
} | ||
/** | ||
* 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(trigger, handler, options = {}) { | ||
if (options.onResult) this.chosenInlineResult(trigger, options.onResult); | ||
return this.on("inline_query", (context, next) => { | ||
if (typeof trigger === "string" && context.query === trigger || // @ts-expect-error | ||
typeof trigger === "function" && trigger(context) || trigger instanceof RegExp && trigger.test(context.query)) { | ||
context.args = trigger instanceof RegExp ? context.query?.match(trigger) : null; | ||
return handler(context); | ||
} | ||
return next(); | ||
}); | ||
} | ||
/** | ||
* 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(trigger, handler) { | ||
return this.on("message", (context, next) => { | ||
const text = context.text ?? context.caption; | ||
if (typeof trigger === "string" && text === trigger || // @ts-expect-error | ||
typeof trigger === "function" && trigger(context) || trigger instanceof RegExp && text && trigger.test(text)) { | ||
context.args = trigger instanceof RegExp ? text?.match(trigger) : null; | ||
return handler(context); | ||
} | ||
return next(); | ||
}); | ||
} | ||
/** | ||
* 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, handler, options) { | ||
if (command.startsWith("/")) | ||
throw new Error("Do not use / in command name"); | ||
return this.on(["message", "business_message"], (context, next) => { | ||
if (context.entities?.some((entity) => { | ||
if (entity.type !== "bot_command" || entity.offset > 0) return false; | ||
const cmd = context.text?.slice(1, entity.length)?.replace(`@${this.info.username}`, ""); | ||
context.args = context.text?.slice(entity.length).trim() || null; | ||
return cmd === command; | ||
})) | ||
return handler(context); | ||
return next(); | ||
}); | ||
} | ||
/** Currently not isolated!!! */ | ||
group(grouped) { | ||
return grouped(this); | ||
} | ||
/** | ||
* Init bot. Call it manually only if you doesn't use {@link Bot.start} | ||
*/ | ||
async init() { | ||
await Promise.all( | ||
this.lazyloadPlugins.map(async (plugin) => this.extend(await plugin)) | ||
); | ||
if (!this.info) { | ||
const info = await this.api.getMe({ | ||
suppress: true | ||
}); | ||
if (info instanceof TelegramError) { | ||
if (info.code === 404) | ||
info.message = "The bot token is incorrect. Check it in BotFather."; | ||
throw info; | ||
} | ||
this.info = info; | ||
} | ||
} | ||
/** | ||
* 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(); | ||
* ``` | ||
*/ | ||
async start({ | ||
webhook, | ||
dropPendingUpdates, | ||
allowedUpdates | ||
} = {}) { | ||
await this.init(); | ||
if (!webhook) { | ||
await this.api.deleteWebhook({ | ||
drop_pending_updates: dropPendingUpdates | ||
}); | ||
await this.updates.startPolling({ | ||
allowed_updates: allowedUpdates | ||
}); | ||
this.runImmutableHooks("onStart", { | ||
plugins: this.dependencies, | ||
// biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info | ||
info: this.info, | ||
updatesFrom: "long-polling" | ||
}); | ||
return this.info; | ||
} | ||
if (this.updates.isStarted) this.updates.stopPolling(); | ||
await this.api.setWebhook({ | ||
...webhook, | ||
drop_pending_updates: dropPendingUpdates, | ||
allowed_updates: allowedUpdates | ||
}); | ||
this.runImmutableHooks("onStart", { | ||
plugins: this.dependencies, | ||
// biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info | ||
info: this.info, | ||
updatesFrom: "webhook" | ||
}); | ||
return this.info; | ||
} | ||
/** | ||
* Stops receiving events via long-polling or webhook | ||
* Currently does not implement graceful shutdown | ||
* */ | ||
async stop() { | ||
if (this.updates.isStarted) this.updates.stopPolling(); | ||
else await this.api.deleteWebhook(); | ||
await this.runImmutableHooks("onStop", { | ||
plugins: this.dependencies, | ||
// biome-ignore lint/style/noNonNullAssertion: bot.init() guarantees this.info | ||
info: this.info | ||
}); | ||
} | ||
} | ||
_init = __decoratorStart(); | ||
Bot = __decorateElement(_init, 0, "Bot", _Bot_decorators, Bot); | ||
__runInitializers(_init, 1, Bot); | ||
const SECRET_TOKEN_HEADER = "X-Telegram-Bot-Api-Secret-Token"; | ||
const WRONG_TOKEN_ERROR = "secret token is invalid"; | ||
const responseOK = new Response("ok!"); | ||
const responseUnauthorized = new Response(WRONG_TOKEN_ERROR, { | ||
status: 401 | ||
// @ts-ignore | ||
}); | ||
const frameworks = { | ||
elysia: ({ body, headers }) => ({ | ||
update: body, | ||
header: headers[SECRET_TOKEN_HEADER], | ||
unauthorized: () => responseUnauthorized | ||
}), | ||
fastify: (request, reply) => ({ | ||
update: request.body, | ||
header: request.headers[SECRET_TOKEN_HEADER], | ||
unauthorized: () => reply.code(401).send(WRONG_TOKEN_ERROR) | ||
}), | ||
hono: (c) => ({ | ||
update: c.req.json(), | ||
header: c.req.header(SECRET_TOKEN_HEADER), | ||
unauthorized: () => c.text(WRONG_TOKEN_ERROR, 401) | ||
}), | ||
express: (req, res) => ({ | ||
update: req.body, | ||
header: req.header(SECRET_TOKEN_HEADER), | ||
unauthorized: () => res.status(401).send(WRONG_TOKEN_ERROR) | ||
}), | ||
koa: (ctx) => ({ | ||
update: ctx.request.body, | ||
header: ctx.get(SECRET_TOKEN_HEADER), | ||
unauthorized: () => { | ||
ctx.status === 401; | ||
ctx.body = WRONG_TOKEN_ERROR; | ||
} | ||
}), | ||
http: (req, res) => ({ | ||
update: new Promise((resolve) => { | ||
let body = ""; | ||
req.on("data", (chunk) => { | ||
body += chunk.toString(); | ||
}); | ||
req.on("end", () => resolve(JSON.parse(body))); | ||
}), | ||
header: req.headers[SECRET_TOKEN_HEADER.toLowerCase()], | ||
unauthorized: () => res.writeHead(401).end(WRONG_TOKEN_ERROR) | ||
}), | ||
"std/http": (req) => ({ | ||
update: req.json(), | ||
header: req.headers.get(SECRET_TOKEN_HEADER), | ||
response: () => responseOK, | ||
unauthorized: () => responseUnauthorized | ||
}), | ||
"Bun.serve": (req) => ({ | ||
update: req.json(), | ||
header: req.headers.get(SECRET_TOKEN_HEADER), | ||
response: () => responseOK, | ||
unauthorized: () => responseUnauthorized | ||
}) | ||
}; | ||
function webhookHandler(bot, framework, secretToken) { | ||
const frameworkAdapter = frameworks[framework]; | ||
return async (...args) => { | ||
const { update, response, header, unauthorized } = frameworkAdapter( | ||
...args | ||
); | ||
if (secretToken && header !== secretToken) return unauthorized(); | ||
await bot.updates.handleUpdate(await update); | ||
if (response) return response(); | ||
}; | ||
} | ||
Symbol.metadata ??= Symbol("Symbol.metadata"); | ||
__exportStar(require("./bot"), exports); | ||
__exportStar(require("./errors"), exports); | ||
__exportStar(require("./types"), exports); | ||
__exportStar(require("./plugin"), exports); | ||
__exportStar(require("./webhook/index"), exports); | ||
__exportStar(require("@gramio/contexts"), exports); | ||
__exportStar(require("@gramio/files"), exports); | ||
__exportStar(require("@gramio/keyboards"), exports); | ||
__exportStar(require("@gramio/format"), exports); | ||
__exportStar(require("@gramio/callback-data"), exports); | ||
export { Bot, ErrorKind, Plugin, TelegramError, webhookHandler }; |
{ | ||
"name": "gramio", | ||
"type": "commonjs", | ||
"version": "0.0.51", | ||
"type": "module", | ||
"version": "0.1.0", | ||
"description": "Powerful, extensible and really type-safe Telegram Bot API framework", | ||
"main": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"main": "dist/index.cjs", | ||
"module": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"import": { | ||
"types": "./dist/index.d.ts", | ||
"default": "./dist/index.js" | ||
}, | ||
"require": { | ||
"types": "./dist/index.d.cts", | ||
"default": "./dist/index.cjs" | ||
} | ||
} | ||
}, | ||
"keywords": [ | ||
@@ -23,3 +36,3 @@ "telegram", | ||
"lint:fix": "bun lint --apply", | ||
"prepublishOnly": "tsc", | ||
"prepublishOnly": "bunx pkgroll", | ||
"jsr": "bun scripts/release-jsr.ts", | ||
@@ -31,7 +44,7 @@ "try-deno": "deno publish --unstable-sloppy-imports --dry-run --allow-slow-types --allow-dirty" | ||
"devDependencies": { | ||
"@biomejs/biome": "1.9.1", | ||
"@types/bun": "^1.1.9", | ||
"@biomejs/biome": "1.9.4", | ||
"@types/bun": "^1.1.12", | ||
"@types/debug": "^4.1.12", | ||
"pkgroll": "^2.5.0", | ||
"typescript": "^5.6.2" | ||
"typescript": "^5.6.3" | ||
}, | ||
@@ -41,6 +54,6 @@ "dependencies": { | ||
"@gramio/contexts": "^0.0.23", | ||
"@gramio/files": "^0.0.12", | ||
"@gramio/format": "^0.1.3", | ||
"@gramio/files": "^0.1.0", | ||
"@gramio/format": "^0.1.4", | ||
"@gramio/keyboards": "^0.3.3", | ||
"@gramio/types": "^7.10.1", | ||
"@gramio/types": "^7.10.2", | ||
"debug": "^4.3.7", | ||
@@ -47,0 +60,0 @@ "inspectable": "^3.0.2", |
@@ -6,3 +6,3 @@ # GramIO | ||
[![Bot API](https://img.shields.io/badge/Bot%20API-7.10-blue?logo=telegram&style=flat&labelColor=000&color=3b82f6)](https://core.telegram.org/bots/api) | ||
[![npm](https://img.shields.io/npm/v/gramio?logo=npm&style=flat&labelColor=000&color=3b82f6)](https://www.npmjs.org/package/@gramio/core) | ||
[![npm](https://img.shields.io/npm/v/gramio?logo=npm&style=flat&labelColor=000&color=3b82f6)](https://www.npmjs.org/package/gramio) | ||
[![JSR](https://jsr.io/badges/@gramio/core)](https://jsr.io/@gramio/core) | ||
@@ -9,0 +9,0 @@ [![JSR Score](https://jsr.io/badges/@gramio/core/score)](https://jsr.io/@gramio/core) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
156442
3337
Yes
7
5
+ Added@gramio/files@0.1.0(transitive)
- Removed@gramio/files@0.0.12(transitive)
Updated@gramio/files@^0.1.0
Updated@gramio/format@^0.1.4
Updated@gramio/types@^7.10.2