@xbibzlibrary/telebibz 0.1.19 → 0.2.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +28 -8
  2. package/CONTRIBUTING.md +2 -2
  3. package/README.id.md +25 -5
  4. package/README.md +26 -6
  5. package/README.zh-CN.md +25 -5
  6. package/RELEASE_AUTOMATION.md +17 -5
  7. package/bin/telebibz.mjs +1 -1
  8. package/dist/src/api/client.d.ts +3 -1
  9. package/dist/src/api/client.d.ts.map +1 -1
  10. package/dist/src/api/client.js +3 -1
  11. package/dist/src/api/client.js.map +1 -1
  12. package/dist/src/api/transport.d.ts +3 -1
  13. package/dist/src/api/transport.d.ts.map +1 -1
  14. package/dist/src/api/transport.js +3 -2
  15. package/dist/src/api/transport.js.map +1 -1
  16. package/dist/src/branding/terminal.d.ts +62 -0
  17. package/dist/src/branding/terminal.d.ts.map +1 -1
  18. package/dist/src/branding/terminal.js +258 -0
  19. package/dist/src/branding/terminal.js.map +1 -1
  20. package/dist/src/cli.d.ts.map +1 -1
  21. package/dist/src/cli.js +7 -3
  22. package/dist/src/cli.js.map +1 -1
  23. package/dist/src/context/context.d.ts +24 -1
  24. package/dist/src/context/context.d.ts.map +1 -1
  25. package/dist/src/context/context.js +102 -8
  26. package/dist/src/context/context.js.map +1 -1
  27. package/dist/src/core/bot.d.ts +18 -1
  28. package/dist/src/core/bot.d.ts.map +1 -1
  29. package/dist/src/core/bot.js +106 -23
  30. package/dist/src/core/bot.js.map +1 -1
  31. package/dist/src/keyboard/index.d.ts +12 -0
  32. package/dist/src/keyboard/index.d.ts.map +1 -1
  33. package/dist/src/keyboard/index.js +13 -1
  34. package/dist/src/keyboard/index.js.map +1 -1
  35. package/dist/src/observability/logger.d.ts +28 -1
  36. package/dist/src/observability/logger.d.ts.map +1 -1
  37. package/dist/src/observability/logger.js +110 -0
  38. package/dist/src/observability/logger.js.map +1 -1
  39. package/dist/src/plugins/plugin.d.ts +2 -0
  40. package/dist/src/plugins/plugin.d.ts.map +1 -1
  41. package/dist/src/plugins/plugin.js +11 -4
  42. package/dist/src/plugins/plugin.js.map +1 -1
  43. package/dist/src/router/router.d.ts +19 -0
  44. package/dist/src/router/router.d.ts.map +1 -1
  45. package/dist/src/router/router.js +125 -23
  46. package/dist/src/router/router.js.map +1 -1
  47. package/dist/src/state/forms.d.ts +0 -1
  48. package/dist/src/state/forms.d.ts.map +1 -1
  49. package/dist/src/state/forms.js +27 -24
  50. package/dist/src/state/forms.js.map +1 -1
  51. package/dist/src/storage/storage.d.ts +10 -0
  52. package/dist/src/storage/storage.d.ts.map +1 -1
  53. package/dist/src/storage/storage.js +20 -2
  54. package/dist/src/storage/storage.js.map +1 -1
  55. package/dist/src/utils/text.d.ts +22 -0
  56. package/dist/src/utils/text.d.ts.map +1 -1
  57. package/dist/src/utils/text.js +0 -0
  58. package/dist/src/utils/text.js.map +1 -1
  59. package/dist/src/webhook/handler.d.ts +3 -0
  60. package/dist/src/webhook/handler.d.ts.map +1 -1
  61. package/dist/src/webhook/handler.js +85 -0
  62. package/dist/src/webhook/handler.js.map +1 -1
  63. package/dist-cjs/src/api/client.js +3 -1
  64. package/dist-cjs/src/api/transport.js +3 -2
  65. package/dist-cjs/src/branding/terminal.js +264 -1
  66. package/dist-cjs/src/cli.js +7 -3
  67. package/dist-cjs/src/context/context.js +102 -8
  68. package/dist-cjs/src/core/bot.js +104 -21
  69. package/dist-cjs/src/keyboard/index.js +13 -1
  70. package/dist-cjs/src/observability/logger.js +113 -1
  71. package/dist-cjs/src/plugins/plugin.js +11 -4
  72. package/dist-cjs/src/router/router.js +126 -24
  73. package/dist-cjs/src/state/forms.js +27 -24
  74. package/dist-cjs/src/storage/storage.js +20 -2
  75. package/dist-cjs/src/utils/text.js +0 -0
  76. package/dist-cjs/src/webhook/handler.js +86 -0
  77. package/docs/API.id.md +57 -3
  78. package/docs/API.md +59 -3
  79. package/docs/API.zh-CN.md +56 -2
  80. package/package.json +3 -3
@@ -3,32 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validators = exports.Form = void 0;
4
4
  class Form {
5
5
  fields = new Map();
6
- values = {};
7
6
  field(definition) { this.fields.set(definition.name, definition); return this; }
8
- async parse(input) { const issues = []; for (const [name, field] of this.fields) {
9
- const raw = input[name];
10
- if ((raw === undefined || raw === null || raw === "") && field.required) {
11
- issues.push({ path: name, message: "Field is required", code: "required" });
12
- continue;
7
+ async parse(input) {
8
+ const issues = [];
9
+ const values = {};
10
+ for (const [name, field] of this.fields) {
11
+ const raw = input[name];
12
+ if ((raw === undefined || raw === null || raw === "") && field.required) {
13
+ issues.push({ path: name, message: "Field is required", code: "required" });
14
+ continue;
15
+ }
16
+ if (raw === undefined || raw === null || raw === "")
17
+ continue;
18
+ try {
19
+ let value = field.parse(raw);
20
+ if (field.transform)
21
+ value = await field.transform(value);
22
+ const error = await field.validate?.(value);
23
+ if (error)
24
+ issues.push({ path: name, message: error, code: "invalid" });
25
+ else
26
+ values[name] = value;
27
+ }
28
+ catch (error) {
29
+ issues.push({ path: name, message: error instanceof Error ? error.message : "Invalid value", code: "parse" });
30
+ }
13
31
  }
14
- if (raw === undefined || raw === null || raw === "")
15
- continue;
16
- try {
17
- let value = field.parse(raw);
18
- if (field.transform)
19
- value = await field.transform(value);
20
- const error = await field.validate?.(value);
21
- if (error)
22
- issues.push({ path: name, message: error, code: "invalid" });
23
- else
24
- this.values[name] = value;
25
- }
26
- catch (error) {
27
- issues.push({ path: name, message: error instanceof Error ? error.message : "Invalid value", code: "parse" });
28
- }
29
- } return issues.length ? { success: false, issues } : { success: true, data: this.values }; }
30
- reset() { for (const key of Object.keys(this.values))
31
- delete this.values[key]; }
32
+ return issues.length ? { success: false, issues } : { success: true, data: values };
33
+ }
34
+ reset() { }
32
35
  }
33
36
  exports.Form = Form;
34
37
  exports.validators = { string: (value) => { if (typeof value !== "string")
@@ -23,6 +23,20 @@ class MemoryStorage {
23
23
  }
24
24
  return entry.value;
25
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
+ }
26
40
  async set(key, value, options = {}) {
27
41
  const expiresAt = expiration(options.ttlMs);
28
42
  this.valuesMap.set(key, expiresAt === undefined ? { value } : { value, expiresAt });
@@ -84,8 +98,12 @@ class JsonFileStorage {
84
98
  await this.ready;
85
99
  this.writeChain = this.writeChain.then(async () => {
86
100
  const output = {};
87
- for await (const [key, value] of this.memory.entries())
88
- output[key] = { value };
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
+ }
89
107
  await (0, promises_1.mkdir)((0, node_path_1.dirname)(this.filePath), { recursive: true });
90
108
  const temporary = `${this.filePath}.${process.pid}.tmp`;
91
109
  await (0, promises_1.writeFile)(temporary, JSON.stringify(output, null, 2), "utf8");
Binary file
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createWebhookHandler = createWebhookHandler;
4
+ exports.webhookCallback = webhookCallback;
4
5
  const node_crypto_1 = require("node:crypto");
5
6
  function createWebhookHandler(bot, options = {}) {
6
7
  const maxBodyBytes = options.maxBodyBytes ?? 1_048_576;
@@ -31,6 +32,91 @@ function createWebhookHandler(bot, options = {}) {
31
32
  }
32
33
  };
33
34
  }
35
+ function webhookCallback(bot, _framework = "express", options = {}) {
36
+ const maxBodyBytes = options.maxBodyBytes ?? 1_048_576;
37
+ return async (req, res) => {
38
+ const rawReq = req;
39
+ const rawRes = res;
40
+ const sendResponse = (status, message) => {
41
+ if (rawRes && typeof rawRes.writeHead === "function" && typeof rawRes.end === "function") {
42
+ rawRes.writeHead(status, { "Content-Type": "text/plain" });
43
+ rawRes.end(message);
44
+ return;
45
+ }
46
+ const expressLike = rawRes;
47
+ if (typeof expressLike?.status === "function" && typeof expressLike.send === "function") {
48
+ expressLike.status(status).send(message);
49
+ return;
50
+ }
51
+ const koaLike = rawRes;
52
+ if (koaLike !== undefined) {
53
+ koaLike.status = status;
54
+ koaLike.body = message;
55
+ }
56
+ };
57
+ if (rawReq.method !== "POST") {
58
+ sendResponse(405, "Method Not Allowed");
59
+ return;
60
+ }
61
+ if (options.secretToken) {
62
+ if (!secureEqual(readHeader(rawReq, "x-telegram-bot-api-secret-token"), options.secretToken)) {
63
+ sendResponse(401, "Unauthorized");
64
+ return;
65
+ }
66
+ }
67
+ try {
68
+ let update;
69
+ if (typeof rawReq.arrayBuffer === "function") {
70
+ // Web-standard Request objects (e.g. Deno, Bun, WinterCG runtimes, or
71
+ // a converted fetch Request) expose the body through arrayBuffer().
72
+ // Checked before `body` because a Request's `body` is a ReadableStream.
73
+ const raw = await rawReq.arrayBuffer();
74
+ if (raw.byteLength > maxBodyBytes) {
75
+ sendResponse(413, "Payload Too Large");
76
+ return;
77
+ }
78
+ update = JSON.parse(new TextDecoder().decode(raw));
79
+ }
80
+ else if (rawReq.body && typeof rawReq.body === "object") {
81
+ update = rawReq.body;
82
+ }
83
+ else {
84
+ const chunks = [];
85
+ let totalBytes = 0;
86
+ for await (const chunk of rawReq) {
87
+ const buffer = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
88
+ totalBytes += buffer.length;
89
+ if (totalBytes > maxBodyBytes) {
90
+ sendResponse(413, "Payload Too Large");
91
+ return;
92
+ }
93
+ chunks.push(buffer);
94
+ }
95
+ const raw = Buffer.concat(chunks).toString("utf8");
96
+ update = JSON.parse(raw);
97
+ }
98
+ if (!Number.isInteger(update?.update_id)) {
99
+ sendResponse(400, "Bad Request");
100
+ return;
101
+ }
102
+ await bot.handleUpdate(update);
103
+ sendResponse(200, "OK");
104
+ }
105
+ catch (error) {
106
+ await options.onError?.(error);
107
+ sendResponse(500, "Internal Server Error");
108
+ }
109
+ };
110
+ }
111
+ function readHeader(req, name) {
112
+ const headers = req.headers;
113
+ if (!headers || typeof headers !== "object")
114
+ return "";
115
+ if (typeof headers.get === "function")
116
+ return headers.get(name) ?? "";
117
+ const value = headers[name];
118
+ return Array.isArray(value) ? value[0] ?? "" : value ?? "";
119
+ }
34
120
  function secureEqual(left, right) {
35
121
  const leftBuffer = Buffer.from(left, "utf8");
36
122
  const rightBuffer = Buffer.from(right, "utf8");
package/docs/API.id.md CHANGED
@@ -68,6 +68,7 @@ type BotStatus =
68
68
  | `transportOptions` | `Omit<FetchTransportOptions, "baseUrl">` | `{}` | Timeout, retry, backoff, jitter, headers, dan fetch implementation. |
69
69
  | `session` | `Storage<string, S>` | storage baru | Penyimpanan session berdasarkan kunci chat/user; dapat memakai adapter persistent. |
70
70
  | `services` | `Record<string, unknown>` | `{}` | Dependency/service yang tersedia melalui `ctx.services`. |
71
+ | `branding` | `boolean` | `true` | Pengalaman startup terminal: efek ketik, glass progress bar, banner rainbow animasi `Tele Bibz`, dan baris update yang mudah dibaca. Hanya dirender pada TTY interaktif. |
71
72
  | `polling.timeout` | `number` | `30` | Long-poll timeout dalam detik untuk `getUpdates`. |
72
73
  | `polling.limit` | `number` | `100` | Jumlah maksimum update per request polling. |
73
74
  | `polling.allowedUpdates` | `string[]` | `[]` | Filter update Telegram. |
@@ -140,6 +141,30 @@ onRegex(expression: RegExp, handler: Middleware<Context<S>>): this
140
141
 
141
142
  Menangani message text menggunakan `RegExp`. Parameter route tidak diekstrak otomatis ke `ctx.params`; gunakan predicate atau middleware custom jika memerlukan ekstraksi.
142
143
 
144
+ ### `bot.on(filter, handler)`
145
+
146
+ ```ts
147
+ on(filter: UpdateFilter | UpdateFilter[], handler: Middleware<Context<S>>): this
148
+ ```
149
+
150
+ Mendaftarkan handler untuk tipe update, opsional dipersempit dengan field payload. Contoh: `"message"`, `"message:text"`, `"message:photo"`, `"edited_message"`, `"channel_post"`, `"callback_query"`, `"callback_query:data"`, `"inline_query"`, `"chat_member"`, `"message_reaction"`, atau array seperti `["message:text", "callback_query:data"]`. Tipe update tidak valid melempar `TypeError` saat registrasi.
151
+
152
+ ### `bot.hears(trigger, handler)`
153
+
154
+ ```ts
155
+ hears(trigger: string | RegExp, handler: Middleware<Context<S>>): this
156
+ ```
157
+
158
+ Menangani message text yang sama persis (string) atau yang cocok dengan `RegExp`.
159
+
160
+ ### `bot.catch(handler)`
161
+
162
+ ```ts
163
+ catch(handler: (error: unknown, ctx: Context<S>) => void | Promise<void>): this
164
+ ```
165
+
166
+ Mendaftarkan error boundary untuk handler update. Jika dipasang, kegagalan handler dicatat, dipancarkan sebagai `update:error`/`bot:error`, dan diteruskan ke handler ini alih-alih menolak `handleUpdate()` — webhook menjawab `200` dan polling berlanjut. Tanpa boundary, error dilempar ulang.
167
+
143
168
  ### `bot.usePlugin(plugin)`
144
169
 
145
170
  ```ts
@@ -633,6 +658,22 @@ new Context<S>(options: ContextOptions<S>): Context<S>
633
658
  | `send` | `send(text, extra?): Promise<Message>` | Mengirim message ke chat update tanpa reply reference. |
634
659
  | `edit` | `edit(text, extra?): Promise<Message \| true>` | Mengedit message update menggunakan `editMessageText`. |
635
660
  | `delete` | `delete(): Promise<true>` | Menghapus message update. |
661
+ | `replyWithHTML` | `replyWithHTML(text, extra?): Promise<Message>` | Membalas dengan `parse_mode: "HTML"`. |
662
+ | `replyWithMarkdown` | `replyWithMarkdown(text, extra?): Promise<Message>` | Membalas dengan `parse_mode: "MarkdownV2"`. |
663
+ | `replyWithPhoto` | `replyWithPhoto(photo, extra?): Promise<Message>` | Mengirim `sendPhoto` dengan quote-reply otomatis. |
664
+ | `replyWithDocument` | `replyWithDocument(document, extra?): Promise<Message>` | Mengirim `sendDocument` dengan quote-reply otomatis. |
665
+ | `replyWithAudio` | `replyWithAudio(audio, extra?): Promise<Message>` | Mengirim `sendAudio` dengan quote-reply otomatis. |
666
+ | `replyWithVideo` | `replyWithVideo(video, extra?): Promise<Message>` | Mengirim `sendVideo` dengan quote-reply otomatis. |
667
+ | `replyWithVoice` | `replyWithVoice(voice, extra?): Promise<Message>` | Mengirim `sendVoice` dengan quote-reply otomatis. |
668
+ | `replyWithAnimation` | `replyWithAnimation(animation, extra?): Promise<Message>` | Mengirim `sendAnimation` dengan quote-reply otomatis. |
669
+ | `replyWithVideoNote` | `replyWithVideoNote(videoNote, extra?): Promise<Message>` | Mengirim `sendVideoNote` dengan quote-reply otomatis. |
670
+ | `replyWithSticker` | `replyWithSticker(sticker, extra?): Promise<Message>` | Mengirim `sendSticker` dengan quote-reply otomatis. |
671
+ | `replyWithMediaGroup` | `replyWithMediaGroup(media, extra?): Promise<Message[]>` | Mengirim album via `sendMediaGroup` dengan quote-reply otomatis. |
672
+ | `replyWithLocation` | `replyWithLocation(latitude, longitude, extra?): Promise<Message>` | Mengirim `sendLocation` dengan quote-reply otomatis. |
673
+ | `replyWithVenue` | `replyWithVenue(latitude, longitude, title, address, extra?): Promise<Message>` | Mengirim `sendVenue` dengan quote-reply otomatis. |
674
+ | `replyWithContact` | `replyWithContact(phoneNumber, firstName, extra?): Promise<Message>` | Mengirim `sendContact` dengan quote-reply otomatis. |
675
+ | `replyWithPoll` | `replyWithPoll(question, options, extra?): Promise<Message>` | Mengirim `sendPoll` dengan quote-reply otomatis. |
676
+ | `replyWithDice` | `replyWithDice(emoji?, extra?): Promise<Message>` | Mengirim `sendDice` dengan quote-reply otomatis. |
636
677
  | `copy` | `copy(fromChatId, messageId, extra?): Promise<unknown>` | Memanggil `copyMessage` ke chat context. |
637
678
  | `forward` | `forward(fromChatId, messageId, extra?): Promise<Message>` | Memanggil `forwardMessage` ke chat context. |
638
679
  | `pin` | `pin(messageId?, extra?): Promise<true>` | Memanggil `pinChatMessage`, default message id dari context. |
@@ -645,7 +686,7 @@ new Context<S>(options: ContextOptions<S>): Context<S>
645
686
  | `getFile` | `getFile(fileId): Promise<unknown>` | Mengambil file berdasarkan id. |
646
687
  | `withReplyMarkup` | `withReplyMarkup(markup): this` | Menyimpan markup di `ctx.state.reply_markup` dan mengembalikan context. Metode ini tidak otomatis mengirim message. |
647
688
 
648
- `reply`, `send`, `getChat`, dan beberapa helper lain melempar error ketika update tidak memiliki chat yang diperlukan. `edit` dan `delete` membutuhkan chat serta message.
689
+ Semua pengirim `replyWith*` menerima parameter native Telegram sebagai `extra` dan otomatis me-quote message yang masuk. `reply_parameters` pada `extra` digabung dengan `message_id` otomatis, bukan menggantikannya. `reply`, `send`, `getChat`, dan beberapa helper lain melempar error ketika update tidak memiliki chat yang diperlukan. `edit` dan `delete` membutuhkan chat serta message.
649
690
 
650
691
  ---
651
692
 
@@ -1263,7 +1304,20 @@ new Menu(id: string): Menu
1263
1304
 
1264
1305
  ## 12. Logging Terminal
1265
1306
 
1266
- This package starts directly after Telegram API connectivity is established. The terminal prints a boxed telebibz attribution, an animated startup status when attached to a TTY, and structured colorful logs for lifecycle, API, polling, webhook, and update events. Set logger format to `json` for machine ingestion.
1307
+ Saat stdout adalah TTY interaktif, setiap `bot.start()` / `bot.launch()` memainkan urutan startup: efek ketik `Installing Dependencies......`, glass progress bar dengan kilau menyapu, dan banner ASCII rainbow animasi `Tele Bibz` (font figlet `Speed`) yang terus mengalir sampai bot terhubung, lalu diam dengan `✓ Connected as @<username>`.
1308
+
1309
+ Setiap update yang ditangani bot dicatat dalam baris yang mudah dibaca:
1310
+
1311
+ ```text
1312
+ [ => ] Message From 123456789 John Doe 29/08/2026 15:04:05
1313
+ ↳ Text: /start
1314
+ [ => ] Callback From 123456789 John Doe 29/08/2026 15:04:07
1315
+ ↳ Data: menu:open
1316
+ ```
1317
+
1318
+ Teks pesan/command dibatasi 50 karakter; data tombol callback ditampilkan penuh. Error dicetak merah lengkap dengan stack. Nonaktifkan dengan `branding: false` pada `Bot`, atau set `logger.format: "json"` untuk log terstruktur — pada mode itu update masuk dikeluarkan sebagai entry `update.received`. Stdout non-interaktif (pipe, Docker, CI) otomatis fallback ke teks polos tanpa animasi.
1319
+
1320
+ Helper branding tambahan yang diekspor untuk aplikasi: `runStartupSequence()`, `startTeleBibzBanner()`, `printTeleBibzBanner()`, `paintRainbow()`, dan `printStatusLine()`.
1267
1321
 
1268
1322
  ## 13. Utilitas Teks
1269
1323
 
@@ -1691,7 +1745,7 @@ Package memvendorkan declaration Telegram berlisensi MIT dan mengeksposnya sebag
1691
1745
 
1692
1746
  ## 19. Kompatibilitas dan batasan yang perlu diketahui
1693
1747
 
1694
- Perpustakaan menargetkan Node.js `>=20`, menggunakan ESM sebagai module utama, serta menyediakan build CommonJS. Webhook membutuhkan runtime yang menyediakan Web `Request`, `Response`, `Headers`, `FormData`, `Blob`, dan `AbortController`; Node.js modern menyediakannya secara native.
1748
+ Perpustakaan menargetkan Node.js `>=22`, menggunakan ESM sebagai module utama, serta menyediakan build CommonJS. Webhook membutuhkan runtime yang menyediakan Web `Request`, `Response`, `Headers`, `FormData`, `Blob`, dan `AbortController`; Node.js modern menyediakannya secara native.
1695
1749
 
1696
1750
  Daftar method yang dihasilkan API dan peta method API bukanlah hal yang sama. `TelegramMethodName` mencakup 184 nama runtime, tetapi `TelegramMethodMap` hanya memiliki parameter/hasil yang bertipe khusus untuk subset yang tercantum pada bagian API client. Untuk method lain, gunakan `api.raw()` atau tambahkan deklarasi tipe di sisi aplikasi.
1697
1751
 
package/docs/API.md CHANGED
@@ -67,6 +67,7 @@ type BotStatus =
67
67
  | `transportOptions` | `Omit<FetchTransportOptions, "baseUrl">` | `{}` | Timeout, retry, backoff, jitter, headers, and fetch implementation. |
68
68
  | `session` | `Storage<string, S>` | new storage | Session storage keyed by chat/user; any storage adapter may be used. |
69
69
  | `services` | `Record<string, unknown>` | `{}` | Dependencies/services available via `ctx.services`. |
70
+ | `branding` | `boolean` | `true` | Terminal startup experience: typing effect, glass progress bar, animated rainbow "Tele Bibz" banner, and human-readable update lines. Only renders on an interactive TTY. |
70
71
  | `polling.timeout` | `number` | `30` | Long-poll timeout in seconds for `getUpdates`. |
71
72
  | `polling.limit` | `number` | `100` | Maximum number of updates per polling request. |
72
73
  | `polling.allowedUpdates` | `string[]` | `[]` | Telegram update filters. |
@@ -139,6 +140,30 @@ onRegex(expression: RegExp, handler: Middleware<Context<S>>): this
139
140
 
140
141
  Handles message text using a `RegExp`. Route parameters are not automatically extracted into `ctx.params`; use a predicate or custom middleware if extraction is needed.
141
142
 
143
+ ### `bot.on(filter, handler)`
144
+
145
+ ```ts
146
+ on(filter: UpdateFilter | UpdateFilter[], handler: Middleware<Context<S>>): this
147
+ ```
148
+
149
+ Registers a handler for update types, optionally narrowed by a payload field. Examples: `"message"`, `"message:text"`, `"message:photo"`, `"edited_message"`, `"channel_post"`, `"callback_query"`, `"callback_query:data"`, `"inline_query"`, `"chat_member"`, `"message_reaction"`, or an array such as `["message:text", "callback_query:data"]`. Invalid update types throw a `TypeError` at registration time.
150
+
151
+ ### `bot.hears(trigger, handler)`
152
+
153
+ ```ts
154
+ hears(trigger: string | RegExp, handler: Middleware<Context<S>>): this
155
+ ```
156
+
157
+ Handles exact message text (string) or message text matching a `RegExp`.
158
+
159
+ ### `bot.catch(handler)`
160
+
161
+ ```ts
162
+ catch(handler: (error: unknown, ctx: Context<S>) => void | Promise<void>): this
163
+ ```
164
+
165
+ Registers the error boundary for update handlers. When set, a handler failure is logged, emitted as `update:error`/`bot:error`, and passed to this handler instead of rejecting `handleUpdate()` — webhook requests answer `200` and polling continues. Without a boundary, the error is rethrown.
166
+
142
167
  ### `bot.usePlugin(plugin)`
143
168
 
144
169
  ```ts
@@ -650,6 +675,23 @@ new Context<S>(options: ContextOptions<S>): Context<S>
650
675
  | `send` | `send(text, extra?): Promise<Message>` | Sends a message to the update chat without a reply reference. |
651
676
  | `edit` | `edit(text, extra?): Promise<Message \| true>` | Edits the update message using `editMessageText`. |
652
677
  | `delete` | `delete(): Promise<true>` | Deletes the update message. |
678
+ | `replyWithHTML` | `replyWithHTML(text, extra?): Promise<Message>` | Replies with `parse_mode: "HTML"`. |
679
+ | `replyWithMarkdown` | `replyWithMarkdown(text, extra?): Promise<Message>` | Replies with `parse_mode: "MarkdownV2"`. |
680
+ | `replyWithPhoto` | `replyWithPhoto(photo, extra?): Promise<Message>` | Sends `sendPhoto` with automatic quote-reply. |
681
+ | `replyWithDocument` | `replyWithDocument(document, extra?): Promise<Message>` | Sends `sendDocument` with automatic quote-reply. |
682
+ | `replyWithAudio` | `replyWithAudio(audio, extra?): Promise<Message>` | Sends `sendAudio` with automatic quote-reply. |
683
+ | `replyWithVideo` | `replyWithVideo(video, extra?): Promise<Message>` | Sends `sendVideo` with automatic quote-reply. |
684
+ | `replyWithVoice` | `replyWithVoice(voice, extra?): Promise<Message>` | Sends `sendVoice` with automatic quote-reply. |
685
+ | `replyWithAnimation` | `replyWithAnimation(animation, extra?): Promise<Message>` | Sends `sendAnimation` with automatic quote-reply. |
686
+ | `replyWithVideoNote` | `replyWithVideoNote(videoNote, extra?): Promise<Message>` | Sends `sendVideoNote` with automatic quote-reply. |
687
+ | `replyWithSticker` | `replyWithSticker(sticker, extra?): Promise<Message>` | Sends `sendSticker` with automatic quote-reply. |
688
+ | `replyWithMediaGroup` | `replyWithMediaGroup(media, extra?): Promise<Message[]>` | Sends an album via `sendMediaGroup` with automatic quote-reply. |
689
+ | `replyWithLocation` | `replyWithLocation(latitude, longitude, extra?): Promise<Message>` | Sends `sendLocation` with automatic quote-reply. |
690
+ | `replyWithVenue` | `replyWithVenue(latitude, longitude, title, address, extra?): Promise<Message>` | Sends `sendVenue` with automatic quote-reply. |
691
+ | `replyWithContact` | `replyWithContact(phoneNumber, firstName, extra?): Promise<Message>` | Sends `sendContact` with automatic quote-reply. |
692
+ | `replyWithPoll` | `replyWithPoll(question, options, extra?): Promise<Message>` | Sends `sendPoll` with automatic quote-reply. |
693
+ | `replyWithDice` | `replyWithDice(emoji?, extra?): Promise<Message>` | Sends `sendDice` with automatic quote-reply. |
694
+ | `sendChatAction` | `sendChatAction(action, extra?): Promise<true>` | Sends a chat action such as `typing`. |
653
695
  | `copy` | `copy(fromChatId, messageId, extra?): Promise<unknown>` | Calls `copyMessage` to the context chat. |
654
696
  | `forward` | `forward(fromChatId, messageId, extra?): Promise<Message>` | Calls `forwardMessage` to the context chat. |
655
697
  | `pin` | `pin(messageId?, extra?): Promise<true>` | Calls `pinChatMessage`; defaults to the context message id. |
@@ -662,7 +704,7 @@ new Context<S>(options: ContextOptions<S>): Context<S>
662
704
  | `getFile` | `getFile(fileId): Promise<unknown>` | Fetches a file by id. |
663
705
  | `withReplyMarkup` | `withReplyMarkup(markup): this` | Stores markup in `ctx.state.reply_markup` and returns the context. This method does not automatically send a message. |
664
706
 
665
- `reply`, `send`, `getChat`, and some other helpers throw an error when the update does not have the required chat. `edit` and `delete` require both chat and message.
707
+ All `replyWith*` senders accept the native Telegram parameters as `extra` and automatically quote the incoming message. Passing `reply_parameters` in `extra` merges with the automatic `message_id` instead of replacing it. `reply`, `send`, `getChat`, and some other helpers throw an error when the update does not have the required chat. `edit` and `delete` require both chat and message.
666
708
 
667
709
  ---
668
710
 
@@ -1281,11 +1323,23 @@ new Menu(id: string): Menu
1281
1323
 
1282
1324
  ## 12. Terminal Logging
1283
1325
 
1284
- The CLI prints a colored Unicode attribution box and an animated startup status when attached to a TTY. The default logger emits compact, readable terminal lines with colored levels and structured context. Use `format: "json"` for machine ingestion, `includeUpdateContent: true` when message text or callback data is explicitly required, and a custom `sink` for application monitoring.
1326
+ When stdout is an interactive TTY, every `bot.start()` / `bot.launch()` plays a startup sequence: a typing effect for `Installing Dependencies......`, a glass progress bar with a sweeping highlight, and the animated rainbow ASCII banner `Tele Bibz` (figlet `Speed` font) that keeps flowing until the bot connects, then freezes with `✓ Connected as @<username>`.
1327
+
1328
+ Every update the bot handles is logged on a human-readable line:
1329
+
1330
+ ```text
1331
+ [ => ] Message From 123456789 John Doe 29/08/2026 15:04:05
1332
+ ↳ Text: /start
1333
+ [ => ] Callback From 123456789 John Doe 29/08/2026 15:04:07
1334
+ ↳ Data: menu:open
1335
+ ```
1336
+
1337
+ Message and command text is truncated to 50 characters; callback button data is shown in full. Errors are printed in red and include the full stack. Pass `branding: false` to `Bot` to disable the startup sequence, and set `logger.format: "json"` for machine ingestion — in that mode incoming updates are emitted as structured `update.received` entries. Non-interactive stdout (pipes, Docker, CI) automatically falls back to plain, uncolored output without animations.
1285
1338
 
1286
1339
  ```ts
1287
1340
  const bot = new Bot({
1288
1341
  token: process.env.TELEGRAM_BOT_TOKEN!,
1342
+ branding: false, // turn off the startup sequence
1289
1343
  logger: {
1290
1344
  level: "debug",
1291
1345
  format: "pretty",
@@ -1295,6 +1349,8 @@ const bot = new Bot({
1295
1349
  });
1296
1350
  ```
1297
1351
 
1352
+ Additional branding helpers exported for applications: `runStartupSequence()`, `startTeleBibzBanner()`, `printTeleBibzBanner()`, `paintRainbow()`, and `printStatusLine()`.
1353
+
1298
1354
  ---
1299
1355
 
1300
1356
  ## 13. Text Utilities
@@ -1723,7 +1779,7 @@ The package vendors MIT-licensed Telegram declarations and exposes them as type-
1723
1779
  ---
1724
1780
  ## 19. Compatibility and limitations to be aware of
1725
1781
 
1726
- The library targets Node.js `>=20`, uses ESM as the primary module, and also provides a CommonJS build. Webhooks require a runtime that provides Web `Request`, `Response`, `Headers`, `FormData`, `Blob`, and `AbortController`; modern Node.js provides these natively.
1782
+ The library targets Node.js `>=22`, uses ESM as the primary module, and also provides a CommonJS build. Webhooks require a runtime that provides Web `Request`, `Response`, `Headers`, `FormData`, `Blob`, and `AbortController`; modern Node.js provides these natively.
1727
1783
 
1728
1784
  The list of generated API methods and the API method map are not the same. `TelegramMethodName` includes 184 runtime names, but `TelegramMethodMap` only has specially-typed parameters/results for the subset listed in the API client section. For other methods, use `api.raw()` or add a type declaration on the application side.
1729
1785
 
package/docs/API.zh-CN.md CHANGED
@@ -68,6 +68,7 @@ type BotStatus =
68
68
  | `transportOptions` | `Omit<FetchTransportOptions, "baseUrl">` | `{}` | 超时、重试、退避、jitter、headers 和 fetch 实现。 |
69
69
  | `session` | `Storage<string, S>` | 新的存储 | 基于 chat/user key 的会话存储,可使用持久化适配器。 |
70
70
  | `services` | `Record<string, unknown>` | `{}` | 通过 `ctx.services` 可用的依赖/服务。 |
71
+ | `branding` | `boolean` | `true` | 终端启动体验:打字效果、glass 进度条、动画彩虹 `Tele Bibz` 横幅以及易读的 update 日志行。仅在交互式 TTY 上渲染。 |
71
72
  | `polling.timeout` | `number` | `30` | 用于 `getUpdates` 的长轮询超时(秒)。 |
72
73
  | `polling.limit` | `number` | `100` | 每次轮询请求的最大 update 数量。 |
73
74
  | `polling.allowedUpdates` | `string[]` | `[]` | Telegram 更新过滤器。 |
@@ -140,6 +141,30 @@ onRegex(expression: RegExp, handler: Middleware<Context<S>>): this
140
141
 
141
142
  使用 `RegExp` 处理消息文本。路由参数不会自动提取到 `ctx.params`;如需提取请使用 predicate 或自定义 middleware。
142
143
 
144
+ ### `bot.on(filter, handler)`
145
+
146
+ ```ts
147
+ on(filter: UpdateFilter | UpdateFilter[], handler: Middleware<Context<S>>): this
148
+ ```
149
+
150
+ 按更新类型注册处理器,并可用 payload 字段收窄。示例:`"message"`、`"message:text"`、`"message:photo"`、`"edited_message"`、`"channel_post"`、`"callback_query"`、`"callback_query:data"`、`"inline_query"`、`"chat_member"`、`"message_reaction"`,或数组如 `["message:text", "callback_query:data"]`。无效的更新类型会在注册时抛出 `TypeError`。
151
+
152
+ ### `bot.hears(trigger, handler)`
153
+
154
+ ```ts
155
+ hears(trigger: string | RegExp, handler: Middleware<Context<S>>): this
156
+ ```
157
+
158
+ 处理完全匹配的文本(string)或匹配 `RegExp` 的消息文本。
159
+
160
+ ### `bot.catch(handler)`
161
+
162
+ ```ts
163
+ catch(handler: (error: unknown, ctx: Context<S>) => void | Promise<void>): this
164
+ ```
165
+
166
+ 注册更新处理器的错误边界。设置后,处理器失败会被记录、以 `update:error`/`bot:error` 事件发出,并转发给该 handler,而不是让 `handleUpdate()` 拒绝 —— webhook 返回 `200`,轮询继续。未设置边界时错误会被重新抛出。
167
+
143
168
  ### `bot.usePlugin(plugin)`
144
169
 
145
170
  ```ts
@@ -633,6 +658,22 @@ new Context<S>(options: ContextOptions<S>): Context<S>
633
658
  | `send` | `send(text, extra?): Promise<Message>` | 向更新的聊天发送消息,不带回复引用. |
634
659
  | `edit` | `edit(text, extra?): Promise<Message \| true>` | 使用 `editMessageText` 编辑更新的消息. |
635
660
  | `delete` | `delete(): Promise<true>` | 删除更新的消息. |
661
+ | `replyWithHTML` | `replyWithHTML(text, extra?): Promise<Message>` | 以 `parse_mode: "HTML"` 回复. |
662
+ | `replyWithMarkdown` | `replyWithMarkdown(text, extra?): Promise<Message>` | 以 `parse_mode: "MarkdownV2"` 回复. |
663
+ | `replyWithPhoto` | `replyWithPhoto(photo, extra?): Promise<Message>` | 发送 `sendPhoto`,自动引用回复. |
664
+ | `replyWithDocument` | `replyWithDocument(document, extra?): Promise<Message>` | 发送 `sendDocument`,自动引用回复. |
665
+ | `replyWithAudio` | `replyWithAudio(audio, extra?): Promise<Message>` | 发送 `sendAudio`,自动引用回复. |
666
+ | `replyWithVideo` | `replyWithVideo(video, extra?): Promise<Message>` | 发送 `sendVideo`,自动引用回复. |
667
+ | `replyWithVoice` | `replyWithVoice(voice, extra?): Promise<Message>` | 发送 `sendVoice`,自动引用回复. |
668
+ | `replyWithAnimation` | `replyWithAnimation(animation, extra?): Promise<Message>` | 发送 `sendAnimation`,自动引用回复. |
669
+ | `replyWithVideoNote` | `replyWithVideoNote(videoNote, extra?): Promise<Message>` | 发送 `sendVideoNote`,自动引用回复. |
670
+ | `replyWithSticker` | `replyWithSticker(sticker, extra?): Promise<Message>` | 发送 `sendSticker`,自动引用回复. |
671
+ | `replyWithMediaGroup` | `replyWithMediaGroup(media, extra?): Promise<Message[]>` | 通过 `sendMediaGroup` 发送相册,自动引用回复. |
672
+ | `replyWithLocation` | `replyWithLocation(latitude, longitude, extra?): Promise<Message>` | 发送 `sendLocation`,自动引用回复. |
673
+ | `replyWithVenue` | `replyWithVenue(latitude, longitude, title, address, extra?): Promise<Message>` | 发送 `sendVenue`,自动引用回复. |
674
+ | `replyWithContact` | `replyWithContact(phoneNumber, firstName, extra?): Promise<Message>` | 发送 `sendContact`,自动引用回复. |
675
+ | `replyWithPoll` | `replyWithPoll(question, options, extra?): Promise<Message>` | 发送 `sendPoll`,自动引用回复. |
676
+ | `replyWithDice` | `replyWithDice(emoji?, extra?): Promise<Message>` | 发送 `sendDice`,自动引用回复. |
636
677
  | `copy` | `copy(fromChatId, messageId, extra?): Promise<unknown>` | 向上下文聊天调用 `copyMessage`. |
637
678
  | `forward` | `forward(fromChatId, messageId, extra?): Promise<Message>` | 向上下文聊天调用 `forwardMessage`. |
638
679
  | `pin` | `pin(messageId?, extra?): Promise<true>` | 调用 `pinChatMessage`,默认消息 ID 来自上下文. |
@@ -1257,7 +1298,20 @@ new Menu(id: string): Menu
1257
1298
 
1258
1299
  ## 12. Terminal Logging
1259
1300
 
1260
- This package starts directly after Telegram API connectivity is established. The terminal prints a boxed telebibz attribution, an animated startup status when attached to a TTY, and structured colorful logs for lifecycle, API, polling, webhook, and update events. Set logger format to `json` for machine ingestion.
1301
+ 当 stdout 是交互式 TTY 时,每次 `bot.start()` / `bot.launch()` 都会播放启动序列:`Installing Dependencies......` 打字效果、带扫过高光的 glass 进度条,以及动画彩虹 ASCII 横幅 `Tele Bibz`(figlet `Speed` 字体)——持续流动直到 bot 连接成功,随后定格为 `✓ Connected as @<username>`。
1302
+
1303
+ bot 处理的每条 update 都会以易读的行格式记录:
1304
+
1305
+ ```text
1306
+ [ => ] Message From 123456789 John Doe 29/08/2026 15:04:05
1307
+ ↳ Text: /start
1308
+ [ => ] Callback From 123456789 John Doe 29/08/2026 15:04:07
1309
+ ↳ Data: menu:open
1310
+ ```
1311
+
1312
+ 普通消息与命令文本截断为 50 个字符;回调按钮数据完整显示。错误以红色打印并附带完整堆栈。向 `Bot` 传入 `branding: false` 可关闭启动序列;设置 `logger.format: "json"` 时,进入的 update 会作为结构化 `update.received` entry 输出。非交互 stdout(管道、Docker、CI)自动回退为无动画的纯文本。
1313
+
1314
+ 面向应用导出的附加 branding helper:`runStartupSequence()`、`startTeleBibzBanner()`、`printTeleBibzBanner()`、`paintRainbow()` 和 `printStatusLine()`。
1261
1315
 
1262
1316
  ## 13. 文本工具
1263
1317
 
@@ -1685,7 +1739,7 @@ Package 内置 MIT 许可的 Telegram declaration,并通过 type-only export
1685
1739
 
1686
1740
  ## 19. 兼容性和需要注意的限制
1687
1741
 
1688
- Library menargetkan Node.js `>=20`,使用 ESM 作为主要模块,并提供 CommonJS 构建。Webhook 需要运行时提供 Web `Request`、`Response`、`Headers`、`FormData`、`Blob` 和 `AbortController`;现代 Node.js 原生提供了这些。
1742
+ 本库面向 Node.js `>=22`,使用 ESM 作为主要模块,并提供 CommonJS 构建。Webhook 需要运行时提供 Web `Request`、`Response`、`Headers`、`FormData`、`Blob` 和 `AbortController`;现代 Node.js 原生提供了这些。
1689
1743
 
1690
1744
  API 生成的方法列表(generated method list)和 API 方法映射(API method map)并不相同。`TelegramMethodName` 包含 184 个运行时名称,但 `TelegramMethodMap` 仅对 API 客户端部分列出的子集提供了带类型的参数/结果。对于其他方法,使用 `api.raw()` 或在应用端添加类型声明。
1691
1745
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xbibzlibrary/telebibz",
3
- "version": "0.1.19",
4
- "description": "Production-grade Telegram Bot framework for Node.js and TypeScript with typed API, routing, webhooks, keyboards, conversations, plugins, queues, and colorful CLI logs.",
3
+ "version": "0.2.1",
4
+ "description": "Telegram Bot framework for Node.js and TypeScript with a typed API client, routing, middleware, webhooks, keyboards, conversations, plugins, queues, and a polished terminal experience.",
5
5
  "keywords": [
6
6
  "telegram",
7
7
  "telegram-bot",
@@ -32,7 +32,7 @@
32
32
  "url": "https://github.com/XbibzOfficial777/telebibz/issues"
33
33
  },
34
34
  "engines": {
35
- "node": ">=20"
35
+ "node": ">=22"
36
36
  },
37
37
  "scripts": {
38
38
  "generate": "node scripts/generate-api.mjs",