UNPKG

@grammyjs/conversations

Version:

Conversational interfaces for grammY

886 lines (885 loc) 36.2 kB
import { type CallbackQueryContext, type CommandContext, Context, type Filter, type FilterQuery, type GameQueryContext, type HearsContext, type MiddlewareFn, type ReactionContext, type ReactionType, type ReactionTypeEmoji, type Update, type User } from "./deps.node.js"; import { type Checkpoint, type ReplayControls } from "./engine.js"; import { ConversationForm } from "./form.js"; import { type ConversationMenuOptions } from "./menu.js"; type MaybeArray<T> = T | T[]; /** Alias for `string` but with auto-complete for common commands */ export type StringWithCommandSuggestions = (string & Record<never, never>) | "start" | "help" | "settings" | "privacy" | "developer_info"; /** * Specifies an external operation and how to serialize and deserialize its * return and error values. * * @typeParam OC Type of the outside context object * @typeParam R Type of the return value * @typeParam I Type of the intermediate (serialized) representation */ export interface ExternalOp<OC extends Context, R, I = any> { /** * The external operation to perform. * * Receives the current context object from the surrounding middleware. This * gives the task access to sessions (if used) and other values that are not * present inside the conversation. * * @param ctx The outside context object of the surrounding middleware */ task(ctx: OC): R | Promise<R>; /** * Converts a value returned from the task to an object that can safely be * passed to `JSON.stringify`. * * @param value A value to serialize */ beforeStore?(value: R): I | Promise<I>; /** * Restores the original value from the intermediate representation that * `beforeStore` generated. * * @param value The value obtained from `JSON.parse` */ afterLoad?(value: I): R | Promise<R>; /** * Converts an error thrown by the task to an object that can safely be * passed to `JSON.stringify`. * * @param value A thrown error */ beforeStoreError?(value: unknown): unknown | Promise<unknown>; /** * Restores the original error from the intermediate representation that * `beforeStoreError` generated. * * @param value The value obtained from `JSON.parse` */ afterLoadError?(value: unknown): unknown | Promise<unknown>; } /** A function that applies a context object to a callback */ type ApplyContext<OC extends Context> = <F extends (ctx: OC) => unknown>(fn: F) => Promise<ReturnType<F>>; /** Options for creating a conversation handle */ export interface ConversationHandleOptions { /** Callback for when the conversation is halted */ onHalt?(): void | Promise<void>; /** Default wait timeout */ maxMillisecondsToWait?: number; /** * `true` if this conversation can be entered while this or another * conversation is already active, and `false` otherwise. Defaults to * `false`. */ parallel?: boolean; } /** * Options for a call to `conversation.wait()`. */ export interface WaitOptions { /** * Specifies a timeout for the wait call. * * When the wait call is reached, `Date.now()` is called. When the wait call * resolves, `Date.now()` is called again, and the two values are compared. * If the wait call 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; } /** * Options for a call to `conversation.skip()`. */ export interface SkipOptions { /** * Determines whether [the outside middleware * system](https://grammy.dev/guide/middleware) should resume after the * update is skipped. * * Pass `{ 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; } /** * Options to pass to a chained `wait` call. */ export interface AndOtherwiseOptions<C extends Context> extends SkipOptions { /** * Callback that will be invoked when the validation fails for a context * object. * * @param ctx The context object that failed validation */ otherwise?(ctx: C): unknown | Promise<unknown>; } /** * Options for a filtered wait call. A filtered wait call is a wait call that * have extra valiation attached, such as `waitFor`, `waitUntil`, etc. */ export interface OtherwiseOptions<C extends Context> extends WaitOptions, AndOtherwiseOptions<C> { } /** * Options for a call to `conversation.halt()`. */ export interface HaltOptions { /** * Determines whether [the outside middleware * system](https://grammy.dev/guide/middleware) should resume after the * conversation is halted. * * Pass `{ next: true }` to make sure that subsequent handlers will run. * This effectively causes `next` to be called by the plugin. * * Defaults to `false`. */ next?: boolean; } /** * A conversation handle lets you control the conversation, such as waiting for * updates, skipping them, halting the conversation, and much more. It is the * first parameter in each conversation builder function and provides the core * features of this plugin. * * ```ts * async function exmaple(conversation, ctx) { * // ^ this is an instance of this class * * // This is how you can wait for updates: * ctx = await conversation.wait() * } * ``` * * Be sure to consult this plugin's documentation: * https://grammy.dev/plugins/conversations */ export declare class Conversation<OC extends Context = Context, C extends Context = Context> { private controls; private hydrate; private escape; private plugins; private options; /** `true` if `external` is currently running, `false` otherwise */ private insideExternal; private menuPool; private combineAnd; /** * Constructs a new conversation handle. * * This is called internally in order to construct the first argument for a * conversation builder function. You typically don't need to construct this * class yourself. * * @param controls Controls for the underlying replay engine * @param hydrate Context construction callback * @param escape Callback to support outside context objects in `external` * @param plugins Middleware to hydrate context objects * @param options Additional configuration options */ constructor(controls: ReplayControls, hydrate: (update: Update) => C, escape: ApplyContext<OC>, plugins: MiddlewareFn<C>, options: ConversationHandleOptions); /** * Waits for a new update and returns the corresponding context object as * soon as it arrives. * * Note that wait calls terminate the conversation function, save the state * of execution, and only resolve when the conversation is replayed. If this * is not obvious to you, it means that you probably should read [the * documentation of this plugin](https://grammy.dev/plugins/conversations) * in order to avoid common pitfalls. * * You can pass a timeout in the optional options object. This lets you * terminate the conversation automatically if the update arrives too late. * * @param options Optional options for wait timeouts etc */ wait(options?: WaitOptions): AndPromise<C>; /** * Performs a filtered wait call that is defined by a given predicate. In * other words, this method waits for an update, and calls `skip` if the * received context object does not pass validation performed by the given * predicate function. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitUntil(ctx => ctx.msg?.text?.endsWith("grammY"), { * otherwise: ctx => ctx.reply("Send a message that ends with grammY!") * }) * ``` * * If you pass a type predicate, the type of the resulting context object * will be narrowed down. * * ```ts * const ctx = await conversation.waitUntil(Context.has.filterQuery(":text")) * const text = ctx.msg.text; * ``` * * You can combine calls to `waitUntil` with other filtered wait calls by * chaining them. * * ```ts * const ctx = await conversation.waitUntil(ctx => ctx.msg?.text?.endsWith("grammY")) * .andFor("::hashtag") * ``` * * @param predicate A predicate function to validate context objects * @param opts Optional options object */ waitUntil<D extends C>(predicate: (ctx: C) => ctx is D, opts?: OtherwiseOptions<C>): AndPromise<D>; waitUntil(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: OtherwiseOptions<C>): AndPromise<C>; /** * Performs a filtered wait call that is defined by a given negated * predicate. In other words, this method waits for an update, and calls * `skip` if the received context object passed validation performed by the * given predicate function. That is the exact same thigs as calling * {@link Conversation.waitUntil} but with the predicate function being * negated. * * If a context object is discarded (the predicate function returns `true` * for it), you can perform any action by specifying `otherwise` in the * options. * * ```ts * const ctx = await conversation.waitUnless(ctx => ctx.msg?.text?.endsWith("grammY"), { * otherwise: ctx => ctx.reply("Send a message that does not end with grammY!") * }) * ``` * * You can combine calls to `waitUnless` with other filtered wait calls by * chaining them. * * ```ts * const ctx = await conversation.waitUnless(ctx => ctx.msg?.text?.endsWith("grammY")) * .andFor("::hashtag") * ``` * * @param predicate A predicate function to discard context objects * @param opts Optional options object */ waitUnless(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: OtherwiseOptions<C>): AndPromise<C>; /** * Performs a filtered wait call that is defined by a filter query. In other * words, this method waits for an update, and calls `skip` if the received * context object does not match the filter query. This uses the same logic * as `bot.on`. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitFor(":text", { * otherwise: ctx => ctx.reply("Please send a text message!") * }) * // Type inference works: * const text = ctx.msg.text; * ``` * * You can combine calls to `waitFor` with other filtered wait calls by * chaining them. * * ```ts * const ctx = await conversation.waitFor(":text").andFor("::hashtag") * ``` * * @param query A filter query to match * @param opts Optional options object */ waitFor<Q extends FilterQuery>(query: Q | Q[], opts?: OtherwiseOptions<C>): AndPromise<Filter<C, Q>>; /** * Performs a filtered wait call that is defined by a hears filter. In other * words, this method waits for an update, and calls `skip` if the received * context object does not contain text that matches the given text or * regular expression. This uses the same logic as `bot.hears`. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitForHears(["yes", "no"], { * otherwise: ctx => ctx.reply("Please send yes or no!") * }) * // Type inference works: * const answer = ctx.match * ``` * * You can combine calls to `waitForHears` with other filtered wait calls by * chaining them. For instance, this can be used to only receive text from * text messages—not including channel posts or media captions. * * ```ts * const ctx = await conversation.waitForHears(["yes", "no"]) * .andFor("message:text") * const text = ctx.message.text * ``` * * @param trigger The text to look for * @param opts Optional options object */ waitForHears(trigger: MaybeArray<string | RegExp>, opts?: OtherwiseOptions<C>): AndPromise<HearsContext<C>>; /** * Performs a filtered wait call that is defined by a command filter. In * other words, this method waits for an update, and calls `skip` if the * received context object does not contain the expected command. This uses * the same logic as `bot.command`. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitForCommand("start", { * otherwise: ctx => ctx.reply("Please send /start!") * }) * // Type inference works for deep links: * const args = ctx.match * ``` * * You can combine calls to `waitForCommand` with other filtered wait calls * by chaining them. For instance, this can be used to only receive commands * from text messages—not including channel posts. * * ```ts * const ctx = await conversation.waitForCommand("start") * .andFor("message") * ``` * * @param command The command to look for * @param opts Optional options object */ waitForCommand(command: MaybeArray<StringWithCommandSuggestions>, opts?: OtherwiseOptions<C>): AndPromise<CommandContext<C>>; /** * Performs a filtered wait call that is defined by a reaction filter. In * other words, this method waits for an update, and calls `skip` if the * received context object does not contain the expected reaction update. * This uses the same logic as `bot.reaction`. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitForReaction('👍', { * otherwise: ctx => ctx.reply("Please upvote a message!") * }) * // Type inference works: * const args = ctx.messageReaction * ``` * * You can combine calls to `waitForReaction` with other filtered wait calls * by chaining them. * * ```ts * const ctx = await conversation.waitForReaction('👍') * .andFrom(ADMIN_USER_ID) * ``` * * @param reaction The reaction to look for * @param opts Optional options object */ waitForReaction(reaction: MaybeArray<ReactionTypeEmoji["emoji"] | ReactionType>, opts?: OtherwiseOptions<C>): AndPromise<ReactionContext<C>>; /** * Performs a filtered wait call that is defined by a callback query filter. * In other words, this method waits for an update, and calls `skip` if the * received context object does not contain the expected callback query * update. This uses the same logic as `bot.callbackQuery`. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitForCallbackQuery(/button-\d+/, { * otherwise: ctx => ctx.reply("Please click a button!") * }) * // Type inference works: * const data = ctx.callbackQuery.data * ``` * * You can combine calls to `waitForCallbackQuery` with other filtered wait * calls by chaining them. * * ```ts * const ctx = await conversation.waitForCallbackQuery('data') * .andFrom(ADMIN_USER_ID) * ``` * * @param trigger The string to look for in the payload * @param opts Optional options object */ waitForCallbackQuery(trigger: MaybeArray<string | RegExp>, opts?: OtherwiseOptions<C>): AndPromise<CallbackQueryContext<C>>; /** * Performs a filtered wait call that is defined by a game query filter. In * other words, this method waits for an update, and calls `skip` if the * received context object does not contain the expected game query update. * This uses the same logic as `bot.gameQuery`. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitForGameQuery(/game-\d+/, { * otherwise: ctx => ctx.reply("Please play a game!") * }) * // Type inference works: * const data = ctx.callbackQuery.game_short_name * ``` * * You can combine calls to `waitForGameQuery` with other filtered wait * calls by chaining them. * * ```ts * const ctx = await conversation.waitForGameQuery('data') * .andFrom(ADMIN_USER_ID) * ``` * * @param trigger The string to look for in the payload * @param opts Optional options object */ waitForGameQuery(trigger: MaybeArray<string | RegExp>, opts?: OtherwiseOptions<C>): AndPromise<GameQueryContext<C>>; /** * Performs a filtered wait call that is defined by a user-specific filter. * In other words, this method waits for an update, and calls `skip` if the * received context object was not triggered by the given user. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitFrom(targetUser, { * otherwise: ctx => ctx.reply("I did not mean you!") * }) * // Type inference works: * const user = ctx.from.first_name * ``` * * You can combine calls to `waitFrom` with other filtered wait calls by * chaining them. * * ```ts * const ctx = await conversation.waitFrom(targetUser).andFor(":text") * ``` * * @param user The user or user identifer to look for * @param opts Optional options object */ waitFrom(user: number | User, opts?: OtherwiseOptions<C>): AndPromise<C & { from: User; }>; /** * Performs a filtered wait call that is defined by a message reply. In * other words, this method waits for an update, and calls `skip` if the * received context object does not contain a reply to a given message. * * If a context object is discarded, you can perform any action by * specifying `otherwise` in the options. * * ```ts * const ctx = await conversation.waitForReplyTo(message, { * otherwise: ctx => ctx.reply("Please reply to this message!", { * reply_parameters: { message_id: message.message_id } * }) * }) * // Type inference works: * const id = ctx.msg.message_id * ``` * * You can combine calls to `waitForReplyTo` with other filtered wait calls * by chaining them. * * ```ts * const ctx = await conversation.waitForReplyTo(message).andFor(":text") * ``` * * @param message_id The message identifer or object to look for in a reply * @param opts Optional options object */ waitForReplyTo(message_id: number | { message_id: number; }, opts?: OtherwiseOptions<C>): AndPromise<Filter<C, "message" | "channel_post">>; /** * Skips the current update. The current update is the update that was * received in the last wait call. * * In a sense, this will undo receiving an update. The replay logs will be * reset so it will look like the conversation had never received the update * in the first place. Note, however, that any API calls performs between * wait and skip are not going to be reversed. In particular, messages will * not be unsent. * * By default, skipping an update drops it. This means that no other * handlers (including downstream middleware) will run. However, if this * conversation is marked as parallel, skip will behave differently and * resume middleware execution by default. This is needed for other parallel * conversations with the same or a different identifier to receive the * update. * * This behavior can be overridden by passing `{ next: true }` or `{ next: * false }` to skip. * * If several wait calls are used concurrently inside the same conversation, * they will resolve one after another until one of them does not skip the * update. The conversation will only skip an update when all concurrent * wait calls skip the update. Specifying `next` for a skip call that is not * the final skip call has no effect. * * @param options Optional options to control middleware resumption */ skip(options?: SkipOptions): Promise<never>; /** * Calls any exit handlers if installed, and then terminates the * conversation immediately. This method never returns. * * By default, this will consume the update. Pass `{ next: true }` to make * sure that downstream middleware is called. * * @param options Optional options to control middleware resumption */ halt(options?: HaltOptions): Promise<never>; /** * Creates a new checkpoint at the current point of the conversation. * * This checkpoint can be passed to `rewind` in order to go back in the * conversation and resume it from an earlier point. * * ```ts * const check = conversation.checkpoint(); * * // Later: * await conversation.rewind(check); * ``` */ checkpoint(): Checkpoint; /** * Rewinds the conversation to a previous point and continues execution from * there. This point is specified by a checkpoint that can be created by * calling {@link Conversation.checkpoint}. * * ```ts * const check = conversation.checkpoint(); * * // Later: * await conversation.rewind(check); * ``` * * @param checkpoint A previously created checkpoint */ rewind(checkpoint: Checkpoint): Promise<never>; /** * Runs a function outside of the replay engine. This provides a safe way to * perform side-effects such as database communication, disk operations, * session access, file downloads, requests to external APIs, randomness, * time-based functions, and more. **It requires any data obtained from the * outside to be serializable.** * * Remember that a conversation function is not executed like a normal * JavaScript function. Instead, it is often interrupted and replayed, * sometimes many times for the same update. If this is not obvious to you, * it means that you probably should read [the documentation of this * plugin](https://grammy.dev/plugins/conversations) in order to avoid * common pitfalls. * * For instance, if you want to access to your database, you only want to * read or write data once, rather than doing it once per replay. `external` * provides an escape hatch to this situation. You can wrap your database * call inside `external` to mark it as something that performs * side-effects. The replay engine inside the conversations plugin will then * make sure to only execute this operation once. This looks as follows. * * ```ts * // Read from database * const data = await conversation.external(async () => { * return await readFromDatabase() * }) * * // Write to database * await conversation.external(async () => { * await writeToDatabase(data) * }) * ``` * * When `external` is called, it returns whichever data the given callback * function returns. Note that this data has to be persisted by the plugin, * so you have to make sure that it can be serialized. The data will be * stored in the storage backend you provided when installing the * conversations plugin via `bot.use`. In particular, it does not work well * to return objects created by an ORM, as these objects have functions * installed on them which will be lost during serialization. * * As a rule of thumb, imagine that all data from `external` is passed * through `JSON.parse(JSON.stringify(data))` (even though this is not what * actually happens under the hood). * * The callback function passed to `external` receives the outside context * object from the current middleware pass. This lets you access properties * on the context object that are only present in the outside middleware * system, but that have not been installed on the context objects inside a * conversation. For example, you can access your session data this way. * * ```ts * // Read from session * const data = await conversation.external((ctx) => { * return ctx.session.data * }) * * // Write to session * await conversation.external((ctx) => { * ctx.session.data = data * }) * ``` * * Note that while a call to `external` is running, you cannot do any of the * following things. * * - start a concurrent call to `external` from the same conversation * - start a nested call to `external` from the same conversation * - start a Bot API call from the same conversation * * Naturally, it is possible to have several concurrent calls to `externals` * if they happen in unrelated chats. This still means that you should keep * the code inside `external` to a minimum and actually only perform the * desired side-effect itself. * * If you want to return data from `external` that cannot be serialized, you * can specify a custom serialization function. This allows you choose a * different intermediate data representation during storage than what is * present at runtime. * * ```ts * // Read bigint from an API but persist it as a string * const largeNumber: bigint = await conversation.external({ * task: () => fetchCoolBigIntFromTheInternet(), * beforeStore: (largeNumber) => String(largeNumber), * afterLoad: (str) => BigInt(str), * }) * ``` * * Note how we read a bigint from the internet, but we convert it to string * during persistence. This now allows us to use a storage adapter that only * handles strings but does not need to support the bigint type. * * @param op An operation to perform outside of the conversation */ external<R, I = any>(op: ExternalOp<OC, R, I>["task"] | ExternalOp<OC, R, I>): Promise<R>; /** * Takes `Date.now()` once when reached, and returns the same value during * every replay. Prefer this over calling `Date.now()` directly. */ now(): Promise<number>; /** * Takes `Math.random()` once when reached, and returns the same value * during every replay. Prefer this over calling `Math.random()` directly. */ random(): Promise<number>; /** * Calls `console.log` only the first time it is reached, but not during * subsequent replays. Prefer this over calling `console.log` directly. */ log(...data: unknown[]): Promise<void>; /** * Calls `console.error` only the first time it is reached, but not during * subsequent replays. Prefer this over calling `console.error` directly. */ error(...data: unknown[]): Promise<void>; /** * Creates a new conversational menu. * * A conversational menu is a an interactive inline keyboard that is sent to * the user from within a conversation. * * ```ts * const menu = conversation.menu() * .text("Send message", ctx => ctx.reply("Hi!")) * .text("Close", ctx => ctx.menu.close()) * * await ctx.reply("Menu message", { reply_markup: menu }) * ``` * * If a menu identifier is specified, conversational menus enable seamless * navigation. * * ```ts * const menu = conversation.menu("root") * .submenu("Open submenu", ctx => ctx.editMessageText("submenu")) * .text("Close", ctx => ctx.menu.close()) * conversation.menu("child", { parent: "root" }) * .back("Go back", ctx => ctx.editMessageText("Root menu")) * * await ctx.reply("Root menu", { reply_markup: menu }) * ``` * * You can also interact with the conversation from inside button handlers. * * ```ts * let name = "" * const menu = conversation.menu() * .text("Set name", async ctx => { * await ctx.reply("What's your name?") * name = await conversation.form.text() * await ctx.editMessageText(name) * }) * .text("Clear name", ctx => { * name = "" * await ctx.editMessageText("No name") * }) * * await ctx.reply("No name (yet)", { reply_markup: menu }) * ``` * * More information about conversational menus can be found [in the * documentation](https://grammy.dev/plugins/conversations). * * @param id Optional menu identifier * @param options Optional menu options */ menu(id?: string, options?: Partial<ConversationMenuOptions<C>>): import("./menu.js").ConversationMenu<C>; /** * A namespace full of various utitilies for building forms. * * Typically, `wait` calls return context objects. Optionally, these context * objects can be accepted or rejected based on validation, such as with * `waitFor` which only returns context objects matching a given filter * query. * * Forms add another level of convenience on top of this. They no longer * require you to deal with context objects. Each form field performs both * validation and selection. This means that it picks out certain property * from the context object—such as the message text—and returns this * property directly. * * As an example, here is how you can wait for a number using the form field * `.number`. * * ```ts * // Wait for a number * const n = await conversation.form.number() * // Send back its square * await ctx.reply(`The square of ${n} is ${n * n}!`) * ``` * * There are many more form fields that let you wait for virtually any type * of message content. * * All form fields give you the option to perform an action if the * validation fails by accepting an `otherwise` function. This is similar to * filtered wait calls. * * ```ts * const text = await conversation.form.select(["Yes", "No"], { * otherwise: ctx => ctx.reply("Please send Yes or No.") * }) * ``` * * In addition, all form fields give you the option to perform some action * when a value is accepted. For example, this is how you can delete * incoming messages. * * ```ts * const text = await conversation.form.select(["Yes", "No"], { * action: ctx => ctx.deleteMessage() * }) * ``` * * Note that either `otherwise` or `action` will be called, but never both * for the same update. */ form: ConversationForm<C>; } /** A promise that also contains methods for chaining filtered wait calls */ export type AndPromise<C extends Context> = Promise<C> & AndExtension<C>; /** A container for methods that filter wait calls */ export interface AndExtension<C extends Context> { /** * Filters down the wait call using another custom predicate function. * Corresponds with {@link Conversation.waitUntil}. * * @param predicate An extra predicate function to check * @param opts Optional options object */ and<D extends C>(predicate: (ctx: C) => ctx is D, opts?: AndOtherwiseOptions<C>): AndPromise<D>; and(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: AndOtherwiseOptions<C>): AndPromise<C>; /** * Filters down the wait call using another negated custom predicate * function. Corresponds with {@link Conversation.waitUnless}. * * @param predicate An extra predicate function to check * @param opts Optional options object */ unless(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: AndOtherwiseOptions<C>): AndPromise<C>; /** * Filters down the wait call using another filter query. Corresponds with * {@link Conversation.waitFor}. * * @param query An extra filter query to check * @param opts Optional options object */ andFor<Q extends FilterQuery>(query: Q | Q[], opts?: AndOtherwiseOptions<C>): AndPromise<Filter<C, Q>>; /** * Filters down the wait call using another hears check. Corresponds with * {@link Conversation.waitForHears}. * * @param trigger An extra text to look for * @param opts Optional options object */ andForHears(trigger: MaybeArray<string | RegExp>, opts?: AndOtherwiseOptions<C>): AndPromise<HearsContext<C>>; /** * Filters down the wait call using another command check. Corresponds with * {@link Conversation.waitForCommand}. * * @param command An extra command to look for * @param opts Optional options object */ andForCommand(command: MaybeArray<StringWithCommandSuggestions>, opts?: AndOtherwiseOptions<C>): AndPromise<CommandContext<C>>; /** * Filters down the wait call using another reaction check. Corresponds with * {@link Conversation.waitForReaction}. * * @param reaction An extra reaction to look for * @param opts Optional options object */ andForReaction(reaction: MaybeArray<ReactionTypeEmoji["emoji"] | ReactionType>, opts?: AndOtherwiseOptions<C>): AndPromise<ReactionContext<C>>; /** * Filters down the wait call using another callback query check. * Corresponds with {@link Conversation.waitForCallbackQuery}. * * @param trigger An extra callback query to look for * @param opts Optional options object */ andForCallbackQuery(trigger: MaybeArray<string | RegExp>, opts?: AndOtherwiseOptions<C>): AndPromise<CallbackQueryContext<C>>; /** * Filters down the wait call using another game query check. Corresponds * with {@link Conversation.waitForGameQuery}. * * @param trigger An extra game query to look for * @param opts Optional options object */ andForGameQuery(trigger: MaybeArray<string | RegExp>, opts?: AndOtherwiseOptions<C>): AndPromise<GameQueryContext<C>>; /** * Filters down the wait call using another check for a user. Corresponds * with {@link Conversation.waitFrom}. * * @param user An extra user to look for * @param opts Optional options object */ andFrom(user: number | User, opts?: AndOtherwiseOptions<C>): AndPromise<C & { from: User; }>; /** * Filters down the wait call using another check for a reply. Corresponds * with {@link Conversation.waitForReplyTo}. * * @param message_id An extra message to look for in a reply * @param opts Optional options object */ andForReplyTo(message_id: number | { message_id: number; }, opts?: AndOtherwiseOptions<C>): AndPromise<Filter<C, "message" | "channel_post">>; } export {};