@xbibzlibrary/telebibz 0.1.2 → 0.1.3
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/CHANGELOG.md +16 -1
- package/README.id.md +158 -0
- package/README.md +53 -33
- package/README.zh-CN.md +158 -0
- package/RELEASE_AUTOMATION.md +66 -0
- package/RELEASE_POLICY.md +2 -2
- package/assets/readme-preview.html +75 -0
- package/assets/telebibz-readme-preview.png +0 -0
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js +1 -0
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/telegram-types/LICENSE +21 -0
- package/dist/src/api/telegram-types/api.d.ts +22 -0
- package/dist/src/api/telegram-types/checklist.d.ts +72 -0
- package/dist/src/api/telegram-types/inline.d.ts +692 -0
- package/dist/src/api/telegram-types/langs.d.ts +193 -0
- package/dist/src/api/telegram-types/manage.d.ts +1144 -0
- package/dist/src/api/telegram-types/markup.d.ts +268 -0
- package/dist/src/api/telegram-types/message.d.ts +1537 -0
- package/dist/src/api/telegram-types/methods.d.ts +2870 -0
- package/dist/src/api/telegram-types/mod.d.ts +14 -0
- package/dist/src/api/telegram-types/passport.d.ts +163 -0
- package/dist/src/api/telegram-types/payment.d.ts +570 -0
- package/dist/src/api/telegram-types/rich.d.ts +1010 -0
- package/dist/src/api/telegram-types/settings.d.ts +120 -0
- package/dist/src/api/telegram-types/story.d.ts +89 -0
- package/dist/src/api/telegram-types/update.d.ts +84 -0
- package/dist/src/api/telegram.d.ts +7 -0
- package/dist/src/api/telegram.d.ts.map +1 -0
- package/dist/src/api/telegram.js +2 -0
- package/dist/src/api/telegram.js.map +1 -0
- package/dist/src/approval/approval.d.ts +8 -0
- package/dist/src/approval/approval.d.ts.map +1 -1
- package/dist/src/approval/approval.js +9 -0
- package/dist/src/approval/approval.js.map +1 -1
- package/dist/src/cache/cache.d.ts +6 -5
- package/dist/src/cache/cache.d.ts.map +1 -1
- package/dist/src/cache/cache.js +7 -3
- package/dist/src/cache/cache.js.map +1 -1
- package/dist/src/context/context.d.ts.map +1 -1
- package/dist/src/context/context.js +26 -3
- package/dist/src/context/context.js.map +1 -1
- package/dist/src/core/bot.d.ts +6 -4
- package/dist/src/core/bot.d.ts.map +1 -1
- package/dist/src/core/bot.js +48 -7
- package/dist/src/core/bot.js.map +1 -1
- package/dist/src/core/events.d.ts +4 -0
- package/dist/src/core/events.d.ts.map +1 -1
- package/dist/src/core/events.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/queue/queue.d.ts +25 -0
- package/dist/src/queue/queue.d.ts.map +1 -1
- package/dist/src/queue/queue.js +175 -51
- package/dist/src/queue/queue.js.map +1 -1
- package/dist/src/router/router.d.ts +8 -1
- package/dist/src/router/router.d.ts.map +1 -1
- package/dist/src/router/router.js +75 -17
- package/dist/src/router/router.js.map +1 -1
- package/dist/src/state/conversation.d.ts +6 -0
- package/dist/src/state/conversation.d.ts.map +1 -1
- package/dist/src/state/conversation.js +79 -11
- package/dist/src/state/conversation.js.map +1 -1
- package/dist/src/state/menu.d.ts +53 -5
- package/dist/src/state/menu.d.ts.map +1 -1
- package/dist/src/state/menu.js +116 -17
- package/dist/src/state/menu.js.map +1 -1
- package/dist/src/storage/storage.d.ts +115 -12
- package/dist/src/storage/storage.d.ts.map +1 -1
- package/dist/src/storage/storage.js +130 -4
- package/dist/src/storage/storage.js.map +1 -1
- package/dist/src/telegram-features.d.ts +33 -0
- package/dist/src/telegram-features.d.ts.map +1 -0
- package/dist/src/telegram-features.js +69 -0
- package/dist/src/telegram-features.js.map +1 -0
- package/dist/src/testing.d.ts +1 -0
- package/dist/src/testing.d.ts.map +1 -1
- package/dist/src/testing.js +16 -0
- package/dist/src/testing.js.map +1 -1
- package/dist-cjs/src/api/index.js +1 -0
- package/dist-cjs/src/api/telegram-types/LICENSE +21 -0
- package/dist-cjs/src/api/telegram-types/api.d.ts +22 -0
- package/dist-cjs/src/api/telegram-types/checklist.d.ts +72 -0
- package/dist-cjs/src/api/telegram-types/inline.d.ts +692 -0
- package/dist-cjs/src/api/telegram-types/langs.d.ts +193 -0
- package/dist-cjs/src/api/telegram-types/manage.d.ts +1144 -0
- package/dist-cjs/src/api/telegram-types/markup.d.ts +268 -0
- package/dist-cjs/src/api/telegram-types/message.d.ts +1537 -0
- package/dist-cjs/src/api/telegram-types/methods.d.ts +2870 -0
- package/dist-cjs/src/api/telegram-types/mod.d.ts +14 -0
- package/dist-cjs/src/api/telegram-types/passport.d.ts +163 -0
- package/dist-cjs/src/api/telegram-types/payment.d.ts +570 -0
- package/dist-cjs/src/api/telegram-types/rich.d.ts +1010 -0
- package/dist-cjs/src/api/telegram-types/settings.d.ts +120 -0
- package/dist-cjs/src/api/telegram-types/story.d.ts +89 -0
- package/dist-cjs/src/api/telegram-types/update.d.ts +84 -0
- package/dist-cjs/src/api/telegram.js +2 -0
- package/dist-cjs/src/approval/approval.js +11 -1
- package/dist-cjs/src/cache/cache.js +7 -3
- package/dist-cjs/src/context/context.js +26 -3
- package/dist-cjs/src/core/bot.js +48 -7
- package/dist-cjs/src/index.js +1 -0
- package/dist-cjs/src/queue/queue.js +177 -51
- package/dist-cjs/src/router/router.js +75 -17
- package/dist-cjs/src/state/conversation.js +79 -11
- package/dist-cjs/src/state/menu.js +118 -18
- package/dist-cjs/src/storage/storage.js +135 -5
- package/dist-cjs/src/telegram-features.js +74 -0
- package/dist-cjs/src/testing.js +17 -0
- package/docs/API.id.md +1800 -0
- package/docs/API.md +1799 -0
- package/docs/API.zh-CN.md +1794 -0
- package/docs/README.md +26 -15
- package/package.json +13 -2
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MemoryStorage = void 0;
|
|
3
|
+
exports.MongoStorage = exports.SqlStorage = exports.RedisStorage = exports.JsonFileStorage = exports.MemoryStorage = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
function expiration(ttlMs) {
|
|
7
|
+
if (ttlMs === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (!Number.isFinite(ttlMs) || ttlMs < 0)
|
|
10
|
+
throw new RangeError("ttlMs must be a finite non-negative number");
|
|
11
|
+
return Date.now() + ttlMs;
|
|
12
|
+
}
|
|
4
13
|
class MemoryStorage {
|
|
5
14
|
valuesMap = new Map();
|
|
6
15
|
locks = new Map();
|
|
@@ -15,10 +24,8 @@ class MemoryStorage {
|
|
|
15
24
|
return entry.value;
|
|
16
25
|
}
|
|
17
26
|
async set(key, value, options = {}) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
entry.expiresAt = Date.now() + options.ttlMs;
|
|
21
|
-
this.valuesMap.set(key, entry);
|
|
27
|
+
const expiresAt = expiration(options.ttlMs);
|
|
28
|
+
this.valuesMap.set(key, expiresAt === undefined ? { value } : { value, expiresAt });
|
|
22
29
|
}
|
|
23
30
|
async delete(key) { return this.valuesMap.delete(key); }
|
|
24
31
|
async has(key) { return (await this.get(key)) !== undefined; }
|
|
@@ -52,3 +59,126 @@ class MemoryStorage {
|
|
|
52
59
|
}
|
|
53
60
|
}
|
|
54
61
|
exports.MemoryStorage = MemoryStorage;
|
|
62
|
+
class JsonFileStorage {
|
|
63
|
+
filePath;
|
|
64
|
+
memory = new MemoryStorage();
|
|
65
|
+
ready;
|
|
66
|
+
writeChain = Promise.resolve();
|
|
67
|
+
constructor(filePath) {
|
|
68
|
+
this.filePath = filePath;
|
|
69
|
+
this.ready = this.load();
|
|
70
|
+
}
|
|
71
|
+
async load() {
|
|
72
|
+
try {
|
|
73
|
+
const raw = await (0, promises_1.readFile)(this.filePath, "utf8");
|
|
74
|
+
const values = JSON.parse(raw);
|
|
75
|
+
for (const [key, entry] of Object.entries(values))
|
|
76
|
+
await this.memory.set(key, entry.value, entry.expiresAt === undefined ? {} : { ttlMs: Math.max(0, entry.expiresAt - Date.now()) });
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error.code !== "ENOENT")
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async persist() {
|
|
84
|
+
await this.ready;
|
|
85
|
+
this.writeChain = this.writeChain.then(async () => {
|
|
86
|
+
const output = {};
|
|
87
|
+
for await (const [key, value] of this.memory.entries())
|
|
88
|
+
output[key] = { value };
|
|
89
|
+
await (0, promises_1.mkdir)((0, node_path_1.dirname)(this.filePath), { recursive: true });
|
|
90
|
+
const temporary = `${this.filePath}.${process.pid}.tmp`;
|
|
91
|
+
await (0, promises_1.writeFile)(temporary, JSON.stringify(output, null, 2), "utf8");
|
|
92
|
+
await (0, promises_1.rename)(temporary, this.filePath);
|
|
93
|
+
});
|
|
94
|
+
await this.writeChain;
|
|
95
|
+
}
|
|
96
|
+
async get(key) { await this.ready; return this.memory.get(key); }
|
|
97
|
+
async set(key, value, options) { await this.ready; await this.memory.set(key, value, options); await this.persist(); }
|
|
98
|
+
async delete(key) { await this.ready; const deleted = await this.memory.delete(key); if (deleted)
|
|
99
|
+
await this.persist(); return deleted; }
|
|
100
|
+
async has(key) { await this.ready; return this.memory.has(key); }
|
|
101
|
+
async clear() { await this.ready; await this.memory.clear(); await this.persist(); }
|
|
102
|
+
async *keys() { await this.ready; yield* this.memory.keys(); }
|
|
103
|
+
async *values() { await this.ready; yield* this.memory.values(); }
|
|
104
|
+
async *entries() { await this.ready; yield* this.memory.entries(); }
|
|
105
|
+
async update(key, updater, options) { await this.ready; const result = await this.memory.update(key, updater, options); await this.persist(); return result; }
|
|
106
|
+
}
|
|
107
|
+
exports.JsonFileStorage = JsonFileStorage;
|
|
108
|
+
class RedisStorage {
|
|
109
|
+
client;
|
|
110
|
+
prefix;
|
|
111
|
+
constructor(client, prefix = "telebibz:") {
|
|
112
|
+
this.client = client;
|
|
113
|
+
this.prefix = prefix;
|
|
114
|
+
}
|
|
115
|
+
key(key) { return `${this.prefix}${key}`; }
|
|
116
|
+
unkey(key) { return key.slice(this.prefix.length); }
|
|
117
|
+
async get(key) { const value = await this.client.get(this.key(key)); return value === null ? undefined : JSON.parse(value); }
|
|
118
|
+
async set(key, value, options = {}) { const ttl = options.ttlMs; const redisOptions = ttl === undefined ? {} : ttl >= 1000 ? { PX: Math.max(1, Math.floor(ttl)) } : { PX: Math.max(1, Math.floor(ttl)) }; await this.client.set(this.key(key), JSON.stringify(value), redisOptions); }
|
|
119
|
+
async delete(key) { return (await this.client.del(this.key(key))) > 0; }
|
|
120
|
+
async has(key) { return (await this.client.exists(this.key(key))) > 0; }
|
|
121
|
+
async clear() { const keys = await this.client.keys(`${this.prefix}*`); if (keys.length)
|
|
122
|
+
await Promise.all(keys.map((key) => this.client.del(key))); }
|
|
123
|
+
async *keys() { for (const key of await this.client.keys(`${this.prefix}*`))
|
|
124
|
+
yield this.unkey(key); }
|
|
125
|
+
async *values() { for await (const [, value] of this.entries())
|
|
126
|
+
yield value; }
|
|
127
|
+
async *entries() { for await (const key of this.keys()) {
|
|
128
|
+
const value = await this.get(key);
|
|
129
|
+
if (value !== undefined)
|
|
130
|
+
yield [key, value];
|
|
131
|
+
} }
|
|
132
|
+
async update(key, updater, options) { const value = await updater(await this.get(key)); await this.set(key, value, options); return value; }
|
|
133
|
+
}
|
|
134
|
+
exports.RedisStorage = RedisStorage;
|
|
135
|
+
class SqlStorage {
|
|
136
|
+
driver;
|
|
137
|
+
constructor(driver) {
|
|
138
|
+
this.driver = driver;
|
|
139
|
+
}
|
|
140
|
+
async get(key) { return this.driver.get(key); }
|
|
141
|
+
async set(key, value, options = {}) { await this.driver.set(key, value, expiration(options.ttlMs)); }
|
|
142
|
+
async delete(key) { return this.driver.delete(key); }
|
|
143
|
+
async has(key) { return this.driver.has(key); }
|
|
144
|
+
async clear() { return this.driver.clear(); }
|
|
145
|
+
async *keys() { for (const [key] of await this.driver.entries())
|
|
146
|
+
yield key; }
|
|
147
|
+
async *values() { for (const [, value] of await this.driver.entries())
|
|
148
|
+
yield value; }
|
|
149
|
+
async *entries() { for (const [key, value, expiresAt] of await this.driver.entries()) {
|
|
150
|
+
if (expiresAt !== undefined && expiresAt <= Date.now()) {
|
|
151
|
+
await this.driver.delete(key);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
yield [key, value];
|
|
155
|
+
} }
|
|
156
|
+
async update(key, updater, options) { const value = await updater(await this.get(key)); await this.set(key, value, options); return value; }
|
|
157
|
+
}
|
|
158
|
+
exports.SqlStorage = SqlStorage;
|
|
159
|
+
class MongoStorage {
|
|
160
|
+
collection;
|
|
161
|
+
constructor(collection) {
|
|
162
|
+
this.collection = collection;
|
|
163
|
+
}
|
|
164
|
+
async get(key) { const document = await this.collection.findOne({ key }); if (!document || (document.expiresAt !== undefined && document.expiresAt <= Date.now()))
|
|
165
|
+
return undefined; return document.value; }
|
|
166
|
+
async set(key, value, options = {}) {
|
|
167
|
+
const expiresAt = expiration(options.ttlMs);
|
|
168
|
+
const document = expiresAt === undefined ? { key, value } : { key, value, expiresAt };
|
|
169
|
+
await this.collection.replaceOne({ key }, document, { upsert: true });
|
|
170
|
+
}
|
|
171
|
+
async delete(key) { return (await this.collection.deleteOne({ key })).deletedCount === 1; }
|
|
172
|
+
async has(key) { return (await this.get(key)) !== undefined; }
|
|
173
|
+
async clear() { await this.collection.deleteMany({}); }
|
|
174
|
+
async *keys() { for (const document of await this.collection.find({}).toArray())
|
|
175
|
+
if (document.expiresAt === undefined || document.expiresAt > Date.now())
|
|
176
|
+
yield document.key; }
|
|
177
|
+
async *values() { for await (const [, value] of this.entries())
|
|
178
|
+
yield value; }
|
|
179
|
+
async *entries() { for (const document of await this.collection.find({}).toArray())
|
|
180
|
+
if (document.expiresAt === undefined || document.expiresAt > Date.now())
|
|
181
|
+
yield [document.key, document.value]; }
|
|
182
|
+
async update(key, updater, options) { const value = await updater(await this.get(key)); await this.set(key, value, options); return value; }
|
|
183
|
+
}
|
|
184
|
+
exports.MongoStorage = MongoStorage;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PaymentsClient = void 0;
|
|
4
|
+
exports.parseWebAppInitData = parseWebAppInitData;
|
|
5
|
+
exports.validateWebAppInitData = validateWebAppInitData;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
function parseWebAppInitData(initData) {
|
|
8
|
+
const params = new URLSearchParams(initData);
|
|
9
|
+
const hash = params.get("hash");
|
|
10
|
+
if (!hash)
|
|
11
|
+
throw new Error("Web App init data is missing hash");
|
|
12
|
+
const data = {};
|
|
13
|
+
for (const [key, value] of params.entries())
|
|
14
|
+
if (key !== "hash")
|
|
15
|
+
data[key] = value;
|
|
16
|
+
const authDateRaw = data.auth_date;
|
|
17
|
+
const authDate = authDateRaw === undefined ? undefined : Number(authDateRaw);
|
|
18
|
+
if (authDate !== undefined && (!Number.isInteger(authDate) || authDate <= 0))
|
|
19
|
+
throw new Error("Web App init data has an invalid auth_date");
|
|
20
|
+
const parseObject = (key) => {
|
|
21
|
+
const value = data[key];
|
|
22
|
+
if (value === undefined)
|
|
23
|
+
return undefined;
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(value);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new Error(`Web App init data field ${key} is not valid JSON`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const result = { raw: initData, hash, data };
|
|
32
|
+
if (authDate !== undefined)
|
|
33
|
+
result.authDate = authDate;
|
|
34
|
+
const user = parseObject("user");
|
|
35
|
+
const receiver = parseObject("receiver");
|
|
36
|
+
const chat = parseObject("chat");
|
|
37
|
+
if (user !== undefined)
|
|
38
|
+
result.user = user;
|
|
39
|
+
if (receiver !== undefined)
|
|
40
|
+
result.receiver = receiver;
|
|
41
|
+
if (chat !== undefined)
|
|
42
|
+
result.chat = chat;
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function validateWebAppInitData(initData, botToken, maxAgeSeconds = 86_400, nowMs = Date.now()) {
|
|
46
|
+
if (!botToken)
|
|
47
|
+
throw new Error("botToken is required");
|
|
48
|
+
if (!Number.isFinite(maxAgeSeconds) || maxAgeSeconds < 0)
|
|
49
|
+
throw new RangeError("maxAgeSeconds must be non-negative");
|
|
50
|
+
const parsed = parseWebAppInitData(initData);
|
|
51
|
+
const checkString = Object.entries(parsed.data).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
52
|
+
const secret = (0, node_crypto_1.createHmac)("sha256", "WebAppData").update(botToken).digest();
|
|
53
|
+
const expected = (0, node_crypto_1.createHmac)("sha256", secret).update(checkString).digest("hex");
|
|
54
|
+
const actual = Buffer.from(parsed.hash, "hex");
|
|
55
|
+
const expectedBuffer = Buffer.from(expected, "hex");
|
|
56
|
+
if (actual.length !== expectedBuffer.length || !(0, node_crypto_1.timingSafeEqual)(actual, expectedBuffer))
|
|
57
|
+
throw new Error("Web App init data signature is invalid");
|
|
58
|
+
if (parsed.authDate !== undefined && nowMs / 1000 - parsed.authDate > maxAgeSeconds)
|
|
59
|
+
throw new Error("Web App init data has expired");
|
|
60
|
+
return parsed;
|
|
61
|
+
}
|
|
62
|
+
class PaymentsClient {
|
|
63
|
+
api;
|
|
64
|
+
constructor(api) {
|
|
65
|
+
this.api = api;
|
|
66
|
+
}
|
|
67
|
+
createInvoiceLink(payload) { return this.api.call("createInvoiceLink", payload); }
|
|
68
|
+
sendInvoice(payload) { return this.api.call("sendInvoice", payload); }
|
|
69
|
+
answerPreCheckoutQuery(payload) { return this.api.call("answerPreCheckoutQuery", payload); }
|
|
70
|
+
answerWebAppQuery(payload) { return this.api.call("answerWebAppQuery", payload); }
|
|
71
|
+
getStarTransactions(payload = {}) { return this.api.call("getStarTransactions", payload); }
|
|
72
|
+
refundStarPayment(payload) { return this.api.call("refundStarPayment", payload); }
|
|
73
|
+
}
|
|
74
|
+
exports.PaymentsClient = PaymentsClient;
|
package/dist-cjs/src/testing.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MockTransport = void 0;
|
|
4
4
|
exports.createMockUpdate = createMockUpdate;
|
|
5
|
+
exports.createMockCallbackUpdate = createMockCallbackUpdate;
|
|
5
6
|
exports.createTestBot = createTestBot;
|
|
6
7
|
exports.createMockContext = createMockContext;
|
|
7
8
|
const bot_js_1 = require("./core/bot.js");
|
|
@@ -14,5 +15,21 @@ class MockTransport {
|
|
|
14
15
|
}
|
|
15
16
|
exports.MockTransport = MockTransport;
|
|
16
17
|
function createMockUpdate(overrides = {}) { return { update_id: 1, message: { message_id: 1, date: Date.now(), chat: { id: 1, type: "private" }, from: { id: 2, is_bot: false, first_name: "Test" }, text: "/start" }, ...overrides }; }
|
|
18
|
+
function createMockCallbackUpdate(overrides = {}) {
|
|
19
|
+
const base = createMockUpdate();
|
|
20
|
+
delete base.message;
|
|
21
|
+
return {
|
|
22
|
+
...base,
|
|
23
|
+
update_id: 2,
|
|
24
|
+
callback_query: {
|
|
25
|
+
id: "callback-1",
|
|
26
|
+
from: { id: 2, is_bot: false, first_name: "Test" },
|
|
27
|
+
message: { message_id: 10, date: Date.now(), chat: { id: 1, type: "private" }, text: "Choose" },
|
|
28
|
+
chat_instance: "chat-instance",
|
|
29
|
+
data: "action",
|
|
30
|
+
},
|
|
31
|
+
...overrides,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
17
34
|
function createTestBot() { const transport = new MockTransport(); transport.respond("getMe", { ok: true, result: { id: 99, is_bot: true, first_name: "TestBot", username: "test_bot" } }); return { bot: new bot_js_1.Bot({ token: "123456:TEST_TOKEN", transport }), transport }; }
|
|
18
35
|
function createMockContext(bot, update = createMockUpdate()) { return new context_js_1.Context({ update, api: bot.api, session: {}, services: {} }); }
|