@xbibzlibrary/telebibz 0.4.4 → 3.0.1
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 +35 -138
- package/LICENSE +1 -1
- package/NOTICE.md +9 -4
- package/README.md +173 -242
- package/examples/01-quickstart.js +13 -0
- package/examples/02-menu-tombol.js +22 -0
- package/examples/03-wizard.js +29 -0
- package/examples/04-broadcast.js +25 -0
- package/examples/05-kirim-file.js +19 -0
- package/examples/06-menu.js +35 -0
- package/examples/07-inline-query.js +18 -0
- package/index.d.ts +81 -0
- package/index.js +42 -0
- package/lib/api.js +154 -0
- package/lib/broadcast.js +35 -0
- package/lib/composer.js +174 -0
- package/lib/context.js +190 -0
- package/lib/errors.js +35 -0
- package/lib/file.js +41 -0
- package/lib/inline-query.js +28 -0
- package/lib/keyboard.js +83 -0
- package/lib/logger.js +38 -0
- package/lib/menus.js +90 -0
- package/lib/net.js +115 -0
- package/lib/ratelimit.js +61 -0
- package/lib/runner.js +45 -0
- package/lib/session.js +36 -0
- package/lib/telebibz.js +190 -0
- package/lib/wizard.js +81 -0
- package/package.json +35 -97
- package/test/all.test.js +334 -0
- package/CODE_OF_CONDUCT.md +0 -37
- package/CONTRIBUTING.md +0 -59
- package/CONTRIBUTION_RULES.md +0 -41
- package/GOVERNANCE.md +0 -47
- package/README.id.md +0 -292
- package/README.zh-CN.md +0 -292
- package/RELEASE_AUTOMATION.md +0 -78
- package/RELEASE_POLICY.md +0 -32
- package/SECURITY.md +0 -47
- package/SHOWCASE.md +0 -29
- package/SUPPORT.md +0 -30
- package/assets/readme-preview.html +0 -75
- package/assets/telebibz-logo.png +0 -0
- package/assets/telebibz-readme-preview.png +0 -0
- package/bin/telebibz.mjs +0 -3
- package/dist/generated/api.d.ts +0 -13
- package/dist/generated/api.d.ts.map +0 -1
- package/dist/generated/api.js +0 -192
- package/dist/generated/api.js.map +0 -1
- package/dist/src/api/client.d.ts +0 -62
- package/dist/src/api/client.d.ts.map +0 -1
- package/dist/src/api/client.js +0 -104
- package/dist/src/api/client.js.map +0 -1
- package/dist/src/api/errors.d.ts +0 -45
- package/dist/src/api/errors.d.ts.map +0 -1
- package/dist/src/api/errors.js +0 -65
- package/dist/src/api/errors.js.map +0 -1
- package/dist/src/api/index.d.ts +0 -6
- package/dist/src/api/index.d.ts.map +0 -1
- package/dist/src/api/index.js +0 -6
- package/dist/src/api/index.js.map +0 -1
- package/dist/src/api/telegram-types/LICENSE +0 -21
- package/dist/src/api/telegram-types/api.d.ts +0 -22
- package/dist/src/api/telegram-types/checklist.d.ts +0 -72
- package/dist/src/api/telegram-types/inline.d.ts +0 -692
- package/dist/src/api/telegram-types/langs.d.ts +0 -193
- package/dist/src/api/telegram-types/manage.d.ts +0 -1144
- package/dist/src/api/telegram-types/markup.d.ts +0 -268
- package/dist/src/api/telegram-types/message.d.ts +0 -1537
- package/dist/src/api/telegram-types/methods.d.ts +0 -2870
- package/dist/src/api/telegram-types/mod.d.ts +0 -14
- package/dist/src/api/telegram-types/passport.d.ts +0 -163
- package/dist/src/api/telegram-types/payment.d.ts +0 -570
- package/dist/src/api/telegram-types/rich.d.ts +0 -1010
- package/dist/src/api/telegram-types/settings.d.ts +0 -120
- package/dist/src/api/telegram-types/story.d.ts +0 -89
- package/dist/src/api/telegram-types/update.d.ts +0 -84
- package/dist/src/api/telegram.d.ts +0 -7
- package/dist/src/api/telegram.d.ts.map +0 -1
- package/dist/src/api/telegram.js +0 -2
- package/dist/src/api/telegram.js.map +0 -1
- package/dist/src/api/transport.d.ts +0 -68
- package/dist/src/api/transport.d.ts.map +0 -1
- package/dist/src/api/transport.js +0 -264
- package/dist/src/api/transport.js.map +0 -1
- package/dist/src/api/types.d.ts +0 -466
- package/dist/src/api/types.d.ts.map +0 -1
- package/dist/src/api/types.js +0 -2
- package/dist/src/api/types.js.map +0 -1
- package/dist/src/branding/terminal.d.ts +0 -77
- package/dist/src/branding/terminal.d.ts.map +0 -1
- package/dist/src/branding/terminal.js +0 -328
- package/dist/src/branding/terminal.js.map +0 -1
- package/dist/src/broadcast/broadcast.d.ts +0 -50
- package/dist/src/broadcast/broadcast.d.ts.map +0 -1
- package/dist/src/broadcast/broadcast.js +0 -56
- package/dist/src/broadcast/broadcast.js.map +0 -1
- package/dist/src/cache/cache.d.ts +0 -34
- package/dist/src/cache/cache.d.ts.map +0 -1
- package/dist/src/cache/cache.js +0 -41
- package/dist/src/cache/cache.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -84
- package/dist/src/cli.js.map +0 -1
- package/dist/src/context/context.d.ts +0 -124
- package/dist/src/context/context.d.ts.map +0 -1
- package/dist/src/context/context.js +0 -302
- package/dist/src/context/context.js.map +0 -1
- package/dist/src/core/bot.d.ts +0 -204
- package/dist/src/core/bot.d.ts.map +0 -1
- package/dist/src/core/bot.js +0 -506
- package/dist/src/core/bot.js.map +0 -1
- package/dist/src/core/events.d.ts +0 -75
- package/dist/src/core/events.d.ts.map +0 -1
- package/dist/src/core/events.js +0 -35
- package/dist/src/core/events.js.map +0 -1
- package/dist/src/core/webhook-reply.d.ts +0 -34
- package/dist/src/core/webhook-reply.d.ts.map +0 -1
- package/dist/src/core/webhook-reply.js +0 -37
- package/dist/src/core/webhook-reply.js.map +0 -1
- package/dist/src/index.d.ts +0 -24
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -24
- package/dist/src/index.js.map +0 -1
- package/dist/src/keyboard/index.d.ts +0 -46
- package/dist/src/keyboard/index.d.ts.map +0 -1
- package/dist/src/keyboard/index.js +0 -55
- package/dist/src/keyboard/index.js.map +0 -1
- package/dist/src/middleware/compose.d.ts +0 -5
- package/dist/src/middleware/compose.d.ts.map +0 -1
- package/dist/src/middleware/compose.js +0 -17
- package/dist/src/middleware/compose.js.map +0 -1
- package/dist/src/observability/logger.d.ts +0 -78
- package/dist/src/observability/logger.d.ts.map +0 -1
- package/dist/src/observability/logger.js +0 -285
- package/dist/src/observability/logger.js.map +0 -1
- package/dist/src/plugins/plugin.d.ts +0 -38
- package/dist/src/plugins/plugin.d.ts.map +0 -1
- package/dist/src/plugins/plugin.js +0 -59
- package/dist/src/plugins/plugin.js.map +0 -1
- package/dist/src/queue/queue.d.ts +0 -77
- package/dist/src/queue/queue.d.ts.map +0 -1
- package/dist/src/queue/queue.js +0 -213
- package/dist/src/queue/queue.js.map +0 -1
- package/dist/src/router/router.d.ts +0 -61
- package/dist/src/router/router.d.ts.map +0 -1
- package/dist/src/router/router.js +0 -183
- package/dist/src/router/router.js.map +0 -1
- package/dist/src/state/conversation.d.ts +0 -56
- package/dist/src/state/conversation.d.ts.map +0 -1
- package/dist/src/state/conversation.js +0 -133
- package/dist/src/state/conversation.js.map +0 -1
- package/dist/src/state/forms.d.ts +0 -34
- package/dist/src/state/forms.d.ts.map +0 -1
- package/dist/src/state/forms.js +0 -44
- package/dist/src/state/forms.js.map +0 -1
- package/dist/src/state/menu.d.ts +0 -78
- package/dist/src/state/menu.d.ts.map +0 -1
- package/dist/src/state/menu.js +0 -127
- package/dist/src/state/menu.js.map +0 -1
- package/dist/src/storage/storage.d.ts +0 -146
- package/dist/src/storage/storage.d.ts.map +0 -1
- package/dist/src/storage/storage.js +0 -195
- package/dist/src/storage/storage.js.map +0 -1
- package/dist/src/telegram-features.d.ts +0 -33
- package/dist/src/telegram-features.d.ts.map +0 -1
- package/dist/src/telegram-features.js +0 -71
- package/dist/src/telegram-features.js.map +0 -1
- package/dist/src/testing.d.ts +0 -24
- package/dist/src/testing.d.ts.map +0 -1
- package/dist/src/testing.js +0 -38
- package/dist/src/testing.js.map +0 -1
- package/dist/src/utils/concurrency.d.ts +0 -25
- package/dist/src/utils/concurrency.d.ts.map +0 -1
- package/dist/src/utils/concurrency.js +0 -52
- package/dist/src/utils/concurrency.js.map +0 -1
- package/dist/src/utils/files.d.ts +0 -45
- package/dist/src/utils/files.d.ts.map +0 -1
- package/dist/src/utils/files.js +0 -53
- package/dist/src/utils/files.js.map +0 -1
- package/dist/src/utils/text.d.ts +0 -39
- package/dist/src/utils/text.d.ts.map +0 -1
- package/dist/src/utils/text.js +0 -56
- package/dist/src/utils/text.js.map +0 -1
- package/dist/src/webhook/handler.d.ts +0 -19
- package/dist/src/webhook/handler.d.ts.map +0 -1
- package/dist/src/webhook/handler.js +0 -141
- package/dist/src/webhook/handler.js.map +0 -1
- package/dist-cjs/generated/api.js +0 -194
- package/dist-cjs/package.json +0 -3
- package/dist-cjs/src/api/client.js +0 -107
- package/dist-cjs/src/api/errors.js +0 -74
- package/dist-cjs/src/api/index.js +0 -21
- package/dist-cjs/src/api/telegram-types/LICENSE +0 -21
- package/dist-cjs/src/api/telegram-types/api.d.ts +0 -22
- package/dist-cjs/src/api/telegram-types/checklist.d.ts +0 -72
- package/dist-cjs/src/api/telegram-types/inline.d.ts +0 -692
- package/dist-cjs/src/api/telegram-types/langs.d.ts +0 -193
- package/dist-cjs/src/api/telegram-types/manage.d.ts +0 -1144
- package/dist-cjs/src/api/telegram-types/markup.d.ts +0 -268
- package/dist-cjs/src/api/telegram-types/message.d.ts +0 -1537
- package/dist-cjs/src/api/telegram-types/methods.d.ts +0 -2870
- package/dist-cjs/src/api/telegram-types/mod.d.ts +0 -14
- package/dist-cjs/src/api/telegram-types/passport.d.ts +0 -163
- package/dist-cjs/src/api/telegram-types/payment.d.ts +0 -570
- package/dist-cjs/src/api/telegram-types/rich.d.ts +0 -1010
- package/dist-cjs/src/api/telegram-types/settings.d.ts +0 -120
- package/dist-cjs/src/api/telegram-types/story.d.ts +0 -89
- package/dist-cjs/src/api/telegram-types/update.d.ts +0 -84
- package/dist-cjs/src/api/telegram.js +0 -2
- package/dist-cjs/src/api/transport.js +0 -267
- package/dist-cjs/src/api/types.js +0 -2
- package/dist-cjs/src/branding/terminal.js +0 -338
- package/dist-cjs/src/broadcast/broadcast.js +0 -58
- package/dist-cjs/src/cache/cache.js +0 -45
- package/dist-cjs/src/cli.js +0 -86
- package/dist-cjs/src/context/context.js +0 -305
- package/dist-cjs/src/core/bot.js +0 -510
- package/dist-cjs/src/core/events.js +0 -38
- package/dist-cjs/src/core/webhook-reply.js +0 -42
- package/dist-cjs/src/index.js +0 -47
- package/dist-cjs/src/keyboard/index.js +0 -61
- package/dist-cjs/src/middleware/compose.js +0 -20
- package/dist-cjs/src/observability/logger.js +0 -293
- package/dist-cjs/src/plugins/plugin.js +0 -63
- package/dist-cjs/src/queue/queue.js +0 -219
- package/dist-cjs/src/router/router.js +0 -186
- package/dist-cjs/src/state/conversation.js +0 -139
- package/dist-cjs/src/state/forms.js +0 -47
- package/dist-cjs/src/state/menu.js +0 -133
- package/dist-cjs/src/storage/storage.js +0 -202
- package/dist-cjs/src/telegram-features.js +0 -76
- package/dist-cjs/src/testing.js +0 -45
- package/dist-cjs/src/utils/concurrency.js +0 -57
- package/dist-cjs/src/utils/files.js +0 -58
- package/dist-cjs/src/utils/text.js +0 -63
- package/dist-cjs/src/webhook/handler.js +0 -144
- package/docs/API.id.md +0 -1935
- package/docs/API.md +0 -1969
- package/docs/API.zh-CN.md +0 -1929
- package/docs/GETTING_STARTED.id.md +0 -85
- package/docs/GETTING_STARTED.md +0 -85
- package/docs/GETTING_STARTED.zh-CN.md +0 -85
- package/docs/GITHUB_PACKAGES.id.md +0 -82
- package/docs/GITHUB_PACKAGES.md +0 -82
- package/docs/GITHUB_PACKAGES.zh-CN.md +0 -82
- package/docs/README.md +0 -48
- package/docs/STORAGE.id.md +0 -105
- package/docs/STORAGE.md +0 -105
- package/docs/STORAGE.zh-CN.md +0 -105
- package/examples/README.md +0 -37
- package/examples/files.ts +0 -35
- package/examples/minimal.ts +0 -12
- package/examples/tsconfig.json +0 -9
- package/examples/webhook.ts +0 -42
- package/examples/wizard-registration.ts +0 -42
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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
|
-
}
|
|
13
|
-
class MemoryStorage {
|
|
14
|
-
valuesMap = new Map();
|
|
15
|
-
locks = new Map();
|
|
16
|
-
async get(key) {
|
|
17
|
-
const entry = this.valuesMap.get(key);
|
|
18
|
-
if (!entry)
|
|
19
|
-
return undefined;
|
|
20
|
-
if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {
|
|
21
|
-
this.valuesMap.delete(key);
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
return entry.value;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Reads the raw entry including expiry metadata. Intended for storage adapters
|
|
28
|
-
* (such as `JsonFileStorage`) that need to persist TTL information across restarts.
|
|
29
|
-
*/
|
|
30
|
-
async readEntry(key) {
|
|
31
|
-
const entry = this.valuesMap.get(key);
|
|
32
|
-
if (!entry)
|
|
33
|
-
return undefined;
|
|
34
|
-
if (entry.expiresAt !== undefined && entry.expiresAt <= Date.now()) {
|
|
35
|
-
this.valuesMap.delete(key);
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
return entry.expiresAt === undefined ? { value: entry.value } : { value: entry.value, expiresAt: entry.expiresAt };
|
|
39
|
-
}
|
|
40
|
-
async set(key, value, options = {}) {
|
|
41
|
-
const expiresAt = expiration(options.ttlMs);
|
|
42
|
-
this.valuesMap.set(key, expiresAt === undefined ? { value } : { value, expiresAt });
|
|
43
|
-
}
|
|
44
|
-
async delete(key) { return this.valuesMap.delete(key); }
|
|
45
|
-
async has(key) { return (await this.get(key)) !== undefined; }
|
|
46
|
-
async clear() { this.valuesMap.clear(); }
|
|
47
|
-
async *keys() { for (const key of this.valuesMap.keys())
|
|
48
|
-
if (await this.has(key))
|
|
49
|
-
yield key; }
|
|
50
|
-
async *values() { for await (const [, value] of this.entries())
|
|
51
|
-
yield value; }
|
|
52
|
-
async *entries() { for (const key of this.valuesMap.keys()) {
|
|
53
|
-
const value = await this.get(key);
|
|
54
|
-
if (value !== undefined)
|
|
55
|
-
yield [key, value];
|
|
56
|
-
} }
|
|
57
|
-
async update(key, updater, options = {}) {
|
|
58
|
-
const previous = this.locks.get(key) ?? Promise.resolve();
|
|
59
|
-
let release;
|
|
60
|
-
const currentLock = new Promise((resolve) => { release = resolve; });
|
|
61
|
-
this.locks.set(key, currentLock);
|
|
62
|
-
await previous;
|
|
63
|
-
try {
|
|
64
|
-
const value = await updater(await this.get(key));
|
|
65
|
-
await this.set(key, value, options);
|
|
66
|
-
return value;
|
|
67
|
-
}
|
|
68
|
-
finally {
|
|
69
|
-
release();
|
|
70
|
-
if (this.locks.get(key) === currentLock)
|
|
71
|
-
this.locks.delete(key);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
exports.MemoryStorage = MemoryStorage;
|
|
76
|
-
class JsonFileStorage {
|
|
77
|
-
filePath;
|
|
78
|
-
memory = new MemoryStorage();
|
|
79
|
-
ready;
|
|
80
|
-
writeChain = Promise.resolve();
|
|
81
|
-
constructor(filePath) {
|
|
82
|
-
this.filePath = filePath;
|
|
83
|
-
this.ready = this.load();
|
|
84
|
-
}
|
|
85
|
-
async load() {
|
|
86
|
-
try {
|
|
87
|
-
const raw = await (0, promises_1.readFile)(this.filePath, "utf8");
|
|
88
|
-
const values = JSON.parse(raw);
|
|
89
|
-
for (const [key, entry] of Object.entries(values))
|
|
90
|
-
await this.memory.set(key, entry.value, entry.expiresAt === undefined ? {} : { ttlMs: Math.max(0, entry.expiresAt - Date.now()) });
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
if (error.code !== "ENOENT")
|
|
94
|
-
throw error;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
async persist() {
|
|
98
|
-
await this.ready;
|
|
99
|
-
this.writeChain = this.writeChain.then(async () => {
|
|
100
|
-
const output = {};
|
|
101
|
-
for await (const key of this.memory.keys()) {
|
|
102
|
-
const entry = await this.memory.readEntry(key);
|
|
103
|
-
if (entry === undefined)
|
|
104
|
-
continue;
|
|
105
|
-
output[key] = entry.expiresAt === undefined ? { value: entry.value } : { value: entry.value, expiresAt: entry.expiresAt };
|
|
106
|
-
}
|
|
107
|
-
await (0, promises_1.mkdir)((0, node_path_1.dirname)(this.filePath), { recursive: true });
|
|
108
|
-
const temporary = `${this.filePath}.${process.pid}.tmp`;
|
|
109
|
-
await (0, promises_1.writeFile)(temporary, JSON.stringify(output, null, 2), "utf8");
|
|
110
|
-
await (0, promises_1.rename)(temporary, this.filePath);
|
|
111
|
-
});
|
|
112
|
-
await this.writeChain;
|
|
113
|
-
}
|
|
114
|
-
async get(key) { await this.ready; return this.memory.get(key); }
|
|
115
|
-
async set(key, value, options) { await this.ready; await this.memory.set(key, value, options); await this.persist(); }
|
|
116
|
-
async delete(key) { await this.ready; const deleted = await this.memory.delete(key); if (deleted)
|
|
117
|
-
await this.persist(); return deleted; }
|
|
118
|
-
async has(key) { await this.ready; return this.memory.has(key); }
|
|
119
|
-
async clear() { await this.ready; await this.memory.clear(); await this.persist(); }
|
|
120
|
-
async *keys() { await this.ready; yield* this.memory.keys(); }
|
|
121
|
-
async *values() { await this.ready; yield* this.memory.values(); }
|
|
122
|
-
async *entries() { await this.ready; yield* this.memory.entries(); }
|
|
123
|
-
async update(key, updater, options) { await this.ready; const result = await this.memory.update(key, updater, options); await this.persist(); return result; }
|
|
124
|
-
}
|
|
125
|
-
exports.JsonFileStorage = JsonFileStorage;
|
|
126
|
-
class RedisStorage {
|
|
127
|
-
client;
|
|
128
|
-
prefix;
|
|
129
|
-
constructor(client, prefix = "telebibz:") {
|
|
130
|
-
this.client = client;
|
|
131
|
-
this.prefix = prefix;
|
|
132
|
-
}
|
|
133
|
-
key(key) { return `${this.prefix}${key}`; }
|
|
134
|
-
unkey(key) { return key.slice(this.prefix.length); }
|
|
135
|
-
async get(key) { const value = await this.client.get(this.key(key)); return value === null ? undefined : JSON.parse(value); }
|
|
136
|
-
async set(key, value, options = {}) { const ttl = options.ttlMs; const redisOptions = ttl === undefined ? {} : { PX: Math.max(1, Math.floor(ttl)) }; await this.client.set(this.key(key), JSON.stringify(value), redisOptions); }
|
|
137
|
-
async delete(key) { return (await this.client.del(this.key(key))) > 0; }
|
|
138
|
-
async has(key) { return (await this.client.exists(this.key(key))) > 0; }
|
|
139
|
-
async clear() { const keys = await this.client.keys(`${this.prefix}*`); if (keys.length)
|
|
140
|
-
await Promise.all(keys.map((key) => this.client.del(key))); }
|
|
141
|
-
async *keys() { for (const key of await this.client.keys(`${this.prefix}*`))
|
|
142
|
-
yield this.unkey(key); }
|
|
143
|
-
async *values() { for await (const [, value] of this.entries())
|
|
144
|
-
yield value; }
|
|
145
|
-
async *entries() { for await (const key of this.keys()) {
|
|
146
|
-
const value = await this.get(key);
|
|
147
|
-
if (value !== undefined)
|
|
148
|
-
yield [key, value];
|
|
149
|
-
} }
|
|
150
|
-
async update(key, updater, options) { const value = await updater(await this.get(key)); await this.set(key, value, options); return value; }
|
|
151
|
-
}
|
|
152
|
-
exports.RedisStorage = RedisStorage;
|
|
153
|
-
class SqlStorage {
|
|
154
|
-
driver;
|
|
155
|
-
constructor(driver) {
|
|
156
|
-
this.driver = driver;
|
|
157
|
-
}
|
|
158
|
-
async get(key) { return this.driver.get(key); }
|
|
159
|
-
async set(key, value, options = {}) { await this.driver.set(key, value, expiration(options.ttlMs)); }
|
|
160
|
-
async delete(key) { return this.driver.delete(key); }
|
|
161
|
-
async has(key) { return this.driver.has(key); }
|
|
162
|
-
async clear() { return this.driver.clear(); }
|
|
163
|
-
async *keys() { for (const [key] of await this.driver.entries())
|
|
164
|
-
yield key; }
|
|
165
|
-
async *values() { for (const [, value] of await this.driver.entries())
|
|
166
|
-
yield value; }
|
|
167
|
-
async *entries() { for (const [key, value, expiresAt] of await this.driver.entries()) {
|
|
168
|
-
if (expiresAt !== undefined && expiresAt <= Date.now()) {
|
|
169
|
-
await this.driver.delete(key);
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
yield [key, value];
|
|
173
|
-
} }
|
|
174
|
-
async update(key, updater, options) { const value = await updater(await this.get(key)); await this.set(key, value, options); return value; }
|
|
175
|
-
}
|
|
176
|
-
exports.SqlStorage = SqlStorage;
|
|
177
|
-
class MongoStorage {
|
|
178
|
-
collection;
|
|
179
|
-
constructor(collection) {
|
|
180
|
-
this.collection = collection;
|
|
181
|
-
}
|
|
182
|
-
async get(key) { const document = await this.collection.findOne({ key }); if (!document || (document.expiresAt !== undefined && document.expiresAt <= Date.now()))
|
|
183
|
-
return undefined; return document.value; }
|
|
184
|
-
async set(key, value, options = {}) {
|
|
185
|
-
const expiresAt = expiration(options.ttlMs);
|
|
186
|
-
const document = expiresAt === undefined ? { key, value } : { key, value, expiresAt };
|
|
187
|
-
await this.collection.replaceOne({ key }, document, { upsert: true });
|
|
188
|
-
}
|
|
189
|
-
async delete(key) { return (await this.collection.deleteOne({ key })).deletedCount === 1; }
|
|
190
|
-
async has(key) { return (await this.get(key)) !== undefined; }
|
|
191
|
-
async clear() { await this.collection.deleteMany({}); }
|
|
192
|
-
async *keys() { for (const document of await this.collection.find({}).toArray())
|
|
193
|
-
if (document.expiresAt === undefined || document.expiresAt > Date.now())
|
|
194
|
-
yield document.key; }
|
|
195
|
-
async *values() { for await (const [, value] of this.entries())
|
|
196
|
-
yield value; }
|
|
197
|
-
async *entries() { for (const document of await this.collection.find({}).toArray())
|
|
198
|
-
if (document.expiresAt === undefined || document.expiresAt > Date.now())
|
|
199
|
-
yield [document.key, document.value]; }
|
|
200
|
-
async update(key, updater, options) { const value = await updater(await this.get(key)); await this.set(key, value, options); return value; }
|
|
201
|
-
}
|
|
202
|
-
exports.MongoStorage = MongoStorage;
|
|
@@ -1,76 +0,0 @@
|
|
|
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
|
-
if (!/^[0-9a-fA-F]{64}$/.test(parsed.hash))
|
|
52
|
-
throw new Error("Web App init data hash must be a 64-character hexadecimal string");
|
|
53
|
-
const checkString = Object.entries(parsed.data).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
54
|
-
const secret = (0, node_crypto_1.createHmac)("sha256", "WebAppData").update(botToken).digest();
|
|
55
|
-
const expected = (0, node_crypto_1.createHmac)("sha256", secret).update(checkString).digest("hex");
|
|
56
|
-
const actual = Buffer.from(parsed.hash, "hex");
|
|
57
|
-
const expectedBuffer = Buffer.from(expected, "hex");
|
|
58
|
-
if (actual.length !== expectedBuffer.length || !(0, node_crypto_1.timingSafeEqual)(actual, expectedBuffer))
|
|
59
|
-
throw new Error("Web App init data signature is invalid");
|
|
60
|
-
if (parsed.authDate !== undefined && nowMs / 1000 - parsed.authDate > maxAgeSeconds)
|
|
61
|
-
throw new Error("Web App init data has expired");
|
|
62
|
-
return parsed;
|
|
63
|
-
}
|
|
64
|
-
class PaymentsClient {
|
|
65
|
-
api;
|
|
66
|
-
constructor(api) {
|
|
67
|
-
this.api = api;
|
|
68
|
-
}
|
|
69
|
-
createInvoiceLink(payload) { return this.api.call("createInvoiceLink", payload); }
|
|
70
|
-
sendInvoice(payload) { return this.api.call("sendInvoice", payload); }
|
|
71
|
-
answerPreCheckoutQuery(payload) { return this.api.call("answerPreCheckoutQuery", payload); }
|
|
72
|
-
answerWebAppQuery(payload) { return this.api.call("answerWebAppQuery", payload); }
|
|
73
|
-
getStarTransactions(payload = {}) { return this.api.call("getStarTransactions", payload); }
|
|
74
|
-
refundStarPayment(payload) { return this.api.call("refundStarPayment", payload); }
|
|
75
|
-
}
|
|
76
|
-
exports.PaymentsClient = PaymentsClient;
|
package/dist-cjs/src/testing.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MockTransport = void 0;
|
|
4
|
-
exports.createMockUpdate = createMockUpdate;
|
|
5
|
-
exports.createMockCallbackUpdate = createMockCallbackUpdate;
|
|
6
|
-
exports.createTestBot = createTestBot;
|
|
7
|
-
exports.createMockContext = createMockContext;
|
|
8
|
-
const bot_js_1 = require("./core/bot.js");
|
|
9
|
-
const context_js_1 = require("./context/context.js");
|
|
10
|
-
class MockTransport {
|
|
11
|
-
calls = [];
|
|
12
|
-
/** Downloads recorded through `download()` (the `file_path` values, in order). */
|
|
13
|
-
downloads = [];
|
|
14
|
-
/** Bytes returned by `download()`; defaults to the UTF-8 encoding of the file path. */
|
|
15
|
-
downloadBytes;
|
|
16
|
-
responses = new Map();
|
|
17
|
-
respond(method, response) { this.responses.set(method, response); return this; }
|
|
18
|
-
async request(request) { this.calls.push(request); const configured = this.responses.get(request.method); const data = typeof configured === "function" ? configured(request.payload) : configured ?? { ok: true, result: true }; return { status: data.ok ? 200 : (data.error_code ?? 500), headers: new Headers(), data: data }; }
|
|
19
|
-
fileUrl(filePath) { return `mock://files/${filePath}`; }
|
|
20
|
-
async download(filePath) { this.downloads.push(filePath); return this.downloadBytes ?? new TextEncoder().encode(filePath); }
|
|
21
|
-
}
|
|
22
|
-
exports.MockTransport = MockTransport;
|
|
23
|
-
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 }; }
|
|
24
|
-
function createMockCallbackUpdate(overrides = {}) {
|
|
25
|
-
const base = createMockUpdate();
|
|
26
|
-
delete base.message;
|
|
27
|
-
return {
|
|
28
|
-
...base,
|
|
29
|
-
update_id: 2,
|
|
30
|
-
callback_query: {
|
|
31
|
-
id: "callback-1",
|
|
32
|
-
from: { id: 2, is_bot: false, first_name: "Test" },
|
|
33
|
-
message: { message_id: 10, date: Date.now(), chat: { id: 1, type: "private" }, text: "Choose" },
|
|
34
|
-
chat_instance: "chat-instance",
|
|
35
|
-
data: "action",
|
|
36
|
-
},
|
|
37
|
-
...overrides,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
function createTestBot() {
|
|
41
|
-
const transport = new MockTransport();
|
|
42
|
-
transport.respond("getMe", { ok: true, result: { id: 99, is_bot: true, first_name: "TestBot", username: "test_bot" } });
|
|
43
|
-
return { bot: new bot_js_1.Bot({ token: "123456:TEST_TOKEN", transport }), transport };
|
|
44
|
-
}
|
|
45
|
-
function createMockContext(bot, update = createMockUpdate()) { return new context_js_1.Context({ update, api: bot.api, session: {}, services: {} }); }
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Minimal concurrency primitives with zero dependencies.
|
|
4
|
-
*
|
|
5
|
-
* These helpers never add proactive delays: they only cap how many async tasks
|
|
6
|
-
* run at the same time. `Infinity` means fully parallel execution.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Limiter = void 0;
|
|
10
|
-
exports.validateConcurrency = validateConcurrency;
|
|
11
|
-
exports.mapWithConcurrency = mapWithConcurrency;
|
|
12
|
-
function validateConcurrency(value, label = "concurrency") {
|
|
13
|
-
if (value === Infinity)
|
|
14
|
-
return;
|
|
15
|
-
if (!Number.isInteger(value) || value < 1)
|
|
16
|
-
throw new RangeError(`${label} must be a positive integer or Infinity`);
|
|
17
|
-
}
|
|
18
|
-
/** Promise semaphore: runs tasks immediately while a slot is free, queues the rest in FIFO order. */
|
|
19
|
-
class Limiter {
|
|
20
|
-
limit;
|
|
21
|
-
active = 0;
|
|
22
|
-
waiters = [];
|
|
23
|
-
constructor(limit) {
|
|
24
|
-
this.limit = limit;
|
|
25
|
-
validateConcurrency(limit);
|
|
26
|
-
}
|
|
27
|
-
/** Number of tasks currently running. */
|
|
28
|
-
get activeCount() { return this.active; }
|
|
29
|
-
/** Number of tasks waiting for a free slot. */
|
|
30
|
-
get queuedCount() { return this.waiters.length; }
|
|
31
|
-
async run(task) {
|
|
32
|
-
if (this.active >= this.limit)
|
|
33
|
-
await new Promise((resolve) => { this.waiters.push(resolve); });
|
|
34
|
-
this.active += 1;
|
|
35
|
-
try {
|
|
36
|
-
return await task();
|
|
37
|
-
}
|
|
38
|
-
finally {
|
|
39
|
-
this.active -= 1;
|
|
40
|
-
this.waiters.shift()?.();
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
exports.Limiter = Limiter;
|
|
45
|
-
/**
|
|
46
|
-
* Maps items through an async worker with a concurrency cap.
|
|
47
|
-
* Results keep the input order; `Infinity` runs everything in parallel.
|
|
48
|
-
*/
|
|
49
|
-
async function mapWithConcurrency(items, limit, worker) {
|
|
50
|
-
validateConcurrency(limit);
|
|
51
|
-
const results = new Array(items.length);
|
|
52
|
-
const limiter = new Limiter(limit);
|
|
53
|
-
await Promise.all(items.map((item, index) => limiter.run(async () => {
|
|
54
|
-
results[index] = await worker(item, index);
|
|
55
|
-
})));
|
|
56
|
-
return results;
|
|
57
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* File-upload validation helpers. Telegram caps uploads at 50 MB for most
|
|
4
|
-
* methods (10 MB for photos) and rejects mismatches between content and
|
|
5
|
-
* declared type; these helpers let applications enforce their own limits
|
|
6
|
-
* before a byte leaves the process.
|
|
7
|
-
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.UploadValidationError = void 0;
|
|
10
|
-
exports.validateUpload = validateUpload;
|
|
11
|
-
exports.assertValidUpload = assertValidUpload;
|
|
12
|
-
/** Thrown by {@link assertValidUpload} when an upload violates the rules. */
|
|
13
|
-
class UploadValidationError extends Error {
|
|
14
|
-
name = "UploadValidationError";
|
|
15
|
-
issues;
|
|
16
|
-
constructor(issues) {
|
|
17
|
-
super(`Upload rejected: ${issues.map((issue) => issue.message).join("; ")}`);
|
|
18
|
-
this.issues = issues;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
exports.UploadValidationError = UploadValidationError;
|
|
22
|
-
function normalizeExtension(extension) {
|
|
23
|
-
const value = extension.trim().toLowerCase();
|
|
24
|
-
return value.startsWith(".") ? value.slice(1) : value;
|
|
25
|
-
}
|
|
26
|
-
function mimeMatches(pattern, mimeType) {
|
|
27
|
-
const normalizedPattern = pattern.trim().toLowerCase();
|
|
28
|
-
if (normalizedPattern.endsWith("/*"))
|
|
29
|
-
return mimeType.toLowerCase().startsWith(normalizedPattern.slice(0, -1));
|
|
30
|
-
return mimeType.toLowerCase() === normalizedPattern;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Validates an upload against the rules and returns every violation found
|
|
34
|
-
* (an empty array means the upload is acceptable).
|
|
35
|
-
*/
|
|
36
|
-
function validateUpload(upload, rules) {
|
|
37
|
-
const issues = [];
|
|
38
|
-
if (rules.maxBytes !== undefined && upload.sizeBytes !== undefined && upload.sizeBytes > rules.maxBytes) {
|
|
39
|
-
issues.push({ code: "too_large", field: "sizeBytes", message: `size ${upload.sizeBytes} bytes exceeds the ${rules.maxBytes} byte limit`, actual: upload.sizeBytes, limit: rules.maxBytes });
|
|
40
|
-
}
|
|
41
|
-
if (rules.allowedMimeTypes !== undefined && rules.allowedMimeTypes.length > 0 && upload.mimeType !== undefined && !rules.allowedMimeTypes.some((pattern) => mimeMatches(pattern, upload.mimeType))) {
|
|
42
|
-
issues.push({ code: "mime_not_allowed", field: "mimeType", message: `MIME type ${upload.mimeType} is not allowed (allowed: ${rules.allowedMimeTypes.join(", ")})`, actual: upload.mimeType, limit: rules.allowedMimeTypes.join(", ") });
|
|
43
|
-
}
|
|
44
|
-
if (rules.allowedExtensions !== undefined && rules.allowedExtensions.length > 0 && upload.fileName !== undefined) {
|
|
45
|
-
const extension = upload.fileName.includes(".") ? upload.fileName.slice(upload.fileName.lastIndexOf(".") + 1) : "";
|
|
46
|
-
const normalized = normalizeExtension(extension);
|
|
47
|
-
if (!normalized || !rules.allowedExtensions.some((allowed) => normalizeExtension(allowed) === normalized)) {
|
|
48
|
-
issues.push({ code: "extension_not_allowed", field: "fileName", message: `extension .${normalized || "?"} is not allowed (allowed: ${rules.allowedExtensions.join(", ")})`, actual: upload.fileName, limit: rules.allowedExtensions.join(", ") });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return issues;
|
|
52
|
-
}
|
|
53
|
-
/** Like {@link validateUpload} but throws {@link UploadValidationError} when any rule is violated. */
|
|
54
|
-
function assertValidUpload(upload, rules) {
|
|
55
|
-
const issues = validateUpload(upload, rules);
|
|
56
|
-
if (issues.length > 0)
|
|
57
|
-
throw new UploadValidationError(issues);
|
|
58
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.html = exports.md = void 0;
|
|
4
|
-
exports.escapeMarkdownV2 = escapeMarkdownV2;
|
|
5
|
-
exports.escapeHtml = escapeHtml;
|
|
6
|
-
exports.splitMessage = splitMessage;
|
|
7
|
-
exports.splitCaption = splitCaption;
|
|
8
|
-
exports.template = template;
|
|
9
|
-
const TELEGRAM_TEXT_LIMIT = 4096;
|
|
10
|
-
const TELEGRAM_CAPTION_LIMIT = 1024;
|
|
11
|
-
function escapeMarkdownV2(value) { return value.replace(/[\\_*\[\]()~`>#+\-=|{}.!]/g, "\\$&"); }
|
|
12
|
-
function escapeHtml(value) { return value.replace(/[&<>\"]/g, (character) => ({ "&": "&", "<": "<", ">": ">", "\"": """ })[character] ?? character); }
|
|
13
|
-
exports.md = {
|
|
14
|
-
bold: (value) => `*${escapeMarkdownV2(value)}*`,
|
|
15
|
-
italic: (value) => `_${escapeMarkdownV2(value)}_`,
|
|
16
|
-
underline: (value) => `__${escapeMarkdownV2(value)}__`,
|
|
17
|
-
strikethrough: (value) => `~${escapeMarkdownV2(value)}~`,
|
|
18
|
-
spoiler: (value) => `||${escapeMarkdownV2(value)}||`,
|
|
19
|
-
link: (label, url) => `[${escapeMarkdownV2(label)}](${url.replace(/[()\\]/g, "\\$&")})`,
|
|
20
|
-
code: (value) => `\`${value.replace(/[`\\]/g, "\\$&")}\``,
|
|
21
|
-
pre: (value, language = "") => `\`\`\`${language}\n${value.replace(/[`\\]/g, "\\$&")}\n\`\`\``,
|
|
22
|
-
blockquote: (value) => value.split("\n").map((line) => `>${escapeMarkdownV2(line)}`).join("\n"),
|
|
23
|
-
expandableBlockquote: (value) => `**>${value.split("\n").map((line) => escapeMarkdownV2(line)).join("\n>")}||`,
|
|
24
|
-
customEmoji: (emoji, customEmojiId) => ``,
|
|
25
|
-
userMention: (label, userId) => `[${escapeMarkdownV2(label)}](tg://user?id=${userId})`,
|
|
26
|
-
escape: escapeMarkdownV2,
|
|
27
|
-
};
|
|
28
|
-
exports.html = {
|
|
29
|
-
escape: escapeHtml,
|
|
30
|
-
bold: (value) => `<b>${escapeHtml(value)}</b>`,
|
|
31
|
-
italic: (value) => `<i>${escapeHtml(value)}</i>`,
|
|
32
|
-
underline: (value) => `<u>${escapeHtml(value)}</u>`,
|
|
33
|
-
strikethrough: (value) => `<s>${escapeHtml(value)}</s>`,
|
|
34
|
-
spoiler: (value) => `<tg-spoiler>${escapeHtml(value)}</tg-spoiler>`,
|
|
35
|
-
link: (label, url) => `<a href="${escapeHtml(url)}">${escapeHtml(label)}</a>`,
|
|
36
|
-
code: (value) => `<code>${escapeHtml(value)}</code>`,
|
|
37
|
-
pre: (value, language) => language ? `<pre><code class="language-${escapeHtml(language)}">${escapeHtml(value)}</code></pre>` : `<pre>${escapeHtml(value)}</pre>`,
|
|
38
|
-
blockquote: (value) => `<blockquote>${escapeHtml(value)}</blockquote>`,
|
|
39
|
-
expandableBlockquote: (value) => `<blockquote expandable>${escapeHtml(value)}</blockquote>`,
|
|
40
|
-
customEmoji: (emoji, customEmojiId) => `<tg-emoji emoji-id="${escapeHtml(customEmojiId)}">${escapeHtml(emoji)}</tg-emoji>`,
|
|
41
|
-
userMention: (label, userId) => `<a href="tg://user?id=${userId}">${escapeHtml(label)}</a>`,
|
|
42
|
-
};
|
|
43
|
-
function splitMessage(text, options = {}) {
|
|
44
|
-
const limit = options.limit ?? TELEGRAM_TEXT_LIMIT;
|
|
45
|
-
if (limit < 1)
|
|
46
|
-
throw new RangeError("limit must be positive");
|
|
47
|
-
if (text.length <= limit)
|
|
48
|
-
return [text];
|
|
49
|
-
const chunks = [];
|
|
50
|
-
let remaining = text;
|
|
51
|
-
while (remaining.length > limit) {
|
|
52
|
-
const window = remaining.slice(0, limit);
|
|
53
|
-
const boundary = Math.max(window.lastIndexOf("\n\n"), window.lastIndexOf("\n"), window.lastIndexOf(" "));
|
|
54
|
-
const cut = boundary > Math.floor(limit * 0.5) ? boundary : limit;
|
|
55
|
-
chunks.push(remaining.slice(0, cut));
|
|
56
|
-
remaining = remaining.slice(cut).replace(/^\s+/, "");
|
|
57
|
-
}
|
|
58
|
-
if (remaining)
|
|
59
|
-
chunks.push(remaining);
|
|
60
|
-
return chunks;
|
|
61
|
-
}
|
|
62
|
-
function splitCaption(text) { return splitMessage(text, { limit: TELEGRAM_CAPTION_LIMIT }); }
|
|
63
|
-
function template(templateText, values) { return templateText.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_match, key) => { const value = key.split(".").reduce((current, part) => current && typeof current === "object" ? current[part] : undefined, values); return value === undefined || value === null ? "" : String(value); }); }
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createWebhookHandler = createWebhookHandler;
|
|
4
|
-
exports.webhookCallback = webhookCallback;
|
|
5
|
-
const node_crypto_1 = require("node:crypto");
|
|
6
|
-
/** Runs the update, optionally claiming the webhook response for the first API call. */
|
|
7
|
-
async function processWithOptionalReply(bot, update, sink) {
|
|
8
|
-
if (sink === undefined) {
|
|
9
|
-
await bot.handleUpdate(update);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
await bot.handleUpdate(update, { webhookReply: sink });
|
|
13
|
-
}
|
|
14
|
-
function replyResponse(payload) {
|
|
15
|
-
if (payload !== undefined)
|
|
16
|
-
return new Response(JSON.stringify(payload), { status: 200, headers: { "content-type": "application/json" } });
|
|
17
|
-
return new Response("OK", { status: 200 });
|
|
18
|
-
}
|
|
19
|
-
function createWebhookHandler(bot, options = {}) {
|
|
20
|
-
const maxBodyBytes = options.maxBodyBytes ?? 1_048_576;
|
|
21
|
-
return async (request) => {
|
|
22
|
-
if (request.method !== "POST")
|
|
23
|
-
return new Response("Method Not Allowed", { status: 405, headers: { allow: "POST" } });
|
|
24
|
-
if (options.secretToken && !secureEqual(request.headers.get("x-telegram-bot-api-secret-token") ?? "", options.secretToken))
|
|
25
|
-
return new Response("Unauthorized", { status: 401 });
|
|
26
|
-
const contentType = request.headers.get("content-type") ?? "";
|
|
27
|
-
if (!/^application\/json(?:\s*;|\s*$)/i.test(contentType))
|
|
28
|
-
return new Response("Unsupported Media Type", { status: 415 });
|
|
29
|
-
const contentLength = Number(request.headers.get("content-length") ?? 0);
|
|
30
|
-
if (contentLength > maxBodyBytes)
|
|
31
|
-
return new Response("Payload Too Large", { status: 413 });
|
|
32
|
-
try {
|
|
33
|
-
const raw = await request.arrayBuffer();
|
|
34
|
-
if (raw.byteLength > maxBodyBytes)
|
|
35
|
-
return new Response("Payload Too Large", { status: 413 });
|
|
36
|
-
const update = JSON.parse(new TextDecoder().decode(raw));
|
|
37
|
-
if (!Number.isInteger(update.update_id))
|
|
38
|
-
return new Response("Bad Request", { status: 400 });
|
|
39
|
-
let replyPayload;
|
|
40
|
-
const sink = options.webhookReply === true ? (payload) => { replyPayload = payload; } : undefined;
|
|
41
|
-
await processWithOptionalReply(bot, update, sink);
|
|
42
|
-
return replyResponse(replyPayload);
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
await options.onError?.(error);
|
|
46
|
-
return new Response("Internal Server Error", { status: 500 });
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function webhookCallback(bot, _framework = "express", options = {}) {
|
|
51
|
-
const maxBodyBytes = options.maxBodyBytes ?? 1_048_576;
|
|
52
|
-
return async (req, res) => {
|
|
53
|
-
const rawReq = req;
|
|
54
|
-
const rawRes = res;
|
|
55
|
-
const sendResponse = (status, message, contentType = "text/plain") => {
|
|
56
|
-
if (rawRes && typeof rawRes.writeHead === "function" && typeof rawRes.end === "function") {
|
|
57
|
-
rawRes.writeHead(status, { "Content-Type": contentType });
|
|
58
|
-
rawRes.end(message);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
const expressLike = rawRes;
|
|
62
|
-
if (typeof expressLike?.status === "function" && typeof expressLike.send === "function") {
|
|
63
|
-
expressLike.status(status).send(message);
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const koaLike = rawRes;
|
|
67
|
-
if (koaLike !== undefined) {
|
|
68
|
-
koaLike.status = status;
|
|
69
|
-
koaLike.body = message;
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
if (rawReq.method !== "POST") {
|
|
73
|
-
sendResponse(405, "Method Not Allowed");
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
if (options.secretToken) {
|
|
77
|
-
if (!secureEqual(readHeader(rawReq, "x-telegram-bot-api-secret-token"), options.secretToken)) {
|
|
78
|
-
sendResponse(401, "Unauthorized");
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
try {
|
|
83
|
-
let update;
|
|
84
|
-
if (typeof rawReq.arrayBuffer === "function") {
|
|
85
|
-
// Web-standard Request objects (e.g. Deno, Bun, WinterCG runtimes, or
|
|
86
|
-
// a converted fetch Request) expose the body through arrayBuffer().
|
|
87
|
-
// Checked before `body` because a Request's `body` is a ReadableStream.
|
|
88
|
-
const raw = await rawReq.arrayBuffer();
|
|
89
|
-
if (raw.byteLength > maxBodyBytes) {
|
|
90
|
-
sendResponse(413, "Payload Too Large");
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
update = JSON.parse(new TextDecoder().decode(raw));
|
|
94
|
-
}
|
|
95
|
-
else if (rawReq.body && typeof rawReq.body === "object") {
|
|
96
|
-
update = rawReq.body;
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
const chunks = [];
|
|
100
|
-
let totalBytes = 0;
|
|
101
|
-
for await (const chunk of rawReq) {
|
|
102
|
-
const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
103
|
-
totalBytes += buffer.length;
|
|
104
|
-
if (totalBytes > maxBodyBytes) {
|
|
105
|
-
sendResponse(413, "Payload Too Large");
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
chunks.push(buffer);
|
|
109
|
-
}
|
|
110
|
-
const raw = Buffer.concat(chunks).toString("utf8");
|
|
111
|
-
update = JSON.parse(raw);
|
|
112
|
-
}
|
|
113
|
-
if (!Number.isInteger(update?.update_id)) {
|
|
114
|
-
sendResponse(400, "Bad Request");
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
let replyPayload;
|
|
118
|
-
const sink = options.webhookReply === true ? (payload) => { replyPayload = payload; } : undefined;
|
|
119
|
-
await processWithOptionalReply(bot, update, sink);
|
|
120
|
-
if (replyPayload !== undefined)
|
|
121
|
-
sendResponse(200, JSON.stringify(replyPayload), "application/json");
|
|
122
|
-
else
|
|
123
|
-
sendResponse(200, "OK");
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
await options.onError?.(error);
|
|
127
|
-
sendResponse(500, "Internal Server Error");
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function readHeader(req, name) {
|
|
132
|
-
const headers = req.headers;
|
|
133
|
-
if (!headers || typeof headers !== "object")
|
|
134
|
-
return "";
|
|
135
|
-
if (typeof headers.get === "function")
|
|
136
|
-
return headers.get(name) ?? "";
|
|
137
|
-
const value = headers[name];
|
|
138
|
-
return Array.isArray(value) ? value[0] ?? "" : value ?? "";
|
|
139
|
-
}
|
|
140
|
-
function secureEqual(left, right) {
|
|
141
|
-
const leftBuffer = Buffer.from(left, "utf8");
|
|
142
|
-
const rightBuffer = Buffer.from(right, "utf8");
|
|
143
|
-
return leftBuffer.length === rightBuffer.length && (0, node_crypto_1.timingSafeEqual)(leftBuffer, rightBuffer);
|
|
144
|
-
}
|