UNPKG

@gramio/scenes

Version:
895 lines (886 loc) 50.4 kB
import * as gramio from 'gramio'; import { Handler, Stringable, Context, Bot, ContextsMapping, MaybePromise, UpdateName, ContextType, CallbackData, ErrorDefinitions, DeriveDefinitions, EventComposer as EventComposer$1, AnyPlugin, MaybeArray, Next as Next$1, Plugin } from 'gramio'; import { Storage } from '@gramio/storage'; import * as _gramio_composer from '@gramio/composer'; import { Next, EventComposer, EventContextOf, DeriveHandler } from '@gramio/composer'; /** The Standard Typed interface. This is a base type extended by other specs. */ interface StandardTypedV1<Input = unknown, Output = Input> { /** The Standard properties. */ readonly "~standard": StandardTypedV1.Props<Input, Output>; } declare namespace StandardTypedV1 { /** The Standard Typed properties interface. */ interface Props<Input = unknown, Output = Input> { /** The version number of the standard. */ readonly version: 1; /** The vendor name of the schema library. */ readonly vendor: string; /** Inferred types associated with the schema. */ readonly types?: Types<Input, Output> | undefined; } /** The Standard Typed types interface. */ interface Types<Input = unknown, Output = Input> { /** The input type of the schema. */ readonly input: Input; /** The output type of the schema. */ readonly output: Output; } /** Infers the input type of a Standard Typed. */ type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["input"]; /** Infers the output type of a Standard Typed. */ type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema["~standard"]["types"]>["output"]; } /** The Standard Schema interface. */ interface StandardSchemaV1<Input = unknown, Output = Input> { /** The Standard Schema properties. */ readonly "~standard": StandardSchemaV1.Props<Input, Output>; } declare namespace StandardSchemaV1 { /** The Standard Schema properties interface. */ interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> { /** Validates unknown input values. */ readonly validate: (value: unknown, options?: StandardSchemaV1.Options | undefined) => Result<Output> | Promise<Result<Output>>; } /** The result interface of the validate function. */ type Result<Output> = SuccessResult<Output> | FailureResult; /** The result interface if validation succeeds. */ interface SuccessResult<Output> { /** The typed output value. */ readonly value: Output; /** A falsy value for `issues` indicates success. */ readonly issues?: undefined; } interface Options { /** Explicit support for additional vendor-specific parameters, if needed. */ readonly libraryOptions?: Record<string, unknown> | undefined; } /** The result interface if validation fails. */ interface FailureResult { /** The issues of failed validation. */ readonly issues: ReadonlyArray<Issue>; } /** The issue interface of the failure output. */ interface Issue { /** The error message of the issue. */ readonly message: string; /** The path of the issue, if any. */ readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined; } /** The path segment interface of the issue. */ interface PathSegment { /** The key representing a path segment. */ readonly key: PropertyKey; } /** The Standard types interface. */ interface Types<Input = unknown, Output = Input> extends StandardTypedV1.Types<Input, Output> { } /** Infers the input type of a Standard. */ type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>; /** Infers the output type of a Standard. */ type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>; } type SceneLifecycleHandler = (ctx: any) => unknown | Promise<unknown>; /** * Per-step record stored on a Scene's `~scene.steps` array. * * Each step is a sub-composer (StepComposer) plus lifecycle hooks * registered via `c.enter()` / `c.exit()` / `c.fallback()` / `c.message()`. * * `composer` is typed as `unknown` here to avoid a circular import with * `step-composer.ts`; consumers cast to the concrete StepComposer type at * the use site. */ interface SceneStepEntry { id: string | number; composer: unknown; enter?: Handler<any>; exit?: Handler<any>; fallback?: Handler<any>; /** * Sugar set by `c.message(text | factory)`. When present and the step's * `enter` hook is not, the runtime sends this on first entry. */ message?: Stringable | ((ctx: any) => Stringable | Promise<Stringable>); /** * Event whitelist set by `c.events(["message"])` or step options. * `undefined` ⇒ default (`"message" | "callback_query"`). */ events?: readonly string[]; } /** * Scene-specific state that lives alongside the Composer's `~` slot. * Stored at `scene["~scene"]` to avoid colliding with composer internals * and to keep augmentation of `@gramio/composer` unnecessary. */ interface SceneInternals<Params = unknown, State extends Record<string | number, any> = Record<string | number, any>, ExitData = unknown> { steps: SceneStepEntry[]; stepsCount: number; /** scene-level onEnter — single-arg, not middleware */ enter?: SceneLifecycleHandler; /** scene-level onExit (lands in step 8) */ exit?: SceneLifecycleHandler; isModule: boolean; params: Params; state: State; exitData: ExitData; } type AnyBot$1 = Bot<any, any, any>; type TelegramEventMap$1 = { [K in keyof ContextsMapping<AnyBot$1>]: InstanceType<ContextsMapping<AnyBot$1>[K]>; }; /** * Base class for `Scene`. Produced by `createComposer` with the gramio method * table (`.command/.callbackQuery/.hears/.on/.use/.derive/.guard/.branch/.when/ * .extend/...`) so a `Scene` instance has the full bot-level DSL out of the * box. Scene-specific methods (`.params/.state/.exitData/.onEnter/.onExit/ * .step/.ask`) are added by the `Scene` subclass. * * Generic slots are left at default (`{}` / no derive accumulation) — Scene * tracks its own type chain via the `Derives` generic on the subclass, and * `.extend()` merges plugin/composer types into that chain. */ declare const SceneComposerBase: _gramio_composer.EventComposerConstructor<Context<AnyBot$1>, TelegramEventMap$1, { reaction<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, trigger: gramio.TelegramReactionTypeEmojiEmoji | gramio.TelegramReactionTypeEmojiEmoji[] | readonly gramio.TelegramReactionTypeEmojiEmoji[], handler: (context: gramio.MessageReactionContext<gramio.AnyBot> & _gramio_composer.EventContextOf<TThis, "message_reaction">) => unknown, macroOptions?: Record<string, unknown>): TThis; callbackQuery<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }, Trigger extends gramio.CallbackData | string | RegExp>(this: TThis, trigger: Trigger, handler: (context: gramio.CallbackQueryContext<gramio.AnyBot> & { queryData: Trigger extends gramio.CallbackData ? ReturnType<Trigger["unpack"]> : Trigger extends RegExp ? RegExpMatchArray : never; } & _gramio_composer.EventContextOf<TThis, "callback_query">) => unknown, macroOptions?: Record<string, unknown>): TThis; chosenInlineResult<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, trigger: RegExp | string | ((context: gramio.ChosenInlineResultContext<gramio.AnyBot>) => boolean), handler: (context: gramio.ChosenInlineResultContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & _gramio_composer.EventContextOf<TThis, "chosen_inline_result">) => unknown, macroOptions?: Record<string, unknown>): TThis; inlineQuery<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, triggerOrHandler: RegExp | string | ((context: gramio.InlineQueryContext<gramio.AnyBot>) => boolean) | ((context: gramio.InlineQueryContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & _gramio_composer.EventContextOf<TThis, "inline_query">) => unknown), maybeHandler?: (context: gramio.InlineQueryContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & _gramio_composer.EventContextOf<TThis, "inline_query">) => unknown, options?: { onResult?: (context: gramio.ChosenInlineResultContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & _gramio_composer.EventContextOf<TThis, "chosen_inline_result">) => unknown; } & Record<string, unknown>): TThis; guestQuery<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, triggerOrHandler: RegExp | string | ((context: gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) => boolean) | ((context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: RegExpMatchArray | null; } & _gramio_composer.EventContextOf<TThis, "guest_message">) => unknown), maybeHandler?: (context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: RegExpMatchArray | null; } & _gramio_composer.EventContextOf<TThis, "guest_message">) => unknown, macroOptions?: Record<string, unknown>): TThis; hears<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, trigger: RegExp | (string | string[] | readonly string[]) | ((context: gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) => boolean), handler: (context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: RegExpMatchArray | null; } & _gramio_composer.EventContextOf<TThis, "message">) => unknown, macroOptions?: Record<string, unknown>): TThis; command<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, command: string | string[] | readonly string[], handlerOrMeta: ((context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: string | null; } & _gramio_composer.EventContextOf<TThis, "message">) => unknown) | gramio.CommandMeta, handlerOrOptions?: ((context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: string | null; } & _gramio_composer.EventContextOf<TThis, "message">) => unknown) | Record<string, unknown>, macroOptions?: Record<string, unknown>): TThis; startParameter<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, parameter: RegExp | (string | string[] | readonly string[]), handler: gramio.Handler<(gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { rawStartPayload: string; } & _gramio_composer.EventContextOf<TThis, "message">>, macroOptions?: Record<string, unknown>): TThis; }>; type Modify<Base, Mod> = Omit<Base, keyof Mod> & Mod; type StateTypesDefault = Record<string | number, any>; type UpdateData<T extends StateTypesDefault> = {}; type ScenesStorage = Storage<Record<`@gramio/scenes:${string | number}`, ScenesStorageData<unknown, unknown>>>; interface ScenesOptions { storage?: ScenesStorage; /** * Controls what happens to updates that arrive while the user is inside * a scene but do not match the current step (wrong update type, or no * step handler claims them). * * - `true` (default): non-matching updates fall through to the outer bot * chain, so global handlers like `.command("cancel")` or `.on("message")` * can still react. The scene's `firstTime` flag is preserved so the user * does not lose their place. * - `false`: scenes greedily consume every update for the active user. * Legacy behavior — useful if you intentionally want to isolate the user * from outer handlers while a scene is active. * * @default true */ passthrough?: boolean; } interface ParentSceneFrame { name: string; params: unknown; state: unknown; stepId: string | number; previousStepId: string | number; parentStack?: ParentSceneFrame[]; } interface ScenesStorageData<Params = any, State = any> { name: string; params: Params; state: State; stepId: string | number; previousStepId: string | number; firstTime: boolean; parentStack?: ParentSceneFrame[]; /** * `true` once `scene.onEnter` has fired for this occupancy of the scene. * Used to distinguish "scene first entry" (fire onEnter) from "step * transition with firstTime=true" (don't re-fire onEnter). Defaults to * `false` on initial entry and is set `true` by dispatchActive after * onEnter runs. */ entered?: boolean; } interface SceneUpdateState { /** * @default sceneData.stepId + 1 */ step?: string | number; firstTime?: boolean; } /** * Extracts the Params generic from a Scene type. Reads from the SCENE * GENERIC, not from the runtime `~scene.params` carrier (which is typed * `unknown` at the class field level and never round-trips the user's * type back out). This is what lets `enter(scene)` reject a missing * params arg and enforce the declared shape when one is required. */ type SceneParamsOf<S> = S extends Scene<infer P, any, any, any> ? P : never; /** * `enter(scene, params?)` typed via two overloads so each case is checked * cleanly without relying on a conditional-rest-args dance (which expect- * type's `toBeCallableWith` can't fully resolve under generic constraints): * * • If the Scene declares `Params = never` (never called `.params<T>()`), * `enter(scene)` is valid with no second argument. * • If the Scene declares params, `enter(scene, params)` is required and * the params shape is enforced against the declared type. */ interface SceneEnterHandler { <S extends Scene<never, any, any, any>>(scene: S): Promise<void>; <S extends AnyScene>(scene: S, params: SceneParamsOf<S>): Promise<void>; } interface EnterExit { enter: SceneEnterHandler; exit: () => MaybePromise<boolean>; } type SceneStepReturn = { id: string | number; previousId: string | number; firstTime: boolean; go: (stepId: string | number, firstTime?: boolean) => Promise<void>; next: () => Promise<void>; previous: () => Promise<void>; }; interface InActiveSceneHandlerReturn<Params, State extends StateTypesDefault, ExitData extends Record<string, unknown> = Record<string, unknown>> extends EnterExit { state: State; params: Params; update: <T extends StateTypesDefault>(state: T, options?: SceneUpdateState) => Promise<UpdateData<T>>; step: SceneStepReturn; reenter: (params?: Params) => Promise<void>; enterSub: SceneEnterHandler; exitSub: (returnData?: ExitData) => Promise<void>; } interface InUnknownScene<Params, State extends StateTypesDefault, GlobalScene extends AnyScene | null = null> extends InActiveSceneHandlerReturn<Params, State> { is<Scene extends AnyScene>(scene: Scene): this is InUnknownScene<Scene["~scene"]["params"], Scene["~scene"]["state"], Scene>; } interface PossibleInUnknownScene<Params, State extends StateTypesDefault, Scene extends AnyScene | null = null> extends EnterExit { current: Scene extends AnyScene ? InActiveSceneHandlerReturn<Scene["~scene"]["params"], Partial<Scene["~scene"]["state"]>> : InUnknownScene<Params, State, Scene> | undefined; } type AnyBot = Bot<any, any, any>; type TelegramEventMap = { [K in keyof ContextsMapping<AnyBot>]: InstanceType<ContextsMapping<AnyBot>[K]>; }; /** * Default event union for step builders. Mirrors the union .ask() uses today * (scene.ts:302) — most scene steps interact via text input or button presses. * Both contexts have `.send()`, so `c.enter(ctx => ctx.send("…"))` typechecks. * * Narrow with `c.events([...])` (chained) or `step("name", { events: [...] }, ...)` * (per-step option) when a step only accepts a subset. */ type DefaultStepEvents = "message" | "callback_query"; /** * Step builder context for the default event union (message + callback_query), * merged with anything `this` has accumulated (scene-level derives, step-level * derives) via `ContextOf<TThis>` / `EventContextOf<TThis, E>`. * * `EventContextOf` picks up both the global TOut AND per-event TDerives, so * derives registered with `.derive("message", ...)` are visible too. * * Both default-union contexts have `.send`, `.api`, `.from`, `.chat` — the * common scene surface. */ type StepCtx<TThis, E extends UpdateName = DefaultStepEvents> = ContextType<AnyBot, E> & EventContextOf<TThis, E>; declare const stepMethods: { /** * Runs once when the user lands on this step (`firstTime === true`). * Replaces the `if (context.scene.step.firstTime) return ctx.send(...)` * boilerplate from the legacy step API. */ enter<TThis, E extends UpdateName = DefaultStepEvents>(this: TThis, handler: (ctx: StepCtx<TThis, E>, next: Next) => unknown): TThis; /** * Runs when the user leaves this step (next/previous/go from inside it, * or scene.exit/reenter while it was current). Per-step counterpart to * scene.onExit. Useful for cleanup and analytics. */ exit<TThis, E extends UpdateName = DefaultStepEvents>(this: TThis, handler: (ctx: StepCtx<TThis, E>, next: Next) => unknown): TThis; /** * Catch-all for events that didn't match any `.command/.on/.callbackQuery/ * .hears/.reaction` handler inside this step. Alternative to a final * wildcard `.on(events, ...)`. */ fallback<TThis, E extends UpdateName = DefaultStepEvents>(this: TThis, handler: (ctx: StepCtx<TThis, E>, next: Next) => unknown): TThis; /** * Sugar over `.enter(ctx => ctx.send(text))`. Accepts a literal Stringable * or a factory that receives the entry context. */ message<TThis, E extends UpdateName = DefaultStepEvents>(this: TThis, text: Stringable | ((ctx: StepCtx<TThis, E>) => Stringable | Promise<Stringable>)): TThis; /** * Narrow the event whitelist for this step. Defaults to message + * callback_query if not called. Mirrors `.on()`'s array-or-single shape. * * Type-only: returns `this` unchanged at the type level for v0. Manually * annotate ctx if you need narrower types in lifecycle handlers. Full * type narrowing is a follow-up. */ events<TThis, E extends UpdateName>(this: TThis, events: E | readonly E[]): TThis; /** * Type-only declaration of the state shape this step contributes. No-op at * runtime — exists so builder steps can opt into state inference until the * automatic builder→state inference lands in a follow-up. * * @example * c.updates<{ name: string }>().on("message", ctx => ctx.scene.update({ name: ctx.text! })) */ /** * @returns the same composer instance, with no type change. TThis is * inferred from the binding; if you call it as `c.updates<T>()`, the * return is typed as `c`. */ updates<_T, TThis = unknown>(this: TThis): TThis; reaction<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, trigger: gramio.TelegramReactionTypeEmojiEmoji | gramio.TelegramReactionTypeEmojiEmoji[] | readonly gramio.TelegramReactionTypeEmojiEmoji[], handler: (context: gramio.MessageReactionContext<gramio.AnyBot> & EventContextOf<TThis, "message_reaction">) => unknown, macroOptions?: Record<string, unknown>): TThis; callbackQuery<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }, Trigger extends CallbackData | string | RegExp>(this: TThis, trigger: Trigger, handler: (context: gramio.CallbackQueryContext<gramio.AnyBot> & { queryData: Trigger extends CallbackData ? ReturnType<Trigger["unpack"]> : Trigger extends RegExp ? RegExpMatchArray : never; } & EventContextOf<TThis, "callback_query">) => unknown, macroOptions?: Record<string, unknown>): TThis; chosenInlineResult<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, trigger: RegExp | string | ((context: gramio.ChosenInlineResultContext<gramio.AnyBot>) => boolean), handler: (context: gramio.ChosenInlineResultContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & EventContextOf<TThis, "chosen_inline_result">) => unknown, macroOptions?: Record<string, unknown>): TThis; inlineQuery<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, triggerOrHandler: RegExp | string | ((context: gramio.InlineQueryContext<gramio.AnyBot>) => boolean) | ((context: gramio.InlineQueryContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & EventContextOf<TThis, "inline_query">) => unknown), maybeHandler?: (context: gramio.InlineQueryContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & EventContextOf<TThis, "inline_query">) => unknown, options?: { onResult?: (context: gramio.ChosenInlineResultContext<gramio.AnyBot> & { args: RegExpMatchArray | null; } & EventContextOf<TThis, "chosen_inline_result">) => unknown; } & Record<string, unknown>): TThis; guestQuery<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, triggerOrHandler: RegExp | string | ((context: gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) => boolean) | ((context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: RegExpMatchArray | null; } & EventContextOf<TThis, "guest_message">) => unknown), maybeHandler?: (context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: RegExpMatchArray | null; } & EventContextOf<TThis, "guest_message">) => unknown, macroOptions?: Record<string, unknown>): TThis; hears<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, trigger: RegExp | (string | string[] | readonly string[]) | ((context: gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) => boolean), handler: (context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: RegExpMatchArray | null; } & EventContextOf<TThis, "message">) => unknown, macroOptions?: Record<string, unknown>): TThis; command<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, command: string | string[] | readonly string[], handlerOrMeta: ((context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: string | null; } & EventContextOf<TThis, "message">) => unknown) | gramio.CommandMeta, handlerOrOptions?: ((context: (gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { args: string | null; } & EventContextOf<TThis, "message">) => unknown) | Record<string, unknown>, macroOptions?: Record<string, unknown>): TThis; startParameter<TThis extends _gramio_composer.ComposerLike<TThis> & { "~": { macros: gramio.MacroDefinitions; commandsMeta?: Map<string, unknown>; Derives?: Record<string, object>; }; chosenInlineResult(trigger: any, handler: any, macroOptions?: any): TThis; }>(this: TThis, parameter: RegExp | (string | string[] | readonly string[]), handler: Handler<(gramio.MessageContext<gramio.AnyBot> & gramio.Require<gramio.MessageContext<gramio.AnyBot>, "from">) & { rawStartPayload: string; } & EventContextOf<TThis, "message">>, macroOptions?: Record<string, unknown>): TThis; }; /** * StepComposer instance pre-seeded with the parent Scene's derives in TOut. * * This is the type you want at `c => c…` callsites — it carries `ctx.scene`, * any scene-level `.derive(...)`-injected fields, and any `.extend(plugin)` * derives all the way into the step's `.enter / .on / .command / ...` * handlers. Without this threading, step ctx is plain * `MessageContext | CallbackQueryContext` and `ctx.scene.update(...)` / * `ctx.scene.exit()` would not type-check. * * The Scene's Derives generic looks like: * `{ global: { scene: ... } & UserDerives; message: ...; callback_query: ... }` * * We pull `Derives["global"]` into TOut so it's visible on every step ctx, * and pull the per-event slots into TDerives so `.on("message", ...)` / * `.command(...)` handlers receive the right narrowed shape too. * * Generic order: the parent scene's `Derives` is what we need; we accept * the whole Scene type and project just that slot to keep callers from * having to extract by hand. */ type StepComposerFor<TSceneDerives extends { global: object; } = { global: {}; }, AccState extends object = {}> = StepComposerStateTracked<EventComposer<Context<AnyBot>, TelegramEventMap, Context<AnyBot>, Context<AnyBot> & TSceneDerives["global"], {}, Omit<TSceneDerives, "global"> extends Record<string, object> ? Omit<TSceneDerives, "global"> : {}, typeof stepMethods> & typeof stepMethods, TSceneDerives, AccState>; /** * Extracts the state contribution from a handler's awaited return type. * * `ctx.scene.update({ k: v })` returns `Promise<UpdateData<{ k: v }>>`, so * `update({k:v})` returns are picked up automatically. Returning * `void`/`undefined`/`Promise<void>` from a handler contributes nothing * (`{}`), so handlers that only send messages don't pollute the state. * * Mirrors `Awaited<ReturnType<Handler>>` extraction already done by the * legacy `step(event, handler)` overload in scene.ts — this generalises * the same trick to every event-handler method on the step builder. */ type ExtractUpdateState<R> = Awaited<R> extends UpdateData<infer T> ? T : {}; /** * Re-typed view of a step composer where each event-handler method * (`.on / .command / .callbackQuery / .hears / .enter / .exit / .fallback`) * accumulates `Awaited<ReturnType<H>>` into a phantom `AccState` generic. * * The accumulated state is what `Scene.step(...)` reads off the builder's * return type to widen the Scene's `State` generic — so that * `ctx.scene.state.X` is properly typed in subsequent step handlers * without any `.state<T>()` annotation. * * Conceptually: `c.on("message", ctx => ctx.scene.update({ name: ctx.text }))` * threads `{ name: string }` into the step's `AccState`; on the next step's * `ctx.scene.state` you see `{ name: string }` typed in. */ /** * Mirror of gramio's `EventContextOf<TThis, E>` projected against a Scene's * `Derives` slot — merges the scene-level global derives plus any per-event * derives registered via `scene.derive("<event>", ...)` so that step handlers * see the same shape gramio's bot-level handlers would. * * `Derives` slots default to `{}` (see `DeriveDefinitions` in gramio), so the * `keyof` conditional safely degrades when an event has no entry. */ type StepEventCtx<E extends UpdateName, TSceneDerives extends { global: object; }> = ContextType<AnyBot, E> & TSceneDerives["global"] & (E extends keyof TSceneDerives ? TSceneDerives[E] : {}); type StepComposerStateTracked<TBase, TSceneDerives extends { global: object; }, AccState extends object> = Omit<TBase, "on" | "command" | "callbackQuery" | "hears" | "enter" | "exit" | "fallback"> & { on<E extends UpdateName, H extends (ctx: StepEventCtx<E, TSceneDerives>, next: Next) => unknown>(event: E | readonly E[], handler: H): StepComposerStateTracked<TBase, TSceneDerives, AccState & ExtractUpdateState<ReturnType<H>>>; command<H extends (ctx: StepEventCtx<"message", TSceneDerives> & { args: string | null; }) => unknown>(name: string | readonly string[], handler: H): StepComposerStateTracked<TBase, TSceneDerives, AccState & ExtractUpdateState<ReturnType<H>>>; callbackQuery<Trigger extends CallbackData | string | RegExp, H extends (ctx: StepEventCtx<"callback_query", TSceneDerives> & { queryData: Trigger extends CallbackData ? ReturnType<Trigger["unpack"]> : Trigger extends RegExp ? RegExpMatchArray : never; }) => unknown>(trigger: Trigger, handler: H): StepComposerStateTracked<TBase, TSceneDerives, AccState & ExtractUpdateState<ReturnType<H>>>; hears<H extends (ctx: StepEventCtx<"message", TSceneDerives> & { args: RegExpMatchArray | null; }) => unknown>(trigger: RegExp | string | readonly string[] | ((ctx: ContextType<AnyBot, "message">) => boolean), handler: H): StepComposerStateTracked<TBase, TSceneDerives, AccState & ExtractUpdateState<ReturnType<H>>>; enter<E extends UpdateName = DefaultStepEvents, H extends (ctx: StepEventCtx<E, TSceneDerives>, next: Next) => unknown = (ctx: StepEventCtx<E, TSceneDerives>, next: Next) => unknown>(handler: H): StepComposerStateTracked<TBase, TSceneDerives, AccState & ExtractUpdateState<ReturnType<H>>>; exit<E extends UpdateName = DefaultStepEvents, H extends (ctx: StepEventCtx<E, TSceneDerives>, next: Next) => unknown = (ctx: StepEventCtx<E, TSceneDerives>, next: Next) => unknown>(handler: H): StepComposerStateTracked<TBase, TSceneDerives, AccState & ExtractUpdateState<ReturnType<H>>>; fallback<E extends UpdateName = DefaultStepEvents, H extends (ctx: StepEventCtx<E, TSceneDerives>, next: Next) => unknown = (ctx: StepEventCtx<E, TSceneDerives>, next: Next) => unknown>(handler: H): StepComposerStateTracked<TBase, TSceneDerives, AccState & ExtractUpdateState<ReturnType<H>>>; }; /** Helper: extract the accumulated state generic from a tracked step composer. */ type ExtractStepState<T> = T extends StepComposerStateTracked<any, any, infer S> ? S : {}; type ContextWithFrom = Pick<ContextType<Bot, "message" | "callback_query">, "from">; declare function getInActiveSceneHandler<Params, State extends StateTypesDefault, ExitData extends Record<string, unknown> = Record<string, unknown>>(context: ContextWithFrom & { scene: InActiveSceneHandlerReturn<any, any>; }, storage: Storage, sceneData: ScenesStorageData<Params, State>, scene: AnyScene, key: `@gramio/scenes:${string | number}`, allowedScenes: string[], allScenes: AnyScene[]): InActiveSceneHandlerReturn<Params, State, ExitData>; type AnyScene = Scene<any, any, any, any>; type StepHandler<T, Return = any> = (context: T, next: Next$1) => any; type SceneDerivesDefinitions<Params, State extends StateTypesDefault, ExitData extends Record<string, unknown> = Record<string, unknown>> = DeriveDefinitions & { global: { scene: ReturnType<typeof getInActiveSceneHandler<Params, State, ExitData>>; }; }; /** * Scene IS an EventComposer. Inherits the full gramio DSL * (`.command/.callbackQuery/.hears/.on/.use/.derive/.guard/.branch/.extend/...`) * and adds scene-specific methods (`.params/.state/.exitData/.onEnter/.step/ * .ask`). Scene-specific data lives on `this["~scene"]` to avoid colliding * with the composer's own `~` slot. */ declare class Scene<Params = never, Errors extends ErrorDefinitions = {}, State extends StateTypesDefault = Record<string, never>, Derives extends SceneDerivesDefinitions<Params, State, any> = SceneDerivesDefinitions<Params, State>> extends SceneComposerBase { name: string; stepsCount: number; /** * Override of the inherited composer's `~` slot to widen its `Out` * (phantom TOut carrier) to include the scene's `Derives["global"]`. * This is what makes `ctx.scene` visible inside scene-level event * handlers (`scene.callbackQuery / .command / .hears / .on / …`): * those methods type `ctx` via `EventContextOf<this, E>`, which reads * `Out` from this slot. With the widening, `ctx.scene` is now present * everywhere — both at the scene level AND inside step builders. */ "~": InstanceType<typeof SceneComposerBase>["~"] & { Out: InstanceType<typeof SceneComposerBase>["~"]["Out"] & Derives["global"]; }; /** @internal — scene-specific state. Stored on a dedicated slot so the * composer's own `~` slot remains untouched. Generics here propagate * Scene's `Params` / `State` into the structural shape so that * `Scene<{id}, ...>` is distinct from `Scene<never, ...>` at the type * level (needed by `SceneEnterHandler` to differentiate the no-params * and with-params overloads at call sites). */ "~scene": SceneInternals<Params, State>; constructor(name?: string); params<SceneParams>(): Scene<SceneParams, Errors, State, Modify<Derives, { global: { scene: Modify<Derives["global"]["scene"], { params: SceneParams; reenter: (params?: SceneParams) => Promise<void>; }>; }; }>>; state<StateParams extends StateTypesDefault>(): Scene<Params, Errors, StateParams, Modify<Derives, { global: Modify<Derives["global"], { scene: Modify<Derives["global"]["scene"], { state: StateParams; }>; }>; }>>; exitData<ExitData extends Record<string, unknown>>(): Scene<Params, Errors, State, Modify<Derives, { global: Modify<Derives["global"], { scene: Modify<Derives["global"]["scene"], { exitSub: (returnData?: ExitData) => Promise<void>; }>; }>; }>>; /** Merge another Scene's middlewares + lifecycle hooks + step list. */ extend<UParams, UErrors extends ErrorDefinitions, UState extends StateTypesDefault, UDerives extends SceneDerivesDefinitions<UParams, UState, any>>(scene: Scene<UParams, UErrors, UState, UDerives>): Scene<Params, Errors & UErrors, Record<string, never> extends State ? UState : Record<string, never> extends UState ? State : State & UState, Derives & UDerives>; extend<UExposed extends object, UDerives extends Record<string, object>>(composer: EventComposer$1<any, any, any, any, UExposed, UDerives, any>): Scene<Params, Errors, State, Derives & { global: UExposed; } & UDerives>; extend<NewPlugin extends AnyPlugin>(plugin: NewPlugin): Scene<Params, Errors & NewPlugin["_"]["Errors"], State, Derives & NewPlugin["_"]["Derives"]>; derive<D extends object>(handler: DeriveHandler<ContextType<Bot, "message"> & Derives["global"], D>): Scene<Params, Errors, State, Modify<Derives, { global: Derives["global"] & D; }>>; derive<D extends object>(handler: DeriveHandler<ContextType<Bot, "message"> & Derives["global"], D>, options: { as: "scoped" | "global"; }): Scene<Params, Errors, State, Modify<Derives, { global: Derives["global"] & D; }>>; derive<E extends UpdateName, D extends object>(event: MaybeArray<E>, handler: DeriveHandler<ContextType<Bot, E> & Derives["global"], D>): Scene<Params, Errors, State, Derives & { [K in E]: D; }>; /** * Override of `.decorate` that preserves Scene<...>. Same reason as * `.derive` above — the base method widens TOut and drops the subclass. */ decorate<D extends object>(values: D): Scene<Params, Errors, State, Modify<Derives, { global: Derives["global"] & D; }>>; decorate<D extends object>(values: D, options: { as: "scoped" | "global"; }): Scene<Params, Errors, State, Modify<Derives, { global: Derives["global"] & D; }>>; /** * Register a handler that runs once when the user enters the scene. * * Fires AFTER scene-level `.derive()` / `.decorate()` middleware has * applied — so derived ctx fields (`ctx.user`, etc.) ARE available. Fires * exactly once per scene occupancy: `step.go(...)` transitions don't * re-trigger it. * * @example * new Scene("checkout") * .derive(async ctx => ({ user: await db.users.find(ctx.from!.id) })) * .onEnter(ctx => analytics.track("checkout_start", { userId: ctx.user.id })) * .step("review", c => c.message("Order looks good?").on("message", ...)) */ onEnter(handler: (context: ContextType<Bot, "message"> & Derives["global"]) => unknown): this; /** * Register a handler that runs when the user leaves this scene — on * `ctx.scene.exit()`, `ctx.scene.exitSub()` (the sub-scene exits), and * `ctx.scene.reenter()` (the prior occupancy of this scene ends before * re-entry). Symmetric to `.onEnter`. Useful for cleanup, analytics, * "thanks for completing" messages. */ onExit(handler: (context: ContextType<Bot, "message"> & Derives["global"]) => unknown): this; /** * Builder, numeric step id (autoincrement). * * The builder's return type is inspected for any `Awaited<ReturnType<H>>` * that contains `UpdateData<T>` — i.e., any handler returning * `ctx.scene.update({…})`. Those T's are merged into Scene's `State` * generic automatically, so subsequent steps see `ctx.scene.state.X` * properly typed without any `.state<T>()` annotation. */ step<B extends (c: StepComposerFor<Derives>) => unknown, StepState extends object = ExtractStepState<ReturnType<B>>>(builder: B): Scene<Params, Errors, Record<string, never> extends State ? StepState : State & StepState, Modify<Derives, { global: Modify<Derives["global"], { scene: Modify<Derives["global"]["scene"], { state: Record<string, never> extends State ? StepState : State & StepState; }>; }>; }>>; /** * Legacy event-filtered step (single event name OR an array of events). * * Listed BEFORE the named-builder overload so TS's overload resolution * tries this first. `T extends UpdateName` then either succeeds (real * event name like `"message"`) and types `ctx` properly, OR fails so TS * falls through to the named-builder overload. Result: `step("message", * (ctx, next) => …)` types `ctx` as `MessageContext`, while * `step("intro", (c) => …)` (with a name that's NOT a known event) * cleanly resolves to the named-builder overload. */ step<T extends UpdateName, Handler extends StepHandler<ContextType<Bot, T> & Derives["global"] & Derives[T], any>, Return = Awaited<ReturnType<Handler>>>(updateName: MaybeArray<T>, handler: Handler): Scene<Params, Errors, Extract<Return, UpdateData<any>> extends UpdateData<infer Type> ? Record<string, never> extends State ? Type : State & Type : State, Modify<Derives, { global: Modify<Derives["global"], { scene: Modify<Derives["global"]["scene"], { state: Extract<Return, UpdateData<any>> extends UpdateData<infer Type> ? Record<string, never> extends State ? Type : State & Type : State; }>; }>; }>>; /** * Builder, named step id. Same state-inference behavior as the numeric * form — any `update({…})` calls inside handlers widen `State`. */ step<B extends (c: StepComposerFor<Derives>) => unknown, StepState extends object = ExtractStepState<ReturnType<B>>>(name: string, builder: B): Scene<Params, Errors, Record<string, never> extends State ? StepState : State & StepState, Modify<Derives, { global: Modify<Derives["global"], { scene: Modify<Derives["global"]["scene"], { state: Record<string, never> extends State ? StepState : State & StepState; }>; }>; }>>; /** @internal Register a builder-style step: creates a fresh StepComposer, * runs the user's builder against it, and stores the entry on `~scene.steps`. */ private _registerBuilderStep; /** @internal Register a legacy event-filtered step as a gated `.use()` * middleware. Preserved for back-compat with the original `.step("message", ctx => ...)` API. */ private _registerLegacyEventStep; ask<Key extends string, Schema extends StandardSchemaV1, Return extends StateTypesDefault = { [key in Key]: StandardSchemaV1.InferOutput<Schema>; }>(key: Key, validator: Schema, firstTimeMessage: Stringable, options?: { /** Custom message when validation fails. Receives all issues from the validator. */ onInvalidInput?: (issues: readonly StandardSchemaV1.Issue[]) => Stringable; }): Scene<Params, Errors, Record<string, never> extends State ? Return : State & Return, Modify<Derives, { global: Modify<Derives["global"], { scene: Modify<Derives["global"]["scene"], { state: Record<string, never> extends State ? Return : State & Return; }>; }>; }>>; dispatch(context: Context<Bot> & { [key: string]: unknown; }, onNext?: () => unknown, passthrough?: Next$1, /** * Middleware fns that have already run for this update (e.g. derive/ * decorate pre-run so `onEnter` could see them in the legacy path) and * must NOT run again here. They `Object.assign` onto the live ctx, so * their effect persists — re-running would only re-fire side effects and * double-count. Filtering by fn identity skips exactly those. */ skipFns?: ReadonlySet<unknown>): Promise<void>; /** * @internal Drop middlewares whose `plugin` is already in the bot's * extended-set (`bot.extend(withUser)`) so a named plugin/composer shared * between the bot chain and a scene runs once per update, not twice. Used by * `dispatch` and by the onEnter setup pre-runs in `dispatchActive`. */ private _dedupeAgainstBot; dispatchActive(context: Context<Bot> & { [key: string]: unknown; }, storage: Storage, key: string, data: ScenesStorageData<unknown, unknown>, passthrough?: Next$1): Promise<void>; } interface ScenesDerivesOptions<WithCurrentScene extends boolean = false> extends ScenesOptions { withCurrentScene?: WithCurrentScene; scenes?: AnyScene[]; /** * You should use the same storage for scenes and scenesDerives */ storage: Storage; } declare function scenesDerives<WithCurrentScene extends boolean = false>(scenesOrOptions: AnyScene[] | ScenesDerivesOptions<WithCurrentScene>, optionsRaw?: ScenesDerivesOptions<WithCurrentScene>): Plugin<{}, gramio.DeriveDefinitions & { message: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; channel_post: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; inline_query: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; chosen_inline_result: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; callback_query: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; shipping_query: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; pre_checkout_query: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; chat_join_request: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; new_chat_members: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; new_chat_title: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; new_chat_photo: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; delete_chat_photo: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; group_chat_created: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; message_auto_delete_timer_changed: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; migrate_to_chat_id: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; migrate_from_chat_id: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; pinned_message: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; invoice: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; successful_payment: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; users_shared: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; chat_shared: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; proximity_alert_triggered: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; video_chat_scheduled: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; video_chat_started: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; video_chat_ended: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; video_chat_participants_invited: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; web_app_data: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; location: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; passport_data: { scene: WithCurrentScene extends true ? PossibleInUnknownScene<any, any> : EnterExit; }; }, {}>; declare function scenes(scenes: AnyScene[], options?: ScenesOptions): Plugin<{}, gramio.DeriveDefinitions & { message: { scene: Omit<EnterExit, "exit">; }; callback_query: { scene: Omit<EnterExit, "exit">; }; }, {}>; export { Scen