@mtcute/dispatcher 0.16.7 → 0.16.13
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/{cjs/callback-data-builder.d.ts → callback-data-builder.d.ts} +2 -2
- package/callback-data-builder.test.d.ts +1 -0
- package/{esm/context → context}/base.d.ts +2 -2
- package/{esm/context → context}/business-message.d.ts +5 -6
- package/{esm/context → context}/callback-query.d.ts +6 -7
- package/{cjs/context → context}/chat-join-request.d.ts +3 -3
- package/{esm/context → context}/chosen-inline-result.d.ts +3 -3
- package/{esm/context → context}/inline-query.d.ts +4 -5
- package/{cjs/context → context}/message.d.ts +11 -6
- package/{cjs/context → context}/parse.d.ts +3 -3
- package/{esm/context → context}/pre-checkout-query.d.ts +3 -3
- package/{esm/context → context}/scene-transition.d.ts +1 -1
- package/{cjs/dispatcher.d.ts → dispatcher.d.ts} +9 -10
- package/{cjs/filters → filters}/bots.d.ts +4 -4
- package/filters/bots.test.d.ts +1 -0
- package/{cjs/filters → filters}/chat.d.ts +3 -3
- package/{esm/filters → filters}/group.d.ts +4 -4
- package/{cjs/filters → filters}/logic.d.ts +1 -1
- package/filters/logic.test.d.ts +1 -0
- package/{esm/filters → filters}/message.d.ts +9 -5
- package/{cjs/filters → filters}/state.d.ts +2 -2
- package/{esm/filters → filters}/text.d.ts +3 -3
- package/{cjs/filters → filters}/types.d.ts +2 -2
- package/{cjs/filters → filters}/updates.d.ts +2 -2
- package/{cjs/filters → filters}/user.d.ts +3 -4
- package/{cjs/handler.d.ts → handler.d.ts} +5 -5
- package/index.cjs +2534 -0
- package/index.d.ts +8 -1
- package/index.js +2529 -1
- package/package.json +28 -24
- package/{cjs/state → state}/key.d.ts +3 -3
- package/state/provider.d.ts +5 -0
- package/{cjs/state → state}/providers/memory.d.ts +3 -4
- package/{esm/state → state}/providers/sqlite.d.ts +3 -3
- package/{cjs/state → state}/repository.d.ts +1 -1
- package/{esm/state → state}/service.d.ts +2 -2
- package/{cjs/state → state}/update-state.d.ts +2 -2
- package/{esm/wizard.d.ts → wizard.d.ts} +5 -6
- package/cjs/callback-data-builder.js +0 -141
- package/cjs/callback-data-builder.js.map +0 -1
- package/cjs/context/base.d.ts +0 -9
- package/cjs/context/base.js +0 -3
- package/cjs/context/base.js.map +0 -1
- package/cjs/context/business-message.d.ts +0 -61
- package/cjs/context/business-message.js +0 -146
- package/cjs/context/business-message.js.map +0 -1
- package/cjs/context/callback-query.d.ts +0 -63
- package/cjs/context/callback-query.js +0 -109
- package/cjs/context/callback-query.js.map +0 -1
- package/cjs/context/chat-join-request.js +0 -35
- package/cjs/context/chat-join-request.js.map +0 -1
- package/cjs/context/chosen-inline-result.d.ts +0 -22
- package/cjs/context/chosen-inline-result.js +0 -36
- package/cjs/context/chosen-inline-result.js.map +0 -1
- package/cjs/context/index.js +0 -25
- package/cjs/context/index.js.map +0 -1
- package/cjs/context/inline-query.d.ts +0 -16
- package/cjs/context/inline-query.js +0 -23
- package/cjs/context/inline-query.js.map +0 -1
- package/cjs/context/message.js +0 -171
- package/cjs/context/message.js.map +0 -1
- package/cjs/context/parse.js +0 -42
- package/cjs/context/parse.js.map +0 -1
- package/cjs/context/pre-checkout-query.d.ts +0 -17
- package/cjs/context/pre-checkout-query.js +0 -27
- package/cjs/context/pre-checkout-query.js.map +0 -1
- package/cjs/context/scene-transition.d.ts +0 -24
- package/cjs/context/scene-transition.js +0 -52
- package/cjs/context/scene-transition.js.map +0 -1
- package/cjs/dispatcher.js +0 -908
- package/cjs/dispatcher.js.map +0 -1
- package/cjs/filters/bots.js +0 -135
- package/cjs/filters/bots.js.map +0 -1
- package/cjs/filters/bundle.js +0 -27
- package/cjs/filters/bundle.js.map +0 -1
- package/cjs/filters/chat.js +0 -56
- package/cjs/filters/chat.js.map +0 -1
- package/cjs/filters/group.d.ts +0 -26
- package/cjs/filters/group.js +0 -69
- package/cjs/filters/group.js.map +0 -1
- package/cjs/filters/index.js +0 -29
- package/cjs/filters/index.js.map +0 -1
- package/cjs/filters/logic.js +0 -112
- package/cjs/filters/logic.js.map +0 -1
- package/cjs/filters/message.d.ts +0 -215
- package/cjs/filters/message.js +0 -191
- package/cjs/filters/message.js.map +0 -1
- package/cjs/filters/state.js +0 -33
- package/cjs/filters/state.js.map +0 -1
- package/cjs/filters/text.d.ts +0 -64
- package/cjs/filters/text.js +0 -136
- package/cjs/filters/text.js.map +0 -1
- package/cjs/filters/types.js +0 -3
- package/cjs/filters/types.js.map +0 -1
- package/cjs/filters/updates.js +0 -40
- package/cjs/filters/updates.js.map +0 -1
- package/cjs/filters/user.js +0 -77
- package/cjs/filters/user.js.map +0 -1
- package/cjs/handler.js +0 -4
- package/cjs/handler.js.map +0 -1
- package/cjs/index.js +0 -31
- package/cjs/index.js.map +0 -1
- package/cjs/package.json +0 -3
- package/cjs/propagation.js +0 -27
- package/cjs/propagation.js.map +0 -1
- package/cjs/state/index.js +0 -22
- package/cjs/state/index.js.map +0 -1
- package/cjs/state/key.js +0 -43
- package/cjs/state/key.js.map +0 -1
- package/cjs/state/provider.d.ts +0 -5
- package/cjs/state/provider.js +0 -3
- package/cjs/state/provider.js.map +0 -1
- package/cjs/state/providers/index.js +0 -19
- package/cjs/state/providers/index.js.map +0 -1
- package/cjs/state/providers/memory.js +0 -81
- package/cjs/state/providers/memory.js.map +0 -1
- package/cjs/state/providers/sqlite.d.ts +0 -28
- package/cjs/state/providers/sqlite.js +0 -100
- package/cjs/state/providers/sqlite.js.map +0 -1
- package/cjs/state/repository.js +0 -3
- package/cjs/state/repository.js.map +0 -1
- package/cjs/state/service.d.ts +0 -20
- package/cjs/state/service.js +0 -70
- package/cjs/state/service.js.map +0 -1
- package/cjs/state/update-state.js +0 -219
- package/cjs/state/update-state.js.map +0 -1
- package/cjs/wizard.d.ts +0 -65
- package/cjs/wizard.js +0 -105
- package/cjs/wizard.js.map +0 -1
- package/esm/callback-data-builder.d.ts +0 -49
- package/esm/callback-data-builder.js +0 -137
- package/esm/callback-data-builder.js.map +0 -1
- package/esm/context/base.js +0 -2
- package/esm/context/base.js.map +0 -1
- package/esm/context/business-message.js +0 -142
- package/esm/context/business-message.js.map +0 -1
- package/esm/context/callback-query.js +0 -103
- package/esm/context/callback-query.js.map +0 -1
- package/esm/context/chat-join-request.d.ts +0 -17
- package/esm/context/chat-join-request.js +0 -31
- package/esm/context/chat-join-request.js.map +0 -1
- package/esm/context/chosen-inline-result.js +0 -32
- package/esm/context/chosen-inline-result.js.map +0 -1
- package/esm/context/index.d.ts +0 -9
- package/esm/context/index.js +0 -9
- package/esm/context/index.js.map +0 -1
- package/esm/context/inline-query.js +0 -19
- package/esm/context/inline-query.js.map +0 -1
- package/esm/context/message.d.ts +0 -77
- package/esm/context/message.js +0 -167
- package/esm/context/message.js.map +0 -1
- package/esm/context/parse.d.ts +0 -13
- package/esm/context/parse.js +0 -39
- package/esm/context/parse.js.map +0 -1
- package/esm/context/pre-checkout-query.js +0 -23
- package/esm/context/pre-checkout-query.js.map +0 -1
- package/esm/context/scene-transition.js +0 -48
- package/esm/context/scene-transition.js.map +0 -1
- package/esm/dispatcher.d.ts +0 -881
- package/esm/dispatcher.js +0 -904
- package/esm/dispatcher.js.map +0 -1
- package/esm/filters/bots.d.ts +0 -64
- package/esm/filters/bots.js +0 -131
- package/esm/filters/bots.js.map +0 -1
- package/esm/filters/bundle.d.ts +0 -10
- package/esm/filters/bundle.js +0 -11
- package/esm/filters/bundle.js.map +0 -1
- package/esm/filters/chat.d.ts +0 -27
- package/esm/filters/chat.js +0 -51
- package/esm/filters/chat.js.map +0 -1
- package/esm/filters/group.js +0 -65
- package/esm/filters/group.js.map +0 -1
- package/esm/filters/index.d.ts +0 -4
- package/esm/filters/index.js +0 -3
- package/esm/filters/index.js.map +0 -1
- package/esm/filters/logic.d.ts +0 -29
- package/esm/filters/logic.js +0 -105
- package/esm/filters/logic.js.map +0 -1
- package/esm/filters/message.js +0 -168
- package/esm/filters/message.js.map +0 -1
- package/esm/filters/state.d.ts +0 -15
- package/esm/filters/state.js +0 -28
- package/esm/filters/state.js.map +0 -1
- package/esm/filters/text.js +0 -129
- package/esm/filters/text.js.map +0 -1
- package/esm/filters/types.d.ts +0 -91
- package/esm/filters/types.js +0 -2
- package/esm/filters/types.js.map +0 -1
- package/esm/filters/updates.d.ts +0 -39
- package/esm/filters/updates.js +0 -34
- package/esm/filters/updates.js.map +0 -1
- package/esm/filters/user.d.ts +0 -25
- package/esm/filters/user.js +0 -71
- package/esm/filters/user.js.map +0 -1
- package/esm/handler.d.ts +0 -41
- package/esm/handler.js +0 -3
- package/esm/handler.js.map +0 -1
- package/esm/index.d.ts +0 -8
- package/esm/index.js +0 -9
- package/esm/index.js.map +0 -1
- package/esm/propagation.d.ts +0 -22
- package/esm/propagation.js +0 -24
- package/esm/propagation.js.map +0 -1
- package/esm/state/index.d.ts +0 -5
- package/esm/state/index.js +0 -6
- package/esm/state/index.js.map +0 -1
- package/esm/state/key.d.ts +0 -24
- package/esm/state/key.js +0 -39
- package/esm/state/key.js.map +0 -1
- package/esm/state/provider.d.ts +0 -5
- package/esm/state/provider.js +0 -2
- package/esm/state/provider.js.map +0 -1
- package/esm/state/providers/index.d.ts +0 -2
- package/esm/state/providers/index.js +0 -3
- package/esm/state/providers/index.js.map +0 -1
- package/esm/state/providers/memory.d.ts +0 -30
- package/esm/state/providers/memory.js +0 -77
- package/esm/state/providers/memory.js.map +0 -1
- package/esm/state/providers/sqlite.js +0 -96
- package/esm/state/providers/sqlite.js.map +0 -1
- package/esm/state/repository.d.ts +0 -62
- package/esm/state/repository.js +0 -2
- package/esm/state/repository.js.map +0 -1
- package/esm/state/service.js +0 -66
- package/esm/state/service.js.map +0 -1
- package/esm/state/update-state.d.ts +0 -151
- package/esm/state/update-state.js +0 -214
- package/esm/state/update-state.js.map +0 -1
- package/esm/wizard.js +0 -101
- package/esm/wizard.js.map +0 -1
- /package/{cjs/context → context}/index.d.ts +0 -0
- /package/{cjs/filters → filters}/bundle.d.ts +0 -0
- /package/{cjs/filters → filters}/index.d.ts +0 -0
- /package/{cjs/index.d.ts → index.d.cts} +0 -0
- /package/{cjs/propagation.d.ts → propagation.d.ts} +0 -0
- /package/{cjs/state → state}/index.d.ts +0 -0
- /package/{cjs/state → state}/providers/index.d.ts +0 -0
package/index.js
CHANGED
|
@@ -1 +1,2529 @@
|
|
|
1
|
-
|
|
1
|
+
import { MtArgumentError, BusinessMessage, CallbackQuery, InlineCallbackQuery, BusinessCallbackQuery, BotChatJoinRequestUpdate, ChosenInlineResult, InlineQuery, Message, MtPeerNotFoundError, PreCheckoutQuery, MtTypeAssertionError, assertNever, MemoryStorageDriver, MtcuteError, RawLocation, RawDocument, User } from "@mtcute/core";
|
|
2
|
+
import { makeInspectable, sleep, LruMap, asyncResettable, timers } from "@mtcute/core/utils.js";
|
|
3
|
+
class CallbackDataBuilder {
|
|
4
|
+
/**
|
|
5
|
+
* @param prefix Prefix for the data. Use something unique across your bot.
|
|
6
|
+
* @param fields Field names in the order they will be serialized.
|
|
7
|
+
*/
|
|
8
|
+
constructor(prefix, ...fields) {
|
|
9
|
+
this.prefix = prefix;
|
|
10
|
+
this._fields = fields;
|
|
11
|
+
}
|
|
12
|
+
_fields;
|
|
13
|
+
sep = ":";
|
|
14
|
+
/**
|
|
15
|
+
* Build a callback data string
|
|
16
|
+
*
|
|
17
|
+
* @param obj Object containing the data
|
|
18
|
+
*/
|
|
19
|
+
build(obj) {
|
|
20
|
+
const ret = this.prefix + this.sep + this._fields.map((f) => {
|
|
21
|
+
const val = obj[f];
|
|
22
|
+
if (val.includes(this.sep)) {
|
|
23
|
+
throw new MtArgumentError(
|
|
24
|
+
`Value for ${f} ${val} contains separator ${this.sep} and cannot be used.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return val;
|
|
28
|
+
}).join(this.sep);
|
|
29
|
+
if (ret.length > 64) {
|
|
30
|
+
throw new MtArgumentError("Resulting callback data is too long.");
|
|
31
|
+
}
|
|
32
|
+
return ret;
|
|
33
|
+
}
|
|
34
|
+
parse(data, safe = false) {
|
|
35
|
+
const parts = data.split(this.sep);
|
|
36
|
+
if (parts[0] !== this.prefix) {
|
|
37
|
+
if (safe) return null;
|
|
38
|
+
throw new MtArgumentError(
|
|
39
|
+
`Invalid data passed: "${data}" (bad prefix, expected ${this.prefix}, got ${parts[0]})`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (parts.length !== this._fields.length + 1) {
|
|
43
|
+
if (safe) return null;
|
|
44
|
+
throw new MtArgumentError(
|
|
45
|
+
`Invalid data passed: "${data}" (bad parts count, expected ${this._fields.length}, got ${parts.length - 1})`
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const ret = {};
|
|
49
|
+
parts.forEach((it, idx) => {
|
|
50
|
+
if (idx === 0) return;
|
|
51
|
+
ret[this._fields[idx - 1]] = it;
|
|
52
|
+
});
|
|
53
|
+
return ret;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Create a filter for this callback data.
|
|
57
|
+
*
|
|
58
|
+
* You can either pass an object with field names as keys and values as strings or regexes,
|
|
59
|
+
* which will be compiled to a RegExp, or a function that will be called with the parsed data.
|
|
60
|
+
* Note that the strings will be passed to `RegExp` **directly**, so you may want to escape them.
|
|
61
|
+
*
|
|
62
|
+
* When using a function, you can either return a boolean, or an object with field names as keys
|
|
63
|
+
* and values as strings or regexes. In the latter case, the resulting object will be matched
|
|
64
|
+
* against the parsed data the same way as if you passed it directly.
|
|
65
|
+
*
|
|
66
|
+
* @param params
|
|
67
|
+
*/
|
|
68
|
+
filter(params = {}) {
|
|
69
|
+
if (typeof params === "function") {
|
|
70
|
+
return async (query) => {
|
|
71
|
+
if (!query.dataStr) return false;
|
|
72
|
+
const data = this.parse(query.dataStr, true);
|
|
73
|
+
if (!data) return false;
|
|
74
|
+
const fnResult = await params(query, data);
|
|
75
|
+
if (typeof fnResult === "boolean") {
|
|
76
|
+
query.match = data;
|
|
77
|
+
return fnResult;
|
|
78
|
+
}
|
|
79
|
+
for (const key in fnResult) {
|
|
80
|
+
const value = data[key];
|
|
81
|
+
if (value === void 0) return false;
|
|
82
|
+
let matchers = fnResult[key];
|
|
83
|
+
if (!Array.isArray(matchers)) matchers = [matchers];
|
|
84
|
+
for (const matcher of matchers) {
|
|
85
|
+
if (typeof matcher === "string") {
|
|
86
|
+
if (value !== matcher) return false;
|
|
87
|
+
} else if (!matcher.test(value)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
query.match = data;
|
|
93
|
+
return true;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const parts = [];
|
|
97
|
+
this._fields.forEach((field) => {
|
|
98
|
+
if (!(field in params)) {
|
|
99
|
+
parts.push(`[^${this.sep}]*?`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const value = params[field];
|
|
103
|
+
if (Array.isArray(value)) {
|
|
104
|
+
parts.push(`(${value.map((i) => typeof i === "string" ? i : i.source).join("|")})`);
|
|
105
|
+
} else {
|
|
106
|
+
parts.push(typeof value === "string" ? value : value.source);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
const regex2 = new RegExp(`^${this.prefix}${this.sep}${parts.join(this.sep)}$`);
|
|
110
|
+
return (query) => {
|
|
111
|
+
const m = query.dataStr?.match(regex2);
|
|
112
|
+
if (!m) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
query.match = this.parse(m[0]);
|
|
116
|
+
return true;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
class BusinessMessageContext extends BusinessMessage {
|
|
121
|
+
constructor(client, message) {
|
|
122
|
+
const msg = Array.isArray(message) ? message[message.length - 1] : message;
|
|
123
|
+
super(msg.update, msg._peers);
|
|
124
|
+
this.client = client;
|
|
125
|
+
this.messages = Array.isArray(message) ? message.map((it) => new BusinessMessageContext(client, it)) : [this];
|
|
126
|
+
this.isMessageGroup = Array.isArray(message);
|
|
127
|
+
}
|
|
128
|
+
// this is primarily for proper types in filters, so don't bother much with actual value
|
|
129
|
+
_name = "new_business_message";
|
|
130
|
+
/**
|
|
131
|
+
* List of messages in the message group.
|
|
132
|
+
*
|
|
133
|
+
* For other updates, this is a list with a single element (`this`).
|
|
134
|
+
*/
|
|
135
|
+
messages;
|
|
136
|
+
/** Whether this update is about a message group */
|
|
137
|
+
isMessageGroup;
|
|
138
|
+
/** Get all custom emojis contained in this message (message group), if any */
|
|
139
|
+
getCustomEmojis() {
|
|
140
|
+
return this.client.getCustomEmojisFromMessages(this.messages);
|
|
141
|
+
}
|
|
142
|
+
/** Send a text message to the same chat (and topic, if applicable) as a given message */
|
|
143
|
+
answerText(...params) {
|
|
144
|
+
const [send, params_ = {}] = params;
|
|
145
|
+
params_.businessConnectionId = this.update.connectionId;
|
|
146
|
+
return this.client.answerText(this, send, params_);
|
|
147
|
+
}
|
|
148
|
+
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
|
149
|
+
answerMedia(...params) {
|
|
150
|
+
const [send, params_ = {}] = params;
|
|
151
|
+
params_.businessConnectionId = this.update.connectionId;
|
|
152
|
+
return this.client.answerMedia(this, send, params_);
|
|
153
|
+
}
|
|
154
|
+
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
|
155
|
+
answerMediaGroup(...params) {
|
|
156
|
+
const [send, params_ = {}] = params;
|
|
157
|
+
params_.businessConnectionId = this.update.connectionId;
|
|
158
|
+
return this.client.answerMediaGroup(this, send, params_);
|
|
159
|
+
}
|
|
160
|
+
/** Send a text message in reply to this message */
|
|
161
|
+
replyText(...params) {
|
|
162
|
+
const [send, params_ = {}] = params;
|
|
163
|
+
params_.businessConnectionId = this.update.connectionId;
|
|
164
|
+
return this.client.replyText(this, send, params_);
|
|
165
|
+
}
|
|
166
|
+
/** Send a media in reply to this message */
|
|
167
|
+
replyMedia(...params) {
|
|
168
|
+
const [send, params_ = {}] = params;
|
|
169
|
+
params_.businessConnectionId = this.update.connectionId;
|
|
170
|
+
return this.client.replyMedia(this, send, params_);
|
|
171
|
+
}
|
|
172
|
+
/** Send a media group in reply to this message */
|
|
173
|
+
replyMediaGroup(...params) {
|
|
174
|
+
const [send, params_ = {}] = params;
|
|
175
|
+
params_.businessConnectionId = this.update.connectionId;
|
|
176
|
+
return this.client.replyMediaGroup(this, send, params_);
|
|
177
|
+
}
|
|
178
|
+
/** Send a text message in reply to this message */
|
|
179
|
+
quoteWithText(params) {
|
|
180
|
+
params.businessConnectionId = this.update.connectionId;
|
|
181
|
+
return this.client.quoteWithText(this, params);
|
|
182
|
+
}
|
|
183
|
+
/** Send a media in reply to this message */
|
|
184
|
+
quoteWithMedia(params) {
|
|
185
|
+
params.businessConnectionId = this.update.connectionId;
|
|
186
|
+
return this.client.quoteWithMedia(this, params);
|
|
187
|
+
}
|
|
188
|
+
/** Send a media group in reply to this message */
|
|
189
|
+
quoteWithMediaGroup(params) {
|
|
190
|
+
params.businessConnectionId = this.update.connectionId;
|
|
191
|
+
return this.client.quoteWithMediaGroup(this, params);
|
|
192
|
+
}
|
|
193
|
+
/** Delete this message (message group) */
|
|
194
|
+
delete(params) {
|
|
195
|
+
return this.client.deleteMessagesById(
|
|
196
|
+
this.chat.inputPeer,
|
|
197
|
+
this.messages.map((it) => it.id),
|
|
198
|
+
params
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
/** Pin this message */
|
|
202
|
+
pin(params) {
|
|
203
|
+
return this.client.pinMessage({
|
|
204
|
+
chatId: this.chat.inputPeer,
|
|
205
|
+
message: this.id,
|
|
206
|
+
...params
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/** Unpin this message */
|
|
210
|
+
unpin() {
|
|
211
|
+
return this.client.unpinMessage({
|
|
212
|
+
chatId: this.chat.inputPeer,
|
|
213
|
+
message: this.id
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/** Edit this message */
|
|
217
|
+
edit(params) {
|
|
218
|
+
return this.client.editMessage({
|
|
219
|
+
chatId: this.chat.inputPeer,
|
|
220
|
+
message: this.id,
|
|
221
|
+
...params
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
/** Forward this message (message group) */
|
|
225
|
+
forwardTo(params) {
|
|
226
|
+
return this.client.forwardMessagesById({
|
|
227
|
+
fromChatId: this.chat.inputPeer,
|
|
228
|
+
messages: this.messages.map((it) => it.id),
|
|
229
|
+
...params
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
/** Send a copy of this message (message group) */
|
|
233
|
+
copy(params) {
|
|
234
|
+
if (this.isMessageGroup) {
|
|
235
|
+
return this.client.sendCopyGroup({
|
|
236
|
+
messages: this.messages,
|
|
237
|
+
...params
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return this.client.sendCopy({
|
|
241
|
+
message: this,
|
|
242
|
+
...params
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/** React to this message */
|
|
246
|
+
react(params) {
|
|
247
|
+
return this.client.sendReaction({
|
|
248
|
+
chatId: this.chat.inputPeer,
|
|
249
|
+
message: this.id,
|
|
250
|
+
...params
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
class CallbackQueryContext extends CallbackQuery {
|
|
255
|
+
constructor(client, query) {
|
|
256
|
+
super(query.raw, query._peers);
|
|
257
|
+
this.client = client;
|
|
258
|
+
}
|
|
259
|
+
_name = "callback_query";
|
|
260
|
+
/** Answer to this callback query */
|
|
261
|
+
answer(params) {
|
|
262
|
+
return this.client.answerCallbackQuery(this.id, params);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Get the message containing the callback button being clicked.
|
|
266
|
+
*
|
|
267
|
+
* Note that the message may have been deleted, in which case
|
|
268
|
+
* `null` will be returned.
|
|
269
|
+
*/
|
|
270
|
+
async getMessage() {
|
|
271
|
+
return this.client.getCallbackQueryMessage(this);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Edit the message that contained the callback button that was clicked.
|
|
275
|
+
*/
|
|
276
|
+
async editMessage(params) {
|
|
277
|
+
return this.client.editMessage({
|
|
278
|
+
chatId: this.raw.peer,
|
|
279
|
+
message: this.raw.msgId,
|
|
280
|
+
...params
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Shortcut for getting the message and editing it.
|
|
285
|
+
*/
|
|
286
|
+
async editMessageWith(handler) {
|
|
287
|
+
const msg = await this.getMessage();
|
|
288
|
+
if (!msg) return;
|
|
289
|
+
const res = await handler(msg);
|
|
290
|
+
if (!res) return;
|
|
291
|
+
return this.editMessage(res);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
class InlineCallbackQueryContext extends InlineCallbackQuery {
|
|
295
|
+
constructor(client, query) {
|
|
296
|
+
super(query.raw, query._peers);
|
|
297
|
+
this.client = client;
|
|
298
|
+
}
|
|
299
|
+
_name = "inline_callback_query";
|
|
300
|
+
/** Answer to this callback query */
|
|
301
|
+
answer(params) {
|
|
302
|
+
return this.client.answerCallbackQuery(this.id, params);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Edit the message that contained the callback button that was clicked.
|
|
306
|
+
*/
|
|
307
|
+
async editMessage(params) {
|
|
308
|
+
return this.client.editInlineMessage({
|
|
309
|
+
messageId: this.raw.msgId,
|
|
310
|
+
...params
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
class BusinessCallbackQueryContext extends BusinessCallbackQuery {
|
|
315
|
+
constructor(client, query) {
|
|
316
|
+
super(query.raw, query._peers);
|
|
317
|
+
this.client = client;
|
|
318
|
+
}
|
|
319
|
+
_name = "business_callback_query";
|
|
320
|
+
/** Answer to this callback query */
|
|
321
|
+
answer(params) {
|
|
322
|
+
return this.client.answerCallbackQuery(this.id, params);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Edit the message that contained the callback button that was clicked.
|
|
326
|
+
*/
|
|
327
|
+
async editMessage(params) {
|
|
328
|
+
return this.client.editMessage({
|
|
329
|
+
message: this.message,
|
|
330
|
+
businessConnectionId: this.connectionId,
|
|
331
|
+
...params
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
class ChatJoinRequestUpdateContext extends BotChatJoinRequestUpdate {
|
|
336
|
+
constructor(client, update) {
|
|
337
|
+
super(update.raw, update._peers);
|
|
338
|
+
this.client = client;
|
|
339
|
+
}
|
|
340
|
+
_name = "bot_chat_join_request";
|
|
341
|
+
/** Approve the request */
|
|
342
|
+
approve() {
|
|
343
|
+
return this.client.hideJoinRequest({
|
|
344
|
+
action: "approve",
|
|
345
|
+
user: this.user.inputPeer,
|
|
346
|
+
chatId: this.chat.inputPeer
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
/** Decline the request */
|
|
350
|
+
decline() {
|
|
351
|
+
return this.client.hideJoinRequest({
|
|
352
|
+
action: "decline",
|
|
353
|
+
user: this.user.inputPeer,
|
|
354
|
+
chatId: this.chat.inputPeer
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
class ChosenInlineResultContext extends ChosenInlineResult {
|
|
359
|
+
constructor(client, result) {
|
|
360
|
+
super(result.raw, result._peers);
|
|
361
|
+
this.client = client;
|
|
362
|
+
}
|
|
363
|
+
_name = "chosen_inline_result";
|
|
364
|
+
/**
|
|
365
|
+
* Edit the message that was sent when this inline result that was chosen.
|
|
366
|
+
*
|
|
367
|
+
* > **Note**: This method can only be used if the message contained a reply markup
|
|
368
|
+
*/
|
|
369
|
+
async editMessage(params) {
|
|
370
|
+
if (!this.raw.msgId) {
|
|
371
|
+
throw new MtArgumentError("No message ID, make sure you have included reply markup!");
|
|
372
|
+
}
|
|
373
|
+
return this.client.editInlineMessage({
|
|
374
|
+
...params,
|
|
375
|
+
messageId: this.raw.msgId
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
class InlineQueryContext extends InlineQuery {
|
|
380
|
+
constructor(client, query) {
|
|
381
|
+
super(query.raw, query._peers);
|
|
382
|
+
this.client = client;
|
|
383
|
+
}
|
|
384
|
+
_name = "inline_query";
|
|
385
|
+
/** Answer to this inline query */
|
|
386
|
+
answer(...params) {
|
|
387
|
+
return this.client.answerInlineQuery(this.id, ...params);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
class MessageContext extends Message {
|
|
391
|
+
constructor(client, message) {
|
|
392
|
+
const msg = Array.isArray(message) ? message[message.length - 1] : message;
|
|
393
|
+
super(msg.raw, msg._peers, msg.isScheduled);
|
|
394
|
+
this.client = client;
|
|
395
|
+
this.messages = Array.isArray(message) ? message.map((it) => new MessageContext(client, it)) : [this];
|
|
396
|
+
this.isMessageGroup = Array.isArray(message);
|
|
397
|
+
}
|
|
398
|
+
// this is primarily for proper types in filters, so don't bother much with actual value
|
|
399
|
+
_name = "new_message";
|
|
400
|
+
/**
|
|
401
|
+
* List of messages in the message group.
|
|
402
|
+
*
|
|
403
|
+
* For other updates, this is a list with a single element (`this`).
|
|
404
|
+
*/
|
|
405
|
+
messages;
|
|
406
|
+
/** Whether this update is about a message group */
|
|
407
|
+
isMessageGroup;
|
|
408
|
+
/**
|
|
409
|
+
* Get complete information about {@link sender}
|
|
410
|
+
*
|
|
411
|
+
* Learn more: [Incomplete peers](https://mtcute.dev/guide/topics/peers.html#incomplete-peers)
|
|
412
|
+
*/
|
|
413
|
+
async getCompleteSender() {
|
|
414
|
+
if (!this.sender.isMin) return this.sender;
|
|
415
|
+
let res;
|
|
416
|
+
if (this.sender.type === "user") {
|
|
417
|
+
[res] = await this.client.getUsers(this.sender);
|
|
418
|
+
} else {
|
|
419
|
+
res = await this.client.getChat(this.sender);
|
|
420
|
+
}
|
|
421
|
+
if (!res) throw new MtPeerNotFoundError("Failed to fetch sender");
|
|
422
|
+
Object.defineProperty(this, "sender", { value: res });
|
|
423
|
+
return res;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Get complete information about {@link chat}
|
|
427
|
+
*
|
|
428
|
+
* Learn more: [Incomplete peers](https://mtcute.dev/guide/topics/peers.html#incomplete-peers)
|
|
429
|
+
*/
|
|
430
|
+
async getCompleteChat() {
|
|
431
|
+
if (!this.chat.isMin) return this.chat;
|
|
432
|
+
const res = await this.client.getChat(this.chat);
|
|
433
|
+
if (!res) throw new MtPeerNotFoundError("Failed to fetch chat");
|
|
434
|
+
Object.defineProperty(this, "chat", { value: res });
|
|
435
|
+
return res;
|
|
436
|
+
}
|
|
437
|
+
/** Get a message that this message is a reply to */
|
|
438
|
+
getReplyTo() {
|
|
439
|
+
return this.client.getReplyTo(this);
|
|
440
|
+
}
|
|
441
|
+
/** If this is a channel post, get its automatic forward in the discussion group */
|
|
442
|
+
getDiscussionMessage() {
|
|
443
|
+
return this.client.getDiscussionMessage({ chatId: this.chat.inputPeer, message: this.id });
|
|
444
|
+
}
|
|
445
|
+
/** Get all custom emojis contained in this message (message group), if any */
|
|
446
|
+
getCustomEmojis() {
|
|
447
|
+
return this.client.getCustomEmojisFromMessages(this.messages);
|
|
448
|
+
}
|
|
449
|
+
/** Send a text message to the same chat (and topic, if applicable) as a given message */
|
|
450
|
+
answerText(...params) {
|
|
451
|
+
return this.client.answerText(this, ...params);
|
|
452
|
+
}
|
|
453
|
+
/** Send a media to the same chat (and topic, if applicable) as a given message */
|
|
454
|
+
answerMedia(...params) {
|
|
455
|
+
return this.client.answerMedia(this, ...params);
|
|
456
|
+
}
|
|
457
|
+
/** Send a media group to the same chat (and topic, if applicable) as a given message */
|
|
458
|
+
answerMediaGroup(...params) {
|
|
459
|
+
return this.client.answerMediaGroup(this, ...params);
|
|
460
|
+
}
|
|
461
|
+
/** Send a text message in reply to this message */
|
|
462
|
+
replyText(...params) {
|
|
463
|
+
return this.client.replyText(this, ...params);
|
|
464
|
+
}
|
|
465
|
+
/** Send a media in reply to this message */
|
|
466
|
+
replyMedia(...params) {
|
|
467
|
+
return this.client.replyMedia(this, ...params);
|
|
468
|
+
}
|
|
469
|
+
/** Send a media group in reply to this message */
|
|
470
|
+
replyMediaGroup(...params) {
|
|
471
|
+
return this.client.replyMediaGroup(this, ...params);
|
|
472
|
+
}
|
|
473
|
+
/** Send a text message in reply to this message */
|
|
474
|
+
quoteWithText(params) {
|
|
475
|
+
return this.client.quoteWithText(this, params);
|
|
476
|
+
}
|
|
477
|
+
/** Send a media in reply to this message */
|
|
478
|
+
quoteWithMedia(params) {
|
|
479
|
+
return this.client.quoteWithMedia(this, params);
|
|
480
|
+
}
|
|
481
|
+
/** Send a media group in reply to this message */
|
|
482
|
+
quoteWithMediaGroup(params) {
|
|
483
|
+
return this.client.quoteWithMediaGroup(this, params);
|
|
484
|
+
}
|
|
485
|
+
/** Send a text as a comment to this message */
|
|
486
|
+
commentText(...params) {
|
|
487
|
+
return this.client.commentText(this, ...params);
|
|
488
|
+
}
|
|
489
|
+
/** Send a media as a comment to this message */
|
|
490
|
+
commentMedia(...params) {
|
|
491
|
+
return this.client.commentMedia(this, ...params);
|
|
492
|
+
}
|
|
493
|
+
/** Send a media group as a comment to this message */
|
|
494
|
+
commentMediaGroup(...params) {
|
|
495
|
+
return this.client.commentMediaGroup(this, ...params);
|
|
496
|
+
}
|
|
497
|
+
/** Delete this message (message group) */
|
|
498
|
+
delete(params) {
|
|
499
|
+
return this.client.deleteMessagesById(
|
|
500
|
+
this.chat.inputPeer,
|
|
501
|
+
this.messages.map((it) => it.id),
|
|
502
|
+
params
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
/** Pin this message */
|
|
506
|
+
pin(params) {
|
|
507
|
+
return this.client.pinMessage({
|
|
508
|
+
chatId: this.chat.inputPeer,
|
|
509
|
+
message: this.id,
|
|
510
|
+
...params
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
/** Unpin this message */
|
|
514
|
+
unpin() {
|
|
515
|
+
return this.client.unpinMessage({
|
|
516
|
+
chatId: this.chat.inputPeer,
|
|
517
|
+
message: this.id
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
/** Edit this message */
|
|
521
|
+
edit(params) {
|
|
522
|
+
return this.client.editMessage({
|
|
523
|
+
chatId: this.chat.inputPeer,
|
|
524
|
+
message: this.id,
|
|
525
|
+
...params
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
/** Forward this message (message group) */
|
|
529
|
+
forwardTo(params) {
|
|
530
|
+
return this.client.forwardMessagesById({
|
|
531
|
+
fromChatId: this.chat.inputPeer,
|
|
532
|
+
messages: this.messages.map((it) => it.id),
|
|
533
|
+
...params
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
/** Send a copy of this message (message group) */
|
|
537
|
+
copy(params) {
|
|
538
|
+
if (this.isMessageGroup) {
|
|
539
|
+
return this.client.sendCopyGroup({
|
|
540
|
+
messages: this.messages,
|
|
541
|
+
...params
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
return this.client.sendCopy({
|
|
545
|
+
message: this,
|
|
546
|
+
...params
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/** React to this message */
|
|
550
|
+
react(params) {
|
|
551
|
+
return this.client.sendReaction({
|
|
552
|
+
chatId: this.chat.inputPeer,
|
|
553
|
+
message: this.id,
|
|
554
|
+
...params
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
class PreCheckoutQueryContext extends PreCheckoutQuery {
|
|
559
|
+
constructor(client, query) {
|
|
560
|
+
super(query.raw, query._peers);
|
|
561
|
+
this.client = client;
|
|
562
|
+
}
|
|
563
|
+
_name = "pre_checkout_query";
|
|
564
|
+
/** Approve the query */
|
|
565
|
+
approve() {
|
|
566
|
+
return this.client.answerPreCheckoutQuery(this.raw.queryId);
|
|
567
|
+
}
|
|
568
|
+
/** Decline the query, optionally with an error */
|
|
569
|
+
decline(error = "") {
|
|
570
|
+
return this.client.answerPreCheckoutQuery(this.raw.queryId, { error });
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function _parsedUpdateToContext(client, update) {
|
|
574
|
+
switch (update.name) {
|
|
575
|
+
case "new_message":
|
|
576
|
+
case "edit_message":
|
|
577
|
+
case "message_group":
|
|
578
|
+
return new MessageContext(client, update.data);
|
|
579
|
+
case "inline_query":
|
|
580
|
+
return new InlineQueryContext(client, update.data);
|
|
581
|
+
case "chosen_inline_result":
|
|
582
|
+
return new ChosenInlineResultContext(client, update.data);
|
|
583
|
+
case "callback_query":
|
|
584
|
+
return new CallbackQueryContext(client, update.data);
|
|
585
|
+
case "inline_callback_query":
|
|
586
|
+
return new InlineCallbackQueryContext(client, update.data);
|
|
587
|
+
case "business_callback_query":
|
|
588
|
+
return new BusinessCallbackQueryContext(client, update.data);
|
|
589
|
+
case "bot_chat_join_request":
|
|
590
|
+
return new ChatJoinRequestUpdateContext(client, update.data);
|
|
591
|
+
case "pre_checkout_query":
|
|
592
|
+
return new PreCheckoutQueryContext(client, update.data);
|
|
593
|
+
case "new_business_message":
|
|
594
|
+
case "edit_business_message":
|
|
595
|
+
case "business_message_group":
|
|
596
|
+
return new BusinessMessageContext(client, update.data);
|
|
597
|
+
}
|
|
598
|
+
const _update = update.data;
|
|
599
|
+
_update.client = client;
|
|
600
|
+
_update._name = update.name;
|
|
601
|
+
return _update;
|
|
602
|
+
}
|
|
603
|
+
class SceneTransitionContext {
|
|
604
|
+
constructor(previousScene, update) {
|
|
605
|
+
this.previousScene = previousScene;
|
|
606
|
+
this.update = update;
|
|
607
|
+
}
|
|
608
|
+
/** Get {@link update}, asserting it is a message-related update */
|
|
609
|
+
get message() {
|
|
610
|
+
if (this.update instanceof MessageContext) {
|
|
611
|
+
return this.update;
|
|
612
|
+
}
|
|
613
|
+
throw new MtTypeAssertionError("SceneTransitionContext.message", "message", this.update._name);
|
|
614
|
+
}
|
|
615
|
+
/** Get {@link update}, asserting it is a business message-related update */
|
|
616
|
+
get businessMessage() {
|
|
617
|
+
if (this.update instanceof BusinessMessageContext) {
|
|
618
|
+
return this.update;
|
|
619
|
+
}
|
|
620
|
+
throw new MtTypeAssertionError("SceneTransitionContext.businessMessage", "business message", this.update._name);
|
|
621
|
+
}
|
|
622
|
+
/** Get {@link update}, asserting it is a callback query update */
|
|
623
|
+
get callbackQuery() {
|
|
624
|
+
if (this.update instanceof CallbackQueryContext) {
|
|
625
|
+
return this.update;
|
|
626
|
+
}
|
|
627
|
+
throw new MtTypeAssertionError("SceneTransitionContext.callbackQuery", "callback query", this.update._name);
|
|
628
|
+
}
|
|
629
|
+
/** Get {@link update}, asserting it is an inline callback query update */
|
|
630
|
+
get inlineCallbackQuery() {
|
|
631
|
+
if (this.update instanceof InlineCallbackQueryContext) {
|
|
632
|
+
return this.update;
|
|
633
|
+
}
|
|
634
|
+
throw new MtTypeAssertionError(
|
|
635
|
+
"SceneTransitionContext.inlineCallbackQuery",
|
|
636
|
+
"inline callback query",
|
|
637
|
+
this.update._name
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
makeInspectable(SceneTransitionContext);
|
|
642
|
+
const defaultStateKeyDelegate = (upd) => {
|
|
643
|
+
if ("type" in upd) {
|
|
644
|
+
return String(upd.id);
|
|
645
|
+
}
|
|
646
|
+
if (upd._name === "new_message" || upd._name === "new_business_message") {
|
|
647
|
+
switch (upd.chat.chatType) {
|
|
648
|
+
case "private":
|
|
649
|
+
case "bot":
|
|
650
|
+
case "channel":
|
|
651
|
+
return String(upd.chat.id);
|
|
652
|
+
case "group":
|
|
653
|
+
case "supergroup":
|
|
654
|
+
case "gigagroup":
|
|
655
|
+
return `${upd.chat.id}_${upd.sender.id}`;
|
|
656
|
+
default:
|
|
657
|
+
assertNever(upd.chat.chatType);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (upd._name === "callback_query") {
|
|
661
|
+
if (upd.chat.chatType === "private") return `${upd.user.id}`;
|
|
662
|
+
return `${upd.chat.id}_${upd.user.id}`;
|
|
663
|
+
}
|
|
664
|
+
return null;
|
|
665
|
+
};
|
|
666
|
+
class MemoryStateRepository {
|
|
667
|
+
constructor(_driver) {
|
|
668
|
+
this._driver = _driver;
|
|
669
|
+
this.state = this._driver.getState("dispatcher_fsm", () => /* @__PURE__ */ new Map());
|
|
670
|
+
this.rl = this._driver.getState("rl", () => /* @__PURE__ */ new Map());
|
|
671
|
+
}
|
|
672
|
+
state;
|
|
673
|
+
rl;
|
|
674
|
+
setState(key, state2, ttl) {
|
|
675
|
+
this.state.set(key, {
|
|
676
|
+
value: state2,
|
|
677
|
+
expiresAt: ttl ? Date.now() + ttl * 1e3 : void 0
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
getState(key, now) {
|
|
681
|
+
const state2 = this.state.get(key);
|
|
682
|
+
if (!state2) return null;
|
|
683
|
+
if (state2.expiresAt && state2.expiresAt < now) {
|
|
684
|
+
this.state.delete(key);
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
return state2.value;
|
|
688
|
+
}
|
|
689
|
+
deleteState(key) {
|
|
690
|
+
this.state.delete(key);
|
|
691
|
+
}
|
|
692
|
+
vacuum(now) {
|
|
693
|
+
for (const [key, state2] of this.state.entries()) {
|
|
694
|
+
if (state2.expiresAt && state2.expiresAt < now) {
|
|
695
|
+
this.state.delete(key);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
for (const [key, state2] of this.rl.entries()) {
|
|
699
|
+
if (state2.reset < now) {
|
|
700
|
+
this.rl.delete(key);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
getRateLimit(key, now, limit, window) {
|
|
705
|
+
const item = this.rl.get(key);
|
|
706
|
+
if (!item) {
|
|
707
|
+
const state2 = {
|
|
708
|
+
reset: now + window * 1e3,
|
|
709
|
+
remaining: limit
|
|
710
|
+
};
|
|
711
|
+
this.rl.set(key, state2);
|
|
712
|
+
return [state2.remaining, state2.reset];
|
|
713
|
+
}
|
|
714
|
+
if (item.reset < now) {
|
|
715
|
+
const state2 = {
|
|
716
|
+
reset: now + window * 1e3,
|
|
717
|
+
remaining: limit
|
|
718
|
+
};
|
|
719
|
+
this.rl.set(key, state2);
|
|
720
|
+
return [state2.remaining, state2.reset];
|
|
721
|
+
}
|
|
722
|
+
item.remaining = item.remaining > 0 ? item.remaining - 1 : 0;
|
|
723
|
+
return [item.remaining, item.reset];
|
|
724
|
+
}
|
|
725
|
+
resetRateLimit(key) {
|
|
726
|
+
this.rl.delete(key);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
class MemoryStateStorage {
|
|
730
|
+
constructor(driver = new MemoryStorageDriver()) {
|
|
731
|
+
this.driver = driver;
|
|
732
|
+
this.state = new MemoryStateRepository(this.driver);
|
|
733
|
+
}
|
|
734
|
+
state;
|
|
735
|
+
}
|
|
736
|
+
class SqliteStateRepository {
|
|
737
|
+
constructor(_driver) {
|
|
738
|
+
this._driver = _driver;
|
|
739
|
+
_driver.registerMigration("state", 1, (db) => {
|
|
740
|
+
db.exec(`
|
|
741
|
+
create table fsm_state (
|
|
742
|
+
key text primary key,
|
|
743
|
+
value text not null,
|
|
744
|
+
expires_at integer
|
|
745
|
+
);
|
|
746
|
+
create table rl_state (
|
|
747
|
+
key text primary key,
|
|
748
|
+
reset integer not null,
|
|
749
|
+
remaining integer not null
|
|
750
|
+
);
|
|
751
|
+
`);
|
|
752
|
+
});
|
|
753
|
+
_driver.onLoad(() => {
|
|
754
|
+
this._setState = _driver.db.prepare(
|
|
755
|
+
"insert or replace into fsm_state (key, value, expires_at) values (?, ?, ?)"
|
|
756
|
+
);
|
|
757
|
+
this._getState = _driver.db.prepare("select value, expires_at from fsm_state where key = ?");
|
|
758
|
+
this._deleteState = _driver.db.prepare("delete from fsm_state where key = ?");
|
|
759
|
+
this._deleteOldState = _driver.db.prepare("delete from fsm_state where expires_at < ?");
|
|
760
|
+
this._setRl = _driver.db.prepare("insert or replace into rl_state (key, reset, remaining) values (?, ?, ?)");
|
|
761
|
+
this._getRl = _driver.db.prepare("select reset, remaining from rl_state where key = ?");
|
|
762
|
+
this._deleteRl = _driver.db.prepare("delete from rl_state where key = ?");
|
|
763
|
+
this._deleteOldRl = _driver.db.prepare("delete from rl_state where reset < ?");
|
|
764
|
+
});
|
|
765
|
+
_driver.registerLegacyMigration("state", (db) => {
|
|
766
|
+
db.exec("drop table state");
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
_setState;
|
|
770
|
+
setState(key, state2, ttl) {
|
|
771
|
+
this._setState.run(key, state2, ttl ? Date.now() + ttl * 1e3 : void 0);
|
|
772
|
+
}
|
|
773
|
+
_getState;
|
|
774
|
+
getState(key, now) {
|
|
775
|
+
const res_ = this._getState.get(key);
|
|
776
|
+
if (!res_) return null;
|
|
777
|
+
const res = res_;
|
|
778
|
+
if (res.expires_at && res.expires_at < now) {
|
|
779
|
+
this._deleteState.run(key);
|
|
780
|
+
return null;
|
|
781
|
+
}
|
|
782
|
+
return res.value;
|
|
783
|
+
}
|
|
784
|
+
_deleteState;
|
|
785
|
+
deleteState(key) {
|
|
786
|
+
this._deleteState.run(key);
|
|
787
|
+
}
|
|
788
|
+
_deleteOldState;
|
|
789
|
+
_deleteOldRl;
|
|
790
|
+
vacuum(now) {
|
|
791
|
+
this._deleteOldState.run(now);
|
|
792
|
+
this._deleteOldRl.run(now);
|
|
793
|
+
}
|
|
794
|
+
_setRl;
|
|
795
|
+
_getRl;
|
|
796
|
+
_deleteRl;
|
|
797
|
+
getRateLimit(key, now, limit, window) {
|
|
798
|
+
const val = this._getRl.get(key);
|
|
799
|
+
if (!val || val.reset < now) {
|
|
800
|
+
const item = {
|
|
801
|
+
reset: now + window * 1e3,
|
|
802
|
+
remaining: limit
|
|
803
|
+
};
|
|
804
|
+
this._setRl.run(key, item.reset, item.remaining);
|
|
805
|
+
return [item.remaining, item.reset];
|
|
806
|
+
}
|
|
807
|
+
if (val.remaining > 0) {
|
|
808
|
+
val.remaining -= 1;
|
|
809
|
+
this._setRl.run(key, val.reset, val.remaining);
|
|
810
|
+
}
|
|
811
|
+
return [val.remaining, val.reset];
|
|
812
|
+
}
|
|
813
|
+
resetRateLimit(key) {
|
|
814
|
+
this._deleteRl.run(key);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
class SqliteStateStorage {
|
|
818
|
+
constructor(driver) {
|
|
819
|
+
this.driver = driver;
|
|
820
|
+
this.state = new SqliteStateRepository(driver);
|
|
821
|
+
}
|
|
822
|
+
state;
|
|
823
|
+
static from(provider) {
|
|
824
|
+
return new SqliteStateStorage(provider.driver);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
class RateLimitError extends MtcuteError {
|
|
828
|
+
constructor(reset) {
|
|
829
|
+
super("You are being rate limited.");
|
|
830
|
+
this.reset = reset;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
class UpdateState {
|
|
834
|
+
_key;
|
|
835
|
+
_localKey;
|
|
836
|
+
_storage;
|
|
837
|
+
_scene;
|
|
838
|
+
_scoped;
|
|
839
|
+
_cached;
|
|
840
|
+
_localStorage;
|
|
841
|
+
_localKeyBase;
|
|
842
|
+
constructor(storage, key, scene, scoped, customStorage, customKey) {
|
|
843
|
+
this._storage = storage;
|
|
844
|
+
this._key = key;
|
|
845
|
+
this._scene = scene;
|
|
846
|
+
this._scoped = scoped;
|
|
847
|
+
this._localStorage = customStorage ?? storage;
|
|
848
|
+
this._localKeyBase = customKey ?? key;
|
|
849
|
+
this._updateLocalKey();
|
|
850
|
+
}
|
|
851
|
+
/** Name of the current scene */
|
|
852
|
+
get scene() {
|
|
853
|
+
return this._scene;
|
|
854
|
+
}
|
|
855
|
+
_updateLocalKey() {
|
|
856
|
+
if (!this._scoped) {
|
|
857
|
+
this._localKey = this._localKeyBase;
|
|
858
|
+
} else {
|
|
859
|
+
this._localKey = this._scene ? `${this._scene}_${this._localKeyBase}` : this._localKeyBase;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
async get(fallback, force) {
|
|
863
|
+
if (typeof fallback === "boolean") {
|
|
864
|
+
force = fallback;
|
|
865
|
+
fallback = void 0;
|
|
866
|
+
}
|
|
867
|
+
if (!force && this._cached !== void 0) {
|
|
868
|
+
if (!this._cached && fallback) {
|
|
869
|
+
return typeof fallback === "function" ? fallback() : fallback;
|
|
870
|
+
}
|
|
871
|
+
return this._cached;
|
|
872
|
+
}
|
|
873
|
+
let res = await this._localStorage.getState(this._localKey);
|
|
874
|
+
if (!res && fallback) {
|
|
875
|
+
res = typeof fallback === "function" ? fallback() : fallback;
|
|
876
|
+
}
|
|
877
|
+
this._cached = res;
|
|
878
|
+
return res;
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Set new state to the storage
|
|
882
|
+
*
|
|
883
|
+
* @param state New state
|
|
884
|
+
* @param ttl TTL for the new state (in seconds)
|
|
885
|
+
*/
|
|
886
|
+
async set(state2, ttl) {
|
|
887
|
+
this._cached = state2;
|
|
888
|
+
await this._localStorage.setState(this._localKey, state2, ttl);
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Merge the given object to the current state.
|
|
892
|
+
*
|
|
893
|
+
* > **Note**: If the storage currently has no state,
|
|
894
|
+
* > then `fallback` must be provided.
|
|
895
|
+
*
|
|
896
|
+
* Basically a shorthand to calling `.get()`,
|
|
897
|
+
* modifying and then calling `.set()`
|
|
898
|
+
*
|
|
899
|
+
* @param state State to be merged
|
|
900
|
+
* @param fallback Default state
|
|
901
|
+
* @param ttl TTL for the new state (in seconds)
|
|
902
|
+
* @param forceLoad Whether to force load the old state from storage
|
|
903
|
+
*/
|
|
904
|
+
async merge(state2, params = {}) {
|
|
905
|
+
const { fallback, ttl, forceLoad } = params;
|
|
906
|
+
const old = await this.get(forceLoad);
|
|
907
|
+
if (!old) {
|
|
908
|
+
if (!fallback) {
|
|
909
|
+
throw new MtArgumentError("Cannot use merge on empty state without fallback.");
|
|
910
|
+
}
|
|
911
|
+
const fallback_ = typeof fallback === "function" ? fallback() : fallback;
|
|
912
|
+
await this.set({ ...fallback_, ...state2 }, ttl);
|
|
913
|
+
} else {
|
|
914
|
+
await this.set({ ...old, ...state2 }, ttl);
|
|
915
|
+
}
|
|
916
|
+
return this._cached;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Delete the state from the storage
|
|
920
|
+
*/
|
|
921
|
+
async delete() {
|
|
922
|
+
this._cached = null;
|
|
923
|
+
await this._localStorage.deleteState(this._localKey);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Enter some scene
|
|
927
|
+
*/
|
|
928
|
+
async enter(scene, params) {
|
|
929
|
+
const { with: with_, ttl, reset = true } = params ?? {};
|
|
930
|
+
if (reset && this._scoped) await this.delete();
|
|
931
|
+
if (!scene["_scene"]) {
|
|
932
|
+
throw new MtArgumentError("Cannot enter a non-scene Dispatcher");
|
|
933
|
+
}
|
|
934
|
+
if (!scene["_parent"]) {
|
|
935
|
+
throw new MtArgumentError("This scene has not been registered");
|
|
936
|
+
}
|
|
937
|
+
this._scene = scene["_scene"];
|
|
938
|
+
this._scoped = scene["_sceneScoped"];
|
|
939
|
+
this._updateLocalKey();
|
|
940
|
+
await this._storage.setCurrentScene(this._key, this._scene, ttl);
|
|
941
|
+
if (with_) {
|
|
942
|
+
if (scene["_customStateKeyDelegate"]) {
|
|
943
|
+
throw new MtArgumentError("Cannot use `with` parameter when the scene uses a custom state key delegate");
|
|
944
|
+
}
|
|
945
|
+
await scene.getState(this._key).set(with_, ttl);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Exit from current scene to the root
|
|
950
|
+
*
|
|
951
|
+
* @param reset
|
|
952
|
+
* Whether to reset scene state (only applicable in case this is a scoped scene)
|
|
953
|
+
*/
|
|
954
|
+
async exit(reset = true) {
|
|
955
|
+
if (reset && this._scoped) await this.delete();
|
|
956
|
+
this._scene = null;
|
|
957
|
+
this._updateLocalKey();
|
|
958
|
+
await this._storage.deleteCurrentScene(this._key);
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Rate limit some handler.
|
|
962
|
+
*
|
|
963
|
+
* When the rate limit exceeds, {@link RateLimitError} is thrown.
|
|
964
|
+
*
|
|
965
|
+
* This is a simple rate-limiting solution that uses
|
|
966
|
+
* the same key as the state. If you need something more
|
|
967
|
+
* sophisticated and/or customizable, you'll have to implement
|
|
968
|
+
* your own rate-limiter.
|
|
969
|
+
*
|
|
970
|
+
* > **Note**: `key` is used to prefix the local key
|
|
971
|
+
* > derived using the given key delegate.
|
|
972
|
+
*
|
|
973
|
+
* @param key Key of the rate limit
|
|
974
|
+
* @param limit Maximum number of requests in `window`
|
|
975
|
+
* @param window Window size in seconds
|
|
976
|
+
* @returns Tuple containing the number of remaining and
|
|
977
|
+
* unix time in ms when the user can try again
|
|
978
|
+
*/
|
|
979
|
+
async rateLimit(key, limit, window) {
|
|
980
|
+
const [remaining, reset] = await this._localStorage.getRateLimit(`${key}:${this._localKey}`, limit, window);
|
|
981
|
+
if (!remaining) {
|
|
982
|
+
throw new RateLimitError(reset);
|
|
983
|
+
}
|
|
984
|
+
return [remaining - 1, reset];
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* Throttle some handler.
|
|
988
|
+
*
|
|
989
|
+
* When the rate limit exceeds, this function waits for it to reset.
|
|
990
|
+
*
|
|
991
|
+
* This is a simple wrapper over {@link rateLimit}, and follows the same logic.
|
|
992
|
+
*
|
|
993
|
+
* > **Note**: `key` is used to prefix the local key
|
|
994
|
+
* > derived using the given key delegate.
|
|
995
|
+
*
|
|
996
|
+
* @param key Key of the rate limit
|
|
997
|
+
* @param limit Maximum number of requests in `window`
|
|
998
|
+
* @param window Window size in seconds
|
|
999
|
+
* @returns Tuple containing the number of remaining and
|
|
1000
|
+
* unix time in ms when the user can try again
|
|
1001
|
+
*/
|
|
1002
|
+
async throttle(key, limit, window) {
|
|
1003
|
+
try {
|
|
1004
|
+
return await this.rateLimit(key, limit, window);
|
|
1005
|
+
} catch (e) {
|
|
1006
|
+
if (e instanceof RateLimitError) {
|
|
1007
|
+
await sleep(e.reset - Date.now());
|
|
1008
|
+
return this.throttle(key, limit, window);
|
|
1009
|
+
}
|
|
1010
|
+
throw e;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Reset the rate limit
|
|
1015
|
+
*
|
|
1016
|
+
* @param key Key of the rate limit
|
|
1017
|
+
*/
|
|
1018
|
+
async resetRateLimit(key) {
|
|
1019
|
+
await this._localStorage.resetRateLimit(`${key}:${this._localKey}`);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
const makeCurrentSceneKey = (key) => `$current_scene_${key}`;
|
|
1023
|
+
class StateService {
|
|
1024
|
+
constructor(provider) {
|
|
1025
|
+
this.provider = provider;
|
|
1026
|
+
}
|
|
1027
|
+
_cache = new LruMap(100);
|
|
1028
|
+
_vacuumTimer;
|
|
1029
|
+
_loaded = false;
|
|
1030
|
+
_load = asyncResettable(async () => {
|
|
1031
|
+
await this.provider.driver.load?.();
|
|
1032
|
+
this._loaded = true;
|
|
1033
|
+
});
|
|
1034
|
+
async load() {
|
|
1035
|
+
await this._load.run();
|
|
1036
|
+
this._vacuumTimer = timers.setInterval(() => {
|
|
1037
|
+
Promise.resolve(this.provider.state.vacuum(Date.now())).catch(() => {
|
|
1038
|
+
});
|
|
1039
|
+
}, 3e5);
|
|
1040
|
+
}
|
|
1041
|
+
async destroy() {
|
|
1042
|
+
await this.provider.driver.save?.();
|
|
1043
|
+
await this.provider.driver.destroy?.();
|
|
1044
|
+
timers.clearInterval(this._vacuumTimer);
|
|
1045
|
+
this._loaded = false;
|
|
1046
|
+
}
|
|
1047
|
+
async getState(key) {
|
|
1048
|
+
if (!this._loaded) await this.load();
|
|
1049
|
+
const cached = this._cache.get(key);
|
|
1050
|
+
if (cached) return cached;
|
|
1051
|
+
const state2 = await this.provider.state.getState(key, Date.now());
|
|
1052
|
+
if (!state2) return null;
|
|
1053
|
+
return JSON.parse(state2);
|
|
1054
|
+
}
|
|
1055
|
+
async setState(key, state2, ttl) {
|
|
1056
|
+
if (!this._loaded) await this.load();
|
|
1057
|
+
this._cache.set(key, state2);
|
|
1058
|
+
await this.provider.state.setState(key, JSON.stringify(state2), ttl);
|
|
1059
|
+
}
|
|
1060
|
+
async deleteState(key) {
|
|
1061
|
+
if (!this._loaded) await this.load();
|
|
1062
|
+
this._cache.delete(key);
|
|
1063
|
+
await this.provider.state.deleteState(key);
|
|
1064
|
+
}
|
|
1065
|
+
getCurrentScene(key) {
|
|
1066
|
+
return this.getState(makeCurrentSceneKey(key));
|
|
1067
|
+
}
|
|
1068
|
+
setCurrentScene(key, scene, ttl) {
|
|
1069
|
+
return this.setState(makeCurrentSceneKey(key), scene, ttl);
|
|
1070
|
+
}
|
|
1071
|
+
deleteCurrentScene(key) {
|
|
1072
|
+
return this.deleteState(makeCurrentSceneKey(key));
|
|
1073
|
+
}
|
|
1074
|
+
getRateLimit(key, limit, window) {
|
|
1075
|
+
return this.provider.state.getRateLimit(key, Date.now(), limit, window);
|
|
1076
|
+
}
|
|
1077
|
+
resetRateLimit(key) {
|
|
1078
|
+
return this.provider.state.resetRateLimit(key);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
class Dispatcher {
|
|
1082
|
+
_groups = /* @__PURE__ */ new Map();
|
|
1083
|
+
_groupsOrder = [];
|
|
1084
|
+
_client;
|
|
1085
|
+
_parent;
|
|
1086
|
+
_children = [];
|
|
1087
|
+
_scenes;
|
|
1088
|
+
_scene;
|
|
1089
|
+
_sceneScoped;
|
|
1090
|
+
_storage;
|
|
1091
|
+
_stateKeyDelegate;
|
|
1092
|
+
_customStateKeyDelegate;
|
|
1093
|
+
_customStorage;
|
|
1094
|
+
_deps = {};
|
|
1095
|
+
_errorHandler;
|
|
1096
|
+
_preUpdateHandler;
|
|
1097
|
+
_postUpdateHandler;
|
|
1098
|
+
_sceneTransitionHandler;
|
|
1099
|
+
constructor(client, params) {
|
|
1100
|
+
this.dispatchRawUpdate = this.dispatchRawUpdate.bind(this);
|
|
1101
|
+
this.dispatchUpdate = this.dispatchUpdate.bind(this);
|
|
1102
|
+
let { storage, key, sceneName } = params ?? {};
|
|
1103
|
+
if (client) {
|
|
1104
|
+
this.bindToClient(client);
|
|
1105
|
+
if (storage) {
|
|
1106
|
+
this._storage = new StateService(storage);
|
|
1107
|
+
this._stateKeyDelegate = key ?? defaultStateKeyDelegate;
|
|
1108
|
+
}
|
|
1109
|
+
} else {
|
|
1110
|
+
if (storage) {
|
|
1111
|
+
this._customStorage = new StateService(storage);
|
|
1112
|
+
}
|
|
1113
|
+
if (key) {
|
|
1114
|
+
this._customStateKeyDelegate = key;
|
|
1115
|
+
}
|
|
1116
|
+
if (sceneName) {
|
|
1117
|
+
if (sceneName[0] === "$") {
|
|
1118
|
+
throw new MtArgumentError("Scene name cannot start with $");
|
|
1119
|
+
}
|
|
1120
|
+
this._scene = sceneName;
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
static for(client, params) {
|
|
1125
|
+
return new Dispatcher(client, params);
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Create a new child dispatcher.
|
|
1129
|
+
*/
|
|
1130
|
+
static child(params) {
|
|
1131
|
+
return new Dispatcher(void 0, params);
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Create a new scene dispatcher
|
|
1135
|
+
*/
|
|
1136
|
+
static scene(name, params) {
|
|
1137
|
+
return new Dispatcher(void 0, { sceneName: name, ...params });
|
|
1138
|
+
}
|
|
1139
|
+
/** For scene dispatchers, name of the scene */
|
|
1140
|
+
get sceneName() {
|
|
1141
|
+
return this._scene;
|
|
1142
|
+
}
|
|
1143
|
+
inject(name, value) {
|
|
1144
|
+
if (this._parent) {
|
|
1145
|
+
throw new MtArgumentError("Cannot inject dependencies to child dispatchers");
|
|
1146
|
+
}
|
|
1147
|
+
if (typeof name === "object") {
|
|
1148
|
+
for (const [k, v] of Object.entries(name)) {
|
|
1149
|
+
this._deps[k] = v;
|
|
1150
|
+
}
|
|
1151
|
+
} else {
|
|
1152
|
+
this._deps[name] = value;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
/**
|
|
1156
|
+
* Get the dependencies injected into this dispatcher.
|
|
1157
|
+
*/
|
|
1158
|
+
get deps() {
|
|
1159
|
+
return this._deps;
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* Bind the dispatcher to the client.
|
|
1163
|
+
* Called by the constructor automatically if
|
|
1164
|
+
* `client` was passed.
|
|
1165
|
+
*
|
|
1166
|
+
* Dispatcher also uses bound client to throw errors
|
|
1167
|
+
*/
|
|
1168
|
+
bindToClient(client) {
|
|
1169
|
+
client.on("update", this.dispatchUpdate);
|
|
1170
|
+
client.on("raw_update", this.dispatchRawUpdate);
|
|
1171
|
+
this._client = client;
|
|
1172
|
+
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Unbind a dispatcher from the client.
|
|
1175
|
+
*/
|
|
1176
|
+
unbind() {
|
|
1177
|
+
if (this._client) {
|
|
1178
|
+
this._client.off("update", this.dispatchUpdate);
|
|
1179
|
+
this._client.off("raw_update", this.dispatchRawUpdate);
|
|
1180
|
+
this._client = void 0;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Destroy the dispatcher and all its children.
|
|
1185
|
+
*
|
|
1186
|
+
* When destroying, all the registered handlers are removed,
|
|
1187
|
+
* and the underlying storage is freed.
|
|
1188
|
+
*/
|
|
1189
|
+
async destroy() {
|
|
1190
|
+
if (this._parent && this._customStorage) {
|
|
1191
|
+
await this._customStorage.destroy();
|
|
1192
|
+
} else if (!this._parent && this._storage) {
|
|
1193
|
+
await this._storage.destroy();
|
|
1194
|
+
}
|
|
1195
|
+
this.removeUpdateHandler("all");
|
|
1196
|
+
for (const child of this._children) {
|
|
1197
|
+
await child.destroy();
|
|
1198
|
+
}
|
|
1199
|
+
for (const scene of this._scenes?.values() ?? []) {
|
|
1200
|
+
await scene.destroy();
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Process a raw update with this dispatcher.
|
|
1205
|
+
* Calling this method without bound client will not work.
|
|
1206
|
+
*
|
|
1207
|
+
* Under the hood asynchronously calls {@link dispatchRawUpdateNow}
|
|
1208
|
+
* with error handler set to client's one.
|
|
1209
|
+
*
|
|
1210
|
+
* @param update Update to process
|
|
1211
|
+
* @param peers Peers index
|
|
1212
|
+
*/
|
|
1213
|
+
dispatchRawUpdate(update, peers) {
|
|
1214
|
+
if (!this._client) return;
|
|
1215
|
+
this.dispatchRawUpdateNow(update, peers).catch((err) => this._client.emitError(err));
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Process a raw update right now in the current stack.
|
|
1219
|
+
*
|
|
1220
|
+
* Unlike {@link dispatchRawUpdate}, this does not schedule
|
|
1221
|
+
* the update to be dispatched, but dispatches it immediately,
|
|
1222
|
+
* and after `await`ing this method you can be certain that the update
|
|
1223
|
+
* was fully processed by all the registered handlers, including children.
|
|
1224
|
+
*
|
|
1225
|
+
* @param update Update to process
|
|
1226
|
+
* @param peers Peers map
|
|
1227
|
+
* @returns Whether the update was handled
|
|
1228
|
+
*/
|
|
1229
|
+
async dispatchRawUpdateNow(update, peers) {
|
|
1230
|
+
if (!this._client) return false;
|
|
1231
|
+
let handled = false;
|
|
1232
|
+
outer: for (const grp of this._groupsOrder) {
|
|
1233
|
+
const group = this._groups.get(grp);
|
|
1234
|
+
if (group.has("raw")) {
|
|
1235
|
+
const handlers = group.get("raw");
|
|
1236
|
+
for (const h of handlers) {
|
|
1237
|
+
let result;
|
|
1238
|
+
if (!h.check || await h.check(this._client, update, peers)) {
|
|
1239
|
+
result = await h.callback(this._client, update, peers);
|
|
1240
|
+
handled = true;
|
|
1241
|
+
} else {
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
switch (result) {
|
|
1245
|
+
case "continue":
|
|
1246
|
+
continue;
|
|
1247
|
+
case "stop":
|
|
1248
|
+
break outer;
|
|
1249
|
+
case "stop-children":
|
|
1250
|
+
return handled;
|
|
1251
|
+
}
|
|
1252
|
+
break;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
for (const child of this._children) {
|
|
1257
|
+
const childHandled = await child.dispatchRawUpdateNow(update, peers);
|
|
1258
|
+
handled ||= childHandled;
|
|
1259
|
+
}
|
|
1260
|
+
return handled;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Process an update with this dispatcher.
|
|
1264
|
+
* Calling this method without bound client will not work.
|
|
1265
|
+
*
|
|
1266
|
+
* Under the hood asynchronously calls {@link dispatchUpdateNow}
|
|
1267
|
+
* with error handler set to client's one.
|
|
1268
|
+
*
|
|
1269
|
+
* @param update Update to process
|
|
1270
|
+
*/
|
|
1271
|
+
dispatchUpdate(update) {
|
|
1272
|
+
if (!this._client) return;
|
|
1273
|
+
this.dispatchUpdateNow(update).catch((err) => this._client.emitError(err));
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Process an update right now in the current stack.
|
|
1277
|
+
*
|
|
1278
|
+
* Unlike {@link dispatchUpdate}, this does not schedule
|
|
1279
|
+
* the update to be dispatched, but dispatches it immediately,
|
|
1280
|
+
* and after `await`ing this method you can be certain that the update
|
|
1281
|
+
* was fully processed by all the registered handlers, including children.
|
|
1282
|
+
*
|
|
1283
|
+
* @param update Update to process
|
|
1284
|
+
* @returns Whether the update was handled
|
|
1285
|
+
*/
|
|
1286
|
+
async dispatchUpdateNow(update) {
|
|
1287
|
+
return this._dispatchUpdateNowImpl(update);
|
|
1288
|
+
}
|
|
1289
|
+
async _dispatchUpdateNowImpl(update, parsedState, parsedScene, forceScene, parsedContext) {
|
|
1290
|
+
if (!this._client) return false;
|
|
1291
|
+
if (parsedScene === void 0) {
|
|
1292
|
+
if (this._storage && this._scenes && (update.name === "new_message" || update.name === "edit_message" || update.name === "callback_query" || update.name === "message_group" || update.name === "new_business_message" || update.name === "edit_business_message" || update.name === "business_message_group")) {
|
|
1293
|
+
if (!parsedContext) parsedContext = _parsedUpdateToContext(this._client, update);
|
|
1294
|
+
const key = await this._stateKeyDelegate(parsedContext);
|
|
1295
|
+
if (key) {
|
|
1296
|
+
parsedScene = await this._storage.getCurrentScene(key);
|
|
1297
|
+
} else {
|
|
1298
|
+
parsedScene = null;
|
|
1299
|
+
}
|
|
1300
|
+
} else {
|
|
1301
|
+
parsedScene = null;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
if (!forceScene && parsedScene !== null) {
|
|
1305
|
+
if (this._scene) {
|
|
1306
|
+
if (this._scene !== parsedScene) {
|
|
1307
|
+
return false;
|
|
1308
|
+
}
|
|
1309
|
+
} else {
|
|
1310
|
+
if (!this._scenes || !this._scenes.has(parsedScene)) {
|
|
1311
|
+
return false;
|
|
1312
|
+
}
|
|
1313
|
+
return this._scenes.get(parsedScene)._dispatchUpdateNowImpl(update, parsedState, parsedScene, true);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
if (parsedState === void 0) {
|
|
1317
|
+
if (this._storage && (update.name === "new_message" || update.name === "edit_message" || update.name === "callback_query" || update.name === "message_group" || update.name === "new_business_message" || update.name === "edit_business_message" || update.name === "business_message_group")) {
|
|
1318
|
+
if (!parsedContext) parsedContext = _parsedUpdateToContext(this._client, update);
|
|
1319
|
+
const key = await this._stateKeyDelegate(parsedContext);
|
|
1320
|
+
if (key) {
|
|
1321
|
+
let customKey;
|
|
1322
|
+
if (!this._customStateKeyDelegate || (customKey = await this._customStateKeyDelegate(parsedContext))) {
|
|
1323
|
+
parsedState = new UpdateState(
|
|
1324
|
+
this._storage,
|
|
1325
|
+
key,
|
|
1326
|
+
this._scene ?? null,
|
|
1327
|
+
this._sceneScoped,
|
|
1328
|
+
this._customStorage,
|
|
1329
|
+
customKey
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
} else {
|
|
1333
|
+
parsedState = null;
|
|
1334
|
+
}
|
|
1335
|
+
} else {
|
|
1336
|
+
parsedState = null;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
let shouldDispatch = true;
|
|
1340
|
+
let shouldDispatchChildren = true;
|
|
1341
|
+
let handled = false;
|
|
1342
|
+
switch (await this._preUpdateHandler?.(update, parsedState)) {
|
|
1343
|
+
case "stop":
|
|
1344
|
+
shouldDispatch = false;
|
|
1345
|
+
break;
|
|
1346
|
+
case "stop-children":
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1349
|
+
if (shouldDispatch) {
|
|
1350
|
+
outer: for (const grp of this._groupsOrder) {
|
|
1351
|
+
const group = this._groups.get(grp);
|
|
1352
|
+
if (group.has(update.name)) {
|
|
1353
|
+
const handlers = group.get(update.name);
|
|
1354
|
+
try {
|
|
1355
|
+
for (const h of handlers) {
|
|
1356
|
+
let result;
|
|
1357
|
+
if (!parsedContext) parsedContext = _parsedUpdateToContext(this._client, update);
|
|
1358
|
+
if (!h.check || await h.check(parsedContext, parsedState)) {
|
|
1359
|
+
result = await h.callback(parsedContext, parsedState);
|
|
1360
|
+
handled = true;
|
|
1361
|
+
} else {
|
|
1362
|
+
continue;
|
|
1363
|
+
}
|
|
1364
|
+
if (parsedState && this._scenes) {
|
|
1365
|
+
const newScene = parsedState.scene;
|
|
1366
|
+
if (parsedScene !== newScene) {
|
|
1367
|
+
const nextDp = newScene ? this._scenes.get(newScene) : this._parent;
|
|
1368
|
+
if (!nextDp) {
|
|
1369
|
+
throw new MtArgumentError(`Scene ${newScene} not found`);
|
|
1370
|
+
}
|
|
1371
|
+
if (nextDp._sceneTransitionHandler) {
|
|
1372
|
+
const transition = new SceneTransitionContext(parsedScene, parsedContext);
|
|
1373
|
+
const transitionResult = await nextDp._sceneTransitionHandler?.(
|
|
1374
|
+
transition,
|
|
1375
|
+
parsedState
|
|
1376
|
+
);
|
|
1377
|
+
switch (transitionResult) {
|
|
1378
|
+
case "stop":
|
|
1379
|
+
return true;
|
|
1380
|
+
case "continue":
|
|
1381
|
+
continue;
|
|
1382
|
+
case "scene": {
|
|
1383
|
+
const scene = parsedState.scene;
|
|
1384
|
+
const dp = scene ? nextDp._scenes.get(scene) : nextDp._parent;
|
|
1385
|
+
return dp._dispatchUpdateNowImpl(update, void 0, scene, true);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
switch (result) {
|
|
1392
|
+
case "continue":
|
|
1393
|
+
continue;
|
|
1394
|
+
case "stop":
|
|
1395
|
+
break outer;
|
|
1396
|
+
case "stop-children":
|
|
1397
|
+
shouldDispatchChildren = false;
|
|
1398
|
+
break outer;
|
|
1399
|
+
case "scene": {
|
|
1400
|
+
if (!parsedState) {
|
|
1401
|
+
throw new MtArgumentError("Cannot use ToScene without state");
|
|
1402
|
+
}
|
|
1403
|
+
const scene = parsedState.scene;
|
|
1404
|
+
const dp = scene ? this._scenes.get(scene) : this._parent;
|
|
1405
|
+
return dp._dispatchUpdateNowImpl(update, void 0, scene, true);
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
break;
|
|
1409
|
+
}
|
|
1410
|
+
} catch (e) {
|
|
1411
|
+
if (this._errorHandler) {
|
|
1412
|
+
const handled2 = await this._errorHandler(e, update, parsedState);
|
|
1413
|
+
if (!handled2) throw e;
|
|
1414
|
+
} else {
|
|
1415
|
+
throw e;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (shouldDispatchChildren) {
|
|
1422
|
+
for (const child of this._children) {
|
|
1423
|
+
const childHandled = await child._dispatchUpdateNowImpl(update);
|
|
1424
|
+
handled ||= childHandled;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
await this._postUpdateHandler?.(handled, update, parsedState);
|
|
1428
|
+
return handled;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Add an update handler to a given handlers group
|
|
1432
|
+
*
|
|
1433
|
+
* @param handler Update handler
|
|
1434
|
+
* @param group Handler group index
|
|
1435
|
+
*/
|
|
1436
|
+
addUpdateHandler(handler, group = 0) {
|
|
1437
|
+
if (!this._groups.has(group)) {
|
|
1438
|
+
this._groups.set(group, /* @__PURE__ */ new Map());
|
|
1439
|
+
this._groupsOrder.push(group);
|
|
1440
|
+
this._groupsOrder.sort((a, b) => a - b);
|
|
1441
|
+
}
|
|
1442
|
+
if (!this._groups.get(group).has(handler.name)) {
|
|
1443
|
+
this._groups.get(group).set(handler.name, []);
|
|
1444
|
+
}
|
|
1445
|
+
this._groups.get(group).get(handler.name).push(handler);
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Remove an update handler (or handlers) from a given
|
|
1449
|
+
* handler group.
|
|
1450
|
+
*
|
|
1451
|
+
* @param handler Update handler to remove, its name or `'all'` to remove all
|
|
1452
|
+
* @param group Handler group index (null to affect all groups)
|
|
1453
|
+
*/
|
|
1454
|
+
removeUpdateHandler(handler, group = 0) {
|
|
1455
|
+
if (group !== null && !this._groups.has(group)) {
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
if (typeof handler === "string") {
|
|
1459
|
+
if (handler === "all") {
|
|
1460
|
+
if (group === null) {
|
|
1461
|
+
this._groups = /* @__PURE__ */ new Map();
|
|
1462
|
+
} else {
|
|
1463
|
+
this._groups.delete(group);
|
|
1464
|
+
}
|
|
1465
|
+
} else if (group !== null) {
|
|
1466
|
+
this._groups.get(group).delete(handler);
|
|
1467
|
+
}
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
if (group === null) return;
|
|
1471
|
+
if (!this._groups.get(group).has(handler.name)) {
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
const idx = this._groups.get(group).get(handler.name).indexOf(handler);
|
|
1475
|
+
if (idx > -1) {
|
|
1476
|
+
this._groups.get(group).get(handler.name).splice(idx, 1);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Register an error handler.
|
|
1481
|
+
*
|
|
1482
|
+
* This is used locally within this dispatcher
|
|
1483
|
+
* (does not affect children/parent) whenever
|
|
1484
|
+
* an error is thrown inside an update handler.
|
|
1485
|
+
* Not used for raw update handlers
|
|
1486
|
+
*
|
|
1487
|
+
* When an error is thrown, but there is no error
|
|
1488
|
+
* handler, it is propagated to `TelegramClient`.
|
|
1489
|
+
*
|
|
1490
|
+
* There can be at most one error handler.
|
|
1491
|
+
* Pass `null` to remove it.
|
|
1492
|
+
*
|
|
1493
|
+
* @param handler Error handler
|
|
1494
|
+
*/
|
|
1495
|
+
onError(handler) {
|
|
1496
|
+
if (handler) this._errorHandler = handler;
|
|
1497
|
+
else this._errorHandler = void 0;
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Register pre-update middleware.
|
|
1501
|
+
*
|
|
1502
|
+
* This is used locally within this dispatcher
|
|
1503
|
+
* (does not affect children/parent) before processing
|
|
1504
|
+
* an update, and can be used to skip this update.
|
|
1505
|
+
*
|
|
1506
|
+
* There can be at most one pre-update middleware.
|
|
1507
|
+
* Pass `null` to remove it.
|
|
1508
|
+
*
|
|
1509
|
+
* @param handler Pre-update middleware
|
|
1510
|
+
*/
|
|
1511
|
+
onPreUpdate(handler) {
|
|
1512
|
+
if (handler) this._preUpdateHandler = handler;
|
|
1513
|
+
else this._preUpdateHandler = void 0;
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Register post-update middleware.
|
|
1517
|
+
*
|
|
1518
|
+
* This is used locally within this dispatcher
|
|
1519
|
+
* (does not affect children/parent) after successfully
|
|
1520
|
+
* processing an update, and can be used for stats.
|
|
1521
|
+
*
|
|
1522
|
+
* There can be at most one post-update middleware.
|
|
1523
|
+
* Pass `null` to remove it.
|
|
1524
|
+
*
|
|
1525
|
+
* @param handler Pre-update middleware
|
|
1526
|
+
*/
|
|
1527
|
+
onPostUpdate(handler) {
|
|
1528
|
+
if (handler) this._postUpdateHandler = handler;
|
|
1529
|
+
else this._postUpdateHandler = void 0;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Set error handler that will propagate
|
|
1533
|
+
* the error to the parent dispatcher
|
|
1534
|
+
*/
|
|
1535
|
+
propagateErrorToParent(err, update, state2) {
|
|
1536
|
+
if (!this.parent) {
|
|
1537
|
+
throw new MtArgumentError("This dispatcher is not a child");
|
|
1538
|
+
}
|
|
1539
|
+
if (this.parent._errorHandler) {
|
|
1540
|
+
return this.parent._errorHandler(err, update, state2);
|
|
1541
|
+
}
|
|
1542
|
+
throw err;
|
|
1543
|
+
}
|
|
1544
|
+
// children //
|
|
1545
|
+
/**
|
|
1546
|
+
* Get parent dispatcher if current dispatcher is a child.
|
|
1547
|
+
* Otherwise, return `null`
|
|
1548
|
+
*/
|
|
1549
|
+
get parent() {
|
|
1550
|
+
return this._parent ?? null;
|
|
1551
|
+
}
|
|
1552
|
+
_prepareChild(child) {
|
|
1553
|
+
if (child._client) {
|
|
1554
|
+
throw new MtArgumentError(
|
|
1555
|
+
`Provided dispatcher is ${child._parent ? "already a child. Use parent.removeChild() before calling addChild()" : "already bound to a client. Use unbind() before calling addChild()"}`
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
child._parent = this;
|
|
1559
|
+
child._client = this._client;
|
|
1560
|
+
child._storage = this._storage;
|
|
1561
|
+
child._deps = this._deps;
|
|
1562
|
+
child._scenes = this._scenes;
|
|
1563
|
+
child._stateKeyDelegate = this._stateKeyDelegate;
|
|
1564
|
+
child._customStorage ??= this._customStorage;
|
|
1565
|
+
child._customStateKeyDelegate ??= this._customStateKeyDelegate;
|
|
1566
|
+
}
|
|
1567
|
+
/**
|
|
1568
|
+
* Add a child dispatcher.
|
|
1569
|
+
*
|
|
1570
|
+
* Child dispatchers are called when dispatching updates
|
|
1571
|
+
* just like normal, except they can be controlled
|
|
1572
|
+
* externally. Additionally, child dispatcher have their own
|
|
1573
|
+
* independent handler grouping that does not interfere with parent's,
|
|
1574
|
+
* including `StopPropagation` (i.e. returning `StopPropagation` will
|
|
1575
|
+
* still call children. To entirely stop, use `StopChildrenPropagation`)
|
|
1576
|
+
*
|
|
1577
|
+
* Note that child dispatchers share the same TelegramClient and
|
|
1578
|
+
* storage binding as the parent, don't bind them manually.
|
|
1579
|
+
*
|
|
1580
|
+
* @param child Other dispatcher
|
|
1581
|
+
*/
|
|
1582
|
+
addChild(child) {
|
|
1583
|
+
if (this._children.includes(child)) return;
|
|
1584
|
+
this._prepareChild(child);
|
|
1585
|
+
this._children.push(child);
|
|
1586
|
+
}
|
|
1587
|
+
addScene(scene, scoped = true) {
|
|
1588
|
+
if (!this._scenes) this._scenes = /* @__PURE__ */ new Map();
|
|
1589
|
+
if (!scene._scene) {
|
|
1590
|
+
throw new MtArgumentError(
|
|
1591
|
+
"Non-scene dispatcher passed to addScene. Use `Dispatcher.scene()` to create one."
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
if (this._scenes.has(scene._scene)) {
|
|
1595
|
+
throw new MtArgumentError(`Scene with name ${scene._scene} is already registered!`);
|
|
1596
|
+
}
|
|
1597
|
+
this._prepareChild(scene);
|
|
1598
|
+
scene._sceneScoped = scoped;
|
|
1599
|
+
this._scenes.set(scene._scene, scene);
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* Remove a child dispatcher.
|
|
1603
|
+
*
|
|
1604
|
+
* Removing child dispatcher will also remove
|
|
1605
|
+
* child dispatcher's client binding.
|
|
1606
|
+
*
|
|
1607
|
+
* If the provided dispatcher is not a child of current,
|
|
1608
|
+
* this function will silently fail.
|
|
1609
|
+
*
|
|
1610
|
+
* @param child Other dispatcher
|
|
1611
|
+
*/
|
|
1612
|
+
removeChild(child) {
|
|
1613
|
+
const idx = this._children.indexOf(child);
|
|
1614
|
+
if (idx > -1) {
|
|
1615
|
+
child._unparent();
|
|
1616
|
+
this._children.splice(idx, 1);
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
_unparent() {
|
|
1620
|
+
this._parent = this._client = void 0;
|
|
1621
|
+
this._deps = {};
|
|
1622
|
+
this._stateKeyDelegate = void 0;
|
|
1623
|
+
this._storage = void 0;
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Extend current dispatcher by copying other dispatcher's
|
|
1627
|
+
* handlers and children to the current.
|
|
1628
|
+
*
|
|
1629
|
+
* This might be more efficient for simple cases, but do note that the handler
|
|
1630
|
+
* groups, children and scenes will get merged (unlike {@link addChild},
|
|
1631
|
+
* where they are independent). Also note that unlike with children,
|
|
1632
|
+
* when adding handlers to `other` *after* you extended
|
|
1633
|
+
* the current dispatcher, changes will not be applied.
|
|
1634
|
+
*
|
|
1635
|
+
* @param other Other dispatcher
|
|
1636
|
+
*/
|
|
1637
|
+
extend(other) {
|
|
1638
|
+
if (other._customStorage || other._customStateKeyDelegate) {
|
|
1639
|
+
throw new MtArgumentError("Provided dispatcher has custom storage and cannot be extended from.");
|
|
1640
|
+
}
|
|
1641
|
+
other._groupsOrder.forEach((group) => {
|
|
1642
|
+
if (!this._groups.has(group)) {
|
|
1643
|
+
this._groups.set(group, other._groups.get(group));
|
|
1644
|
+
this._groupsOrder.push(group);
|
|
1645
|
+
} else {
|
|
1646
|
+
const otherGrp = other._groups.get(group);
|
|
1647
|
+
const selfGrp = this._groups.get(group);
|
|
1648
|
+
for (const typ of otherGrp.keys()) {
|
|
1649
|
+
if (!selfGrp.has(typ)) {
|
|
1650
|
+
selfGrp.set(typ, otherGrp.get(typ));
|
|
1651
|
+
} else {
|
|
1652
|
+
selfGrp.get(typ).push(...otherGrp.get(typ));
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
other._children.forEach((it) => {
|
|
1658
|
+
it._unparent();
|
|
1659
|
+
this.addChild(it);
|
|
1660
|
+
});
|
|
1661
|
+
if (other._scenes) {
|
|
1662
|
+
const otherScenes = other._scenes;
|
|
1663
|
+
if (!this._scenes) this._scenes = /* @__PURE__ */ new Map();
|
|
1664
|
+
const myScenes = this._scenes;
|
|
1665
|
+
for (const key of otherScenes.keys()) {
|
|
1666
|
+
otherScenes.get(key)._unparent();
|
|
1667
|
+
if (myScenes.has(key)) {
|
|
1668
|
+
myScenes.delete(key);
|
|
1669
|
+
}
|
|
1670
|
+
this.addScene(otherScenes.get(key), otherScenes.get(key)._sceneScoped);
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
this._groupsOrder.sort((a, b) => a - b);
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Create a clone of this dispatcher, that has the same handlers,
|
|
1677
|
+
* but is not bound to a client or to a parent dispatcher.
|
|
1678
|
+
*
|
|
1679
|
+
* Custom Storage and key delegate are copied too.
|
|
1680
|
+
*
|
|
1681
|
+
* By default, child dispatchers (and scenes) are ignored, since
|
|
1682
|
+
* that requires cloning every single one of them recursively
|
|
1683
|
+
* and then binding them back.
|
|
1684
|
+
*
|
|
1685
|
+
* @param children Whether to also clone children and scenes
|
|
1686
|
+
*/
|
|
1687
|
+
clone(children = false) {
|
|
1688
|
+
const dp = new Dispatcher();
|
|
1689
|
+
for (const key of this._groups.keys()) {
|
|
1690
|
+
const idx = key;
|
|
1691
|
+
dp._groups.set(idx, /* @__PURE__ */ new Map());
|
|
1692
|
+
for (const type of this._groups.get(idx).keys()) {
|
|
1693
|
+
dp._groups.get(idx).set(type, [...this._groups.get(idx).get(type)]);
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
dp._groupsOrder = [...this._groupsOrder];
|
|
1697
|
+
dp._errorHandler = this._errorHandler;
|
|
1698
|
+
dp._customStateKeyDelegate = this._customStateKeyDelegate;
|
|
1699
|
+
dp._customStorage = this._customStorage;
|
|
1700
|
+
if (children) {
|
|
1701
|
+
this._children.forEach((it) => {
|
|
1702
|
+
const child = it.clone(true);
|
|
1703
|
+
dp.addChild(child);
|
|
1704
|
+
});
|
|
1705
|
+
if (this._scenes) {
|
|
1706
|
+
for (const key of this._scenes.keys()) {
|
|
1707
|
+
const scene = this._scenes.get(key).clone(true);
|
|
1708
|
+
dp.addScene(scene, this._scenes.get(key)._sceneScoped);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
return dp;
|
|
1713
|
+
}
|
|
1714
|
+
getState(object) {
|
|
1715
|
+
if (!this._storage) {
|
|
1716
|
+
throw new MtArgumentError("Cannot use getUpdateState() filter without state storage");
|
|
1717
|
+
}
|
|
1718
|
+
if (typeof object === "string") {
|
|
1719
|
+
return new UpdateState(this._storage, object, this._scene ?? null, this._sceneScoped, this._customStorage);
|
|
1720
|
+
}
|
|
1721
|
+
return Promise.resolve(this._stateKeyDelegate(object)).then((key) => {
|
|
1722
|
+
if (!key) {
|
|
1723
|
+
throw new MtArgumentError("Cannot derive key from given object");
|
|
1724
|
+
}
|
|
1725
|
+
if (!this._customStateKeyDelegate) {
|
|
1726
|
+
return new UpdateState(this._storage, key, this._scene ?? null, this._sceneScoped, this._customStorage);
|
|
1727
|
+
}
|
|
1728
|
+
return Promise.resolve(this._customStateKeyDelegate(object)).then((customKey) => {
|
|
1729
|
+
if (!customKey) {
|
|
1730
|
+
throw new MtArgumentError("Cannot derive custom key from given object");
|
|
1731
|
+
}
|
|
1732
|
+
return new UpdateState(
|
|
1733
|
+
this._storage,
|
|
1734
|
+
key,
|
|
1735
|
+
this._scene ?? null,
|
|
1736
|
+
this._sceneScoped,
|
|
1737
|
+
this._customStorage,
|
|
1738
|
+
customKey
|
|
1739
|
+
);
|
|
1740
|
+
});
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
/**
|
|
1744
|
+
* Get global state.
|
|
1745
|
+
*
|
|
1746
|
+
* This will load the state for the given object
|
|
1747
|
+
* ignoring local custom storage, key delegate and scene scope.
|
|
1748
|
+
*/
|
|
1749
|
+
getGlobalState(object) {
|
|
1750
|
+
if (!this._parent) {
|
|
1751
|
+
throw new MtArgumentError("This dispatcher does not have a parent");
|
|
1752
|
+
}
|
|
1753
|
+
return Promise.resolve(this._stateKeyDelegate(object)).then((key) => {
|
|
1754
|
+
if (!key) {
|
|
1755
|
+
throw new MtArgumentError("Cannot derive key from given object");
|
|
1756
|
+
}
|
|
1757
|
+
return new UpdateState(this._storage, key, this._scene ?? null, false);
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1760
|
+
// addUpdateHandler convenience wrappers //
|
|
1761
|
+
_addKnownHandler(name, filter, handler, group) {
|
|
1762
|
+
if (typeof handler === "number" || typeof handler === "undefined") {
|
|
1763
|
+
this.addUpdateHandler(
|
|
1764
|
+
{
|
|
1765
|
+
name,
|
|
1766
|
+
callback: filter
|
|
1767
|
+
},
|
|
1768
|
+
handler
|
|
1769
|
+
);
|
|
1770
|
+
} else {
|
|
1771
|
+
this.addUpdateHandler(
|
|
1772
|
+
{
|
|
1773
|
+
name,
|
|
1774
|
+
callback: handler,
|
|
1775
|
+
check: filter
|
|
1776
|
+
},
|
|
1777
|
+
group
|
|
1778
|
+
);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
/** @internal */
|
|
1782
|
+
onRawUpdate(filter, handler, group) {
|
|
1783
|
+
this._addKnownHandler("raw", filter, handler, group);
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Register a scene transition handler
|
|
1787
|
+
*
|
|
1788
|
+
* This handler is called whenever a scene transition occurs
|
|
1789
|
+
* in the context of the scene that is being entered,
|
|
1790
|
+
* and before any of the its own handlers are called,
|
|
1791
|
+
* and can be used to customize the transition behavior:
|
|
1792
|
+
* - `Stop` to prevent dispatching the update any further **even if ToScene/ToRoot was used**
|
|
1793
|
+
* - `Continue` same as Stop, but still dispatch the update to children
|
|
1794
|
+
* - `ToScene` to prevent the transition and dispatch the update to the scene entered in the transition handler
|
|
1795
|
+
*
|
|
1796
|
+
* > **Note**: if multiple `state.enter()` calls were made within the same update,
|
|
1797
|
+
* > this handler will only be called for the last one.
|
|
1798
|
+
*
|
|
1799
|
+
* @param handler Raw update handler
|
|
1800
|
+
* @param group Handler group index
|
|
1801
|
+
*/
|
|
1802
|
+
onSceneTransition(handler) {
|
|
1803
|
+
if (handler) this._sceneTransitionHandler = handler;
|
|
1804
|
+
else this._sceneTransitionHandler = void 0;
|
|
1805
|
+
}
|
|
1806
|
+
/** @internal */
|
|
1807
|
+
onAnyCallbackQuery(filter, handler, group) {
|
|
1808
|
+
this._addKnownHandler("callback_query", filter, handler, group);
|
|
1809
|
+
this._addKnownHandler("inline_callback_query", filter, handler, group);
|
|
1810
|
+
this._addKnownHandler("business_callback_query", filter, handler, group);
|
|
1811
|
+
}
|
|
1812
|
+
/** @internal */
|
|
1813
|
+
onNewMessage(filter, handler, group) {
|
|
1814
|
+
this._addKnownHandler("new_message", filter, handler, group);
|
|
1815
|
+
}
|
|
1816
|
+
/** @internal */
|
|
1817
|
+
onEditMessage(filter, handler, group) {
|
|
1818
|
+
this._addKnownHandler("edit_message", filter, handler, group);
|
|
1819
|
+
}
|
|
1820
|
+
/** @internal */
|
|
1821
|
+
onMessageGroup(filter, handler, group) {
|
|
1822
|
+
this._addKnownHandler("message_group", filter, handler, group);
|
|
1823
|
+
}
|
|
1824
|
+
/** @internal */
|
|
1825
|
+
onDeleteMessage(filter, handler, group) {
|
|
1826
|
+
this._addKnownHandler("delete_message", filter, handler, group);
|
|
1827
|
+
}
|
|
1828
|
+
/** @internal */
|
|
1829
|
+
onChatMemberUpdate(filter, handler, group) {
|
|
1830
|
+
this._addKnownHandler("chat_member", filter, handler, group);
|
|
1831
|
+
}
|
|
1832
|
+
/** @internal */
|
|
1833
|
+
onInlineQuery(filter, handler, group) {
|
|
1834
|
+
this._addKnownHandler("inline_query", filter, handler, group);
|
|
1835
|
+
}
|
|
1836
|
+
/** @internal */
|
|
1837
|
+
onChosenInlineResult(filter, handler, group) {
|
|
1838
|
+
this._addKnownHandler("chosen_inline_result", filter, handler, group);
|
|
1839
|
+
}
|
|
1840
|
+
/** @internal */
|
|
1841
|
+
onCallbackQuery(filter, handler, group) {
|
|
1842
|
+
this._addKnownHandler("callback_query", filter, handler, group);
|
|
1843
|
+
}
|
|
1844
|
+
/** @internal */
|
|
1845
|
+
onInlineCallbackQuery(filter, handler, group) {
|
|
1846
|
+
this._addKnownHandler("inline_callback_query", filter, handler, group);
|
|
1847
|
+
}
|
|
1848
|
+
/** @internal */
|
|
1849
|
+
onBusinessCallbackQuery(filter, handler, group) {
|
|
1850
|
+
this._addKnownHandler("business_callback_query", filter, handler, group);
|
|
1851
|
+
}
|
|
1852
|
+
/** @internal */
|
|
1853
|
+
onPollUpdate(filter, handler, group) {
|
|
1854
|
+
this._addKnownHandler("poll", filter, handler, group);
|
|
1855
|
+
}
|
|
1856
|
+
/** @internal */
|
|
1857
|
+
onPollVote(filter, handler, group) {
|
|
1858
|
+
this._addKnownHandler("poll_vote", filter, handler, group);
|
|
1859
|
+
}
|
|
1860
|
+
/** @internal */
|
|
1861
|
+
onUserStatusUpdate(filter, handler, group) {
|
|
1862
|
+
this._addKnownHandler("user_status", filter, handler, group);
|
|
1863
|
+
}
|
|
1864
|
+
/** @internal */
|
|
1865
|
+
onUserTyping(filter, handler, group) {
|
|
1866
|
+
this._addKnownHandler("user_typing", filter, handler, group);
|
|
1867
|
+
}
|
|
1868
|
+
/** @internal */
|
|
1869
|
+
onHistoryRead(filter, handler, group) {
|
|
1870
|
+
this._addKnownHandler("history_read", filter, handler, group);
|
|
1871
|
+
}
|
|
1872
|
+
/** @internal */
|
|
1873
|
+
onBotStopped(filter, handler, group) {
|
|
1874
|
+
this._addKnownHandler("bot_stopped", filter, handler, group);
|
|
1875
|
+
}
|
|
1876
|
+
/** @internal */
|
|
1877
|
+
onBotChatJoinRequest(filter, handler, group) {
|
|
1878
|
+
this._addKnownHandler("bot_chat_join_request", filter, handler, group);
|
|
1879
|
+
}
|
|
1880
|
+
/** @internal */
|
|
1881
|
+
onChatJoinRequest(filter, handler, group) {
|
|
1882
|
+
this._addKnownHandler("chat_join_request", filter, handler, group);
|
|
1883
|
+
}
|
|
1884
|
+
/** @internal */
|
|
1885
|
+
onPreCheckoutQuery(filter, handler, group) {
|
|
1886
|
+
this._addKnownHandler("pre_checkout_query", filter, handler, group);
|
|
1887
|
+
}
|
|
1888
|
+
/** @internal */
|
|
1889
|
+
onStoryUpdate(filter, handler, group) {
|
|
1890
|
+
this._addKnownHandler("story", filter, handler, group);
|
|
1891
|
+
}
|
|
1892
|
+
/** @internal */
|
|
1893
|
+
onDeleteStory(filter, handler, group) {
|
|
1894
|
+
this._addKnownHandler("delete_story", filter, handler, group);
|
|
1895
|
+
}
|
|
1896
|
+
/** @internal */
|
|
1897
|
+
onBotReactionUpdate(filter, handler, group) {
|
|
1898
|
+
this._addKnownHandler("bot_reaction", filter, handler, group);
|
|
1899
|
+
}
|
|
1900
|
+
/** @internal */
|
|
1901
|
+
onBotReactionCountUpdate(filter, handler, group) {
|
|
1902
|
+
this._addKnownHandler("bot_reaction_count", filter, handler, group);
|
|
1903
|
+
}
|
|
1904
|
+
/** @internal */
|
|
1905
|
+
onBusinessConnectionUpdate(filter, handler, group) {
|
|
1906
|
+
this._addKnownHandler("business_connection", filter, handler, group);
|
|
1907
|
+
}
|
|
1908
|
+
/** @internal */
|
|
1909
|
+
onNewBusinessMessage(filter, handler, group) {
|
|
1910
|
+
this._addKnownHandler("new_business_message", filter, handler, group);
|
|
1911
|
+
}
|
|
1912
|
+
/** @internal */
|
|
1913
|
+
onEditBusinessMessage(filter, handler, group) {
|
|
1914
|
+
this._addKnownHandler("edit_business_message", filter, handler, group);
|
|
1915
|
+
}
|
|
1916
|
+
/** @internal */
|
|
1917
|
+
onBusinessMessageGroup(filter, handler, group) {
|
|
1918
|
+
this._addKnownHandler("business_message_group", filter, handler, group);
|
|
1919
|
+
}
|
|
1920
|
+
/** @internal */
|
|
1921
|
+
onDeleteBusinessMessage(filter, handler, group) {
|
|
1922
|
+
this._addKnownHandler("delete_business_message", filter, handler, group);
|
|
1923
|
+
}
|
|
1924
|
+
// end-codegen
|
|
1925
|
+
}
|
|
1926
|
+
function chat(type) {
|
|
1927
|
+
return (msg) => msg.chat.chatType === type;
|
|
1928
|
+
}
|
|
1929
|
+
const chatId = (id) => {
|
|
1930
|
+
const indexId = /* @__PURE__ */ new Set();
|
|
1931
|
+
const indexUsername = /* @__PURE__ */ new Set();
|
|
1932
|
+
let matchSelf = false;
|
|
1933
|
+
if (!Array.isArray(id)) id = [id];
|
|
1934
|
+
id.forEach((id2) => {
|
|
1935
|
+
if (id2 === "me" || id2 === "self") {
|
|
1936
|
+
matchSelf = true;
|
|
1937
|
+
} else if (typeof id2 === "number") {
|
|
1938
|
+
indexId.add(id2);
|
|
1939
|
+
} else {
|
|
1940
|
+
indexUsername.add(id2);
|
|
1941
|
+
}
|
|
1942
|
+
});
|
|
1943
|
+
return (upd) => {
|
|
1944
|
+
switch (upd._name) {
|
|
1945
|
+
case "poll_vote": {
|
|
1946
|
+
const peer = upd.peer;
|
|
1947
|
+
return peer.type === "chat" && (indexId.has(peer.id) || Boolean(peer.usernames?.some((u) => indexUsername.has(u.username))));
|
|
1948
|
+
}
|
|
1949
|
+
case "history_read":
|
|
1950
|
+
case "user_typing": {
|
|
1951
|
+
const id2 = upd.chatId;
|
|
1952
|
+
return matchSelf && id2 === upd.client.storage.self.getCached()?.userId || indexId.has(id2);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
const chat2 = upd.chat;
|
|
1956
|
+
return matchSelf && chat2.isSelf || indexId.has(chat2.id) || Boolean(chat2.usernames?.some((u) => indexUsername.has(u.username)));
|
|
1957
|
+
};
|
|
1958
|
+
};
|
|
1959
|
+
const any = () => true;
|
|
1960
|
+
function not(fn) {
|
|
1961
|
+
return (upd, state2) => {
|
|
1962
|
+
const res = fn(upd, state2);
|
|
1963
|
+
if (typeof res === "boolean") return !res;
|
|
1964
|
+
return res.then((r) => !r);
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
function and(...fns) {
|
|
1968
|
+
return (upd, state2) => {
|
|
1969
|
+
let i = 0;
|
|
1970
|
+
const max = fns.length;
|
|
1971
|
+
const next = () => {
|
|
1972
|
+
if (i === max) return true;
|
|
1973
|
+
const res = fns[i++](upd, state2);
|
|
1974
|
+
if (typeof res === "boolean") {
|
|
1975
|
+
if (!res) return false;
|
|
1976
|
+
return next();
|
|
1977
|
+
}
|
|
1978
|
+
return res.then((r) => {
|
|
1979
|
+
if (!r) return false;
|
|
1980
|
+
return next();
|
|
1981
|
+
});
|
|
1982
|
+
};
|
|
1983
|
+
return next();
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
function or(...fns) {
|
|
1987
|
+
return (upd, state2) => {
|
|
1988
|
+
let i = 0;
|
|
1989
|
+
const max = fns.length;
|
|
1990
|
+
const next = () => {
|
|
1991
|
+
if (i === max) return false;
|
|
1992
|
+
const res = fns[i++](upd, state2);
|
|
1993
|
+
if (typeof res === "boolean") {
|
|
1994
|
+
if (res) return true;
|
|
1995
|
+
return next();
|
|
1996
|
+
}
|
|
1997
|
+
return res.then((r) => {
|
|
1998
|
+
if (r) return true;
|
|
1999
|
+
return next();
|
|
2000
|
+
});
|
|
2001
|
+
};
|
|
2002
|
+
return next();
|
|
2003
|
+
};
|
|
2004
|
+
}
|
|
2005
|
+
function command(commands, {
|
|
2006
|
+
prefixes = "/",
|
|
2007
|
+
caseSensitive = false
|
|
2008
|
+
} = {}) {
|
|
2009
|
+
if (!Array.isArray(commands)) commands = [commands];
|
|
2010
|
+
if (!caseSensitive) {
|
|
2011
|
+
commands = commands.map((i) => typeof i === "string" ? i.toLowerCase() : i);
|
|
2012
|
+
}
|
|
2013
|
+
const argumentsRe = /(["'])(.*?)(?<!\\)\1|(\S+)/g;
|
|
2014
|
+
const unescapeRe = /\\(['"])/;
|
|
2015
|
+
const commandsRe = [];
|
|
2016
|
+
commands.forEach((cmd) => {
|
|
2017
|
+
if (typeof cmd !== "string") cmd = cmd.source;
|
|
2018
|
+
commandsRe.push(new RegExp(`^(${cmd})(?:\\s|$|@([a-zA-Z0-9_]+?bot)(?:\\s|$))`, caseSensitive ? "" : "i"));
|
|
2019
|
+
});
|
|
2020
|
+
if (prefixes === null) prefixes = [];
|
|
2021
|
+
if (typeof prefixes === "string") prefixes = [prefixes];
|
|
2022
|
+
const _prefixes = prefixes;
|
|
2023
|
+
const check = (msg) => {
|
|
2024
|
+
if (msg.isMessageGroup) return check(msg.messages[0]);
|
|
2025
|
+
for (const pref of _prefixes) {
|
|
2026
|
+
if (!msg.text.startsWith(pref)) continue;
|
|
2027
|
+
const withoutPrefix = msg.text.slice(pref.length);
|
|
2028
|
+
for (const regex2 of commandsRe) {
|
|
2029
|
+
const m = withoutPrefix.match(regex2);
|
|
2030
|
+
if (!m) continue;
|
|
2031
|
+
const lastGroup = m[m.length - 1];
|
|
2032
|
+
if (lastGroup) {
|
|
2033
|
+
const self = msg.client.storage.self.getCached();
|
|
2034
|
+
if (self && self.isBot && !self.usernames.includes(lastGroup)) {
|
|
2035
|
+
return false;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
const match = m.slice(1, -1);
|
|
2039
|
+
if (!caseSensitive) match[0] = match[0].toLowerCase();
|
|
2040
|
+
withoutPrefix.slice(m[0].length).replace(argumentsRe, ($0, $1, $2, $3) => {
|
|
2041
|
+
match.push(($2 || $3 || "").replace(unescapeRe, "$1"));
|
|
2042
|
+
return "";
|
|
2043
|
+
});
|
|
2044
|
+
msg.command = match;
|
|
2045
|
+
return true;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
return false;
|
|
2049
|
+
};
|
|
2050
|
+
return check;
|
|
2051
|
+
}
|
|
2052
|
+
const start = and(chat("private"), command("start"));
|
|
2053
|
+
const startGroup = and(or(chat("supergroup"), chat("group")), command("start"));
|
|
2054
|
+
function deeplinkBase(base) {
|
|
2055
|
+
return (params) => {
|
|
2056
|
+
if (!Array.isArray(params)) {
|
|
2057
|
+
return and(start, (_msg) => {
|
|
2058
|
+
const msg = _msg;
|
|
2059
|
+
if (msg.command.length !== 2) return false;
|
|
2060
|
+
const p = msg.command[1];
|
|
2061
|
+
if (typeof params === "string" && p === params) return true;
|
|
2062
|
+
const m = p.match(params);
|
|
2063
|
+
if (!m) return false;
|
|
2064
|
+
msg.command.push(...m.slice(1));
|
|
2065
|
+
return true;
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
return and(base, (_msg) => {
|
|
2069
|
+
const msg = _msg;
|
|
2070
|
+
if (msg.command.length !== 2) return false;
|
|
2071
|
+
const p = msg.command[1];
|
|
2072
|
+
for (const param of params) {
|
|
2073
|
+
if (typeof param === "string" && p === param) return true;
|
|
2074
|
+
const m = p.match(param);
|
|
2075
|
+
if (!m) continue;
|
|
2076
|
+
msg.command.push(...m.slice(1));
|
|
2077
|
+
return true;
|
|
2078
|
+
}
|
|
2079
|
+
return false;
|
|
2080
|
+
});
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
const deeplink = deeplinkBase(start);
|
|
2084
|
+
const deeplinkGroup = deeplinkBase(startGroup);
|
|
2085
|
+
function every(filter) {
|
|
2086
|
+
return (ctx, state2) => {
|
|
2087
|
+
let i = 0;
|
|
2088
|
+
const upds = ctx.messages;
|
|
2089
|
+
const max = upds.length;
|
|
2090
|
+
const next = () => {
|
|
2091
|
+
if (i === max) return true;
|
|
2092
|
+
const res = filter(upds[i++], state2);
|
|
2093
|
+
if (typeof res === "boolean") {
|
|
2094
|
+
if (!res) return false;
|
|
2095
|
+
return next();
|
|
2096
|
+
}
|
|
2097
|
+
return res.then((r) => {
|
|
2098
|
+
if (!r) return false;
|
|
2099
|
+
return next();
|
|
2100
|
+
});
|
|
2101
|
+
};
|
|
2102
|
+
return next();
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
function some(filter) {
|
|
2106
|
+
return (ctx, state2) => {
|
|
2107
|
+
let i = 0;
|
|
2108
|
+
const upds = ctx.messages;
|
|
2109
|
+
const max = upds.length;
|
|
2110
|
+
const next = () => {
|
|
2111
|
+
if (i === max) return false;
|
|
2112
|
+
const res = filter(upds[i++], state2);
|
|
2113
|
+
if (typeof res === "boolean") {
|
|
2114
|
+
if (res) return true;
|
|
2115
|
+
return next();
|
|
2116
|
+
}
|
|
2117
|
+
return res.then((r) => {
|
|
2118
|
+
if (r) return true;
|
|
2119
|
+
return next();
|
|
2120
|
+
});
|
|
2121
|
+
};
|
|
2122
|
+
return next();
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
const incoming = (msg) => !msg.isOutgoing;
|
|
2126
|
+
const outgoing = (msg) => msg.isOutgoing;
|
|
2127
|
+
const scheduled = (msg) => msg.isScheduled;
|
|
2128
|
+
const reply = (msg) => msg.replyToMessage !== null;
|
|
2129
|
+
function replyOrigin(origin) {
|
|
2130
|
+
return (msg) => msg.replyToMessage?.originIs(origin) ?? false;
|
|
2131
|
+
}
|
|
2132
|
+
const media = (msg) => msg.media !== null;
|
|
2133
|
+
function mediaOf(type) {
|
|
2134
|
+
return (msg) => msg.media?.type === type;
|
|
2135
|
+
}
|
|
2136
|
+
const photo = mediaOf("photo");
|
|
2137
|
+
const dice = mediaOf("dice");
|
|
2138
|
+
const contact = mediaOf("contact");
|
|
2139
|
+
const audio = mediaOf("audio");
|
|
2140
|
+
const voice = mediaOf("voice");
|
|
2141
|
+
const sticker = mediaOf("sticker");
|
|
2142
|
+
const document = mediaOf("document");
|
|
2143
|
+
const anyVideo = mediaOf("video");
|
|
2144
|
+
const location = mediaOf("location");
|
|
2145
|
+
const liveLocation = mediaOf("live_location");
|
|
2146
|
+
const game = mediaOf("game");
|
|
2147
|
+
const webpage = mediaOf("webpage");
|
|
2148
|
+
const venue = mediaOf("venue");
|
|
2149
|
+
const poll = mediaOf("poll");
|
|
2150
|
+
const invoice = mediaOf("invoice");
|
|
2151
|
+
const anyLocation = (msg) => msg.media instanceof RawLocation;
|
|
2152
|
+
const anyDocument = (msg) => msg.media instanceof RawDocument;
|
|
2153
|
+
const video = (msg) => msg.media?.type === "video" && !msg.media.isAnimation && !msg.media.isRound;
|
|
2154
|
+
const animation = (msg) => msg.media?.type === "video" && msg.media.isAnimation && !msg.media.isRound;
|
|
2155
|
+
const roundMessage = (msg) => msg.media?.type === "video" && !msg.media.isAnimation && msg.media.isRound;
|
|
2156
|
+
function stickerByType(type) {
|
|
2157
|
+
return (msg) => msg.media?.type === "sticker" && msg.media.stickerType === type;
|
|
2158
|
+
}
|
|
2159
|
+
function stickerBySourceType(type) {
|
|
2160
|
+
return (msg) => msg.media?.type === "sticker" && msg.media.sourceType === type;
|
|
2161
|
+
}
|
|
2162
|
+
const text = (msg) => msg.media === null && !msg.isService;
|
|
2163
|
+
const service = (msg) => msg.isService;
|
|
2164
|
+
function action(type) {
|
|
2165
|
+
if (Array.isArray(type)) {
|
|
2166
|
+
const index = {};
|
|
2167
|
+
type.forEach((it) => index[it] = true);
|
|
2168
|
+
return (msg) => msg.action?.type in index;
|
|
2169
|
+
}
|
|
2170
|
+
return (msg) => msg.action?.type === type;
|
|
2171
|
+
}
|
|
2172
|
+
function sender(type) {
|
|
2173
|
+
return (msg) => msg.sender.type === type;
|
|
2174
|
+
}
|
|
2175
|
+
function replyTo(filter) {
|
|
2176
|
+
return async (msg, state2) => {
|
|
2177
|
+
if (!msg.replyToMessage?.id) return false;
|
|
2178
|
+
const reply2 = msg._name === "new_message" ? await msg.getReplyTo() : msg.replyTo;
|
|
2179
|
+
if (!reply2) return false;
|
|
2180
|
+
if (msg._name === "new_message") {
|
|
2181
|
+
msg.getReplyTo = () => Promise.resolve(reply2);
|
|
2182
|
+
}
|
|
2183
|
+
if (!filter) return true;
|
|
2184
|
+
return filter(reply2, state2);
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
function withCompleteSender(filter) {
|
|
2188
|
+
return async (msg, state2) => {
|
|
2189
|
+
try {
|
|
2190
|
+
await msg.getCompleteSender();
|
|
2191
|
+
} catch {
|
|
2192
|
+
return false;
|
|
2193
|
+
}
|
|
2194
|
+
if (!filter) return true;
|
|
2195
|
+
return filter(msg, state2);
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function withCompleteChat(filter) {
|
|
2199
|
+
return async (msg, state2) => {
|
|
2200
|
+
try {
|
|
2201
|
+
await msg.getCompleteChat();
|
|
2202
|
+
} catch {
|
|
2203
|
+
return false;
|
|
2204
|
+
}
|
|
2205
|
+
if (!filter) return true;
|
|
2206
|
+
return filter(msg, state2);
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
const stateEmpty = async (upd, state2) => {
|
|
2210
|
+
if (!state2) return false;
|
|
2211
|
+
return !await state2.get();
|
|
2212
|
+
};
|
|
2213
|
+
function state(predicate) {
|
|
2214
|
+
return async (upd, state2) => {
|
|
2215
|
+
if (!state2) return false;
|
|
2216
|
+
const data = await state2.get();
|
|
2217
|
+
if (!data) return false;
|
|
2218
|
+
return predicate(data);
|
|
2219
|
+
};
|
|
2220
|
+
}
|
|
2221
|
+
function extractText(obj) {
|
|
2222
|
+
switch (obj._name) {
|
|
2223
|
+
case "new_message":
|
|
2224
|
+
case "new_business_message":
|
|
2225
|
+
return obj.text;
|
|
2226
|
+
case "inline_query":
|
|
2227
|
+
return obj.query;
|
|
2228
|
+
case "chosen_inline_result":
|
|
2229
|
+
return obj.id;
|
|
2230
|
+
case "callback_query":
|
|
2231
|
+
case "inline_callback_query":
|
|
2232
|
+
case "business_callback_query":
|
|
2233
|
+
if (obj.raw.data) return obj.dataStr;
|
|
2234
|
+
}
|
|
2235
|
+
return null;
|
|
2236
|
+
}
|
|
2237
|
+
function regex(regex2) {
|
|
2238
|
+
return (obj) => {
|
|
2239
|
+
const txt = extractText(obj);
|
|
2240
|
+
if (!txt) return false;
|
|
2241
|
+
const m = txt.match(regex2);
|
|
2242
|
+
if (m) {
|
|
2243
|
+
obj.match = m;
|
|
2244
|
+
return true;
|
|
2245
|
+
}
|
|
2246
|
+
return false;
|
|
2247
|
+
};
|
|
2248
|
+
}
|
|
2249
|
+
function equals(str, ignoreCase = false) {
|
|
2250
|
+
if (ignoreCase) {
|
|
2251
|
+
str = str.toLowerCase();
|
|
2252
|
+
return (obj) => extractText(obj)?.toLowerCase() === str;
|
|
2253
|
+
}
|
|
2254
|
+
return (obj) => extractText(obj) === str;
|
|
2255
|
+
}
|
|
2256
|
+
function contains(str, ignoreCase = false) {
|
|
2257
|
+
if (ignoreCase) {
|
|
2258
|
+
str = str.toLowerCase();
|
|
2259
|
+
return (obj) => {
|
|
2260
|
+
const txt = extractText(obj);
|
|
2261
|
+
return txt != null && txt.toLowerCase().includes(str);
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
return (obj) => {
|
|
2265
|
+
const txt = extractText(obj);
|
|
2266
|
+
return txt != null && txt.includes(str);
|
|
2267
|
+
};
|
|
2268
|
+
}
|
|
2269
|
+
function startsWith(str, ignoreCase = false) {
|
|
2270
|
+
if (ignoreCase) {
|
|
2271
|
+
str = str.toLowerCase();
|
|
2272
|
+
return (obj) => {
|
|
2273
|
+
const txt = extractText(obj);
|
|
2274
|
+
return txt != null && txt.toLowerCase().substring(0, str.length) === str;
|
|
2275
|
+
};
|
|
2276
|
+
}
|
|
2277
|
+
return (obj) => {
|
|
2278
|
+
const txt = extractText(obj);
|
|
2279
|
+
return txt != null && txt.substring(0, str.length) === str;
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
function endsWith(str, ignoreCase = false) {
|
|
2283
|
+
if (ignoreCase) {
|
|
2284
|
+
str = str.toLowerCase();
|
|
2285
|
+
return (obj) => {
|
|
2286
|
+
const txt = extractText(obj);
|
|
2287
|
+
return txt != null && txt.toLowerCase().substring(0, str.length) === str;
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2290
|
+
return (obj) => {
|
|
2291
|
+
const txt = extractText(obj);
|
|
2292
|
+
return txt != null && txt.substring(0, str.length) === str;
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
const chatMember = (types) => {
|
|
2296
|
+
if (Array.isArray(types)) {
|
|
2297
|
+
const index = {};
|
|
2298
|
+
types.forEach((typ) => index[typ] = true);
|
|
2299
|
+
return (upd) => upd.type in index;
|
|
2300
|
+
}
|
|
2301
|
+
return (upd) => upd.type === types;
|
|
2302
|
+
};
|
|
2303
|
+
const userStatus = (statuses) => {
|
|
2304
|
+
if (Array.isArray(statuses)) {
|
|
2305
|
+
const index = {};
|
|
2306
|
+
statuses.forEach((typ) => index[typ] = true);
|
|
2307
|
+
return (upd) => upd.status in index;
|
|
2308
|
+
}
|
|
2309
|
+
return (upd) => upd.status === statuses;
|
|
2310
|
+
};
|
|
2311
|
+
const chatMemberSelf = (upd) => upd.isSelf;
|
|
2312
|
+
const me = (msg) => msg.sender.type === "user" && msg.sender.isSelf || msg.isOutgoing;
|
|
2313
|
+
const bot = (msg) => msg.sender.constructor === User && msg.sender.isBot;
|
|
2314
|
+
const userId = (id) => {
|
|
2315
|
+
const indexId = /* @__PURE__ */ new Set();
|
|
2316
|
+
const indexUsername = /* @__PURE__ */ new Set();
|
|
2317
|
+
let matchSelf = false;
|
|
2318
|
+
if (!Array.isArray(id)) id = [id];
|
|
2319
|
+
id.forEach((id2) => {
|
|
2320
|
+
if (id2 === "me" || id2 === "self") {
|
|
2321
|
+
matchSelf = true;
|
|
2322
|
+
} else if (typeof id2 === "string") {
|
|
2323
|
+
indexUsername.add(id2);
|
|
2324
|
+
} else {
|
|
2325
|
+
indexId.add(id2);
|
|
2326
|
+
}
|
|
2327
|
+
});
|
|
2328
|
+
return (upd) => {
|
|
2329
|
+
switch (upd._name) {
|
|
2330
|
+
case "new_message":
|
|
2331
|
+
case "edit_message":
|
|
2332
|
+
case "new_business_message":
|
|
2333
|
+
case "edit_business_message": {
|
|
2334
|
+
const sender2 = upd.sender;
|
|
2335
|
+
return matchSelf && sender2.isSelf || indexId.has(sender2.id) || indexUsername.has(sender2.username);
|
|
2336
|
+
}
|
|
2337
|
+
case "user_status":
|
|
2338
|
+
case "user_typing": {
|
|
2339
|
+
const id2 = upd.userId;
|
|
2340
|
+
return matchSelf && id2 === upd.client.storage.self.getCached()?.userId || indexId.has(id2);
|
|
2341
|
+
}
|
|
2342
|
+
case "poll_vote":
|
|
2343
|
+
case "story":
|
|
2344
|
+
case "delete_story": {
|
|
2345
|
+
const peer = upd.peer;
|
|
2346
|
+
if (peer.type !== "user") return false;
|
|
2347
|
+
return matchSelf && peer.isSelf || indexId.has(peer.id) || Boolean(peer.usernames?.some((u) => indexUsername.has(u.username)));
|
|
2348
|
+
}
|
|
2349
|
+
case "history_read": {
|
|
2350
|
+
const id2 = upd.chatId;
|
|
2351
|
+
return matchSelf && id2 === upd.client.storage.self.getCached()?.userId || indexId.has(id2);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
const user = upd.user;
|
|
2355
|
+
return matchSelf && user.isSelf || indexId.has(user.id) || Boolean(user.usernames?.some((u) => indexUsername.has(u.username)));
|
|
2356
|
+
};
|
|
2357
|
+
};
|
|
2358
|
+
const bundle = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2359
|
+
__proto__: null,
|
|
2360
|
+
action,
|
|
2361
|
+
and,
|
|
2362
|
+
animation,
|
|
2363
|
+
any,
|
|
2364
|
+
anyDocument,
|
|
2365
|
+
anyLocation,
|
|
2366
|
+
anyVideo,
|
|
2367
|
+
audio,
|
|
2368
|
+
bot,
|
|
2369
|
+
chat,
|
|
2370
|
+
chatId,
|
|
2371
|
+
chatMember,
|
|
2372
|
+
chatMemberSelf,
|
|
2373
|
+
command,
|
|
2374
|
+
contact,
|
|
2375
|
+
contains,
|
|
2376
|
+
deeplink,
|
|
2377
|
+
deeplinkGroup,
|
|
2378
|
+
dice,
|
|
2379
|
+
document,
|
|
2380
|
+
endsWith,
|
|
2381
|
+
equals,
|
|
2382
|
+
every,
|
|
2383
|
+
game,
|
|
2384
|
+
incoming,
|
|
2385
|
+
invoice,
|
|
2386
|
+
liveLocation,
|
|
2387
|
+
location,
|
|
2388
|
+
me,
|
|
2389
|
+
media,
|
|
2390
|
+
mediaOf,
|
|
2391
|
+
not,
|
|
2392
|
+
or,
|
|
2393
|
+
outgoing,
|
|
2394
|
+
photo,
|
|
2395
|
+
poll,
|
|
2396
|
+
regex,
|
|
2397
|
+
reply,
|
|
2398
|
+
replyOrigin,
|
|
2399
|
+
replyTo,
|
|
2400
|
+
roundMessage,
|
|
2401
|
+
scheduled,
|
|
2402
|
+
sender,
|
|
2403
|
+
service,
|
|
2404
|
+
some,
|
|
2405
|
+
start,
|
|
2406
|
+
startGroup,
|
|
2407
|
+
startsWith,
|
|
2408
|
+
state,
|
|
2409
|
+
stateEmpty,
|
|
2410
|
+
sticker,
|
|
2411
|
+
stickerBySourceType,
|
|
2412
|
+
stickerByType,
|
|
2413
|
+
text,
|
|
2414
|
+
userId,
|
|
2415
|
+
userStatus,
|
|
2416
|
+
venue,
|
|
2417
|
+
video,
|
|
2418
|
+
voice,
|
|
2419
|
+
webpage,
|
|
2420
|
+
withCompleteChat,
|
|
2421
|
+
withCompleteSender
|
|
2422
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
2423
|
+
var PropagationAction = /* @__PURE__ */ ((PropagationAction2) => {
|
|
2424
|
+
PropagationAction2["Stop"] = "stop";
|
|
2425
|
+
PropagationAction2["StopChildren"] = "stop-children";
|
|
2426
|
+
PropagationAction2["Continue"] = "continue";
|
|
2427
|
+
PropagationAction2["ToScene"] = "scene";
|
|
2428
|
+
return PropagationAction2;
|
|
2429
|
+
})(PropagationAction || {});
|
|
2430
|
+
var WizardSceneAction = /* @__PURE__ */ ((WizardSceneAction2) => {
|
|
2431
|
+
WizardSceneAction2["Next"] = "next";
|
|
2432
|
+
WizardSceneAction2["Stay"] = "stay";
|
|
2433
|
+
WizardSceneAction2["Exit"] = "exit";
|
|
2434
|
+
return WizardSceneAction2;
|
|
2435
|
+
})(WizardSceneAction || {});
|
|
2436
|
+
class WizardScene extends Dispatcher {
|
|
2437
|
+
_steps = 0;
|
|
2438
|
+
_defaultState = {};
|
|
2439
|
+
constructor(name, params) {
|
|
2440
|
+
super(void 0, { sceneName: name, ...params });
|
|
2441
|
+
}
|
|
2442
|
+
setDefaultState(defaultState) {
|
|
2443
|
+
this._defaultState = defaultState;
|
|
2444
|
+
}
|
|
2445
|
+
/**
|
|
2446
|
+
* Get the total number of registered steps
|
|
2447
|
+
*/
|
|
2448
|
+
get totalSteps() {
|
|
2449
|
+
return this._steps;
|
|
2450
|
+
}
|
|
2451
|
+
/**
|
|
2452
|
+
* Go to the Nth step
|
|
2453
|
+
*/
|
|
2454
|
+
async goToStep(state2, step) {
|
|
2455
|
+
if (step >= this._steps) {
|
|
2456
|
+
await state2.exit();
|
|
2457
|
+
} else {
|
|
2458
|
+
await state2.merge({ $step: step }, this._defaultState);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
/**
|
|
2462
|
+
* Skip N steps
|
|
2463
|
+
*/
|
|
2464
|
+
async skip(state2, count = 1) {
|
|
2465
|
+
const { $step } = await state2.get() || {};
|
|
2466
|
+
if ($step === void 0) throw new Error("Wizard state is not initialized");
|
|
2467
|
+
return this.goToStep(state2, $step + count);
|
|
2468
|
+
}
|
|
2469
|
+
/**
|
|
2470
|
+
* Filter that will only pass if the current step is `step`
|
|
2471
|
+
*/
|
|
2472
|
+
// eslint-disable-next-line ts/no-empty-object-type
|
|
2473
|
+
static onNthStep(step) {
|
|
2474
|
+
const filter = state((it) => it.$step === step);
|
|
2475
|
+
if (step === 0) return or(stateEmpty, filter);
|
|
2476
|
+
return filter;
|
|
2477
|
+
}
|
|
2478
|
+
/**
|
|
2479
|
+
* Filter that will only pass if the current step is the one after last one added
|
|
2480
|
+
*/
|
|
2481
|
+
// eslint-disable-next-line ts/no-empty-object-type
|
|
2482
|
+
onCurrentStep() {
|
|
2483
|
+
return WizardScene.onNthStep(this._steps);
|
|
2484
|
+
}
|
|
2485
|
+
/**
|
|
2486
|
+
* Add a step to the wizard
|
|
2487
|
+
*/
|
|
2488
|
+
addStep(handler) {
|
|
2489
|
+
const step = this._steps++;
|
|
2490
|
+
this.onNewMessage(WizardScene.onNthStep(step), async (msg, state2) => {
|
|
2491
|
+
const result = await handler(msg, state2);
|
|
2492
|
+
if (typeof result === "number") {
|
|
2493
|
+
await this.goToStep(state2, result);
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
switch (result) {
|
|
2497
|
+
case "next": {
|
|
2498
|
+
await this.goToStep(state2, step + 1);
|
|
2499
|
+
break;
|
|
2500
|
+
}
|
|
2501
|
+
case "exit":
|
|
2502
|
+
await state2.exit();
|
|
2503
|
+
break;
|
|
2504
|
+
}
|
|
2505
|
+
});
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
export {
|
|
2509
|
+
BusinessCallbackQueryContext,
|
|
2510
|
+
BusinessMessageContext,
|
|
2511
|
+
CallbackDataBuilder,
|
|
2512
|
+
CallbackQueryContext,
|
|
2513
|
+
ChatJoinRequestUpdateContext,
|
|
2514
|
+
ChosenInlineResultContext,
|
|
2515
|
+
Dispatcher,
|
|
2516
|
+
InlineCallbackQueryContext,
|
|
2517
|
+
InlineQueryContext,
|
|
2518
|
+
MemoryStateStorage,
|
|
2519
|
+
MessageContext,
|
|
2520
|
+
PreCheckoutQueryContext,
|
|
2521
|
+
PropagationAction,
|
|
2522
|
+
RateLimitError,
|
|
2523
|
+
SqliteStateStorage,
|
|
2524
|
+
UpdateState,
|
|
2525
|
+
WizardScene,
|
|
2526
|
+
WizardSceneAction,
|
|
2527
|
+
defaultStateKeyDelegate,
|
|
2528
|
+
bundle as filters
|
|
2529
|
+
};
|