@grammyjs/conversations
Version:
Conversational interfaces for grammY
531 lines (530 loc) • 21.4 kB
TypeScript
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>;
}