UNPKG

@grammyjs/conversations

Version:

Conversational interfaces for grammY

531 lines (530 loc) 21.4 kB
import type { Animation, Audio, Contact, Context, Dice, Document, File, Game, Location, MessageEntity, PaidMediaInfo, PhotoSize, Poll, Sticker, Story, Venue, Video, VideoNote, Voice } from "./deps.node.js"; /** * An action to perform after a context object passed form valiation. * * A form action receives the validated context object as the first argument. It * receives the extracted value as a second argument. * * @param ctx The current context object * @param result The value returned by the form field * @typeParam C A custom context type * @typeParam T The type of form field */ export type FormAction<C extends Context, T> = (ctx: C, result: T) => unknown | Promise<unknown>; /** * A callback function to be invoked after a context object failed form * validation. * * A form action receives the rejected context object as the first argument. * * If validation fails and an error is specified, it is possible to use * {@link OtherwiseWithReason} instead. * * @param ctx The current context object * @typeParam C A custom context type */ export type Otherwise<C extends Context> = (ctx: C) => unknown | Promise<unknown>; /** * A callback function to be invoked after a context object failed form * validation. * * A form action receives the rejected context object as the first argument. * * If validation fails and a reason is specified as an error of the * {@link Result} return value of the validation function, the form action * receives the error as a second argument. If no reason was specified, use * {@link Otherwise} instead. * * @param ctx The current context object * @param reason A reason why validation failed * @typeParam C A custom context type * @typeParam R A type of reason defined by the validation function */ export type OtherwiseWithReason<C extends Context, R> = (ctx: C, reason: R) => unknown | Promise<unknown>; /** * Options to pass to a form field. Can be either a {@link FormAction} function * or a {@link FormConfig} object. * * If the validation function is able to provide a reason as to why validation * has failed, it is possible use {@link FormOptionsWithReason} instead. * * @typeParam C A custom context type * @typeParam C A form field type */ export type FormOptions<C extends Context, T> = FormAction<C, T> | FormConfig<C, T>; /** * Options to pass to a form field with an enhanced validation function that can * provide a reason as to why validation has failed. Can be either a * {@link FormAction} function or a {@link FormConfigWithReason} object. * * If the validation function does not provide a reason, use {@link FormOptions} * instead. * * @typeParam C A custom context type * @typeParam T A type of success value for the validation function * @typeParam R A type of error value for the validation function */ export type FormOptionsWithReason<C extends Context, T, R> = FormAction<C, T> | FormConfigWithReason<C, T, R>; /** * A base options bag object for a form field. This holds all properties that * are common among {@link FormConfig} and {@link FormConfigWithReason}. * * @typeParam C A custom context type * @typeParam T A form field type */ export interface FormConfigShared<C extends Context, T> { /** * Determines whether [the outside middleware * system](https://grammy.dev/guide/middleware) should resume after the * update is skipped in case that form validation fails. * * Specify `next: true` to make sure that subsequent handlers will run. This * effectively causes `next` to be called by the plugin. * * Defaults to `false` unless the conversation is marked as parallel, in * which case this option defaults to `true`. */ next?: boolean; /** * Specifies a timeout for the wait call. * * When the form field is reached, `Date.now()` is called. When the form * field resolves, `Date.now()` is called again, and the two values are * compared. If the form field resolved more than the specified number of * milliseconds after it was reached initially, then the conversation will * be halted, any exit handlers will be called, and the surrounding * middleware will resume normally so that subsequent handlers can run. * * To the outside middleware system, this will look like the conversation * was never active. */ maxMilliseconds?: number; /** * Collation key for the wait call, safety measure to protect against data * corruption. This is used extensively by the plugin internally, but it is * rarely useful to changes this behavior. */ collationKey?: string; /** * A form action to perform on the context object after the form validation * succeeds for a context object, and before the respective value is * returned form the form field. */ action?: FormAction<C, T>; } /** * An options bag object for a form field. Contains all properties of * {@link FormConfigShared} as well as an `otherwise` function to be invoked * when form valiation fails. * * If a reason is specified by the form validation, it is possible to use * {@link FormConfigWithReason} instead. * * @typeParam C A custom context type * @typeParam T A form field type */ export interface FormConfig<C extends Context, T> extends FormConfigShared<C, T> { /** * Callback that will be invoked when the form validation fails for a * context object. */ otherwise?: Otherwise<C>; } /** * An options bag object for a form field. Contains all properties of * {@link FormConfigShared} as well as an `otherwise` function to be invoked * when form valiation fails with a known reason. * * If no reason is specified by the form validation, use {@link FormConfig} * instead. * * @typeParam C A custom context type * @typeParam T A type of success value for the validation function * @typeParam R A type of error value for the validation function */ export interface FormConfigWithReason<C extends Context, T, R> extends FormConfigShared<C, T> { /** * Callback that will be invoked when the form validation fails with a * reason for a context object. */ otherwise?: OtherwiseWithReason<C, R>; } /** A value that may be absent */ export type Maybe<T> = { ok: false; } | { ok: true; value: T; }; /** A value or an error */ export type Result<T, E> = { ok: false; error: E; } | { ok: true; value: T; }; /** * An object that fully specifies how to build a form field. * * Contains as properties of {@link FormConfig} as well as a function that can * validate context objects. * * If the validation function can provide a reason as to why validation has * failed, it is possible to use {@link FormBuilderWithReason} instead. * * @typeParam C A custom context type * @typeParam T The type of value this form field returns */ export interface FormBuilder<C extends Context, T> extends FormConfig<C, T> { /** * A function that validates a given context. * * When validation succeeds, a value can be extracted from the context * object. The result of the validation as well as the data extraction needs * to be encoded using a {@link Maybe} type. * * @param ctx A context object to validate */ validate(ctx: C): Maybe<T> | Promise<Maybe<T>>; } /** * An object that fully specifies how to build a form field. * * Contains as properties of {@link FormConfigWithReason} as well as a function * that can validate context objects. If a context object does not pass * validation, the validation function is able to provide a reason. * * If the validation function cannot provide reason as to why validation has * failed, it is possible to use {@link FormBuilder} instead. * * @typeParam C A custom context type * @typeParam T A type of success value for the validation function * @typeParam R A type of error value for the validation function */ export interface FormBuilderWithReason<C extends Context, T, R> extends FormConfigWithReason<C, T, R> { /** * A function that validates a given context. * * When validation succeeds, a value can be extracted from the context * object. When validation fails, an error can be generated. The result of * the validation needs to be encoded using a {@link Result} type. * * @param ctx A context object to validate */ validate(ctx: C): Result<T, R> | Promise<Result<T, R>>; } /** * A container for form building utilities. * * Each method on this class represents a differnt type of form field which can * validate context objects and extract data from it. */ export declare class ConversationForm<C extends Context> { private readonly conversation; /** Constructs a new form based on wait and skip callbacks */ constructor(conversation: { wait: (opts: { maxMilliseconds?: number; collationKey?: string; }) => Promise<C>; skip: (opts: { next?: boolean; }) => Promise<never>; }); /** * Generic form field that can be used to build any other type of form * field. This is heavily used internally. * * Most likely, you will not need this because there is a more convenient * option. However, you can still use it if the type of form field you need * is not supported out of the box. * * @param builder A form field definition object * @typeParam T A type of value to be extracted from the form field * @typeParam R A type of reason to be specified if validation fails */ build<T, R>(builder: FormBuilderWithReason<C, T, R>): Promise<T>; build<T, R>(builder: FormBuilder<C, T>): Promise<T>; /** * Form field that checks if the incoming update contains a message or * channel post with text, and returns this text as string. Does not check * for captions. * * Accepts an optional options object that lets you perform actions when * text is received, when a non-text update is received, and more. * * @param options Optional options */ text(options?: FormOptions<C, string>): Promise<string>; /** * Form field that checks if the incoming update contains a message or * channel post with text that can be parsed to a number, and returns this * number. Does not check captions. * * The conversion to number uses `parseFloat`. * * Accepts an optional options object that lets you perform actions when a * number is received, when a non-number update is received, and more. * * @param options Optional options */ number(options?: FormOptions<C, number>): Promise<number>; /** * Form field that checks if the incoming update contains a message or * channel post with text that can be parsed to an integer, and returns this * integer as a `number`. Does not check for captions. * * The conversion to number uses `parseInt`. * * Accepts an optional options object that lets you specify the radix to use * as well as perform actions when a number is received, when a non-number * update is received, and more. * * @param options Optional options */ int(options?: FormOptions<C, number> & { /** The radix to use for parsing the integer */ radix?: number; }): Promise<number>; /** * Form field that checks if the incoming update contains a message or * channel post with one of several predefined strings, and returns the * actual text as string. Does not check captions. * * This is especially useful when working with custom keyboards. * * ```ts * const keyboard = new Keyboard() * .text("A").text("B") * .text("C").text("D") * .oneTime() * await ctx.reply("A, B, C, or D?", { reply_markup: keyboard }) * const answer = await conversation.form.select(["A", "B", "C", "D"], { * otherwise: ctx => ctx.reply("Please use one of the buttons!") * }) * switch (answer) { * case "A": * case "B": * case "C": * case "D": * // ... * } * ``` * * Accepts an optional options object that lets you perform actions when * text is received, when a non-text update is received, and more. * * @param entries A string array of accepted values * @param options Optional options */ select<E extends string>(entries: E[], options?: FormOptions<C, E>): Promise<E>; /** * Form field that checks if the incoming update contains a message or * channel post with a given type of message entity, and returns this * entity. The form field relies on `ctx.entities()` for data extraction, so * both texts and captions are checked. * * Accepts an optional options object that lets you perform actions when * text is received, when a non-text update is received, and more. * * @param type One or more types of message entities to accept * @param options Optional options */ entity<M extends MessageEntity>(type: M["type"] | M["type"][], options?: FormOptions<C, MessageEntity & { text: string; }>): Promise<MessageEntity & { text: string; }>; /** * Form field that checks if the incoming update contains a message or * channel post with an animation, and returns the received animation * object. * * Accepts an optional options object that lets you perform actions when an * animation is received, when a non-animation update is received, and more. * * @param options Optional options */ animation(options?: FormOptions<C, Animation>): Promise<Animation>; /** * Form field that checks if the incoming update contains a message or * channel post with an audio message, and returns the received audio * object. * * Accepts an optional options object that lets you perform actions when an * audio message is received, when a non-audio update is received, and more. * * @param options Optional options */ audio(options?: FormOptions<C, Audio>): Promise<Audio>; /** * Form field that checks if the incoming update contains a message or * channel post with a document message, and returns the received document * object. * * Accepts an optional options object that lets you perform actions when a * document message is received, when a non-document update is received, and * more. * * @param options Optional options */ document(options?: FormOptions<C, Document>): Promise<Document>; /** * Form field that checks if the incoming update contains a message or * channel post with paid media, and returns the received paid media object. * * Accepts an optional options object that lets you perform actions when a * paid media message is received, when a non-paid media update is received, * and more. * * @param options Optional options */ paidMedia(options?: FormOptions<C, PaidMediaInfo>): Promise<PaidMediaInfo>; /** * Form field that checks if the incoming update contains a message or * channel post with a photo, and returns the received array of `PhotoSize` * objects. * * Accepts an optional options object that lets you perform actions when a * photo is received, when a non-photo update is received, and more. * * @param options Optional options */ photo(options?: FormOptions<C, PhotoSize[]>): Promise<PhotoSize[]>; /** * Form field that checks if the incoming update contains a message or * channel post with a sticker, and returns the received sticker object. * * Accepts an optional options object that lets you perform actions when a * sticker is received, when a non-sticker update is received, and more. * * @param options Optional options */ sticker(options?: FormOptions<C, Sticker>): Promise<Sticker>; /** * Form field that checks if the incoming update contains a message or * channel post with a story, and returns the received story object. * * Accepts an optional options object that lets you perform actions when a * story is received, when a non-story update is received, and more. * * @param options Optional options */ story(options?: FormOptions<C, Story>): Promise<Story>; /** * Form field that checks if the incoming update contains a message or * channel post with a video, and returns the received video object. * * Accepts an optional options object that lets you perform actions when a * video is received, when a non-video update is received, and more. * * @param options Optional options */ video(options?: FormOptions<C, Video>): Promise<Video>; /** * Form field that checks if the incoming update contains a message or * channel post with a video note, and returns the received video note * object. * * Accepts an optional options object that lets you perform actions when a * video note is received, when a non-video note update is received, and * more. * * @param options Optional options */ video_note(options?: FormOptions<C, VideoNote>): Promise<VideoNote>; /** * Form field that checks if the incoming update contains a message or * channel post with a voice message, and returns the received voice object. * * Accepts an optional options object that lets you perform actions when a * voice message is received, when a non-voice message update is received, * and more. * * @param options Optional options */ voice(options?: FormOptions<C, Voice>): Promise<Voice>; /** * Form field that checks if the incoming update contains a message or * channel post with a contact, and returns the received contact object. * * Accepts an optional options object that lets you perform actions when a * contact is received, when a non-contact update is received, and more. * * @param options Optional options */ contact(options?: FormOptions<C, Contact>): Promise<Contact>; /** * Form field that checks if the incoming update contains a message or * channel post with dice, and returns the received dice object. * * Accepts an optional options object that lets you perform actions when * dice are received, when a non-dice update is received, and more. * * @param options Optional options */ dice(options?: FormOptions<C, Dice>): Promise<Dice>; /** * Form field that checks if the incoming update contains a message or * channel post with a game, and returns the received game object. * * Accepts an optional options object that lets you perform actions when a * game is received, when a non-game update is received, and more. * * @param options Optional options */ game(options?: FormOptions<C, Game>): Promise<Game>; /** * Form field that checks if the incoming update contains a message or * channel post with a poll, and returns the received poll object. * * Accepts an optional options object that lets you perform actions when a * poll is received, when a non-poll update is received, and more. * * @param options Optional options */ poll(options?: FormOptions<C, Poll>): Promise<Poll>; /** * Form field that checks if the incoming update contains a message or * channel post with a venue, and returns the received venue object. * * Accepts an optional options object that lets you perform actions when a * venue is received, when a non-venue update is received, and more. * * @param options Optional options */ venue(options?: FormOptions<C, Venue>): Promise<Venue>; /** * Form field that checks if the incoming update contains a message or * channel post with a location, and returns the received location object. * * Accepts an optional options object that lets you perform actions when a * location is received, when a non-location update is received, and more. * * @param options Optional options */ location(options?: FormOptions<C, Location>): Promise<Location>; /** * Form field that checks if the incoming update contains a message or * channel post with a photo or video, and returns the received media * object. * * Accepts an optional options object that lets you perform actions when a * media is received, when a non-media update is received, and more. * * @param options Optional options */ media(options?: FormOptions<C, PhotoSize[] | Video>): Promise<PhotoSize[] | Video>; /** * Form field that checks if the incoming update contains a message or * channel post with a file, calls `await ctx.getFile()`, and returns the * received file object. * * Accepts an optional options object that lets you perform actions when a * file is received, when a non-file update is received, and more. * * @param options Optional options */ file(options?: FormOptions<C, File>): Promise<File>; }