@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,2 @@
|
|
1
|
+
export { Api, type ApiClientOptions, type CallbackQueryContext, type CommandContext, Composer, Context, type Filter, type FilterQuery, type GameQueryContext, GrammyError, type HearsContext, HttpError, type Middleware, type MiddlewareFn, type ReactionContext, } from "grammy";
|
2
|
+
export type { Animation, ApiError, Audio, Contact, CopyTextButton, Dice, Document, File, Game, InlineKeyboardButton, InlineKeyboardMarkup, Location, LoginUrl, MessageEntity, PaidMediaInfo, PhotoSize, Poll, ReactionType, ReactionTypeEmoji, Sticker, Story, SwitchInlineQueryChosenChat, Update, User, UserFromGetMe, Venue, Video, VideoNote, Voice, } from "grammy/types";
|
package/out/deps.node.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export { Api, Composer, Context, GrammyError, HttpError, } from "grammy";
|
package/out/engine.d.ts
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
import { type Checkpoint, type ReplayState } from "./state.js";
|
2
|
+
export { type Checkpoint, type ReplayState } from "./state.js";
|
3
|
+
/**
|
4
|
+
* Controls for a replay. This is the object that a {@link ReplayEngine} passes
|
5
|
+
* to the replay function when executing it.
|
6
|
+
*/
|
7
|
+
export interface ReplayControls {
|
8
|
+
/**
|
9
|
+
* Interrupts the current replay and record this event in the replay logs.
|
10
|
+
* The replay will finish with an {@link Interrupted} result.
|
11
|
+
*
|
12
|
+
* Use {@link ReplayEngine.supply} to supply the result of this interrupt to
|
13
|
+
* the underlying replay state. When replaying the modified state, this call
|
14
|
+
* to `interrupt` will resolve with the supplied value.
|
15
|
+
*
|
16
|
+
* You also need to pass a key that identifies this type of interrupt. It is
|
17
|
+
* stored in the replay state and will be collated with the key that is
|
18
|
+
* passed to `interrupt` during the repeated call. If the two keys do not
|
19
|
+
* match, this means that a bad replay was detected and an error will be
|
20
|
+
* thrown. You should discard the replay state and restart the replay from
|
21
|
+
* scratch.
|
22
|
+
*
|
23
|
+
* @param key A key to collate interrupts across replays
|
24
|
+
*/
|
25
|
+
interrupt(key: string): Promise<unknown>;
|
26
|
+
/**
|
27
|
+
* Cancels the replay. This tells the replay engine that a supplied
|
28
|
+
* interrupt value should be rejected. The replay will finish with a
|
29
|
+
* {@link Canceled} result.
|
30
|
+
*
|
31
|
+
* A message object can be passed to `cancel`. This can be used to
|
32
|
+
* communicate to the caller why the interrupt value was rejected.
|
33
|
+
*
|
34
|
+
* @param message A message specifiying the reason for the cancelation
|
35
|
+
*/
|
36
|
+
cancel(message?: unknown): Promise<never>;
|
37
|
+
/**
|
38
|
+
* Performs an action.
|
39
|
+
*
|
40
|
+
* Actions are a way to signal to the replay engine that a particular piece
|
41
|
+
* of code should not be run repeatedly. The result of the action will be
|
42
|
+
* stored in the underlying replay log. During a subsequent replay, the
|
43
|
+
* action will not be repeated. Instead, the return is taken from the replay
|
44
|
+
* log.
|
45
|
+
*
|
46
|
+
* You also need to pass a key that identifies this type of action. It is
|
47
|
+
* stored in the replay state and will be collated with the key that is
|
48
|
+
* passed to `action` during the repeated call. If the two keys do not
|
49
|
+
* match, this means that a bad replay was detected and an error will be
|
50
|
+
* thrown. You should discard the replay state and restart the replay from
|
51
|
+
* scratch.
|
52
|
+
*
|
53
|
+
* @param fn The action to perform
|
54
|
+
* @param key A key to collate actions across replays
|
55
|
+
*/
|
56
|
+
action<R = unknown>(fn: () => R | Promise<R>, key: string): Promise<R>;
|
57
|
+
/**
|
58
|
+
* Creates a checkpoint at the current position of the replay. This can be
|
59
|
+
* passed to {@link ReplayEngine.reset} in order to restart a replay from an
|
60
|
+
* arbitrary position.
|
61
|
+
*/
|
62
|
+
checkpoint(): Checkpoint;
|
63
|
+
}
|
64
|
+
/** A function to be replayed by a {@link ReplayEngine} */
|
65
|
+
export type Builder = (controls: ReplayControls) => void | Promise<void>;
|
66
|
+
/** The result of a replay performed by a {@link ReplayEngine} */
|
67
|
+
export type ReplayResult = Returned | Thrown | Interrupted | Canceled;
|
68
|
+
/**
|
69
|
+
* This result is returned by a {@link ReplayEngine} when the builder function
|
70
|
+
* completes normally by returning.
|
71
|
+
*/
|
72
|
+
export interface Returned {
|
73
|
+
/**
|
74
|
+
* Type of the replay result, indicates that the replay has completed
|
75
|
+
* normally because the builder function has returned.
|
76
|
+
*/
|
77
|
+
type: "returned";
|
78
|
+
/** The return value of the builder function */
|
79
|
+
returnValue: unknown;
|
80
|
+
}
|
81
|
+
/**
|
82
|
+
* This result is returned by a {@link ReplayEngine} when the builder function
|
83
|
+
* throws an error.
|
84
|
+
*/
|
85
|
+
export interface Thrown {
|
86
|
+
/**
|
87
|
+
* Type of the replay result, indicates that the replay has completed
|
88
|
+
* because the builder function has thrown an error.
|
89
|
+
*/
|
90
|
+
type: "thrown";
|
91
|
+
/** The error thrown by the builder function */
|
92
|
+
error: unknown;
|
93
|
+
}
|
94
|
+
/**
|
95
|
+
* This result is returned by a {@link ReplayEngine} when the builder function
|
96
|
+
* interrupts itself by calling {@link ReplayControls.interrupt}.
|
97
|
+
*/
|
98
|
+
export interface Interrupted {
|
99
|
+
/**
|
100
|
+
* Type of the replay result, indicates that the replay has completed
|
101
|
+
* because the builder function has interrupted itself.
|
102
|
+
*/
|
103
|
+
type: "interrupted";
|
104
|
+
/** The replay state left behind by the replay engine */
|
105
|
+
state: ReplayState;
|
106
|
+
/** The list of concurrent interrupts that were performed */
|
107
|
+
interrupts: number[];
|
108
|
+
}
|
109
|
+
/**
|
110
|
+
* This result is returned by a {@link ReplayEngine} when the builder function
|
111
|
+
* cancels itself by calling {@link ReplayControls.cancel}.
|
112
|
+
*/
|
113
|
+
export interface Canceled {
|
114
|
+
/**
|
115
|
+
* Type of the replay result, indicates that the replay has completed
|
116
|
+
* because the builder function has canceled itself.
|
117
|
+
*/
|
118
|
+
type: "canceled";
|
119
|
+
/** The message passed to the last concurrent cancel call */
|
120
|
+
message?: unknown;
|
121
|
+
}
|
122
|
+
/**
|
123
|
+
* A replay engine takes control of the event loop of the JavaScript runtime and
|
124
|
+
* lets you execute a JavaScript function in abnormal ways. The function
|
125
|
+
* execution can be halted, resumed, aborted, and reversed. This lets you run a
|
126
|
+
* function partially and persist the state of execution in a database. Later,
|
127
|
+
* function execution can be resumed from where it was left off.
|
128
|
+
*
|
129
|
+
* Replay engines are the fundamental building block of the conversations
|
130
|
+
* plugin. In a sense, everything else is just a number of wrapper layers to
|
131
|
+
* make working with replay engines more convenient, and to integrate the power
|
132
|
+
* of replay engines into your bot's middleware system.
|
133
|
+
*
|
134
|
+
* Using a standalone replay engine is straightforward.
|
135
|
+
*
|
136
|
+
* 1. Create an instance of this class and pass a normal JavaScript function to
|
137
|
+
* the constructor. The function receives a {@link ReplayControls} object as
|
138
|
+
* its only parameter.
|
139
|
+
* 2. Call {@link ReplayEngine.play} to begin a new execution. It returns a
|
140
|
+
* {@link ReplayResult} object.
|
141
|
+
* 3. Use the {@link ReplayState} you obtained inside the result object and
|
142
|
+
* resume execution by calling {@link ReplayEngine.replay}.
|
143
|
+
*
|
144
|
+
* The `ReplayEngine` class furthermore provides you with static helper methods
|
145
|
+
* to supply values to interrupts, and to reset the replay state to a previously
|
146
|
+
* created checkpoint.
|
147
|
+
*/
|
148
|
+
export declare class ReplayEngine {
|
149
|
+
private readonly builder;
|
150
|
+
/**
|
151
|
+
* Constructs a new replay engine from a builder function. The function
|
152
|
+
* receives a single parameter that can be used to control the replay.
|
153
|
+
*
|
154
|
+
* @param builder A builder function to be executed and replayed
|
155
|
+
*/
|
156
|
+
constructor(builder: Builder);
|
157
|
+
/**
|
158
|
+
* Begins a new execution of the builder function. This starts based on
|
159
|
+
* fresh state. The execution is independent from any previously created
|
160
|
+
* executions.
|
161
|
+
*
|
162
|
+
* A {@link ReplayResult} object is returned to communicate the outcome of
|
163
|
+
* the execution.
|
164
|
+
*/
|
165
|
+
play(): Promise<ReplayResult>;
|
166
|
+
/**
|
167
|
+
* Resumes execution based on a previously created replay state. This is the
|
168
|
+
* most important method of this class.
|
169
|
+
*
|
170
|
+
* A {@link ReplayResult} object is returned to communicate the outcome of
|
171
|
+
* the execution.
|
172
|
+
*
|
173
|
+
* @param state A previously created replay state
|
174
|
+
*/
|
175
|
+
replay(state: ReplayState): Promise<ReplayResult>;
|
176
|
+
/**
|
177
|
+
* Creates a new replay state with a single unresolved interrupt. This state
|
178
|
+
* can be used as a starting point to replay arbitrary builder functions.
|
179
|
+
*
|
180
|
+
* You need to pass the collation key for the aforementioned first
|
181
|
+
* interrupt. This must be the same value that the builder function will
|
182
|
+
* pass to its first interrupt.
|
183
|
+
*
|
184
|
+
* @param key The builder functions first collation key
|
185
|
+
*/
|
186
|
+
static open(key: string): readonly [ReplayState, number];
|
187
|
+
/**
|
188
|
+
* Mutates a given replay state by supplying a value for a given interrupt.
|
189
|
+
* The next time the state is replayed, the targeted interrupt will return
|
190
|
+
* this value.
|
191
|
+
*
|
192
|
+
* The interrupt value has to be one of the interrupts of a previously
|
193
|
+
* received {@link Interrupted} result.
|
194
|
+
*
|
195
|
+
* In addition to mutating the replay state, a checkpoint is created and
|
196
|
+
* returned. This checkpoint may be used to reset the replay state to its
|
197
|
+
* previous value. This will undo this and all following mutations.
|
198
|
+
*
|
199
|
+
* @param state A replay state to mutate
|
200
|
+
* @param interrupt An interrupt to resolve
|
201
|
+
* @param value The value to supply
|
202
|
+
*/
|
203
|
+
static supply(state: ReplayState, interrupt: number, value: unknown): Checkpoint;
|
204
|
+
/**
|
205
|
+
* Resets a given replay state to a previously received checkpoint by
|
206
|
+
* mutating the replay state.
|
207
|
+
*
|
208
|
+
* @param state The state to mutate
|
209
|
+
* @param checkpoint The checkpoint to which to return
|
210
|
+
*/
|
211
|
+
static reset(state: ReplayState, checkpoint: Checkpoint): void;
|
212
|
+
}
|
package/out/engine.js
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
import { resolver } from "./resolve.js";
|
2
|
+
import { create, cursor, inspect, mutate, } from "./state.js";
|
3
|
+
/**
|
4
|
+
* A replay engine takes control of the event loop of the JavaScript runtime and
|
5
|
+
* lets you execute a JavaScript function in abnormal ways. The function
|
6
|
+
* execution can be halted, resumed, aborted, and reversed. This lets you run a
|
7
|
+
* function partially and persist the state of execution in a database. Later,
|
8
|
+
* function execution can be resumed from where it was left off.
|
9
|
+
*
|
10
|
+
* Replay engines are the fundamental building block of the conversations
|
11
|
+
* plugin. In a sense, everything else is just a number of wrapper layers to
|
12
|
+
* make working with replay engines more convenient, and to integrate the power
|
13
|
+
* of replay engines into your bot's middleware system.
|
14
|
+
*
|
15
|
+
* Using a standalone replay engine is straightforward.
|
16
|
+
*
|
17
|
+
* 1. Create an instance of this class and pass a normal JavaScript function to
|
18
|
+
* the constructor. The function receives a {@link ReplayControls} object as
|
19
|
+
* its only parameter.
|
20
|
+
* 2. Call {@link ReplayEngine.play} to begin a new execution. It returns a
|
21
|
+
* {@link ReplayResult} object.
|
22
|
+
* 3. Use the {@link ReplayState} you obtained inside the result object and
|
23
|
+
* resume execution by calling {@link ReplayEngine.replay}.
|
24
|
+
*
|
25
|
+
* The `ReplayEngine` class furthermore provides you with static helper methods
|
26
|
+
* to supply values to interrupts, and to reset the replay state to a previously
|
27
|
+
* created checkpoint.
|
28
|
+
*/
|
29
|
+
export class ReplayEngine {
|
30
|
+
/**
|
31
|
+
* Constructs a new replay engine from a builder function. The function
|
32
|
+
* receives a single parameter that can be used to control the replay.
|
33
|
+
*
|
34
|
+
* @param builder A builder function to be executed and replayed
|
35
|
+
*/
|
36
|
+
constructor(builder) {
|
37
|
+
this.builder = builder;
|
38
|
+
}
|
39
|
+
/**
|
40
|
+
* Begins a new execution of the builder function. This starts based on
|
41
|
+
* fresh state. The execution is independent from any previously created
|
42
|
+
* executions.
|
43
|
+
*
|
44
|
+
* A {@link ReplayResult} object is returned to communicate the outcome of
|
45
|
+
* the execution.
|
46
|
+
*/
|
47
|
+
async play() {
|
48
|
+
const state = create();
|
49
|
+
return await this.replay(state);
|
50
|
+
}
|
51
|
+
/**
|
52
|
+
* Resumes execution based on a previously created replay state. This is the
|
53
|
+
* most important method of this class.
|
54
|
+
*
|
55
|
+
* A {@link ReplayResult} object is returned to communicate the outcome of
|
56
|
+
* the execution.
|
57
|
+
*
|
58
|
+
* @param state A previously created replay state
|
59
|
+
*/
|
60
|
+
async replay(state) {
|
61
|
+
return await replayState(this.builder, state);
|
62
|
+
}
|
63
|
+
/**
|
64
|
+
* Creates a new replay state with a single unresolved interrupt. This state
|
65
|
+
* can be used as a starting point to replay arbitrary builder functions.
|
66
|
+
*
|
67
|
+
* You need to pass the collation key for the aforementioned first
|
68
|
+
* interrupt. This must be the same value that the builder function will
|
69
|
+
* pass to its first interrupt.
|
70
|
+
*
|
71
|
+
* @param key The builder functions first collation key
|
72
|
+
*/
|
73
|
+
static open(key) {
|
74
|
+
const state = create();
|
75
|
+
const mut = mutate(state);
|
76
|
+
const int = mut.op(key);
|
77
|
+
return [state, int];
|
78
|
+
}
|
79
|
+
/**
|
80
|
+
* Mutates a given replay state by supplying a value for a given interrupt.
|
81
|
+
* The next time the state is replayed, the targeted interrupt will return
|
82
|
+
* this value.
|
83
|
+
*
|
84
|
+
* The interrupt value has to be one of the interrupts of a previously
|
85
|
+
* received {@link Interrupted} result.
|
86
|
+
*
|
87
|
+
* In addition to mutating the replay state, a checkpoint is created and
|
88
|
+
* returned. This checkpoint may be used to reset the replay state to its
|
89
|
+
* previous value. This will undo this and all following mutations.
|
90
|
+
*
|
91
|
+
* @param state A replay state to mutate
|
92
|
+
* @param interrupt An interrupt to resolve
|
93
|
+
* @param value The value to supply
|
94
|
+
*/
|
95
|
+
static supply(state, interrupt, value) {
|
96
|
+
const get = inspect(state);
|
97
|
+
const checkpoint = get.checkpoint();
|
98
|
+
const mut = mutate(state);
|
99
|
+
mut.done(interrupt, value);
|
100
|
+
return checkpoint;
|
101
|
+
}
|
102
|
+
/**
|
103
|
+
* Resets a given replay state to a previously received checkpoint by
|
104
|
+
* mutating the replay state.
|
105
|
+
*
|
106
|
+
* @param state The state to mutate
|
107
|
+
* @param checkpoint The checkpoint to which to return
|
108
|
+
*/
|
109
|
+
static reset(state, checkpoint) {
|
110
|
+
const mut = mutate(state);
|
111
|
+
mut.reset(checkpoint);
|
112
|
+
}
|
113
|
+
}
|
114
|
+
async function replayState(builder, state) {
|
115
|
+
const cur = cursor(state);
|
116
|
+
// Set up interrupt and action tracking
|
117
|
+
let interrupted = false;
|
118
|
+
const interrupts = [];
|
119
|
+
let boundary = resolver();
|
120
|
+
const actions = new Set();
|
121
|
+
function updateBoundary() {
|
122
|
+
if (interrupted && actions.size === 0) {
|
123
|
+
boundary.resolve();
|
124
|
+
}
|
125
|
+
}
|
126
|
+
async function runBoundary() {
|
127
|
+
while (!boundary.isResolved()) {
|
128
|
+
await boundary.promise;
|
129
|
+
// clear microtask queue and check if another action was started
|
130
|
+
await new Promise((r) => setTimeout(r, 0));
|
131
|
+
}
|
132
|
+
}
|
133
|
+
// Set up event loop tracking to prevent
|
134
|
+
// premature returns with floating promises
|
135
|
+
let promises = 0; // counts the number of promises on the event loop
|
136
|
+
let dirty = resolver(); // resolves as soon as the event loop is clear
|
137
|
+
let complete = false; // locks the engine after the event loop has cleared
|
138
|
+
function begin() {
|
139
|
+
if (complete) {
|
140
|
+
throw new Error("Cannot begin another operation after the conversation has completed, are you missing an `await`?");
|
141
|
+
}
|
142
|
+
promises++;
|
143
|
+
if (boundary.isResolved()) {
|
144
|
+
// new action was started after interrupt, reset boundary
|
145
|
+
boundary = resolver();
|
146
|
+
}
|
147
|
+
}
|
148
|
+
function end() {
|
149
|
+
promises--;
|
150
|
+
if (promises === 0) {
|
151
|
+
dirty.resolve();
|
152
|
+
dirty = resolver();
|
153
|
+
}
|
154
|
+
}
|
155
|
+
// Collect data to return to caller
|
156
|
+
let canceled = false;
|
157
|
+
let message = undefined;
|
158
|
+
let returned = false;
|
159
|
+
let returnValue = undefined;
|
160
|
+
// Define replay controls
|
161
|
+
async function interrupt(key) {
|
162
|
+
if (returned || (interrupted && interrupts.length === 0)) {
|
163
|
+
// Already returned or canceled, so we must no longer perform an interrupt.
|
164
|
+
await boom();
|
165
|
+
}
|
166
|
+
begin();
|
167
|
+
const res = await cur.perform(async (op) => {
|
168
|
+
interrupted = true;
|
169
|
+
interrupts.push(op);
|
170
|
+
updateBoundary();
|
171
|
+
await boom();
|
172
|
+
}, key);
|
173
|
+
end();
|
174
|
+
return res;
|
175
|
+
}
|
176
|
+
async function cancel(key) {
|
177
|
+
canceled = true;
|
178
|
+
interrupted = true;
|
179
|
+
message = key;
|
180
|
+
updateBoundary();
|
181
|
+
return await boom();
|
182
|
+
}
|
183
|
+
async function action(fn, key) {
|
184
|
+
begin();
|
185
|
+
const res = await cur.perform(async (op) => {
|
186
|
+
actions.add(op);
|
187
|
+
const ret = await fn();
|
188
|
+
actions.delete(op);
|
189
|
+
updateBoundary();
|
190
|
+
return ret;
|
191
|
+
}, key);
|
192
|
+
end();
|
193
|
+
return res;
|
194
|
+
}
|
195
|
+
function checkpoint() {
|
196
|
+
return cur.checkpoint();
|
197
|
+
}
|
198
|
+
const controls = { interrupt, cancel, action, checkpoint };
|
199
|
+
// Perform replay
|
200
|
+
async function run() {
|
201
|
+
returnValue = await builder(controls);
|
202
|
+
returned = true;
|
203
|
+
// wait for pending ops to complete
|
204
|
+
while (promises > 0) {
|
205
|
+
await dirty.promise;
|
206
|
+
// clear microtask queue and check again
|
207
|
+
await new Promise((r) => setTimeout(r, 0));
|
208
|
+
}
|
209
|
+
}
|
210
|
+
try {
|
211
|
+
const boundaryPromise = runBoundary();
|
212
|
+
const runPromise = run();
|
213
|
+
await Promise.race([boundaryPromise, runPromise]);
|
214
|
+
if (returned) {
|
215
|
+
return { type: "returned", returnValue };
|
216
|
+
}
|
217
|
+
else if (boundary.isResolved()) {
|
218
|
+
if (canceled) {
|
219
|
+
return { type: "canceled", message };
|
220
|
+
}
|
221
|
+
else {
|
222
|
+
return { type: "interrupted", state, interrupts };
|
223
|
+
}
|
224
|
+
}
|
225
|
+
else {
|
226
|
+
throw new Error("Neither returned nor interrupted!"); // should never happen
|
227
|
+
}
|
228
|
+
}
|
229
|
+
catch (error) {
|
230
|
+
return { type: "thrown", error };
|
231
|
+
}
|
232
|
+
finally {
|
233
|
+
complete = true;
|
234
|
+
}
|
235
|
+
}
|
236
|
+
function boom() {
|
237
|
+
return new Promise(() => { });
|
238
|
+
}
|