@rilong/grammyjs-conversations-esm 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/out/conversation.d.ts +885 -0
- package/out/conversation.js +832 -0
- package/out/deps.node.d.ts +2 -0
- package/out/deps.node.js +1 -0
- package/out/engine.d.ts +212 -0
- package/out/engine.js +238 -0
- package/out/form.d.ts +530 -0
- package/out/form.js +598 -0
- package/out/menu.d.ts +593 -0
- package/out/menu.js +698 -0
- package/out/mod.d.ts +8 -0
- package/out/mod.js +8 -0
- package/out/nope.d.ts +16 -0
- package/out/nope.js +35 -0
- package/out/plugin.d.ts +678 -0
- package/out/plugin.js +578 -0
- package/out/resolve.d.ts +43 -0
- package/out/resolve.js +21 -0
- package/out/state.d.ts +147 -0
- package/out/state.js +125 -0
- package/out/storage.d.ts +169 -0
- package/out/storage.js +105 -0
- package/package.json +43 -0
@@ -0,0 +1,885 @@
|
|
1
|
+
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";
|
2
|
+
import { type Checkpoint, type ReplayControls } from "./engine.js";
|
3
|
+
import { ConversationForm } from "./form.js";
|
4
|
+
import { type ConversationMenuOptions } from "./menu.js";
|
5
|
+
type MaybeArray<T> = T | T[];
|
6
|
+
/** Alias for `string` but with auto-complete for common commands */
|
7
|
+
export type StringWithCommandSuggestions = (string & Record<never, never>) | "start" | "help" | "settings" | "privacy" | "developer_info";
|
8
|
+
/**
|
9
|
+
* Specifies an external operation and how to serialize and deserialize its
|
10
|
+
* return and error values.
|
11
|
+
*
|
12
|
+
* @typeParam OC Type of the outside context object
|
13
|
+
* @typeParam R Type of the return value
|
14
|
+
* @typeParam I Type of the intermediate (serialized) representation
|
15
|
+
*/
|
16
|
+
export interface ExternalOp<OC extends Context, R, I = any> {
|
17
|
+
/**
|
18
|
+
* The external operation to perform.
|
19
|
+
*
|
20
|
+
* Receives the current context object from the surrounding middleware. This
|
21
|
+
* gives the task access to sessions (if used) and other values that are not
|
22
|
+
* present inside the conversation.
|
23
|
+
*
|
24
|
+
* @param ctx The outside context object of the surrounding middleware
|
25
|
+
*/
|
26
|
+
task(ctx: OC): R | Promise<R>;
|
27
|
+
/**
|
28
|
+
* Converts a value returned from the task to an object that can safely be
|
29
|
+
* passed to `JSON.stringify`.
|
30
|
+
*
|
31
|
+
* @param value A value to serialize
|
32
|
+
*/
|
33
|
+
beforeStore?(value: R): I | Promise<I>;
|
34
|
+
/**
|
35
|
+
* Restores the original value from the intermediate representation that
|
36
|
+
* `beforeStore` generated.
|
37
|
+
*
|
38
|
+
* @param value The value obtained from `JSON.parse`
|
39
|
+
*/
|
40
|
+
afterLoad?(value: I): R | Promise<R>;
|
41
|
+
/**
|
42
|
+
* Converts an error thrown by the task to an object that can safely be
|
43
|
+
* passed to `JSON.stringify`.
|
44
|
+
*
|
45
|
+
* @param value A thrown error
|
46
|
+
*/
|
47
|
+
beforeStoreError?(value: unknown): unknown | Promise<unknown>;
|
48
|
+
/**
|
49
|
+
* Restores the original error from the intermediate representation that
|
50
|
+
* `beforeStoreError` generated.
|
51
|
+
*
|
52
|
+
* @param value The value obtained from `JSON.parse`
|
53
|
+
*/
|
54
|
+
afterLoadError?(value: unknown): unknown | Promise<unknown>;
|
55
|
+
}
|
56
|
+
/** A function that applies a context object to a callback */
|
57
|
+
type ApplyContext<OC extends Context> = <F extends (ctx: OC) => unknown>(fn: F) => Promise<ReturnType<F>>;
|
58
|
+
/** Options for creating a conversation handle */
|
59
|
+
export interface ConversationHandleOptions {
|
60
|
+
/** Callback for when the conversation is halted */
|
61
|
+
onHalt?(): void | Promise<void>;
|
62
|
+
/** Default wait timeout */
|
63
|
+
maxMillisecondsToWait?: number;
|
64
|
+
/**
|
65
|
+
* `true` if this conversation can be entered while this or another
|
66
|
+
* conversation is already active, and `false` otherwise. Defaults to
|
67
|
+
* `false`.
|
68
|
+
*/
|
69
|
+
parallel?: boolean;
|
70
|
+
}
|
71
|
+
/**
|
72
|
+
* Options for a call to `conversation.wait()`.
|
73
|
+
*/
|
74
|
+
export interface WaitOptions {
|
75
|
+
/**
|
76
|
+
* Specifies a timeout for the wait call.
|
77
|
+
*
|
78
|
+
* When the wait call is reached, `Date.now()` is called. When the wait call
|
79
|
+
* resolves, `Date.now()` is called again, and the two values are compared.
|
80
|
+
* If the wait call resolved more than the specified number of milliseconds
|
81
|
+
* after it was reached initially, then the conversation will be halted, any
|
82
|
+
* exit handlers will be called, and the surrounding middleware will resume
|
83
|
+
* normally so that subsequent handlers can run.
|
84
|
+
*
|
85
|
+
* To the outside middleware system, this will look like the conversation
|
86
|
+
* was never active.
|
87
|
+
*/
|
88
|
+
maxMilliseconds?: number;
|
89
|
+
/**
|
90
|
+
* Collation key for the wait call, safety measure to protect against data
|
91
|
+
* corruption. This is used extensively by the plugin internally, but it is
|
92
|
+
* rarely useful to changes this behavior.
|
93
|
+
*/
|
94
|
+
collationKey?: string;
|
95
|
+
}
|
96
|
+
/**
|
97
|
+
* Options for a call to `conversation.skip()`.
|
98
|
+
*/
|
99
|
+
export interface SkipOptions {
|
100
|
+
/**
|
101
|
+
* Determines whether [the outside middleware
|
102
|
+
* system](https://grammy.dev/guide/middleware) should resume after the
|
103
|
+
* update is skipped.
|
104
|
+
*
|
105
|
+
* Pass `{ next: true }` to make sure that subsequent handlers will run.
|
106
|
+
* This effectively causes `next` to be called by the plugin.
|
107
|
+
*
|
108
|
+
* Defaults to `false` unless the conversation is marked as parallel, in
|
109
|
+
* which case this option defaults to `true`.
|
110
|
+
*/
|
111
|
+
next?: boolean;
|
112
|
+
}
|
113
|
+
/**
|
114
|
+
* Options to pass to a chained `wait` call.
|
115
|
+
*/
|
116
|
+
export interface AndOtherwiseOptions<C extends Context> extends SkipOptions {
|
117
|
+
/**
|
118
|
+
* Callback that will be invoked when the validation fails for a context
|
119
|
+
* object.
|
120
|
+
*
|
121
|
+
* @param ctx The context object that failed validation
|
122
|
+
*/
|
123
|
+
otherwise?(ctx: C): unknown | Promise<unknown>;
|
124
|
+
}
|
125
|
+
/**
|
126
|
+
* Options for a filtered wait call. A filtered wait call is a wait call that
|
127
|
+
* have extra valiation attached, such as `waitFor`, `waitUntil`, etc.
|
128
|
+
*/
|
129
|
+
export interface OtherwiseOptions<C extends Context> extends WaitOptions, AndOtherwiseOptions<C> {
|
130
|
+
}
|
131
|
+
/**
|
132
|
+
* Options for a call to `conversation.halt()`.
|
133
|
+
*/
|
134
|
+
export interface HaltOptions {
|
135
|
+
/**
|
136
|
+
* Determines whether [the outside middleware
|
137
|
+
* system](https://grammy.dev/guide/middleware) should resume after the
|
138
|
+
* conversation is halted.
|
139
|
+
*
|
140
|
+
* Pass `{ next: true }` to make sure that subsequent handlers will run.
|
141
|
+
* This effectively causes `next` to be called by the plugin.
|
142
|
+
*
|
143
|
+
* Defaults to `false`.
|
144
|
+
*/
|
145
|
+
next?: boolean;
|
146
|
+
}
|
147
|
+
/**
|
148
|
+
* A conversation handle lets you control the conversation, such as waiting for
|
149
|
+
* updates, skipping them, halting the conversation, and much more. It is the
|
150
|
+
* first parameter in each conversation builder function and provides the core
|
151
|
+
* features of this plugin.
|
152
|
+
*
|
153
|
+
* ```ts
|
154
|
+
* async function exmaple(conversation, ctx) {
|
155
|
+
* // ^ this is an instance of this class
|
156
|
+
*
|
157
|
+
* // This is how you can wait for updates:
|
158
|
+
* ctx = await conversation.wait()
|
159
|
+
* }
|
160
|
+
* ```
|
161
|
+
*
|
162
|
+
* Be sure to consult this plugin's documentation:
|
163
|
+
* https://grammy.dev/plugins/conversations
|
164
|
+
*/
|
165
|
+
export declare class Conversation<OC extends Context = Context, C extends Context = Context> {
|
166
|
+
private controls;
|
167
|
+
private hydrate;
|
168
|
+
private escape;
|
169
|
+
private plugins;
|
170
|
+
private options;
|
171
|
+
/** `true` if `external` is currently running, `false` otherwise */
|
172
|
+
private insideExternal;
|
173
|
+
private menuPool;
|
174
|
+
private combineAnd;
|
175
|
+
/**
|
176
|
+
* Constructs a new conversation handle.
|
177
|
+
*
|
178
|
+
* This is called internally in order to construct the first argument for a
|
179
|
+
* conversation builder function. You typically don't need to construct this
|
180
|
+
* class yourself.
|
181
|
+
*
|
182
|
+
* @param controls Controls for the underlying replay engine
|
183
|
+
* @param hydrate Context construction callback
|
184
|
+
* @param escape Callback to support outside context objects in `external`
|
185
|
+
* @param plugins Middleware to hydrate context objects
|
186
|
+
* @param options Additional configuration options
|
187
|
+
*/
|
188
|
+
constructor(controls: ReplayControls, hydrate: (update: Update) => C, escape: ApplyContext<OC>, plugins: MiddlewareFn<C>, options: ConversationHandleOptions);
|
189
|
+
/**
|
190
|
+
* Waits for a new update and returns the corresponding context object as
|
191
|
+
* soon as it arrives.
|
192
|
+
*
|
193
|
+
* Note that wait calls terminate the conversation function, save the state
|
194
|
+
* of execution, and only resolve when the conversation is replayed. If this
|
195
|
+
* is not obvious to you, it means that you probably should read [the
|
196
|
+
* documentation of this plugin](https://grammy.dev/plugins/conversations)
|
197
|
+
* in order to avoid common pitfalls.
|
198
|
+
*
|
199
|
+
* You can pass a timeout in the optional options object. This lets you
|
200
|
+
* terminate the conversation automatically if the update arrives too late.
|
201
|
+
*
|
202
|
+
* @param options Optional options for wait timeouts etc
|
203
|
+
*/
|
204
|
+
wait(options?: WaitOptions): AndPromise<C>;
|
205
|
+
/**
|
206
|
+
* Performs a filtered wait call that is defined by a given predicate. In
|
207
|
+
* other words, this method waits for an update, and calls `skip` if the
|
208
|
+
* received context object does not pass validation performed by the given
|
209
|
+
* predicate function.
|
210
|
+
*
|
211
|
+
* If a context object is discarded, you can perform any action by
|
212
|
+
* specifying `otherwise` in the options.
|
213
|
+
*
|
214
|
+
* ```ts
|
215
|
+
* const ctx = await conversation.waitUntil(ctx => ctx.msg?.text?.endsWith("grammY"), {
|
216
|
+
* otherwise: ctx => ctx.reply("Send a message that ends with grammY!")
|
217
|
+
* })
|
218
|
+
* ```
|
219
|
+
*
|
220
|
+
* If you pass a type predicate, the type of the resulting context object
|
221
|
+
* will be narrowed down.
|
222
|
+
*
|
223
|
+
* ```ts
|
224
|
+
* const ctx = await conversation.waitUntil(Context.has.filterQuery(":text"))
|
225
|
+
* const text = ctx.msg.text;
|
226
|
+
* ```
|
227
|
+
*
|
228
|
+
* You can combine calls to `waitUntil` with other filtered wait calls by
|
229
|
+
* chaining them.
|
230
|
+
*
|
231
|
+
* ```ts
|
232
|
+
* const ctx = await conversation.waitUntil(ctx => ctx.msg?.text?.endsWith("grammY"))
|
233
|
+
* .andFor("::hashtag")
|
234
|
+
* ```
|
235
|
+
*
|
236
|
+
* @param predicate A predicate function to validate context objects
|
237
|
+
* @param opts Optional options object
|
238
|
+
*/
|
239
|
+
waitUntil<D extends C>(predicate: (ctx: C) => ctx is D, opts?: OtherwiseOptions<C>): AndPromise<D>;
|
240
|
+
waitUntil(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: OtherwiseOptions<C>): AndPromise<C>;
|
241
|
+
/**
|
242
|
+
* Performs a filtered wait call that is defined by a given negated
|
243
|
+
* predicate. In other words, this method waits for an update, and calls
|
244
|
+
* `skip` if the received context object passed validation performed by the
|
245
|
+
* given predicate function. That is the exact same thigs as calling
|
246
|
+
* {@link Conversation.waitUntil} but with the predicate function being
|
247
|
+
* negated.
|
248
|
+
*
|
249
|
+
* If a context object is discarded (the predicate function returns `true`
|
250
|
+
* for it), you can perform any action by specifying `otherwise` in the
|
251
|
+
* options.
|
252
|
+
*
|
253
|
+
* ```ts
|
254
|
+
* const ctx = await conversation.waitUnless(ctx => ctx.msg?.text?.endsWith("grammY"), {
|
255
|
+
* otherwise: ctx => ctx.reply("Send a message that does not end with grammY!")
|
256
|
+
* })
|
257
|
+
* ```
|
258
|
+
*
|
259
|
+
* You can combine calls to `waitUnless` with other filtered wait calls by
|
260
|
+
* chaining them.
|
261
|
+
*
|
262
|
+
* ```ts
|
263
|
+
* const ctx = await conversation.waitUnless(ctx => ctx.msg?.text?.endsWith("grammY"))
|
264
|
+
* .andFor("::hashtag")
|
265
|
+
* ```
|
266
|
+
*
|
267
|
+
* @param predicate A predicate function to discard context objects
|
268
|
+
* @param opts Optional options object
|
269
|
+
*/
|
270
|
+
waitUnless(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: OtherwiseOptions<C>): AndPromise<C>;
|
271
|
+
/**
|
272
|
+
* Performs a filtered wait call that is defined by a filter query. In other
|
273
|
+
* words, this method waits for an update, and calls `skip` if the received
|
274
|
+
* context object does not match the filter query. This uses the same logic
|
275
|
+
* as `bot.on`.
|
276
|
+
*
|
277
|
+
* If a context object is discarded, you can perform any action by
|
278
|
+
* specifying `otherwise` in the options.
|
279
|
+
*
|
280
|
+
* ```ts
|
281
|
+
* const ctx = await conversation.waitFor(":text", {
|
282
|
+
* otherwise: ctx => ctx.reply("Please send a text message!")
|
283
|
+
* })
|
284
|
+
* // Type inference works:
|
285
|
+
* const text = ctx.msg.text;
|
286
|
+
* ```
|
287
|
+
*
|
288
|
+
* You can combine calls to `waitFor` with other filtered wait calls by
|
289
|
+
* chaining them.
|
290
|
+
*
|
291
|
+
* ```ts
|
292
|
+
* const ctx = await conversation.waitFor(":text").andFor("::hashtag")
|
293
|
+
* ```
|
294
|
+
*
|
295
|
+
* @param query A filter query to match
|
296
|
+
* @param opts Optional options object
|
297
|
+
*/
|
298
|
+
waitFor<Q extends FilterQuery>(query: Q | Q[], opts?: OtherwiseOptions<C>): AndPromise<Filter<C, Q>>;
|
299
|
+
/**
|
300
|
+
* Performs a filtered wait call that is defined by a hears filter. In other
|
301
|
+
* words, this method waits for an update, and calls `skip` if the received
|
302
|
+
* context object does not contain text that matches the given text or
|
303
|
+
* regular expression. This uses the same logic as `bot.hears`.
|
304
|
+
*
|
305
|
+
* If a context object is discarded, you can perform any action by
|
306
|
+
* specifying `otherwise` in the options.
|
307
|
+
*
|
308
|
+
* ```ts
|
309
|
+
* const ctx = await conversation.waitForHears(["yes", "no"], {
|
310
|
+
* otherwise: ctx => ctx.reply("Please send yes or no!")
|
311
|
+
* })
|
312
|
+
* // Type inference works:
|
313
|
+
* const answer = ctx.match
|
314
|
+
* ```
|
315
|
+
*
|
316
|
+
* You can combine calls to `waitForHears` with other filtered wait calls by
|
317
|
+
* chaining them. For instance, this can be used to only receive text from
|
318
|
+
* text messages—not including channel posts or media captions.
|
319
|
+
*
|
320
|
+
* ```ts
|
321
|
+
* const ctx = await conversation.waitForHears(["yes", "no"])
|
322
|
+
* .andFor("message:text")
|
323
|
+
* const text = ctx.message.text
|
324
|
+
* ```
|
325
|
+
*
|
326
|
+
* @param trigger The text to look for
|
327
|
+
* @param opts Optional options object
|
328
|
+
*/
|
329
|
+
waitForHears(trigger: MaybeArray<string | RegExp>, opts?: OtherwiseOptions<C>): AndPromise<HearsContext<C>>;
|
330
|
+
/**
|
331
|
+
* Performs a filtered wait call that is defined by a command filter. In
|
332
|
+
* other words, this method waits for an update, and calls `skip` if the
|
333
|
+
* received context object does not contain the expected command. This uses
|
334
|
+
* the same logic as `bot.command`.
|
335
|
+
*
|
336
|
+
* If a context object is discarded, you can perform any action by
|
337
|
+
* specifying `otherwise` in the options.
|
338
|
+
*
|
339
|
+
* ```ts
|
340
|
+
* const ctx = await conversation.waitForCommand("start", {
|
341
|
+
* otherwise: ctx => ctx.reply("Please send /start!")
|
342
|
+
* })
|
343
|
+
* // Type inference works for deep links:
|
344
|
+
* const args = ctx.match
|
345
|
+
* ```
|
346
|
+
*
|
347
|
+
* You can combine calls to `waitForCommand` with other filtered wait calls
|
348
|
+
* by chaining them. For instance, this can be used to only receive commands
|
349
|
+
* from text messages—not including channel posts.
|
350
|
+
*
|
351
|
+
* ```ts
|
352
|
+
* const ctx = await conversation.waitForCommand("start")
|
353
|
+
* .andFor("message")
|
354
|
+
* ```
|
355
|
+
*
|
356
|
+
* @param command The command to look for
|
357
|
+
* @param opts Optional options object
|
358
|
+
*/
|
359
|
+
waitForCommand(command: MaybeArray<StringWithCommandSuggestions>, opts?: OtherwiseOptions<C>): AndPromise<CommandContext<C>>;
|
360
|
+
/**
|
361
|
+
* Performs a filtered wait call that is defined by a reaction filter. In
|
362
|
+
* other words, this method waits for an update, and calls `skip` if the
|
363
|
+
* received context object does not contain the expected reaction update.
|
364
|
+
* This uses the same logic as `bot.reaction`.
|
365
|
+
*
|
366
|
+
* If a context object is discarded, you can perform any action by
|
367
|
+
* specifying `otherwise` in the options.
|
368
|
+
*
|
369
|
+
* ```ts
|
370
|
+
* const ctx = await conversation.waitForReaction('👍', {
|
371
|
+
* otherwise: ctx => ctx.reply("Please upvote a message!")
|
372
|
+
* })
|
373
|
+
* // Type inference works:
|
374
|
+
* const args = ctx.messageReaction
|
375
|
+
* ```
|
376
|
+
*
|
377
|
+
* You can combine calls to `waitForReaction` with other filtered wait calls
|
378
|
+
* by chaining them.
|
379
|
+
*
|
380
|
+
* ```ts
|
381
|
+
* const ctx = await conversation.waitForReaction('👍')
|
382
|
+
* .andFrom(ADMIN_USER_ID)
|
383
|
+
* ```
|
384
|
+
*
|
385
|
+
* @param reaction The reaction to look for
|
386
|
+
* @param opts Optional options object
|
387
|
+
*/
|
388
|
+
waitForReaction(reaction: MaybeArray<ReactionTypeEmoji["emoji"] | ReactionType>, opts?: OtherwiseOptions<C>): AndPromise<ReactionContext<C>>;
|
389
|
+
/**
|
390
|
+
* Performs a filtered wait call that is defined by a callback query filter.
|
391
|
+
* In other words, this method waits for an update, and calls `skip` if the
|
392
|
+
* received context object does not contain the expected callback query
|
393
|
+
* update. This uses the same logic as `bot.callbackQuery`.
|
394
|
+
*
|
395
|
+
* If a context object is discarded, you can perform any action by
|
396
|
+
* specifying `otherwise` in the options.
|
397
|
+
*
|
398
|
+
* ```ts
|
399
|
+
* const ctx = await conversation.waitForCallbackQuery(/button-\d+/, {
|
400
|
+
* otherwise: ctx => ctx.reply("Please click a button!")
|
401
|
+
* })
|
402
|
+
* // Type inference works:
|
403
|
+
* const data = ctx.callbackQuery.data
|
404
|
+
* ```
|
405
|
+
*
|
406
|
+
* You can combine calls to `waitForCallbackQuery` with other filtered wait
|
407
|
+
* calls by chaining them.
|
408
|
+
*
|
409
|
+
* ```ts
|
410
|
+
* const ctx = await conversation.waitForCallbackQuery('data')
|
411
|
+
* .andFrom(ADMIN_USER_ID)
|
412
|
+
* ```
|
413
|
+
*
|
414
|
+
* @param trigger The string to look for in the payload
|
415
|
+
* @param opts Optional options object
|
416
|
+
*/
|
417
|
+
waitForCallbackQuery(trigger: MaybeArray<string | RegExp>, opts?: OtherwiseOptions<C>): AndPromise<CallbackQueryContext<C>>;
|
418
|
+
/**
|
419
|
+
* Performs a filtered wait call that is defined by a game query filter. In
|
420
|
+
* other words, this method waits for an update, and calls `skip` if the
|
421
|
+
* received context object does not contain the expected game query update.
|
422
|
+
* This uses the same logic as `bot.gameQuery`.
|
423
|
+
*
|
424
|
+
* If a context object is discarded, you can perform any action by
|
425
|
+
* specifying `otherwise` in the options.
|
426
|
+
*
|
427
|
+
* ```ts
|
428
|
+
* const ctx = await conversation.waitForGameQuery(/game-\d+/, {
|
429
|
+
* otherwise: ctx => ctx.reply("Please play a game!")
|
430
|
+
* })
|
431
|
+
* // Type inference works:
|
432
|
+
* const data = ctx.callbackQuery.game_short_name
|
433
|
+
* ```
|
434
|
+
*
|
435
|
+
* You can combine calls to `waitForGameQuery` with other filtered wait
|
436
|
+
* calls by chaining them.
|
437
|
+
*
|
438
|
+
* ```ts
|
439
|
+
* const ctx = await conversation.waitForGameQuery('data')
|
440
|
+
* .andFrom(ADMIN_USER_ID)
|
441
|
+
* ```
|
442
|
+
*
|
443
|
+
* @param trigger The string to look for in the payload
|
444
|
+
* @param opts Optional options object
|
445
|
+
*/
|
446
|
+
waitForGameQuery(trigger: MaybeArray<string | RegExp>, opts?: OtherwiseOptions<C>): AndPromise<GameQueryContext<C>>;
|
447
|
+
/**
|
448
|
+
* Performs a filtered wait call that is defined by a user-specific filter.
|
449
|
+
* In other words, this method waits for an update, and calls `skip` if the
|
450
|
+
* received context object was not triggered by the given user.
|
451
|
+
*
|
452
|
+
* If a context object is discarded, you can perform any action by
|
453
|
+
* specifying `otherwise` in the options.
|
454
|
+
*
|
455
|
+
* ```ts
|
456
|
+
* const ctx = await conversation.waitFrom(targetUser, {
|
457
|
+
* otherwise: ctx => ctx.reply("I did not mean you!")
|
458
|
+
* })
|
459
|
+
* // Type inference works:
|
460
|
+
* const user = ctx.from.first_name
|
461
|
+
* ```
|
462
|
+
*
|
463
|
+
* You can combine calls to `waitFrom` with other filtered wait calls by
|
464
|
+
* chaining them.
|
465
|
+
*
|
466
|
+
* ```ts
|
467
|
+
* const ctx = await conversation.waitFrom(targetUser).andFor(":text")
|
468
|
+
* ```
|
469
|
+
*
|
470
|
+
* @param user The user or user identifer to look for
|
471
|
+
* @param opts Optional options object
|
472
|
+
*/
|
473
|
+
waitFrom(user: number | User, opts?: OtherwiseOptions<C>): AndPromise<C & {
|
474
|
+
from: User;
|
475
|
+
}>;
|
476
|
+
/**
|
477
|
+
* Performs a filtered wait call that is defined by a message reply. In
|
478
|
+
* other words, this method waits for an update, and calls `skip` if the
|
479
|
+
* received context object does not contain a reply to a given message.
|
480
|
+
*
|
481
|
+
* If a context object is discarded, you can perform any action by
|
482
|
+
* specifying `otherwise` in the options.
|
483
|
+
*
|
484
|
+
* ```ts
|
485
|
+
* const ctx = await conversation.waitForReplyTo(message, {
|
486
|
+
* otherwise: ctx => ctx.reply("Please reply to this message!", {
|
487
|
+
* reply_parameters: { message_id: message.message_id }
|
488
|
+
* })
|
489
|
+
* })
|
490
|
+
* // Type inference works:
|
491
|
+
* const id = ctx.msg.message_id
|
492
|
+
* ```
|
493
|
+
*
|
494
|
+
* You can combine calls to `waitForReplyTo` with other filtered wait calls
|
495
|
+
* by chaining them.
|
496
|
+
*
|
497
|
+
* ```ts
|
498
|
+
* const ctx = await conversation.waitForReplyTo(message).andFor(":text")
|
499
|
+
* ```
|
500
|
+
*
|
501
|
+
* @param message_id The message identifer or object to look for in a reply
|
502
|
+
* @param opts Optional options object
|
503
|
+
*/
|
504
|
+
waitForReplyTo(message_id: number | {
|
505
|
+
message_id: number;
|
506
|
+
}, opts?: OtherwiseOptions<C>): AndPromise<Filter<C, "message" | "channel_post">>;
|
507
|
+
/**
|
508
|
+
* Skips the current update. The current update is the update that was
|
509
|
+
* received in the last wait call.
|
510
|
+
*
|
511
|
+
* In a sense, this will undo receiving an update. The replay logs will be
|
512
|
+
* reset so it will look like the conversation had never received the update
|
513
|
+
* in the first place. Note, however, that any API calls performs between
|
514
|
+
* wait and skip are not going to be reversed. In particular, messages will
|
515
|
+
* not be unsent.
|
516
|
+
*
|
517
|
+
* By default, skipping an update drops it. This means that no other
|
518
|
+
* handlers (including downstream middleware) will run. However, if this
|
519
|
+
* conversation is marked as parallel, skip will behave differently and
|
520
|
+
* resume middleware execution by default. This is needed for other parallel
|
521
|
+
* conversations with the same or a different identifier to receive the
|
522
|
+
* update.
|
523
|
+
*
|
524
|
+
* This behavior can be overridden by passing `{ next: true }` or `{ next:
|
525
|
+
* false }` to skip.
|
526
|
+
*
|
527
|
+
* If several wait calls are used concurrently inside the same conversation,
|
528
|
+
* they will resolve one after another until one of them does not skip the
|
529
|
+
* update. The conversation will only skip an update when all concurrent
|
530
|
+
* wait calls skip the update. Specifying `next` for a skip call that is not
|
531
|
+
* the final skip call has no effect.
|
532
|
+
*
|
533
|
+
* @param options Optional options to control middleware resumption
|
534
|
+
*/
|
535
|
+
skip(options?: SkipOptions): Promise<never>;
|
536
|
+
/**
|
537
|
+
* Calls any exit handlers if installed, and then terminates the
|
538
|
+
* conversation immediately. This method never returns.
|
539
|
+
*
|
540
|
+
* By default, this will consume the update. Pass `{ next: true }` to make
|
541
|
+
* sure that downstream middleware is called.
|
542
|
+
*
|
543
|
+
* @param options Optional options to control middleware resumption
|
544
|
+
*/
|
545
|
+
halt(options?: HaltOptions): Promise<never>;
|
546
|
+
/**
|
547
|
+
* Creates a new checkpoint at the current point of the conversation.
|
548
|
+
*
|
549
|
+
* This checkpoint can be passed to `rewind` in order to go back in the
|
550
|
+
* conversation and resume it from an earlier point.
|
551
|
+
*
|
552
|
+
* ```ts
|
553
|
+
* const check = conversation.checkpoint();
|
554
|
+
*
|
555
|
+
* // Later:
|
556
|
+
* await conversation.rewind(check);
|
557
|
+
* ```
|
558
|
+
*/
|
559
|
+
checkpoint(): Checkpoint;
|
560
|
+
/**
|
561
|
+
* Rewinds the conversation to a previous point and continues execution from
|
562
|
+
* there. This point is specified by a checkpoint that can be created by
|
563
|
+
* calling {@link Conversation.checkpoint}.
|
564
|
+
*
|
565
|
+
* ```ts
|
566
|
+
* const check = conversation.checkpoint();
|
567
|
+
*
|
568
|
+
* // Later:
|
569
|
+
* await conversation.rewind(check);
|
570
|
+
* ```
|
571
|
+
*
|
572
|
+
* @param checkpoint A previously created checkpoint
|
573
|
+
*/
|
574
|
+
rewind(checkpoint: Checkpoint): Promise<never>;
|
575
|
+
/**
|
576
|
+
* Runs a function outside of the replay engine. This provides a safe way to
|
577
|
+
* perform side-effects such as database communication, disk operations,
|
578
|
+
* session access, file downloads, requests to external APIs, randomness,
|
579
|
+
* time-based functions, and more. **It requires any data obtained from the
|
580
|
+
* outside to be serializable.**
|
581
|
+
*
|
582
|
+
* Remember that a conversation function is not executed like a normal
|
583
|
+
* JavaScript function. Instead, it is often interrupted and replayed,
|
584
|
+
* sometimes many times for the same update. If this is not obvious to you,
|
585
|
+
* it means that you probably should read [the documentation of this
|
586
|
+
* plugin](https://grammy.dev/plugins/conversations) in order to avoid
|
587
|
+
* common pitfalls.
|
588
|
+
*
|
589
|
+
* For instance, if you want to access to your database, you only want to
|
590
|
+
* read or write data once, rather than doing it once per replay. `external`
|
591
|
+
* provides an escape hatch to this situation. You can wrap your database
|
592
|
+
* call inside `external` to mark it as something that performs
|
593
|
+
* side-effects. The replay engine inside the conversations plugin will then
|
594
|
+
* make sure to only execute this operation once. This looks as follows.
|
595
|
+
*
|
596
|
+
* ```ts
|
597
|
+
* // Read from database
|
598
|
+
* const data = await conversation.external(async () => {
|
599
|
+
* return await readFromDatabase()
|
600
|
+
* })
|
601
|
+
*
|
602
|
+
* // Write to database
|
603
|
+
* await conversation.external(async () => {
|
604
|
+
* await writeToDatabase(data)
|
605
|
+
* })
|
606
|
+
* ```
|
607
|
+
*
|
608
|
+
* When `external` is called, it returns whichever data the given callback
|
609
|
+
* function returns. Note that this data has to be persisted by the plugin,
|
610
|
+
* so you have to make sure that it can be serialized. The data will be
|
611
|
+
* stored in the storage backend you provided when installing the
|
612
|
+
* conversations plugin via `bot.use`. In particular, it does not work well
|
613
|
+
* to return objects created by an ORM, as these objects have functions
|
614
|
+
* installed on them which will be lost during serialization.
|
615
|
+
*
|
616
|
+
* As a rule of thumb, imagine that all data from `external` is passed
|
617
|
+
* through `JSON.parse(JSON.stringify(data))` (even though this is not what
|
618
|
+
* actually happens under the hood).
|
619
|
+
*
|
620
|
+
* The callback function passed to `external` receives the outside context
|
621
|
+
* object from the current middleware pass. This lets you access properties
|
622
|
+
* on the context object that are only present in the outside middleware
|
623
|
+
* system, but that have not been installed on the context objects inside a
|
624
|
+
* conversation. For example, you can access your session data this way.
|
625
|
+
*
|
626
|
+
* ```ts
|
627
|
+
* // Read from session
|
628
|
+
* const data = await conversation.external((ctx) => {
|
629
|
+
* return ctx.session.data
|
630
|
+
* })
|
631
|
+
*
|
632
|
+
* // Write to session
|
633
|
+
* await conversation.external((ctx) => {
|
634
|
+
* ctx.session.data = data
|
635
|
+
* })
|
636
|
+
* ```
|
637
|
+
*
|
638
|
+
* Note that while a call to `external` is running, you cannot do any of the
|
639
|
+
* following things.
|
640
|
+
*
|
641
|
+
* - start a concurrent call to `external` from the same conversation
|
642
|
+
* - start a nested call to `external` from the same conversation
|
643
|
+
* - start a Bot API call from the same conversation
|
644
|
+
*
|
645
|
+
* Naturally, it is possible to have several concurrent calls to `externals`
|
646
|
+
* if they happen in unrelated chats. This still means that you should keep
|
647
|
+
* the code inside `external` to a minimum and actually only perform the
|
648
|
+
* desired side-effect itself.
|
649
|
+
*
|
650
|
+
* If you want to return data from `external` that cannot be serialized, you
|
651
|
+
* can specify a custom serialization function. This allows you choose a
|
652
|
+
* different intermediate data representation during storage than what is
|
653
|
+
* present at runtime.
|
654
|
+
*
|
655
|
+
* ```ts
|
656
|
+
* // Read bigint from an API but persist it as a string
|
657
|
+
* const largeNumber: bigint = await conversation.external({
|
658
|
+
* task: () => fetchCoolBigIntFromTheInternet(),
|
659
|
+
* beforeStore: (largeNumber) => String(largeNumber),
|
660
|
+
* afterLoad: (str) => BigInt(str),
|
661
|
+
* })
|
662
|
+
* ```
|
663
|
+
*
|
664
|
+
* Note how we read a bigint from the internet, but we convert it to string
|
665
|
+
* during persistence. This now allows us to use a storage adapter that only
|
666
|
+
* handles strings but does not need to support the bigint type.
|
667
|
+
*
|
668
|
+
* @param op An operation to perform outside of the conversation
|
669
|
+
*/
|
670
|
+
external<R, I = any>(op: ExternalOp<OC, R, I>["task"] | ExternalOp<OC, R, I>): Promise<R>;
|
671
|
+
/**
|
672
|
+
* Takes `Date.now()` once when reached, and returns the same value during
|
673
|
+
* every replay. Prefer this over calling `Date.now()` directly.
|
674
|
+
*/
|
675
|
+
now(): Promise<number>;
|
676
|
+
/**
|
677
|
+
* Takes `Math.random()` once when reached, and returns the same value
|
678
|
+
* during every replay. Prefer this over calling `Math.random()` directly.
|
679
|
+
*/
|
680
|
+
random(): Promise<number>;
|
681
|
+
/**
|
682
|
+
* Calls `console.log` only the first time it is reached, but not during
|
683
|
+
* subsequent replays. Prefer this over calling `console.log` directly.
|
684
|
+
*/
|
685
|
+
log(...data: unknown[]): Promise<void>;
|
686
|
+
/**
|
687
|
+
* Calls `console.error` only the first time it is reached, but not during
|
688
|
+
* subsequent replays. Prefer this over calling `console.error` directly.
|
689
|
+
*/
|
690
|
+
error(...data: unknown[]): Promise<void>;
|
691
|
+
/**
|
692
|
+
* Creates a new conversational menu.
|
693
|
+
*
|
694
|
+
* A conversational menu is a an interactive inline keyboard that is sent to
|
695
|
+
* the user from within a conversation.
|
696
|
+
*
|
697
|
+
* ```ts
|
698
|
+
* const menu = conversation.menu()
|
699
|
+
* .text("Send message", ctx => ctx.reply("Hi!"))
|
700
|
+
* .text("Close", ctx => ctx.menu.close())
|
701
|
+
*
|
702
|
+
* await ctx.reply("Menu message", { reply_markup: menu })
|
703
|
+
* ```
|
704
|
+
*
|
705
|
+
* If a menu identifier is specified, conversational menus enable seamless
|
706
|
+
* navigation.
|
707
|
+
*
|
708
|
+
* ```ts
|
709
|
+
* const menu = conversation.menu("root")
|
710
|
+
* .submenu("Open submenu", ctx => ctx.editMessageText("submenu"))
|
711
|
+
* .text("Close", ctx => ctx.menu.close())
|
712
|
+
* conversation.menu("child", { parent: "root" })
|
713
|
+
* .back("Go back", ctx => ctx.editMessageText("Root menu"))
|
714
|
+
*
|
715
|
+
* await ctx.reply("Root menu", { reply_markup: menu })
|
716
|
+
* ```
|
717
|
+
*
|
718
|
+
* You can also interact with the conversation from inside button handlers.
|
719
|
+
*
|
720
|
+
* ```ts
|
721
|
+
* let name = ""
|
722
|
+
* const menu = conversation.menu()
|
723
|
+
* .text("Set name", async ctx => {
|
724
|
+
* await ctx.reply("What's your name?")
|
725
|
+
* name = await conversation.form.text()
|
726
|
+
* await ctx.editMessageText(name)
|
727
|
+
* })
|
728
|
+
* .text("Clear name", ctx => {
|
729
|
+
* name = ""
|
730
|
+
* await ctx.editMessageText("No name")
|
731
|
+
* })
|
732
|
+
*
|
733
|
+
* await ctx.reply("No name (yet)", { reply_markup: menu })
|
734
|
+
* ```
|
735
|
+
*
|
736
|
+
* More information about conversational menus can be found [in the
|
737
|
+
* documentation](https://grammy.dev/plugins/conversations).
|
738
|
+
*
|
739
|
+
* @param id Optional menu identifier
|
740
|
+
* @param options Optional menu options
|
741
|
+
*/
|
742
|
+
menu(id?: string, options?: Partial<ConversationMenuOptions<C>>): import("./menu.js").ConversationMenu<C>;
|
743
|
+
/**
|
744
|
+
* A namespace full of various utitilies for building forms.
|
745
|
+
*
|
746
|
+
* Typically, `wait` calls return context objects. Optionally, these context
|
747
|
+
* objects can be accepted or rejected based on validation, such as with
|
748
|
+
* `waitFor` which only returns context objects matching a given filter
|
749
|
+
* query.
|
750
|
+
*
|
751
|
+
* Forms add another level of convenience on top of this. They no longer
|
752
|
+
* require you to deal with context objects. Each form field performs both
|
753
|
+
* validation and selection. This means that it picks out certain property
|
754
|
+
* from the context object—such as the message text—and returns this
|
755
|
+
* property directly.
|
756
|
+
*
|
757
|
+
* As an example, here is how you can wait for a number using the form field
|
758
|
+
* `.number`.
|
759
|
+
*
|
760
|
+
* ```ts
|
761
|
+
* // Wait for a number
|
762
|
+
* const n = await conversation.form.number()
|
763
|
+
* // Send back its square
|
764
|
+
* await ctx.reply(`The square of ${n} is ${n * n}!`)
|
765
|
+
* ```
|
766
|
+
*
|
767
|
+
* There are many more form fields that let you wait for virtually any type
|
768
|
+
* of message content.
|
769
|
+
*
|
770
|
+
* All form fields give you the option to perform an action if the
|
771
|
+
* validation fails by accepting an `otherwise` function. This is similar to
|
772
|
+
* filtered wait calls.
|
773
|
+
*
|
774
|
+
* ```ts
|
775
|
+
* const text = await conversation.form.select(["Yes", "No"], {
|
776
|
+
* otherwise: ctx => ctx.reply("Please send Yes or No.")
|
777
|
+
* })
|
778
|
+
* ```
|
779
|
+
*
|
780
|
+
* In addition, all form fields give you the option to perform some action
|
781
|
+
* when a value is accepted. For example, this is how you can delete
|
782
|
+
* incoming messages.
|
783
|
+
*
|
784
|
+
* ```ts
|
785
|
+
* const text = await conversation.form.select(["Yes", "No"], {
|
786
|
+
* action: ctx => ctx.deleteMessage()
|
787
|
+
* })
|
788
|
+
* ```
|
789
|
+
*
|
790
|
+
* Note that either `otherwise` or `action` will be called, but never both
|
791
|
+
* for the same update.
|
792
|
+
*/
|
793
|
+
form: ConversationForm<C>;
|
794
|
+
}
|
795
|
+
/** A promise that also contains methods for chaining filtered wait calls */
|
796
|
+
export type AndPromise<C extends Context> = Promise<C> & AndExtension<C>;
|
797
|
+
/** A container for methods that filter wait calls */
|
798
|
+
export interface AndExtension<C extends Context> {
|
799
|
+
/**
|
800
|
+
* Filters down the wait call using another custom predicate function.
|
801
|
+
* Corresponds with {@link Conversation.waitUntil}.
|
802
|
+
*
|
803
|
+
* @param predicate An extra predicate function to check
|
804
|
+
* @param opts Optional options object
|
805
|
+
*/
|
806
|
+
and<D extends C>(predicate: (ctx: C) => ctx is D, opts?: AndOtherwiseOptions<C>): AndPromise<D>;
|
807
|
+
and(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: AndOtherwiseOptions<C>): AndPromise<C>;
|
808
|
+
/**
|
809
|
+
* Filters down the wait call using another negated custom predicate
|
810
|
+
* function. Corresponds with {@link Conversation.waitUnless}.
|
811
|
+
*
|
812
|
+
* @param predicate An extra predicate function to check
|
813
|
+
* @param opts Optional options object
|
814
|
+
*/
|
815
|
+
unless(predicate: (ctx: C) => boolean | Promise<boolean>, opts?: AndOtherwiseOptions<C>): AndPromise<C>;
|
816
|
+
/**
|
817
|
+
* Filters down the wait call using another filter query. Corresponds with
|
818
|
+
* {@link Conversation.waitFor}.
|
819
|
+
*
|
820
|
+
* @param query An extra filter query to check
|
821
|
+
* @param opts Optional options object
|
822
|
+
*/
|
823
|
+
andFor<Q extends FilterQuery>(query: Q | Q[], opts?: AndOtherwiseOptions<C>): AndPromise<Filter<C, Q>>;
|
824
|
+
/**
|
825
|
+
* Filters down the wait call using another hears check. Corresponds with
|
826
|
+
* {@link Conversation.waitForHears}.
|
827
|
+
*
|
828
|
+
* @param trigger An extra text to look for
|
829
|
+
* @param opts Optional options object
|
830
|
+
*/
|
831
|
+
andForHears(trigger: MaybeArray<string | RegExp>, opts?: AndOtherwiseOptions<C>): AndPromise<HearsContext<C>>;
|
832
|
+
/**
|
833
|
+
* Filters down the wait call using another command check. Corresponds with
|
834
|
+
* {@link Conversation.waitForCommand}.
|
835
|
+
*
|
836
|
+
* @param command An extra command to look for
|
837
|
+
* @param opts Optional options object
|
838
|
+
*/
|
839
|
+
andForCommand(command: MaybeArray<StringWithCommandSuggestions>, opts?: AndOtherwiseOptions<C>): AndPromise<CommandContext<C>>;
|
840
|
+
/**
|
841
|
+
* Filters down the wait call using another reaction check. Corresponds with
|
842
|
+
* {@link Conversation.waitForReaction}.
|
843
|
+
*
|
844
|
+
* @param reaction An extra reaction to look for
|
845
|
+
* @param opts Optional options object
|
846
|
+
*/
|
847
|
+
andForReaction(reaction: MaybeArray<ReactionTypeEmoji["emoji"] | ReactionType>, opts?: AndOtherwiseOptions<C>): AndPromise<ReactionContext<C>>;
|
848
|
+
/**
|
849
|
+
* Filters down the wait call using another callback query check.
|
850
|
+
* Corresponds with {@link Conversation.waitForCallbackQuery}.
|
851
|
+
*
|
852
|
+
* @param trigger An extra callback query to look for
|
853
|
+
* @param opts Optional options object
|
854
|
+
*/
|
855
|
+
andForCallbackQuery(trigger: MaybeArray<string | RegExp>, opts?: AndOtherwiseOptions<C>): AndPromise<CallbackQueryContext<C>>;
|
856
|
+
/**
|
857
|
+
* Filters down the wait call using another game query check. Corresponds
|
858
|
+
* with {@link Conversation.waitForGameQuery}.
|
859
|
+
*
|
860
|
+
* @param trigger An extra game query to look for
|
861
|
+
* @param opts Optional options object
|
862
|
+
*/
|
863
|
+
andForGameQuery(trigger: MaybeArray<string | RegExp>, opts?: AndOtherwiseOptions<C>): AndPromise<GameQueryContext<C>>;
|
864
|
+
/**
|
865
|
+
* Filters down the wait call using another check for a user. Corresponds
|
866
|
+
* with {@link Conversation.waitFrom}.
|
867
|
+
*
|
868
|
+
* @param user An extra user to look for
|
869
|
+
* @param opts Optional options object
|
870
|
+
*/
|
871
|
+
andFrom(user: number | User, opts?: AndOtherwiseOptions<C>): AndPromise<C & {
|
872
|
+
from: User;
|
873
|
+
}>;
|
874
|
+
/**
|
875
|
+
* Filters down the wait call using another check for a reply. Corresponds
|
876
|
+
* with {@link Conversation.waitForReplyTo}.
|
877
|
+
*
|
878
|
+
* @param message_id An extra message to look for in a reply
|
879
|
+
* @param opts Optional options object
|
880
|
+
*/
|
881
|
+
andForReplyTo(message_id: number | {
|
882
|
+
message_id: number;
|
883
|
+
}, opts?: AndOtherwiseOptions<C>): AndPromise<Filter<C, "message" | "channel_post">>;
|
884
|
+
}
|
885
|
+
export {};
|