@monixlite/grammy-scenes 1.2.0 → 1.2.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/README.md +74 -153
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,38 +1,39 @@
|
|
|
1
1
|
# @monixlite/grammy-scenes
|
|
2
2
|
|
|
3
|
-
A lightweight
|
|
4
|
-
The
|
|
3
|
+
A lightweight Finite State Machine (FSM) engine for Telegram bots built on grammY.
|
|
4
|
+
The library provides step-based scenes with declarative definitions, session-backed state, deterministic transitions, and extensible navigation.
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## Installation
|
|
9
9
|
|
|
10
|
-
```
|
|
10
|
+
```
|
|
11
11
|
npm install @monixlite/grammy-scenes
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## Requirements
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
* Node.js >= 16
|
|
17
|
+
* grammY >= 1.19
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
## Core
|
|
21
|
+
## Core Concepts
|
|
22
|
+
|
|
23
|
+
A scene is a sequence of ordered steps grouped under a shared title. Each step is defined declaratively and reacts to Telegram updates via callbacks.
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
Each step is described by an object containing:
|
|
25
|
+
The active scene state is stored in `ctx.session.scene`.
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
• a set of callbacks reacting to Telegram events (enter, message, callbackQuery)
|
|
27
|
+
Each step definition contains:
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
* scene identifier (title + step)
|
|
30
|
+
* callbacks for lifecycle and update handling
|
|
30
31
|
|
|
31
32
|
---
|
|
32
33
|
|
|
33
34
|
## Basic Usage
|
|
34
35
|
|
|
35
|
-
```
|
|
36
|
+
```
|
|
36
37
|
const { Bot, session } = require("grammy");
|
|
37
38
|
const { Scenes } = require("@monixlite/grammy-scenes");
|
|
38
39
|
|
|
@@ -46,7 +47,7 @@ const bot = new Bot("..."); {
|
|
|
46
47
|
};
|
|
47
48
|
|
|
48
49
|
|
|
49
|
-
const
|
|
50
|
+
const scenes = new Scenes([
|
|
50
51
|
[
|
|
51
52
|
{
|
|
52
53
|
scene: {
|
|
@@ -76,21 +77,20 @@ const scene = new Scenes([
|
|
|
76
77
|
const { phone } = ctx.session;
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
await ctx.reply(
|
|
80
|
+
await ctx.reply([
|
|
80
81
|
`• Your phone number: ${phone}`,
|
|
81
|
-
`• Is this correct
|
|
82
|
-
]
|
|
82
|
+
`• Is this correct?`,
|
|
83
|
+
].join("\n"), {
|
|
83
84
|
reply_markup: {
|
|
84
85
|
inline_keyboard: [
|
|
85
86
|
[
|
|
86
87
|
{
|
|
87
88
|
text: "• Yes",
|
|
88
|
-
callback_data: "confirm:yes"
|
|
89
|
+
callback_data: "confirm:yes",
|
|
89
90
|
},
|
|
90
|
-
|
|
91
91
|
{
|
|
92
92
|
text: "• No",
|
|
93
|
-
callback_data: "confirm:no"
|
|
93
|
+
callback_data: "confirm:no",
|
|
94
94
|
},
|
|
95
95
|
],
|
|
96
96
|
],
|
|
@@ -106,7 +106,7 @@ const scene = new Scenes([
|
|
|
106
106
|
|
|
107
107
|
|
|
108
108
|
await ctx.answerCallbackQuery();
|
|
109
|
-
await ctx.reply(`• Thank you!
|
|
109
|
+
await ctx.reply(`• Thank you! ${phone} saved.`);
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
return "stop";
|
|
@@ -126,7 +126,7 @@ const scene = new Scenes([
|
|
|
126
126
|
},
|
|
127
127
|
],
|
|
128
128
|
], {
|
|
129
|
-
|
|
129
|
+
"callbacks:enter:buttons:cancel": {
|
|
130
130
|
enabled: true,
|
|
131
131
|
|
|
132
132
|
component: {
|
|
@@ -144,19 +144,20 @@ bot.callbackQuery("scene:cancel", async (ctx) => {
|
|
|
144
144
|
await ctx.editMessageReplyMarkup(null);
|
|
145
145
|
await ctx.answerCallbackQuery();
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
|
|
148
|
+
await ctx.reply("• Scene cancelled");
|
|
148
149
|
});
|
|
149
150
|
|
|
150
151
|
|
|
151
152
|
bot.command("start", async (ctx) => {
|
|
152
|
-
|
|
153
|
+
await scenes.enter(ctx, {
|
|
153
154
|
title: "auth",
|
|
154
155
|
step: "phone",
|
|
155
156
|
});
|
|
156
157
|
});
|
|
157
158
|
|
|
158
159
|
|
|
159
|
-
bot.use(
|
|
160
|
+
bot.use(scenes.middleware);
|
|
160
161
|
|
|
161
162
|
bot.start();
|
|
162
163
|
```
|
|
@@ -165,9 +166,7 @@ bot.start();
|
|
|
165
166
|
|
|
166
167
|
## Scene Definition
|
|
167
168
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
```js
|
|
169
|
+
```
|
|
171
170
|
{
|
|
172
171
|
scene: {
|
|
173
172
|
title: string,
|
|
@@ -186,59 +185,35 @@ Each scene step is described by the following structure:
|
|
|
186
185
|
}
|
|
187
186
|
```
|
|
188
187
|
|
|
189
|
-
`SceneResult`
|
|
188
|
+
`SceneResult` determines the next transition.
|
|
190
189
|
|
|
191
190
|
---
|
|
192
191
|
|
|
193
192
|
## Callbacks
|
|
194
193
|
|
|
195
|
-
###
|
|
196
|
-
|
|
197
|
-
Triggered when entering a scene step.
|
|
198
|
-
Used to send messages, keyboards, or initialize data.
|
|
194
|
+
### enter(ctx)
|
|
199
195
|
|
|
200
|
-
|
|
196
|
+
Executed on step entry.
|
|
201
197
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
• If the cancel button option is enabled, it is automatically appended to the inline keyboard
|
|
198
|
+
* Used for messages and keyboards
|
|
199
|
+
* Cancel button can be injected automatically
|
|
205
200
|
|
|
206
|
-
|
|
201
|
+
### update(ctx)
|
|
207
202
|
|
|
208
|
-
|
|
203
|
+
Executed on any update while the step is active.
|
|
209
204
|
|
|
210
|
-
|
|
205
|
+
* Runs before `message` and `query`
|
|
206
|
+
* Suitable for guards, timeouts, media
|
|
207
|
+
* Return value controls navigation
|
|
211
208
|
|
|
212
|
-
|
|
209
|
+
### message(ctx)
|
|
213
210
|
|
|
214
|
-
|
|
215
|
-
• Receives the raw grammY context
|
|
216
|
-
• Can be used for global step logic (timeouts, guards, media handling)
|
|
217
|
-
• Return value controls navigation using standard SceneResult rules
|
|
211
|
+
Triggered on text messages during the step.
|
|
218
212
|
|
|
219
|
-
|
|
213
|
+
### query[]
|
|
220
214
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
Triggered when a text message is received while the step is active.
|
|
224
|
-
|
|
225
|
-
---
|
|
226
|
-
|
|
227
|
-
### callbacks.query
|
|
228
|
-
|
|
229
|
-
An array of `callbackQuery` handlers.
|
|
230
|
-
|
|
231
|
-
Each handler has the following shape:
|
|
232
|
-
|
|
233
|
-
```js
|
|
234
|
-
{
|
|
235
|
-
match: RegExp,
|
|
236
|
-
handler: async (ctx) => SceneResult
|
|
237
|
-
}
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
When `match` matches `ctx.callbackQuery.data`, the corresponding handler is executed.
|
|
241
|
-
Its return value is processed using the same transition logic as `callbacks.message`.
|
|
215
|
+
CallbackQuery handlers matched by regexp.
|
|
216
|
+
The handler return value follows standard transition rules.
|
|
242
217
|
|
|
243
218
|
---
|
|
244
219
|
|
|
@@ -246,122 +221,74 @@ Its return value is processed using the same transition logic as `callbacks.mess
|
|
|
246
221
|
|
|
247
222
|
### scenes.enter(ctx, scene)
|
|
248
223
|
|
|
249
|
-
Forces
|
|
250
|
-
|
|
251
|
-
```js
|
|
252
|
-
await scenes.enter(ctx, {
|
|
253
|
-
title: "auth",
|
|
254
|
-
step: "phone",
|
|
255
|
-
});
|
|
256
|
-
```
|
|
224
|
+
Forces entering a scene step.
|
|
257
225
|
|
|
258
226
|
Behavior:
|
|
259
227
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
---
|
|
266
|
-
|
|
267
|
-
### Cancel a Scene Manually
|
|
228
|
+
* Writes to `ctx.session.scene`
|
|
229
|
+
* Resolves step
|
|
230
|
+
* Executes `enter`
|
|
231
|
+
* Temporarily patches `ctx.reply` for cancel button injection
|
|
268
232
|
|
|
269
|
-
|
|
233
|
+
### Manual Termination
|
|
270
234
|
|
|
271
|
-
```
|
|
235
|
+
```
|
|
272
236
|
ctx.session.scene = null;
|
|
273
237
|
```
|
|
274
238
|
|
|
275
|
-
After this, the middleware stops processing scene events.
|
|
276
|
-
|
|
277
239
|
---
|
|
278
240
|
|
|
279
241
|
## Middleware
|
|
280
242
|
|
|
281
|
-
```
|
|
243
|
+
```
|
|
282
244
|
bot.use(session(...));
|
|
283
245
|
bot.use(scenes.middleware);
|
|
284
246
|
```
|
|
285
247
|
|
|
286
248
|
The middleware:
|
|
287
249
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
• Executes step transitions
|
|
250
|
+
* Resolves the active step
|
|
251
|
+
* Dispatches updates to callbacks
|
|
252
|
+
* Applies transitions
|
|
292
253
|
|
|
293
|
-
|
|
254
|
+
Must be registered after session middleware.
|
|
294
255
|
|
|
295
256
|
---
|
|
296
257
|
|
|
297
258
|
## Step Navigation
|
|
298
259
|
|
|
299
|
-
Transitions are
|
|
300
|
-
|
|
301
|
-
### Terminating a Scene
|
|
260
|
+
Transitions are driven by callback return values.
|
|
302
261
|
|
|
303
|
-
|
|
304
|
-
→ `ctx.session.scene = null`
|
|
262
|
+
### Transitions
|
|
305
263
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
264
|
+
* `undefined`, `"next"`, `">"` → next step
|
|
265
|
+
* `"prev"`, `"<"` → previous step
|
|
266
|
+
* `"stop"`, `"exit"`, `"!"` → terminate scene
|
|
267
|
+
* `"^step"` → jump within current scene
|
|
268
|
+
* `"^scene:step"` → jump to another scene
|
|
269
|
+
* `{ step }` → absolute step
|
|
270
|
+
* `{ scene: { title, step } }` → absolute scene
|
|
309
271
|
|
|
310
|
-
|
|
311
|
-
• `"stop" | "exit" | "!"` → terminate the scene
|
|
312
|
-
• `"<" | "prev"` → move to the previous step
|
|
313
|
-
• `">" | "next"` → move to the next step
|
|
314
|
-
• `"^step"` → jump to a step within the current scene
|
|
315
|
-
• `"^scene:step"` → jump to another scene
|
|
316
|
-
• `{ step: "..." }` → jump to a specific step
|
|
317
|
-
• `{ scene: { title, step } }` → jump to another scene
|
|
318
|
-
|
|
319
|
-
---
|
|
272
|
+
### Termination
|
|
320
273
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
• `undefined` / `"next"` / `">"` → next step
|
|
324
|
-
• `"prev"` / `"<"` → previous step
|
|
325
|
-
|
|
326
|
-
---
|
|
327
|
-
|
|
328
|
-
### Absolute Transitions
|
|
329
|
-
|
|
330
|
-
• `"^step"` → step within the current scene
|
|
331
|
-
• `"^scene:step"` → jump to another scene
|
|
332
|
-
|
|
333
|
-
---
|
|
274
|
+
Scene termination sets:
|
|
334
275
|
|
|
335
|
-
### Object-Based Transitions
|
|
336
|
-
|
|
337
|
-
```js
|
|
338
|
-
return { step: "confirm" };
|
|
339
276
|
```
|
|
340
|
-
|
|
341
|
-
```js
|
|
342
|
-
return {
|
|
343
|
-
scene: {
|
|
344
|
-
title: "auth",
|
|
345
|
-
step: "phone",
|
|
346
|
-
},
|
|
347
|
-
};
|
|
277
|
+
ctx.session.scene = null;
|
|
348
278
|
```
|
|
349
279
|
|
|
350
280
|
---
|
|
351
281
|
|
|
352
282
|
## Options
|
|
353
283
|
|
|
354
|
-
Options are passed as the second argument to `new Scenes()`.
|
|
355
|
-
|
|
356
284
|
### callbacks:enter:buttons:cancel
|
|
357
285
|
|
|
358
|
-
|
|
286
|
+
Injects a cancel button into all `enter` replies.
|
|
359
287
|
|
|
360
|
-
```
|
|
288
|
+
```
|
|
361
289
|
{
|
|
362
290
|
"callbacks:enter:buttons:cancel": {
|
|
363
291
|
enabled: true,
|
|
364
|
-
|
|
365
292
|
component: {
|
|
366
293
|
text: "Cancel",
|
|
367
294
|
callback_data: "scene:cancel",
|
|
@@ -370,29 +297,23 @@ Adds a cancel button to all messages sent inside `callbacks.enter`.
|
|
|
370
297
|
}
|
|
371
298
|
```
|
|
372
299
|
|
|
373
|
-
|
|
300
|
+
Notes:
|
|
374
301
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
• Scene cancellation logic must be implemented by the user
|
|
302
|
+
* Only affects `enter`
|
|
303
|
+
* Cancellation logic is user-defined
|
|
378
304
|
|
|
379
305
|
---
|
|
380
306
|
|
|
381
|
-
##
|
|
307
|
+
## API
|
|
382
308
|
|
|
383
309
|
### new Scenes(scenes, options)
|
|
384
310
|
|
|
385
|
-
Creates a scene manager
|
|
386
|
-
|
|
387
|
-
Parameters:
|
|
388
|
-
|
|
389
|
-
• `scenes` — an array of grouped scene steps
|
|
390
|
-
• `options` — configuration object
|
|
311
|
+
Creates a scene manager.
|
|
391
312
|
|
|
392
313
|
Exports:
|
|
393
314
|
|
|
394
|
-
|
|
395
|
-
|
|
315
|
+
* `scenes.middleware`
|
|
316
|
+
* `scenes.enter(ctx, scene)`
|
|
396
317
|
|
|
397
318
|
---
|
|
398
319
|
|