@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.
Files changed (258) hide show
  1. package/CHANGELOG.md +35 -138
  2. package/LICENSE +1 -1
  3. package/NOTICE.md +9 -4
  4. package/README.md +173 -242
  5. package/examples/01-quickstart.js +13 -0
  6. package/examples/02-menu-tombol.js +22 -0
  7. package/examples/03-wizard.js +29 -0
  8. package/examples/04-broadcast.js +25 -0
  9. package/examples/05-kirim-file.js +19 -0
  10. package/examples/06-menu.js +35 -0
  11. package/examples/07-inline-query.js +18 -0
  12. package/index.d.ts +81 -0
  13. package/index.js +42 -0
  14. package/lib/api.js +154 -0
  15. package/lib/broadcast.js +35 -0
  16. package/lib/composer.js +174 -0
  17. package/lib/context.js +190 -0
  18. package/lib/errors.js +35 -0
  19. package/lib/file.js +41 -0
  20. package/lib/inline-query.js +28 -0
  21. package/lib/keyboard.js +83 -0
  22. package/lib/logger.js +38 -0
  23. package/lib/menus.js +90 -0
  24. package/lib/net.js +115 -0
  25. package/lib/ratelimit.js +61 -0
  26. package/lib/runner.js +45 -0
  27. package/lib/session.js +36 -0
  28. package/lib/telebibz.js +190 -0
  29. package/lib/wizard.js +81 -0
  30. package/package.json +35 -97
  31. package/test/all.test.js +334 -0
  32. package/CODE_OF_CONDUCT.md +0 -37
  33. package/CONTRIBUTING.md +0 -59
  34. package/CONTRIBUTION_RULES.md +0 -41
  35. package/GOVERNANCE.md +0 -47
  36. package/README.id.md +0 -292
  37. package/README.zh-CN.md +0 -292
  38. package/RELEASE_AUTOMATION.md +0 -78
  39. package/RELEASE_POLICY.md +0 -32
  40. package/SECURITY.md +0 -47
  41. package/SHOWCASE.md +0 -29
  42. package/SUPPORT.md +0 -30
  43. package/assets/readme-preview.html +0 -75
  44. package/assets/telebibz-logo.png +0 -0
  45. package/assets/telebibz-readme-preview.png +0 -0
  46. package/bin/telebibz.mjs +0 -3
  47. package/dist/generated/api.d.ts +0 -13
  48. package/dist/generated/api.d.ts.map +0 -1
  49. package/dist/generated/api.js +0 -192
  50. package/dist/generated/api.js.map +0 -1
  51. package/dist/src/api/client.d.ts +0 -62
  52. package/dist/src/api/client.d.ts.map +0 -1
  53. package/dist/src/api/client.js +0 -104
  54. package/dist/src/api/client.js.map +0 -1
  55. package/dist/src/api/errors.d.ts +0 -45
  56. package/dist/src/api/errors.d.ts.map +0 -1
  57. package/dist/src/api/errors.js +0 -65
  58. package/dist/src/api/errors.js.map +0 -1
  59. package/dist/src/api/index.d.ts +0 -6
  60. package/dist/src/api/index.d.ts.map +0 -1
  61. package/dist/src/api/index.js +0 -6
  62. package/dist/src/api/index.js.map +0 -1
  63. package/dist/src/api/telegram-types/LICENSE +0 -21
  64. package/dist/src/api/telegram-types/api.d.ts +0 -22
  65. package/dist/src/api/telegram-types/checklist.d.ts +0 -72
  66. package/dist/src/api/telegram-types/inline.d.ts +0 -692
  67. package/dist/src/api/telegram-types/langs.d.ts +0 -193
  68. package/dist/src/api/telegram-types/manage.d.ts +0 -1144
  69. package/dist/src/api/telegram-types/markup.d.ts +0 -268
  70. package/dist/src/api/telegram-types/message.d.ts +0 -1537
  71. package/dist/src/api/telegram-types/methods.d.ts +0 -2870
  72. package/dist/src/api/telegram-types/mod.d.ts +0 -14
  73. package/dist/src/api/telegram-types/passport.d.ts +0 -163
  74. package/dist/src/api/telegram-types/payment.d.ts +0 -570
  75. package/dist/src/api/telegram-types/rich.d.ts +0 -1010
  76. package/dist/src/api/telegram-types/settings.d.ts +0 -120
  77. package/dist/src/api/telegram-types/story.d.ts +0 -89
  78. package/dist/src/api/telegram-types/update.d.ts +0 -84
  79. package/dist/src/api/telegram.d.ts +0 -7
  80. package/dist/src/api/telegram.d.ts.map +0 -1
  81. package/dist/src/api/telegram.js +0 -2
  82. package/dist/src/api/telegram.js.map +0 -1
  83. package/dist/src/api/transport.d.ts +0 -68
  84. package/dist/src/api/transport.d.ts.map +0 -1
  85. package/dist/src/api/transport.js +0 -264
  86. package/dist/src/api/transport.js.map +0 -1
  87. package/dist/src/api/types.d.ts +0 -466
  88. package/dist/src/api/types.d.ts.map +0 -1
  89. package/dist/src/api/types.js +0 -2
  90. package/dist/src/api/types.js.map +0 -1
  91. package/dist/src/branding/terminal.d.ts +0 -77
  92. package/dist/src/branding/terminal.d.ts.map +0 -1
  93. package/dist/src/branding/terminal.js +0 -328
  94. package/dist/src/branding/terminal.js.map +0 -1
  95. package/dist/src/broadcast/broadcast.d.ts +0 -50
  96. package/dist/src/broadcast/broadcast.d.ts.map +0 -1
  97. package/dist/src/broadcast/broadcast.js +0 -56
  98. package/dist/src/broadcast/broadcast.js.map +0 -1
  99. package/dist/src/cache/cache.d.ts +0 -34
  100. package/dist/src/cache/cache.d.ts.map +0 -1
  101. package/dist/src/cache/cache.js +0 -41
  102. package/dist/src/cache/cache.js.map +0 -1
  103. package/dist/src/cli.d.ts +0 -2
  104. package/dist/src/cli.d.ts.map +0 -1
  105. package/dist/src/cli.js +0 -84
  106. package/dist/src/cli.js.map +0 -1
  107. package/dist/src/context/context.d.ts +0 -124
  108. package/dist/src/context/context.d.ts.map +0 -1
  109. package/dist/src/context/context.js +0 -302
  110. package/dist/src/context/context.js.map +0 -1
  111. package/dist/src/core/bot.d.ts +0 -204
  112. package/dist/src/core/bot.d.ts.map +0 -1
  113. package/dist/src/core/bot.js +0 -506
  114. package/dist/src/core/bot.js.map +0 -1
  115. package/dist/src/core/events.d.ts +0 -75
  116. package/dist/src/core/events.d.ts.map +0 -1
  117. package/dist/src/core/events.js +0 -35
  118. package/dist/src/core/events.js.map +0 -1
  119. package/dist/src/core/webhook-reply.d.ts +0 -34
  120. package/dist/src/core/webhook-reply.d.ts.map +0 -1
  121. package/dist/src/core/webhook-reply.js +0 -37
  122. package/dist/src/core/webhook-reply.js.map +0 -1
  123. package/dist/src/index.d.ts +0 -24
  124. package/dist/src/index.d.ts.map +0 -1
  125. package/dist/src/index.js +0 -24
  126. package/dist/src/index.js.map +0 -1
  127. package/dist/src/keyboard/index.d.ts +0 -46
  128. package/dist/src/keyboard/index.d.ts.map +0 -1
  129. package/dist/src/keyboard/index.js +0 -55
  130. package/dist/src/keyboard/index.js.map +0 -1
  131. package/dist/src/middleware/compose.d.ts +0 -5
  132. package/dist/src/middleware/compose.d.ts.map +0 -1
  133. package/dist/src/middleware/compose.js +0 -17
  134. package/dist/src/middleware/compose.js.map +0 -1
  135. package/dist/src/observability/logger.d.ts +0 -78
  136. package/dist/src/observability/logger.d.ts.map +0 -1
  137. package/dist/src/observability/logger.js +0 -285
  138. package/dist/src/observability/logger.js.map +0 -1
  139. package/dist/src/plugins/plugin.d.ts +0 -38
  140. package/dist/src/plugins/plugin.d.ts.map +0 -1
  141. package/dist/src/plugins/plugin.js +0 -59
  142. package/dist/src/plugins/plugin.js.map +0 -1
  143. package/dist/src/queue/queue.d.ts +0 -77
  144. package/dist/src/queue/queue.d.ts.map +0 -1
  145. package/dist/src/queue/queue.js +0 -213
  146. package/dist/src/queue/queue.js.map +0 -1
  147. package/dist/src/router/router.d.ts +0 -61
  148. package/dist/src/router/router.d.ts.map +0 -1
  149. package/dist/src/router/router.js +0 -183
  150. package/dist/src/router/router.js.map +0 -1
  151. package/dist/src/state/conversation.d.ts +0 -56
  152. package/dist/src/state/conversation.d.ts.map +0 -1
  153. package/dist/src/state/conversation.js +0 -133
  154. package/dist/src/state/conversation.js.map +0 -1
  155. package/dist/src/state/forms.d.ts +0 -34
  156. package/dist/src/state/forms.d.ts.map +0 -1
  157. package/dist/src/state/forms.js +0 -44
  158. package/dist/src/state/forms.js.map +0 -1
  159. package/dist/src/state/menu.d.ts +0 -78
  160. package/dist/src/state/menu.d.ts.map +0 -1
  161. package/dist/src/state/menu.js +0 -127
  162. package/dist/src/state/menu.js.map +0 -1
  163. package/dist/src/storage/storage.d.ts +0 -146
  164. package/dist/src/storage/storage.d.ts.map +0 -1
  165. package/dist/src/storage/storage.js +0 -195
  166. package/dist/src/storage/storage.js.map +0 -1
  167. package/dist/src/telegram-features.d.ts +0 -33
  168. package/dist/src/telegram-features.d.ts.map +0 -1
  169. package/dist/src/telegram-features.js +0 -71
  170. package/dist/src/telegram-features.js.map +0 -1
  171. package/dist/src/testing.d.ts +0 -24
  172. package/dist/src/testing.d.ts.map +0 -1
  173. package/dist/src/testing.js +0 -38
  174. package/dist/src/testing.js.map +0 -1
  175. package/dist/src/utils/concurrency.d.ts +0 -25
  176. package/dist/src/utils/concurrency.d.ts.map +0 -1
  177. package/dist/src/utils/concurrency.js +0 -52
  178. package/dist/src/utils/concurrency.js.map +0 -1
  179. package/dist/src/utils/files.d.ts +0 -45
  180. package/dist/src/utils/files.d.ts.map +0 -1
  181. package/dist/src/utils/files.js +0 -53
  182. package/dist/src/utils/files.js.map +0 -1
  183. package/dist/src/utils/text.d.ts +0 -39
  184. package/dist/src/utils/text.d.ts.map +0 -1
  185. package/dist/src/utils/text.js +0 -56
  186. package/dist/src/utils/text.js.map +0 -1
  187. package/dist/src/webhook/handler.d.ts +0 -19
  188. package/dist/src/webhook/handler.d.ts.map +0 -1
  189. package/dist/src/webhook/handler.js +0 -141
  190. package/dist/src/webhook/handler.js.map +0 -1
  191. package/dist-cjs/generated/api.js +0 -194
  192. package/dist-cjs/package.json +0 -3
  193. package/dist-cjs/src/api/client.js +0 -107
  194. package/dist-cjs/src/api/errors.js +0 -74
  195. package/dist-cjs/src/api/index.js +0 -21
  196. package/dist-cjs/src/api/telegram-types/LICENSE +0 -21
  197. package/dist-cjs/src/api/telegram-types/api.d.ts +0 -22
  198. package/dist-cjs/src/api/telegram-types/checklist.d.ts +0 -72
  199. package/dist-cjs/src/api/telegram-types/inline.d.ts +0 -692
  200. package/dist-cjs/src/api/telegram-types/langs.d.ts +0 -193
  201. package/dist-cjs/src/api/telegram-types/manage.d.ts +0 -1144
  202. package/dist-cjs/src/api/telegram-types/markup.d.ts +0 -268
  203. package/dist-cjs/src/api/telegram-types/message.d.ts +0 -1537
  204. package/dist-cjs/src/api/telegram-types/methods.d.ts +0 -2870
  205. package/dist-cjs/src/api/telegram-types/mod.d.ts +0 -14
  206. package/dist-cjs/src/api/telegram-types/passport.d.ts +0 -163
  207. package/dist-cjs/src/api/telegram-types/payment.d.ts +0 -570
  208. package/dist-cjs/src/api/telegram-types/rich.d.ts +0 -1010
  209. package/dist-cjs/src/api/telegram-types/settings.d.ts +0 -120
  210. package/dist-cjs/src/api/telegram-types/story.d.ts +0 -89
  211. package/dist-cjs/src/api/telegram-types/update.d.ts +0 -84
  212. package/dist-cjs/src/api/telegram.js +0 -2
  213. package/dist-cjs/src/api/transport.js +0 -267
  214. package/dist-cjs/src/api/types.js +0 -2
  215. package/dist-cjs/src/branding/terminal.js +0 -338
  216. package/dist-cjs/src/broadcast/broadcast.js +0 -58
  217. package/dist-cjs/src/cache/cache.js +0 -45
  218. package/dist-cjs/src/cli.js +0 -86
  219. package/dist-cjs/src/context/context.js +0 -305
  220. package/dist-cjs/src/core/bot.js +0 -510
  221. package/dist-cjs/src/core/events.js +0 -38
  222. package/dist-cjs/src/core/webhook-reply.js +0 -42
  223. package/dist-cjs/src/index.js +0 -47
  224. package/dist-cjs/src/keyboard/index.js +0 -61
  225. package/dist-cjs/src/middleware/compose.js +0 -20
  226. package/dist-cjs/src/observability/logger.js +0 -293
  227. package/dist-cjs/src/plugins/plugin.js +0 -63
  228. package/dist-cjs/src/queue/queue.js +0 -219
  229. package/dist-cjs/src/router/router.js +0 -186
  230. package/dist-cjs/src/state/conversation.js +0 -139
  231. package/dist-cjs/src/state/forms.js +0 -47
  232. package/dist-cjs/src/state/menu.js +0 -133
  233. package/dist-cjs/src/storage/storage.js +0 -202
  234. package/dist-cjs/src/telegram-features.js +0 -76
  235. package/dist-cjs/src/testing.js +0 -45
  236. package/dist-cjs/src/utils/concurrency.js +0 -57
  237. package/dist-cjs/src/utils/files.js +0 -58
  238. package/dist-cjs/src/utils/text.js +0 -63
  239. package/dist-cjs/src/webhook/handler.js +0 -144
  240. package/docs/API.id.md +0 -1935
  241. package/docs/API.md +0 -1969
  242. package/docs/API.zh-CN.md +0 -1929
  243. package/docs/GETTING_STARTED.id.md +0 -85
  244. package/docs/GETTING_STARTED.md +0 -85
  245. package/docs/GETTING_STARTED.zh-CN.md +0 -85
  246. package/docs/GITHUB_PACKAGES.id.md +0 -82
  247. package/docs/GITHUB_PACKAGES.md +0 -82
  248. package/docs/GITHUB_PACKAGES.zh-CN.md +0 -82
  249. package/docs/README.md +0 -48
  250. package/docs/STORAGE.id.md +0 -105
  251. package/docs/STORAGE.md +0 -105
  252. package/docs/STORAGE.zh-CN.md +0 -105
  253. package/examples/README.md +0 -37
  254. package/examples/files.ts +0 -35
  255. package/examples/minimal.ts +0 -12
  256. package/examples/tsconfig.json +0 -9
  257. package/examples/webhook.ts +0 -42
  258. package/examples/wizard-registration.ts +0 -42
package/docs/API.id.md DELETED
@@ -1,1935 +0,0 @@
1
- # Referensi API telebibz — Bahasa Indonesia
2
-
3
- [English](API.md) · **Bahasa Indonesia** · [简体中文](API.zh-CN.md)
4
-
5
- ![telebibz overview](https://cdn.jsdelivr.net/npm/@xbibzlibrary/telebibz@latest/assets/telebibz-readme-preview.png)
6
-
7
- Dokumen ini adalah referensi API untuk rilis `@xbibzlibrary/telebibz` yang sedang dipublikasikan. Seluruh signature dan perilaku yang dijelaskan di sini dipetakan dari source TypeScript yang diekspor package. Jika suatu tipe Telegram belum memiliki pemetaan parameter/result khusus, package tetap menyediakan akses runtime melalui API dinamis, tetapi tipe parameternya masih generik.
8
-
9
- > **Status implementasi.** Dokumentasi ini menjelaskan kemampuan yang tersedia pada rilis saat ini. `JsonFileStorage`, storage Redis/SQL/Mongo berbasis driver, session/conversation berbasis Storage, cron lima field lengkap, `MenuController`, terminal status output branded, structured logging dengan redaction, validasi Web App, `PaymentsClient`, dan declaration `TelegramTypes` sudah tersedia. Core method map tetap khusus untuk inferensi request/result tertentu, sedangkan `api.raw()` tersedia untuk method Telegram berikutnya.
10
-
11
- ## Instalasi dan import
12
-
13
- ```bash
14
- npm install @xbibzlibrary/telebibz
15
- ```
16
-
17
- ESM:
18
-
19
- ```ts
20
- import {
21
- Bot,
22
- InlineKeyboard,
23
- compose,
24
- escapeHtml,
25
- type Context,
26
- } from "@xbibzlibrary/telebibz";
27
- ```
28
-
29
- CommonJS:
30
-
31
- ```js
32
- const { Bot, InlineKeyboard } = require("@xbibzlibrary/telebibz");
33
- ```
34
-
35
- Subpath exports yang tersedia adalah sebagai berikut.
36
-
37
- | Subpath | Isi |
38
- |---|---|
39
- | `@xbibzlibrary/telebibz` | Seluruh public API utama dari `src/index.ts` |
40
- | `@xbibzlibrary/telebibz/api` | Client, transport, errors, dan semua tipe API Telegram |
41
- | `@xbibzlibrary/telebibz/keyboard` | `InlineKeyboard`, `ReplyKeyboard`, dan helpers keyboard |
42
- | `@xbibzlibrary/telebibz/testing` | `MockTransport` dan test factories |
43
-
44
- ---
45
-
46
- ## 1. Bot inti
47
-
48
- ### `BotStatus`
49
-
50
- ```ts
51
- type BotStatus =
52
- | "created"
53
- | "initialized"
54
- | "starting"
55
- | "running"
56
- | "stopping"
57
- | "stopped"
58
- | "error";
59
- ```
60
-
61
- ### `BotOptions<S>`
62
-
63
- | Properti | Tipe | Default | Keterangan |
64
- |---|---|---:|---|
65
- | `token` | `string` | wajib | Token BotFather dengan format `<digits>:<token>`. |
66
- | `apiBaseUrl` | `string` | `https://api.telegram.org` | Base URL API Telegram. Akhiran `/` dihapus secara otomatis. |
67
- | `transport` | `Transport` | `FetchTransport` | Transport kustom untuk mock, proxy, atau implementasi lain. |
68
- | `transportOptions` | `Omit<FetchTransportOptions, "baseUrl">` | `{}` | Timeout, retry, backoff, jitter, headers, dan fetch implementation. |
69
- | `session` | `Storage<string, S>` | storage baru | Penyimpanan session berdasarkan kunci chat/user; dapat memakai adapter persistent. |
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. |
72
- | `polling.timeout` | `number` | `30` | Long-poll timeout dalam detik untuk `getUpdates`. |
73
- | `polling.limit` | `number` | `100` | Jumlah maksimum update per request polling. |
74
- | `polling.allowedUpdates` | `string[]` | `[]` | Filter update Telegram. |
75
- | `polling.retryDelayMs` | `number` | `500` | Delay awal ketika polling gagal. |
76
- | `polling.maxRetryDelayMs` | `number` | `30000` | Batas maksimum delay reconnect. |
77
- | `updates.concurrency` | `number` | `Infinity` | Batas jumlah update yang diproses bersamaan. Update selalu berjalan paralel antar chat dan tetap berurutan di dalam satu chat, sehingga burst 1000+ pesan tertangani sekaligus. |
78
- | `handlerTimeout` | `number` | `90000` | Timeout pemrosesan per update dalam ms (`Infinity` untuk menonaktifkan). Saat timeout, alur error update berjalan (`update:error`, `bot:error`, boundary `catch()`) dan `handleUpdate()` melempar `UpdateTimeoutError`, sementara handler tetap berjalan sampai selesai di background. |
79
- | `contextType` | `new (options: ContextOptions<S>) => Context<S>` | `Context` | Subclass `Context` kustom yang diinstansiasi untuk setiap update (`contextType` milik Telegraf). |
80
-
81
- ### Konstruktor `Bot`
82
-
83
- ```ts
84
- new Bot<S extends object = Record<string, unknown>>(
85
- options: string | BotOptions<S>,
86
- ): Bot<S>
87
- ```
88
-
89
- Jika argumen berupa string, string tersebut dianggap sebagai token. Konstruktor membuat `ApiClient`, router, event bus, plugin manager, session storage, dan structured runtime logging yang selalu aktif. Konstruktor langsung memancarkan event `bot:created` secara asinkron.
90
-
91
- Konstruktor melempar `Error` jika token kosong atau tidak sesuai pola token Telegram.
92
-
93
- ### Properti dan getter `Bot`
94
-
95
- | API | Tipe | Deskripsi |
96
- |---|---|---|
97
- | `api` | `ApiClient` | Client Telegram typed/dynamic. |
98
- | `router` | `Router<Context<S>>` | Router utama bot. |
99
- | `events` | `EventBus<EventMap>` | Event bus untuk lifecycle, update, API, webhook, dan polling. |
100
- | `plugins` | `PluginManager<Context<S>>` | Manajer lifecycle plugin. |
101
- | `session` | `Storage<string, S>` | Session bot; dapat memakai adapter persistent. |
102
- | `services` | `Record<string, unknown>` | Salinan service yang diberikan saat konstruktor. |
103
- | `token` | `string` | Token bot yang dipakai client. |
104
- | `status` | `BotStatus` | Status lifecycle terkini. |
105
- | `botInfo` | `User \| undefined` | Hasil `getMe()` terakhir yang tersimpan. |
106
-
107
- ### `bot.use(...middleware)`
108
-
109
- ```ts
110
- use(...middleware: Middleware<Context<S>>[]): this
111
- ```
112
-
113
- Menambahkan middleware global. Middleware dijalankan sebelum router pada setiap update, sesuai urutan registrasi. Mengembalikan instance bot untuk chaining.
114
-
115
- ### `bot.command(name, handler)`
116
-
117
- ```ts
118
- command(name: string, handler: Middleware<Context<S>>): this
119
- ```
120
-
121
- Mendaftarkan command Telegram tanpa awalan `/` maupun dengan awalan `/`. Pencocokan mengambil token pertama setelah `/` dan mengabaikan bot mention setelah `@`. Contoh `/start@my_bot` cocok dengan `"start"`.
122
-
123
- ### `bot.callback(pattern, handler)`
124
-
125
- ```ts
126
- callback(pattern: string | RegExp, handler: Middleware<Context<S>>): this
127
- ```
128
-
129
- Jalan pintas untuk route callback query. String yang berakhiran `*` berarti pencocokan prefix; string lain harus sama persis.
130
-
131
- ### `bot.onText(text, handler)`
132
-
133
- ```ts
134
- onText(text: string, handler: Middleware<Context<S>>): this
135
- ```
136
-
137
- Menangani message yang `message.text`-nya sama persis dengan `text`.
138
-
139
- ### `bot.onRegex(expression, handler)`
140
-
141
- ```ts
142
- onRegex(expression: RegExp, handler: Middleware<Context<S>>): this
143
- ```
144
-
145
- Menangani message text menggunakan `RegExp`. Parameter route tidak diekstrak otomatis ke `ctx.params`; gunakan predicate atau middleware custom jika memerlukan ekstraksi.
146
-
147
- ### `bot.on(filter, handler)`
148
-
149
- ```ts
150
- on(filter: UpdateFilter | UpdateFilter[], handler: Middleware<Context<S>>): this
151
- ```
152
-
153
- 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.
154
-
155
- ### `bot.hears(trigger, handler)`
156
-
157
- ```ts
158
- hears(trigger: string | RegExp, handler: Middleware<Context<S>>): this
159
- ```
160
-
161
- Menangani message text yang sama persis (string) atau yang cocok dengan `RegExp`.
162
-
163
- ### `bot.catch(handler)`
164
-
165
- ```ts
166
- catch(handler: (error: unknown, ctx: Context<S>) => void | Promise<void>): this
167
- ```
168
-
169
- 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.
170
-
171
- ### `bot.usePlugin(plugin)`
172
-
173
- ```ts
174
- usePlugin(plugin: Plugin<Context<S>>): this
175
- ```
176
-
177
- Mendaftarkan plugin. Nama plugin harus unik.
178
-
179
- ### `bot.init()`
180
-
181
- ```ts
182
- init(): Promise<this>
183
- ```
184
-
185
- Memanggil `getMe()`, menyimpan informasi bot, menginisialisasi plugin, dan mengembalikan bot yang siap untuk polling atau pemrosesan update manual.
186
-
187
- `init()` idempoten ketika status sudah `initialized` atau `running`.
188
-
189
- ### `bot.start()`
190
-
191
- ```ts
192
- start(): Promise<void>
193
- ```
194
-
195
- Jalan pintas untuk `launch({ mode: "polling" })`. Method ini menjalankan long polling dan menunggu sampai polling dihentikan atau gagal secara fatal.
196
-
197
- ### `bot.launch(options?)`
198
-
199
- ```ts
200
- launch(options?: {
201
- mode: "polling";
202
- timeout?: number;
203
- allowedUpdates?: string[];
204
- dropPendingUpdates?: boolean;
205
- }): Promise<void>
206
- ```
207
-
208
- Menjalankan bot dalam mode polling. Saat mulai, lifecycle berpindah melalui `starting` lalu `running`, kemudian loop `getUpdates()` memproses setiap batch update secara konkuren: update dari chat berbeda berjalan paralel, sedangkan update dari chat yang sama menjaga urutan kedatangannya. Kegagalan polling memancarkan `polling:reconnect` dan menggunakan backoff eksponensial. `dropPendingUpdates: true` (juga tersedia di `bot.start()`) membuang semua update yang ditahan Telegram sebelum panggilan `getUpdates` pertama, memakai mekanisme `deleteWebhook({ drop_pending_updates: true })` yang sama dengan Telegraf.
209
-
210
- Mode selain `"polling"` melempar error dan menyarankan penggunaan `createWebhookHandler()` untuk webhook.
211
-
212
- ### `bot.stop()`
213
-
214
- ```ts
215
- stop(): Promise<void>
216
- ```
217
-
218
- Menghentikan polling melalui `AbortController`, menjalankan `plugins.dispose()`, mengubah status menjadi `stopped`, dan memancarkan event stopping/stopped. Pemanggilan ketika status `created` atau `stopped` tidak melakukan apa-apa.
219
-
220
- ### `bot.restart()`
221
-
222
- ```ts
223
- restart(): Promise<void>
224
- ```
225
-
226
- Menjalankan `stop()` lalu `start()`.
227
-
228
- ### `bot.health()`
229
-
230
- ```ts
231
- health(): Promise<HealthStatus>
232
- ```
233
-
234
- Memanggil `getMe()` untuk memeriksa keterjangkauan API. Tidak melempar error untuk kegagalan request; kegagalan dikembalikan sebagai `apiReachable: false` dan pesan error.
235
-
236
- ```ts
237
- interface HealthStatus {
238
- status: BotStatus;
239
- apiReachable: boolean;
240
- bot?: User;
241
- checkedAt: string; // ISO timestamp
242
- error?: string;
243
- }
244
- ```
245
-
246
- ### `bot.getMe()`
247
-
248
- ```ts
249
- getMe(): Promise<User>
250
- ```
251
-
252
- Mengambil data bot dari Telegram dan memperbarui `botInfo`.
253
-
254
- ### `bot.setCommands(commands, scope?, languageCode?)`
255
-
256
- ```ts
257
- setCommands(
258
- commands: BotCommand[],
259
- scope?: BotCommandScope,
260
- languageCode?: string,
261
- ): Promise<true>
262
- ```
263
-
264
- Jalan pintas ke `setMyCommands`. `languageCode` dipetakan menjadi field Telegram `language_code`.
265
-
266
- ### `bot.deleteCommands(scope?, languageCode?)`
267
-
268
- ```ts
269
- deleteCommands(
270
- scope?: BotCommandScope,
271
- languageCode?: string,
272
- ): Promise<true>
273
- ```
274
-
275
- Jalan pintas ke `deleteMyCommands`.
276
-
277
- ### `bot.downloadFile(fileId, options?)`
278
-
279
- ```ts
280
- downloadFile(
281
- fileId: string,
282
- options?: { signal?: AbortSignal; destination?: string },
283
- ): Promise<DownloadedFile>
284
- ```
285
-
286
- Me-resolve `fileId` lewat `getFile`, lalu mengunduh byte mentahnya melalui endpoint download transport. Berikan `destination` untuk juga menyimpan byte ke path file lokal (`savedTo` terisi pada hasil). Melempar `TelegramError` (kind `validation`) saat Telegram tidak mengembalikan `file_path` atau transport tidak bisa mengunduh, dan `TelegramNetworkError` saat unduhan gagal. Telegram membatasi unduhan pada 20 MB; `url` hasilnya tetap valid minimal satu jam.
287
-
288
- ```ts
289
- const file = await bot.downloadFile(photoFileId, { destination: "downloads/photo.jpg" });
290
- console.log(file.fileName, file.sizeBytes, file.url, file.savedTo);
291
- ```
292
-
293
- ### `bot.handleUpdate(update)`
294
-
295
- ```ts
296
- handleUpdate(update: Update, options?: { webhookReply?: WebhookReplySink }): Promise<void>
297
- ```
298
-
299
- Memproses satu update secara manual. Method menentukan kunci session dari `chat.id` dan `from.id`, membuat `Context` (dari `contextType` yang dikonfigurasi), memancarkan event `update` dan `message`, menjalankan middleware lalu router, dan menyimpan session setelah pipeline selesai.
300
-
301
- Update dari chat berbeda diproses paralel; update dari chat yang sama diserialisasi sesuai urutan kedatangan, sehingga session, wizard, dan conversation tidak pernah saling tumpang tindih dan penulisan session tidak pernah hilang. Burst update konkuren hanya memicu satu inisialisasi `getMe`. Seluruh proses per update dijaga `handlerTimeout` (default 90 detik, sama dengan Telegraf): saat timeout, error mengalir lewat `update:error`/`bot:error` dan boundary `catch()`, dan `handleUpdate()` melempar `UpdateTimeoutError` sementara handler tetap berjalan di background.
302
-
303
- `options.webhookReply` memasang responder ala Telegraf: panggilan API keluar pertama selama update ini dijawab lewat respons HTTP webhook, bukan request terpisah, dan resolve dengan `true` (Telegram tidak pernah mengirim hasil method kembali ke respons webhook).
304
-
305
- Error pipeline mengubah status bot menjadi `error`, memancarkan `bot:error`, lalu dilempar kembali.
306
-
307
- ### `bot.handleUpdates(updates)`
308
-
309
- ```ts
310
- handleUpdates(updates: readonly Update[]): Promise<void>
311
- ```
312
-
313
- Menangani satu batch update sekaligus: setiap chat dalam batch langsung diproses — paralel antar chat, berurutan per chat — sehingga burst 1000 pesan tidak pernah terhambat oleh satu handler yang lambat. Kegagalan handler individual dicatat ke log, dipancarkan sebagai `update:error`, dan diteruskan ke error boundary `catch()`; kegagalan tersebut tidak pernah menolak promise ini. Loop polling memakai method ini untuk setiap batch `getUpdates`.
314
-
315
- ### `bot.broadcast(chatIds, send, options?)`
316
-
317
- ```ts
318
- broadcast(
319
- chatIds: readonly ChatId[],
320
- send: (chatId: ChatId) => Promise<unknown>,
321
- options?: BroadcastOptions,
322
- ): Promise<BroadcastReport>
323
- ```
324
-
325
- Mengirim ke banyak chat secara paralel — dibuat untuk broadcast ke 1000+ user. Tidak ada cooldown proaktif: semua chat langsung dicoba sekaligus (sampai `options.concurrency`, default `Infinity`). Ketika Telegram menjawab 429, pengiriman otomatis diulang setelah tepat delay `retry_after` yang diperintahkan Telegram (maksimal `options.maxAttempts`, default `10`), sehingga burst tetap terkirim lengkap, bukan gagal. Error yang tidak bisa di-retry (misalnya chat yang tidak bisa dihubungi bot) dicatat per chat pada laporan yang dikembalikan.
326
-
327
- ```ts
328
- const report = await bot.broadcast(
329
- subscriberIds,
330
- (chatId) => bot.api.methods.sendMessage({ chat_id: chatId, text: "Newsletter #42" }),
331
- { onProgress: (progress) => console.log(`${progress.delivered}/${progress.total} terkirim`) },
332
- );
333
- console.log(`Terkirim ${report.delivered} dari ${report.total} dalam ${report.durationMs}ms`);
334
- for (const failure of report.failures) console.warn(`Gagal: ${failure.chatId} — ${failure.error}`);
335
- ```
336
-
337
- #### `BroadcastOptions` dan `BroadcastReport`
338
-
339
- | Properti | Tipe | Default | Deskripsi |
340
- |---|---|---:|---|
341
- | `BroadcastOptions.concurrency` | `number` | `Infinity` | Berapa chat dikirimi pesan secara bersamaan. |
342
- | `BroadcastOptions.maxAttempts` | `number` | `10` | Percobaan per chat ketika Telegram menjawab 429. |
343
- | `BroadcastOptions.onProgress` | `(progress: BroadcastProgress) => void` | — | Dipanggil setelah setiap chat selesai. |
344
- | `BroadcastOptions.signal` | `AbortSignal` | — | Membatalkan pengiriman tertunda; pesan yang sudah terkirim tetap terkirim. |
345
- | `BroadcastReport.total` | `number` | — | Jumlah chat dalam sesi broadcast. |
346
- | `BroadcastReport.delivered` | `number` | — | Chat yang menerima pesan. |
347
- | `BroadcastReport.failed` | `number` | — | Chat yang tidak menerima. |
348
- | `BroadcastReport.durationMs` | `number` | — | Durasi total sesi broadcast. |
349
- | `BroadcastReport.failures` | `BroadcastFailure[]` | — | Catatan per chat `{ chatId, attempts, error, errorKind }`. |
350
-
351
- ### `UpdateTimeoutError` dan helper webhook-reply
352
-
353
- ```ts
354
- class UpdateTimeoutError extends Error {
355
- readonly name = "UpdateTimeoutError";
356
- readonly updateId: number;
357
- }
358
- ```
359
-
360
- Dilempar oleh `handleUpdate()` ketika satu update melebihi `handlerTimeout`. Handler itu sendiri tetap berjalan; error juga mengalir lewat `update:error`, `bot:error`, dan boundary `catch()`.
361
-
362
- ```ts
363
- type WebhookReplySink = (payload: Record<string, unknown>) => void;
364
- runWithWebhookReply(sink, fn): Promise<T> // memasang responder untuk semua panggilan API di dalam fn
365
- runWithoutWebhookReply(fn): Promise<T> // panggilan internal library yang tidak pernah mengklaim slot
366
- ```
367
-
368
- Diekspor agar webhook server kustom bisa memasang webhook reply dengan cara yang sama seperti `createWebhookHandler`.
369
-
370
- ### Contoh bot minimal
371
-
372
- ```ts
373
- import { Bot, InlineKeyboard } from "@xbibzlibrary/telebibz";
374
-
375
- const bot = new Bot({
376
- token: process.env.TELEGRAM_BOT_TOKEN!,
377
- polling: { allowedUpdates: ["message", "callback_query"] },
378
- });
379
-
380
- bot.command("start", async (ctx) => {
381
- await ctx.reply("Halo dari telebibz", {
382
- reply_markup: new InlineKeyboard()
383
- .text("Status", "status")
384
- .build(),
385
- });
386
- });
387
-
388
- bot.callback("status", async (ctx) => {
389
- await ctx.answerCallbackQuery("Bot aktif");
390
- await ctx.reply("Status: running");
391
- });
392
-
393
- await bot.start();
394
- ```
395
-
396
- ---
397
-
398
- ## 2. Bus peristiwa
399
-
400
- ### `EventMap`
401
-
402
- | Peristiwa | Muatan |
403
- |---|---|
404
- | `bot:created` | `{ bot: unknown }` |
405
- | `bot:initialized` | `{ bot: unknown }` |
406
- | `bot:starting` | `{ bot: unknown }` |
407
- | `bot:started` | `{ bot: unknown }` |
408
- | `bot:stopping` | `{ bot: unknown }` |
409
- | `bot:stopped` | `{ bot: unknown }` |
410
- | `bot:error` | `{ bot: unknown; error: unknown }` |
411
- | `update` | `{ update: unknown }` |
412
- | `message` | `{ message: unknown }` |
413
- | `command` | `{ name: string; update: unknown }` |
414
- | `callback` | `{ data: string; update: unknown }` |
415
- | `api:request` | `{ method: string; payload: unknown }` |
416
- | `api:response` | `{ method: string; durationMs: number; response: unknown }` |
417
- | `api:error` | `{ method: string; durationMs: number; error: unknown }` |
418
- | `webhook:request` | `{ update: unknown }` |
419
- | `polling:reconnect` | `{ error: unknown; attempt: number }` |
420
-
421
- ### `EventBus<Events>`
422
-
423
- ```ts
424
- new EventBus<Events extends Record<string, unknown> = EventMap>()
425
- ```
426
-
427
- | Metode | Tanda tangan | Perilaku |
428
- |---|---|---|
429
- | `on` | `on<K>(event: K, listener: (payload: Events[K]) => void \| Promise<void>): () => void` | Menambah listener dan mengembalikan fungsi unsubscribe. |
430
- | `once` | `once<K>(event: K, listener: ...): () => void` | Listener hanya dipanggil sekali, lalu dilepas. |
431
- | `off` | `off<K>(event: K, listener: ...): void` | Melepas listener tertentu. |
432
- | `emit` | `emit<K>(event: K, payload: Events[K]): Promise<void>` | Memanggil listener secara berurutan dan menunggu masing-masing. |
433
- | `removeAllListeners` | `removeAllListeners(): void` | Menghapus semua listener. |
434
- | `listenerCount` | `listenerCount<K>(event: K): number` | Mengembalikan jumlah listener event. |
435
-
436
- ```ts
437
- const unsubscribe = bot.events.on("bot:error", ({ error }) => {
438
- console.error(error);
439
- });
440
- unsubscribe();
441
- ```
442
-
443
- ---
444
-
445
- ## 3. API client, transport, dan error
446
-
447
- ### Tipe dasar
448
-
449
- ```ts
450
- type ChatId = number | string;
451
- type ParseMode = "Markdown" | "MarkdownV2" | "HTML";
452
- type InputFile =
453
- | string
454
- | Uint8Array
455
- | ArrayBuffer
456
- | Blob
457
- | NodeJS.ReadableStream
458
- | { source: string | Uint8Array | ArrayBuffer | Blob | NodeJS.ReadableStream; filename?: string };
459
- ```
460
-
461
- `InputFile` string dapat berupa string biasa atau path file ketika digunakan sebagai `source` dalam object upload. Pada Node.js, path absolut, `./...`, dan `../...` dibaca oleh `FetchTransport` lalu dikirim sebagai multipart file.
462
-
463
- ### `TelegramResponse<T>`
464
-
465
- ```ts
466
- interface TelegramResponse<T> {
467
- ok: boolean;
468
- result?: T;
469
- description?: string;
470
- error_code?: number;
471
- parameters?: ResponseParameters;
472
- }
473
- ```
474
-
475
- ### `TransportRequest`, `TransportResponse`, dan `Transport`
476
-
477
- ```ts
478
- interface TransportRequest {
479
- method: string;
480
- payload?: Record<string, unknown>;
481
- signal?: AbortSignal;
482
- }
483
-
484
- interface TransportResponse<T = unknown> {
485
- status: number;
486
- headers: Headers;
487
- data: TelegramResponse<T>;
488
- }
489
-
490
- interface Transport {
491
- request<T>(request: TransportRequest): Promise<TransportResponse<T>>;
492
- }
493
- ```
494
-
495
- ### `FetchTransportOptions`
496
-
497
- | Properti | Default | Deskripsi |
498
- |---|---:|---|
499
- | `baseUrl` | `https://api.telegram.org` | Prefix URL sebelum `/<method>`. |
500
- | `fetch` | `globalThis.fetch` | Implementasi fetch custom. |
501
- | `timeoutMs` | `30000` | Timeout per attempt. |
502
- | `retries` | `2` | Jumlah retry network error setelah attempt awal. |
503
- | `backoffMs` | `250` | Delay exponential awal. |
504
- | `maxBackoffMs` | `8000` | Batas delay transport. |
505
- | `jitter` | `0.2` | Variasi acak ±20% dari exponential delay. |
506
- | `floodGate` | `true` | Ketika Telegram menjawab 429, permintaan BARU ditunda sampai jendela `retry_after` yang diperintahkan Telegram berlalu. Bukan cooldown proaktif — penundaan satu-satunya hanyalah yang diminta Telegram sendiri. |
507
- | `headers` | `{}` | Header tambahan. |
508
-
509
- ### `new FetchTransport(options?)`
510
-
511
- ```ts
512
- new FetchTransport(options?: FetchTransportOptions): FetchTransport
513
- ```
514
-
515
- Transport bawaan berbasis `fetch`. Payload tanpa upload dikirim sebagai JSON. Payload yang mengandung `Uint8Array`, `ArrayBuffer`, `Blob`, atau nested upload dikirim sebagai `multipart/form-data` menggunakan `FormData`.
516
-
517
- ### `fetchTransport.request(request)`
518
-
519
- ```ts
520
- request<T>(request: TransportRequest): Promise<TransportResponse<T>>
521
- ```
522
-
523
- Mengirim POST ke `${baseUrl}/${method}`. Method dengan awalan `/` dinormalisasi. AbortSignal eksternal diteruskan ke controller internal. Network error yang dianggap retryable akan diulang dengan exponential backoff dan jitter; ketika retry habis, error dibungkus sebagai `TelegramNetworkError`.
524
-
525
- ### `ApiHookContext`, `ApiClientOptions`, dan `ApiMethods`
526
-
527
- ```ts
528
- interface ApiHookContext {
529
- method: string;
530
- payload: unknown;
531
- startedAt: number;
532
- durationMs?: number;
533
- response?: TelegramResponse<unknown>;
534
- error?: unknown;
535
- }
536
-
537
- interface ApiClientOptions {
538
- transport: Transport;
539
- hooks?: {
540
- onRequest?: (context: ApiHookContext) => void | Promise<void>;
541
- onResponse?: (context: ApiHookContext) => void | Promise<void>;
542
- onError?: (context: ApiHookContext) => void | Promise<void>;
543
- };
544
- }
545
- ```
546
-
547
- `ApiMethods` adalah mapped type dari 184 `TelegramMethodName`:
548
-
549
- ```ts
550
- type ApiMethods = {
551
- [M in TelegramMethodName]:
552
- (...args: ApiCallArgs<M>) => Promise<ApiResult<M>>;
553
- };
554
- ```
555
-
556
- ### `new ApiClient(options)`
557
-
558
- ```ts
559
- new ApiClient(options: ApiClientOptions): ApiClient
560
- ```
561
-
562
- Membuat proxy method dinamis pada `client.methods`. Hook `onRequest` dipanggil sebelum transport, `onResponse` setelah response diterima, dan `onError` ketika request gagal atau response Telegram `ok: false`.
563
-
564
- ### `api.methods.<method>(params?)`
565
-
566
- Method dinamis dapat dipanggil langsung. Method yang memiliki parameter kosong seperti `getMe()` dipanggil tanpa argumen; method lain menerima satu object parameter.
567
-
568
- ```ts
569
- const me = await bot.api.methods.getMe();
570
- const chat = await bot.api.methods.getChat({ chat_id: "@channel" });
571
- const message = await bot.api.methods.sendMessage({
572
- chat_id: 123456789,
573
- text: "Hello",
574
- });
575
- ```
576
-
577
- ### `api.call(method, ...args)`
578
-
579
- ```ts
580
- call<M extends TelegramMethodName>(
581
- method: M,
582
- ...args: ApiCallArgs<M>
583
- ): Promise<ApiResult<M>>
584
- ```
585
-
586
- Bentuk bertipe untuk pemanggilan method berdasarkan string literal.
587
-
588
- ### `api.request(method, payload?, signal?)`
589
-
590
- ```ts
591
- request<M extends TelegramMethodName>(
592
- method: M,
593
- payload?: ApiParams<M>,
594
- signal?: AbortSignal,
595
- ): Promise<ApiResult<M>>
596
- ```
597
-
598
- Method request tingkat rendah yang memungkinkan `AbortSignal` eksplisit.
599
-
600
- ### `api.raw(method, payload?, signal?)`
601
-
602
- ```ts
603
- raw(
604
- method: string,
605
- payload?: Record<string, unknown>,
606
- signal?: AbortSignal,
607
- ): Promise<unknown>
608
- ```
609
-
610
- Memanggil method string sembarang di transport. Gunakan ini untuk method Telegram atau parameter baru yang belum masuk `TelegramMethodMap`. Response `ok: false` tetap diubah menjadi `TelegramError`.
611
-
612
- ### `api.downloadFile(fileId, options?)`
613
-
614
- ```ts
615
- downloadFile(fileId: string, options?: { signal?: AbortSignal }): Promise<DownloadedFile>
616
- ```
617
-
618
- Inti `bot.downloadFile` di level API client: me-resolve `getFile`, memvalidasi `file_path` tersedia, lalu mengunduh byte melalui transport.
619
-
620
- ### `DownloadedFile`
621
-
622
- ```ts
623
- interface DownloadedFile {
624
- file: File; // objek File Telegram dari getFile
625
- bytes: Uint8Array; // byte mentah file (maks 20 MB sesuai Telegram)
626
- filePath: string; // file_path yang dipakai untuk unduhan
627
- url: string; // URL unduhan langsung, valid minimal satu jam
628
- fileName: string; // segmen path terakhir dari filePath
629
- sizeBytes: number; // panjang byte
630
- savedTo?: string; // terisi saat Bot.downloadFile menyimpan file ke disk
631
- }
632
- ```
633
-
634
- ### `fetchTransport.fileUrl(filePath)` dan `fetchTransport.download(filePath, signal?)`
635
-
636
- ```ts
637
- fileUrl(filePath: string): string
638
- download(filePath: string, signal?: AbortSignal): Promise<Uint8Array>
639
- ```
640
-
641
- `FetchTransport` memetakan base URL `/bot<token>` ke endpoint download `/file/bot<token>`; `download` melakukan GET byte (batas bawah timeout 120 detik untuk file besar) dan melempar `TelegramNetworkError` saat HTTP gagal. Keduanya member opsional pada interface `Transport`, jadi transport kustom boleh menghilangkannya — `downloadFile` lalu gagal dengan error validasi yang jelas, bukan crash.
642
-
643
- ### Parameter dan hasil bertipe yang tersedia
644
-
645
- Tipe berikut dipetakan khusus pada rilis ini.
646
-
647
- | Method | Parameter | Result |
648
- |---|---|---|
649
- | `getMe` | tidak ada | `User` |
650
- | `getUpdates` | `GetUpdatesParams` | `Update[]` |
651
- | `setWebhook` | `SetWebhookParams` | `boolean` |
652
- | `deleteWebhook` | `{ drop_pending_updates?: boolean }` | `boolean` |
653
- | `getWebhookInfo` | tidak ada | `WebhookInfo` |
654
- | `sendMessage` | `SendMessageParams` | `Message` |
655
- | `editMessageText` | `EditMessageTextParams` | `Message \| true` |
656
- | `deleteMessage` | `DeleteMessageParams` | `true` |
657
- | `answerCallbackQuery` | `AnswerCallbackQueryParams` | `true` |
658
- | `getChat` | `GetChatParams` | `Chat` |
659
- | `getFile` | `GetFileParams` | `File` |
660
- | `getUserProfilePhotos` | `{ user_id: number; offset?: number; limit?: number }` | `UserProfilePhotos` |
661
- | `sendPhoto` | `SendPhotoParams` | `Message` |
662
- | `sendDocument` | `SendDocumentParams` | `Message` |
663
-
664
- Tipe parameter tambahan yang tersedia adalah `ReplyParameters`, `LinkPreviewOptions`, `InlineKeyboardButton`, `ReplyMarkup`, `BotCommand`, `BotCommandScope`, dan seluruh tipe update Telegram yang diekspor dari `api/types.ts`.
665
-
666
- ### Error API
667
-
668
- ```ts
669
- type TelegramErrorKind =
670
- | "retryable"
671
- | "rate-limit"
672
- | "authentication"
673
- | "validation"
674
- | "network"
675
- | "server"
676
- | "unknown";
677
- ```
678
-
679
- #### `TelegramError`
680
-
681
- ```ts
682
- new TelegramError(message: string, options: {
683
- method: string;
684
- payload: unknown;
685
- errorCode?: number;
686
- parameters?: ResponseParameters;
687
- status?: number;
688
- kind?: TelegramErrorKind;
689
- cause?: unknown;
690
- })
691
- ```
692
-
693
- Properti publik adalah `kind`, `errorCode`, `parameters`, `method`, `payload`, dan `status`. Getter `retryAfter` membaca `parameters.retry_after`; getter `migrateToChatId` membaca `parameters.migrate_to_chat_id`.
694
-
695
- #### Subclass error
696
-
697
- | Class | `name` | `kind` paksa |
698
- |---|---|---|
699
- | `TelegramRateLimitError` | `TelegramRateLimitError` | `rate-limit` |
700
- | `TelegramAuthError` | `TelegramAuthError` | `authentication` |
701
- | `TelegramValidationError` | `TelegramValidationError` | `validation` |
702
- | `TelegramNetworkError` | `TelegramNetworkError` | `network` |
703
-
704
- Keempat subclass memakai constructor options yang sama seperti `TelegramError`.
705
-
706
- #### `classifyTelegramError(errorCode?, status?)`
707
-
708
- ```ts
709
- classifyTelegramError(
710
- errorCode?: number,
711
- status?: number,
712
- ): TelegramErrorKind
713
- ```
714
-
715
- Klasifikasi aktual: `429` menjadi `rate-limit`; error `401` atau HTTP `401/403` menjadi `authentication`; error code `400–499` menjadi `validation`; HTTP `500+` menjadi `server`; selain itu `unknown`.
716
-
717
- #### `telegramErrorFromResponse(response, context)`
718
-
719
- ```ts
720
- telegramErrorFromResponse<T>(
721
- response: TelegramResponse<T>,
722
- context: { method: string; payload: unknown; status?: number },
723
- ): TelegramError
724
- ```
725
-
726
- Mengubah response Telegram gagal menjadi subclass yang sesuai. Error `429`, auth, dan validation menghasilkan subclass khusus; error lain menghasilkan `TelegramError` biasa.
727
-
728
- ---
729
-
730
- ## 4. Konteks
731
-
732
- ### `ContextOptions<S>`
733
-
734
- ```ts
735
- interface ContextOptions<S extends object = Record<string, unknown>> {
736
- update: Update;
737
- api: ApiClient;
738
- session: S;
739
- services: Record<string, unknown>;
740
- }
741
- ```
742
-
743
- ### `Context<S>` properties
744
-
745
- | Properti | Isi |
746
- |---|---|
747
- | `update` | Update mentah Telegram. |
748
- | `api` | `ApiClient` bot. |
749
- | `session` | Objek session yang dapat diubah milik update key saat ini. |
750
- | `state` | Objek transient per-konteks, tidak otomatis disimpan ke session. |
751
- | `services` | Layanan yang dikirim melalui `BotOptions.services`. |
752
- | `params` | Objek parameter route; router bawaan saat ini tidak mengisi otomatis. |
753
- | `message` | Message utama dari message/edited/channel/business/guest update. |
754
- | `chat` | `message.chat` bila tersedia. |
755
- | `from` / `sender` | Pengguna dari message, callback query, atau inline query. |
756
- | `callbackQuery` | `update.callback_query`. |
757
- | `inlineQuery` | `update.inline_query`. |
758
- | `poll` | `update.poll`. |
759
- | `pollAnswer` | `update.poll_answer`. |
760
- | `chatMember` | `update.chat_member`. |
761
- | `myChatMember` | `update.my_chat_member`. |
762
- | `chatJoinRequest` | `update.chat_join_request`. |
763
- | `reaction` | `update.message_reaction`. |
764
- | `boost` | `chat_boost` atau `removed_chat_boost`. |
765
-
766
- ### `new Context(options)`
767
-
768
- ```ts
769
- new Context<S>(options: ContextOptions<S>): Context<S>
770
- ```
771
-
772
- ### Metode pesan Context
773
-
774
- | Method | Signature | Perilaku |
775
- |---|---|---|
776
- | `reply` | `reply(text, extra?): Promise<Message>` | Mengirim message ke chat update dan mengisi `reply_parameters.message_id` bila ada message. |
777
- | `send` | `send(text, extra?): Promise<Message>` | Mengirim message ke chat update tanpa reply reference. |
778
- | `edit` | `edit(text, extra?): Promise<Message \| true>` | Mengedit message update menggunakan `editMessageText`. |
779
- | `delete` | `delete(): Promise<true>` | Menghapus message update. |
780
- | `replyWithHTML` | `replyWithHTML(text, extra?): Promise<Message>` | Membalas dengan `parse_mode: "HTML"`. |
781
- | `replyWithMarkdown` | `replyWithMarkdown(text, extra?): Promise<Message>` | Membalas dengan `parse_mode: "MarkdownV2"`. |
782
- | `replyWithPhoto` | `replyWithPhoto(photo, extra?): Promise<Message>` | Mengirim `sendPhoto` dengan quote-reply otomatis. |
783
- | `replyWithDocument` | `replyWithDocument(document, extra?): Promise<Message>` | Mengirim `sendDocument` dengan quote-reply otomatis. |
784
- | `replyWithAudio` | `replyWithAudio(audio, extra?): Promise<Message>` | Mengirim `sendAudio` dengan quote-reply otomatis. |
785
- | `replyWithVideo` | `replyWithVideo(video, extra?): Promise<Message>` | Mengirim `sendVideo` dengan quote-reply otomatis. |
786
- | `replyWithVoice` | `replyWithVoice(voice, extra?): Promise<Message>` | Mengirim `sendVoice` dengan quote-reply otomatis. |
787
- | `replyWithAnimation` | `replyWithAnimation(animation, extra?): Promise<Message>` | Mengirim `sendAnimation` dengan quote-reply otomatis. |
788
- | `replyWithVideoNote` | `replyWithVideoNote(videoNote, extra?): Promise<Message>` | Mengirim `sendVideoNote` dengan quote-reply otomatis. |
789
- | `replyWithSticker` | `replyWithSticker(sticker, extra?): Promise<Message>` | Mengirim `sendSticker` dengan quote-reply otomatis. |
790
- | `replyWithMediaGroup` | `replyWithMediaGroup(media, extra?): Promise<Message[]>` | Mengirim album via `sendMediaGroup` dengan quote-reply otomatis. |
791
- | `replyWithLocation` | `replyWithLocation(latitude, longitude, extra?): Promise<Message>` | Mengirim `sendLocation` dengan quote-reply otomatis. |
792
- | `replyWithVenue` | `replyWithVenue(latitude, longitude, title, address, extra?): Promise<Message>` | Mengirim `sendVenue` dengan quote-reply otomatis. |
793
- | `replyWithContact` | `replyWithContact(phoneNumber, firstName, extra?): Promise<Message>` | Mengirim `sendContact` dengan quote-reply otomatis. |
794
- | `replyWithPoll` | `replyWithPoll(question, options, extra?): Promise<Message>` | Mengirim `sendPoll` dengan quote-reply otomatis. |
795
- | `replyWithDice` | `replyWithDice(emoji?, extra?): Promise<Message>` | Mengirim `sendDice` dengan quote-reply otomatis. |
796
- | `copy` | `copy(fromChatId, messageId, extra?): Promise<unknown>` | Memanggil `copyMessage` ke chat context. |
797
- | `forward` | `forward(fromChatId, messageId, extra?): Promise<Message>` | Memanggil `forwardMessage` ke chat context. |
798
- | `pin` | `pin(messageId?, extra?): Promise<true>` | Memanggil `pinChatMessage`, default message id dari context. |
799
- | `unpin` | `unpin(messageId?, extra?): Promise<true>` | Memanggil `unpinChatMessage`, default message id dari context. |
800
- | `react` | `react(reaction, extra?): Promise<true>` | Memanggil `setMessageReaction`. |
801
- | `answerCallbackQuery` | `answerCallbackQuery(text?, extra?): Promise<true>` | Menjawab callback query aktif. Error jika bukan callback update. |
802
- | `answerInlineQuery` | `answerInlineQuery(results, extra?): Promise<true>` | Menjawab inline query aktif. Error jika bukan inline update. |
803
- | `getChat` | `getChat(): Promise<Chat>` | Mengambil detail chat context. |
804
- | `getUserProfilePhotos` | `getUserProfilePhotos(userId?, extra?): Promise<unknown>` | Mengambil foto profil user context. |
805
- | `getFile` | `getFile(fileId): Promise<unknown>` | Mengambil file berdasarkan id. |
806
- | `withReplyMarkup` | `withReplyMarkup(markup): this` | Menyimpan markup di `ctx.state.reply_markup` dan mengembalikan context. Metode ini tidak otomatis mengirim message. |
807
-
808
- 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.
809
-
810
- ### Method admin, chat, dan forum pada Context (paritas penuh Telegraf)
811
-
812
- Semua method di bawah beraksi pada chat update (`ctx.chat`) dan menerima parameter native Telegram lewat `extra`; semuanya melempar error jelas bila update tidak memiliki chat. Gunakan `ctx.api.methods.*` untuk menargetkan chat lain.
813
-
814
- | Grup | Method |
815
- |---|---|
816
- | Moderasi | `banChatMember(userId, untilDate?, extra?)`, `unbanChatMember(userId, onlyIfBanned?, extra?)`, `restrictChatMember(userId, permissions, untilDate?, extra?)`, `promoteChatMember(userId, extra?)`, `banChatSenderChat(senderChatId, extra?)`, `unbanChatSenderChat(senderChatId, extra?)` |
817
- | Manajemen chat | `setChatTitle(title)`, `setChatDescription(description?)`, `setChatPhoto(photo)`, `deleteChatPhoto()`, `setChatPermissions(permissions, extra?)`, `leaveChat()`, `unpinAllChatMessages(extra?)`, `setChatStickerSet(name)`, `deleteChatStickerSet()` |
818
- | Info chat & member | `getChatAdministrators(): Promise<ChatMember[]>`, `getChatMemberCount(): Promise<number>`, `getChatMember(userId): Promise<ChatMember>` |
819
- | Invite link | `exportChatInviteLink(): Promise<string>`, `createChatInviteLink(extra?)`, `editChatInviteLink(inviteLink, extra?)`, `revokeChatInviteLink(inviteLink)` |
820
- | Join request | `approveChatJoinRequest(userId)`, `declineChatJoinRequest(userId)` |
821
- | Poll & live location | `replyWithQuiz(question, options, extra?)` (sendPoll dengan `type: "quiz"`), `stopPoll(messageId?, extra?)`, `editMessageLiveLocation(latitude?, longitude?, extra?)`, `stopMessageLiveLocation(extra?)` |
822
- | Game & pembayaran | `replyWithGame(gameShortName, extra?)`, `setGameScore(userId, score, extra?)`, `getGameHighScores(userId?, extra?)`, `replyWithInvoice(title, description, payload, providerToken, currency, prices, extra?)` |
823
- | Forum topic | `createForumTopic(name, extra?)`, `editForumTopic(extra?)`, `closeForumTopic(threadId?)`, `reopenForumTopic(threadId?)`, `deleteForumTopic(threadId?)`, `unpinAllForumTopicMessages(threadId?)`, `getForumTopicIconStickers()`, `editGeneralForumTopic(name)`, `closeGeneralForumTopic()`, `reopenGeneralForumTopic()`, `hideGeneralForumTopic()`, `unhideGeneralForumTopic()` |
824
-
825
- `threadId` default ke `message_thread_id` message context. `replyWithQuiz`, `replyWithGame`, dan `replyWithInvoice` me-quote message masuk seperti semua pengirim `replyWith*`.
826
-
827
- ---
828
-
829
- ## 5. Middleware dan router
830
-
831
- ### Jenis middleware
832
-
833
- ```ts
834
- type Next = () => Promise<void>;
835
- type Middleware<Context> = (ctx: Context, next: Next) => void | Promise<void>;
836
- ```
837
-
838
- ### `compose(middleware)`
839
-
840
- ```ts
841
- compose<Context>(
842
- middleware: readonly Middleware<Context>[],
843
- ): (ctx: Context) => Promise<void>
844
- ```
845
-
846
- Menyusun middleware dengan pola onion. `next()` menjalankan middleware berikutnya. Jika middleware yang sama memanggil `next()` lebih dari sekali, compose melempar `Error("next() called multiple times")`.
847
-
848
- ### `middleware(handler)`
849
-
850
- ```ts
851
- middleware<Context>(handler: Middleware<Context>): Middleware<Context>
852
- ```
853
-
854
- Pembantu identitas untuk memberi anotasi/inferensi tipe pada middleware.
855
-
856
- ### `RoutableContext`
857
-
858
- Context minimal yang dibutuhkan router: `update`, `message`, `callbackQuery`, dan `params`.
859
-
860
- ### `Router<Context>`
861
-
862
- ```ts
863
- new Router<Context extends RoutableContext>(): Router<Context>
864
- ```
865
-
866
- Route diproses menurut prioritas dan urutan registrasi. Route yang cocok tidak menghentikan route berikutnya secara otomatis; semua route yang cocok dapat dijalankan. Jika tidak ada route yang cocok, `terminal` pada `handle` dipanggil.
867
-
868
- | Metode | Tanda tangan | Pencocokan |
869
- |---|---|---|
870
- | `use` | `use(...middleware): this` | Middleware global router dengan priority paling tinggi untuk dijalankan lebih awal. |
871
- | `route` | `route(matcher, ...middleware): this` | Matcher boolean atau async custom. |
872
- | `command` | `command(name: string \| RegExp, ...middleware): this` | Command pertama dari message text yang diawali `/`. |
873
- | `text` | `text(value: string, ...middleware): this` | Pencocokan teks persis. |
874
- | `regex` | `regex(expression: RegExp, ...middleware): this` | `RegExp.test` atas message text atau string kosong. |
875
- | `callback` | `callback(pattern: string \| RegExp, ...middleware): this` | Exact, prefix dengan suffix `*`, atau regex atas callback data. |
876
- | `chat` | `chat(chatId: number \| string, ...middleware): this` | Cocokkan `message.chat.id`, numeric atau string-equivalent. |
877
- | `predicate` | `predicate(matcher, ...middleware): this` | Alias semantik untuk custom matcher. |
878
- | `nest` | `nest(child: Router<Context>): this` | Menjalankan router child sebagai nested route. |
879
- | `handle` | `handle(ctx, terminal?): Promise<void>` | Mengevaluasi dan menjalankan seluruh route yang cocok. |
880
-
881
- ```ts
882
- const router = new Router<Context>();
883
- router.use(async (ctx, next) => {
884
- console.log("before");
885
- await next();
886
- });
887
- router.callback("page:*", async (ctx) => {
888
- await ctx.answerCallbackQuery();
889
- });
890
- router.predicate((ctx) => Boolean(ctx.from?.id), async (ctx) => {
891
- await ctx.reply("Authenticated update");
892
- });
893
- ```
894
-
895
- **Catatan RegExp.** Router memanggil `.test()` langsung. Untuk ekspresi dengan flag `g` atau `y`, sifat stateful `lastIndex` JavaScript dapat memengaruhi pencocokan berulang.
896
-
897
- ---
898
-
899
- ## 6. Pembuat keyboard
900
-
901
- ### `InlineKeyboard`
902
-
903
- ```ts
904
- new InlineKeyboard(): InlineKeyboard
905
- InlineKeyboard.from(rows: InlineKeyboardButton[][]): InlineKeyboard
906
- ```
907
-
908
- Builder menyimpan baris secara dapat diubah (mutable) dan seluruh metode builder mengembalikan `this`.
909
-
910
- | Method | Signature | Deskripsi |
911
- |---|---|---|
912
- | `from` | `static from(rows): InlineKeyboard` | Membuat keyboard dari rows dan menyalin setiap row. |
913
- | `text` | `text(text, callbackData): this` | Tombol callback. |
914
- | `url` | `url(text, url): this` | Tombol URL. |
915
- | `webApp` | `webApp(text, url): this` | Tombol Web App. |
916
- | `pay` | `pay(text = "Pay"): this` | Tombol pembayaran. |
917
- | `copy` | `copy(text, copiedText): this` | Tombol salin teks. |
918
- | `button` | `button(button): this` | Menambahkan satu tombol ke baris terakhir atau membuat baris pertama. |
919
- | `row` | `row(...buttons): this` | Menambahkan baris baru. |
920
- | `conditional` | `conditional(condition, factory): this` | Menjalankan factory hanya jika kondisi bernilai true. |
921
- | `grid` | `grid(buttons, columns): this` | Membagi tombol ke baris berdasarkan jumlah kolom. |
922
- | `build` | `build(): InlineKeyboardMarkup` | Menghasilkan markup baru. |
923
- | `asReplyMarkup` | `asReplyMarkup(): InlineKeyboardMarkup` | Alias dari `build`. |
924
-
925
- Setiap inline button wajib memiliki text dan tepat satu action. Callback data dibatasi maksimum 64 bytes UTF-8; pelanggaran melempar `RangeError`.
926
-
927
- ```ts
928
- const keyboard = new InlineKeyboard()
929
- .text("Izinkan", "approve:123")
930
- .url("Dokumentasi", "https://example.com")
931
- .row(
932
- { text: "A", callback_data: "a" },
933
- { text: "B", callback_data: "b" },
934
- )
935
- .build();
936
- ```
937
-
938
- ### `ReplyKeyboard`
939
-
940
- ```ts
941
- new ReplyKeyboard(): ReplyKeyboard
942
- ```
943
-
944
- | Method | Signature | Deskripsi |
945
- |---|---|---|
946
- | `text` | `text(text): this` | Tombol teks biasa. |
947
- | `contact` | `contact(text): this` | Meminta kontak. |
948
- | `location` | `location(text): this` | Meminta lokasi. |
949
- | `poll` | `poll(text, type?): this` | Meminta poll `quiz` atau `regular`. |
950
- | `webApp` | `webApp(text, url): this` | Tombol Web App. |
951
- | `button` | `button(button): this` | Tambah satu tombol ke baris terakhir. |
952
- | `row` | `row(...buttons): this` | Tambah baris baru. |
953
- | `grid` | `grid(buttons, columns): this` | Membagi tombol menjadi grid. |
954
- | `build` | `build(options?): ReplyKeyboardMarkup` | Menghasilkan markup dan menggabungkan opsi. |
955
- | `asReplyMarkup` | `asReplyMarkup(): ReplyKeyboardMarkup` | Alias dari `build()` tanpa opsi. |
956
-
957
- `columns` harus integer positif; jika tidak, `grid` melempar `RangeError`.
958
-
959
- ### `removeKeyboard(selective?)`
960
-
961
- ```ts
962
- removeKeyboard(selective = false): ReplyMarkup
963
- ```
964
-
965
- Menghasilkan `{ remove_keyboard: true }`, dengan `selective: true` bila diminta.
966
-
967
- ### `forceReply(placeholder?, selective?)`
968
-
969
- ```ts
970
- forceReply(placeholder?: string, selective = false): ReplyMarkup
971
- ```
972
-
973
- Menghasilkan ForceReply. Placeholder hanya ditambahkan jika bernilai truthy.
974
-
975
- ---
976
-
977
- ## 7. Storage dan cache
978
-
979
- ### `Storage<K, V>`
980
-
981
- ```ts
982
- interface Storage<K, V> {
983
- get(key: K): Promise<V | undefined>;
984
- set(key: K, value: V, options?: { ttlMs?: number }): Promise<void>;
985
- delete(key: K): Promise<boolean>;
986
- has(key: K): Promise<boolean>;
987
- clear(): Promise<void>;
988
- keys(): AsyncIterable<K>;
989
- values(): AsyncIterable<V>;
990
- entries(): AsyncIterable<[K, V]>;
991
- update<T extends V>(
992
- key: K,
993
- updater: (current: V | undefined) => T | Promise<T>,
994
- options?: { ttlMs?: number },
995
- ): Promise<T>;
996
- }
997
- ```
998
-
999
- ### `MemoryStorage<K, V>`
1000
-
1001
- ```ts
1002
- new MemoryStorage<K, V>(): MemoryStorage<K, V>
1003
- ```
1004
-
1005
- Implementasi in-memory berbasis `Map`. TTL dibersihkan saat diperlukan ketika key dibaca atau diiterasi; tidak ada timer latar belakang. `update` membuat operasi updater per key berjalan serial sehingga update konkuren untuk key yang sama tidak saling menimpa secara tak terduga.
1006
-
1007
- ```ts
1008
- const sessions = new MemoryStorage<string, { count: number }>();
1009
- await sessions.set("user:1", { count: 0 }, { ttlMs: 60_000 });
1010
- await sessions.update("user:1", (current) => ({
1011
- count: (current?.count ?? 0) + 1,
1012
- }));
1013
- ```
1014
-
1015
- ### `Cache<K, V>`
1016
-
1017
- ```ts
1018
- interface Cache<K = string, V = unknown> {
1019
- get(key: K): Promise<V | undefined>;
1020
- set(key: K, value: V, ttlMs?: number): Promise<void>;
1021
- delete(key: K): Promise<boolean>;
1022
- invalidate(prefix?: string): Promise<void>;
1023
- getOrSet(key: K, factory: () => V | Promise<V>, ttlMs?: number): Promise<V>;
1024
- }
1025
- ```
1026
-
1027
- ### `MemoryCache`
1028
-
1029
- ```ts
1030
- new MemoryCache(namespace = "telebibz"): MemoryCache
1031
- ```
1032
-
1033
- Cache yang menggunakan string sebagai kunci dan menerapkan namespace internal pada setiap key.
1034
-
1035
- | Method | Perilaku |
1036
- |---|---|
1037
- | `get` | Mengambil value atau `undefined`. |
1038
- | `set` | Menyimpan value dengan TTL opsional. |
1039
- | `delete` | Menghapus key dan mengembalikan boolean. |
1040
- | `invalidate(prefix = "")` | Menghapus semua key dalam namespace yang diawali oleh prefix. |
1041
- | `getOrSet` | Mengembalikan nilai dari cache jika ada; jika tidak ada, menjalankan factory, menyimpan hasilnya, lalu mengembalikannya. |
1042
-
1043
- `getOrSet` tidak menggunakan lock deduplikasi; factory dapat dijalankan lebih dari sekali bila dipanggil konkuren saat key belum tersedia.
1044
-
1045
- ### `RateLimitResult`
1046
-
1047
- ```ts
1048
- interface RateLimitResult {
1049
- allowed: boolean;
1050
- remaining: number;
1051
- resetAt: number;
1052
- retryAfterMs?: number;
1053
- }
1054
- ```
1055
-
1056
- ### `TokenBucketLimiter`
1057
-
1058
- ```ts
1059
- new TokenBucketLimiter(
1060
- capacity: number,
1061
- refillPerSecond: number,
1062
- ): TokenBucketLimiter
1063
- ```
1064
-
1065
- Constructor melempar `RangeError` jika salah satu nilai tidak positif. `consume(key, cost = 1)` mengurangi token bila tersedia; jika tidak cukup, mengembalikan `allowed: false` serta estimasi `retryAfterMs`. `clear(key?)` menghapus satu bucket atau seluruh bucket.
1066
-
1067
- ---
1068
-
1069
- ## 8. Queue dan scheduler
1070
-
1071
- ### `Job<T>` dan `QueueOptions`
1072
-
1073
- ```ts
1074
- interface Job<T = unknown> {
1075
- id: string;
1076
- data: T;
1077
- attempts: number;
1078
- priority: number;
1079
- runAt: number;
1080
- status: "queued" | "running" | "completed" | "failed" | "cancelled";
1081
- error?: unknown;
1082
- }
1083
-
1084
- interface QueueOptions {
1085
- concurrency?: number;
1086
- retries?: number;
1087
- backoffMs?: number;
1088
- maxBackoffMs?: number;
1089
- }
1090
- ```
1091
-
1092
- ### `TaskQueue<T>`
1093
-
1094
- ```ts
1095
- new TaskQueue<T>(
1096
- worker: (job: Job<T>, signal: AbortSignal) => Promise<void>,
1097
- options?: QueueOptions,
1098
- ): TaskQueue<T>
1099
- ```
1100
-
1101
- | Method | Signature | Deskripsi |
1102
- |---|---|---|
1103
- | `add` | `add(data, options?): Job<T>` | Menambah job; options `id`, `priority`, `delayMs`. Job langsung dijadwalkan. |
1104
- | `get` | `get(id): Job<T> \| undefined` | Mengembalikan salinan status job. |
1105
- | `cancel` | `cancel(id): boolean` | Membatalkan queued/running job dan abort signal worker. |
1106
- | `onIdle` | `onIdle(): Promise<void>` | Menunggu sampai pending dan active kosong. |
1107
- | `close` | `close(): Promise<void>` | Menghentikan draining baru dan membatalkan controller aktif. |
1108
-
1109
- Job dengan `priority` lebih besar dijalankan lebih dahulu; jika sama, job dengan `runAt` lebih awal dijalankan lebih dahulu. Percobaan ulang dilakukan sampai nilai `retries` terlampaui. Penundaan percobaan ulang bersifat eksponensial dengan batas `maxBackoffMs` bawaan 30 detik.
1110
-
1111
- ### `ScheduledJob`
1112
-
1113
- ```ts
1114
- interface ScheduledJob {
1115
- id: string;
1116
- cancel: () => void;
1117
- }
1118
- ```
1119
-
1120
- ### `Scheduler`
1121
-
1122
- ```ts
1123
- new Scheduler(): Scheduler
1124
- ```
1125
-
1126
- | Method | Signature | Deskripsi |
1127
- |---|---|---|
1128
- | `every` | `every(id, intervalMs, task): ScheduledJob` | Menjalankan task menggunakan `setInterval`. Mengganti timer dengan id sama. |
1129
- | `after` | `after(id, delayMs, task): ScheduledJob` | Menjalankan task sekali menggunakan `setTimeout`. |
1130
- | `cron` | `cron(id, expression, task): ScheduledJob` | Mendukung format sederhana `*/N` pada field menit, setara interval `N * 60_000`. |
1131
- | `cancel` | `cancel(id): boolean` | Membatalkan timer. |
1132
- | `clear` | `clear(): void` | Membatalkan semua timer. |
1133
-
1134
- Format cron penuh tidak didukung oleh built-in scheduler. Ekspresi selain `*/N` melempar `Error`.
1135
-
1136
- ### `Limiter` dan `mapWithConcurrency`
1137
-
1138
- ```ts
1139
- new Limiter(limit: number): Limiter
1140
-
1141
- mapWithConcurrency<T, R>(
1142
- items: readonly T[],
1143
- limit: number,
1144
- worker: (item: T, index: number) => Promise<R>,
1145
- ): Promise<R[]>
1146
- ```
1147
-
1148
- `Limiter` adalah semaphore berbasis promise: task langsung berjalan selama ada slot kosong dan mengantre FIFO setelahnya. `limit` menerima bilangan bulat positif atau `Infinity` (sepenuhnya paralel — default library). `mapWithConcurrency` memetakan item melalui async worker dengan batas yang sama sambil menjaga urutan hasil. Primitif ini tidak menambahkan delay apa pun — hanya membatasi jumlah task yang berjalan bersamaan. `Limiter` mengekspos `activeCount` dan `queuedCount` untuk observability.
1149
-
1150
- ## 9. Plugin dan services
1151
-
1152
- ### `Plugin<Context>`
1153
-
1154
- ```ts
1155
- interface Plugin<Context = unknown> {
1156
- name: string;
1157
- version?: string;
1158
- install?: (api: PluginApi<Context>) => void | Promise<void>;
1159
- setup?: (api: PluginApi<Context>) => void | Promise<void>;
1160
- onStart?: (api: PluginApi<Context>) => void | Promise<void>;
1161
- onUpdate?: (context: Context) => void | Promise<void>;
1162
- onStop?: (api: PluginApi<Context>) => void | Promise<void>;
1163
- dispose?: (api: PluginApi<Context>) => void | Promise<void>;
1164
- }
1165
- ```
1166
-
1167
- ### `PluginApi<Context>`
1168
-
1169
- ```ts
1170
- interface PluginApi<Context> {
1171
- bot: unknown;
1172
- services: ServiceContainer;
1173
- registerMiddleware: (middleware: unknown) => void;
1174
- registerRoute: (route: unknown) => void;
1175
- }
1176
- ```
1177
-
1178
- Pada rilis ini, `registerMiddleware` dan `registerRoute` tersedia sebagai hook API tetapi manajer implementasi belum menghubungkan keduanya secara otomatis ke bot/router. Plugin dapat memakai `api.bot` dan `api.services` secara langsung.
1179
-
1180
- ### `ServiceContainer`
1181
-
1182
- ```ts
1183
- new ServiceContainer(): ServiceContainer
1184
- ```
1185
-
1186
- | Method | Signature | Deskripsi |
1187
- |---|---|---|
1188
- | `register` | `register<T>(name: string \| symbol, value: T): this` | Menyimpan service dan mendukung chaining. |
1189
- | `get` | `get<T>(name: string \| symbol): T` | Mengambil service; melempar jika belum terdaftar. |
1190
- | `has` | `has(name: string \| symbol): boolean` | Memeriksa keberadaan service. |
1191
- | `delete` | `delete(name: string \| symbol): boolean` | Menghapus service. |
1192
-
1193
- ### `PluginManager<Context>`
1194
-
1195
- ```ts
1196
- new PluginManager<Context>(bot: unknown): PluginManager<Context>
1197
- ```
1198
-
1199
- | Method | Perilaku |
1200
- |---|---|
1201
- | `use(plugin)` | Menambah plugin; nama duplikat melempar kesalahan. |
1202
- | `setup()` | Untuk setiap plugin, menjalankan `install` lalu `setup`. |
1203
- | `start()` | Menjalankan `onStart` sesuai urutan registrasi. |
1204
- | `update(context)` | Menjalankan `onUpdate` sesuai urutan registrasi. |
1205
- | `stop()` | Menjalankan `onStop`. |
1206
- | `dispose()` | Menjalankan `dispose` dalam urutan pendaftaran terbalik. |
1207
- | `list()` | Mengembalikan daftar plugin hanya baca. |
1208
-
1209
- `Bot.handleUpdate()` pada rilis ini tidak memanggil `plugins.update()` secara otomatis; panggil manajer secara eksplisit bila plugin memerlukan siklus hidup pembaruan.
1210
-
1211
- ---
1212
-
1213
- ## 10. Webhook
1214
-
1215
- ### `WebhookOptions`
1216
-
1217
- ```ts
1218
- interface WebhookOptions {
1219
- secretToken?: string;
1220
- maxBodyBytes?: number;
1221
- onError?: (error: unknown) => void | Promise<void>;
1222
- webhookReply?: boolean;
1223
- }
1224
- ```
1225
-
1226
- `webhookReply` (default `false`) mengaktifkan webhook reply ala Telegraf: saat memproses update, panggilan API keluar pertama dijawab lewat respons HTTP webhook itu sendiri (`{"method":"sendMessage", ...}`), sehingga Telegram mengeksekusi method tanpa request kedua. Panggilan itu resolve dengan `true` karena Telegram tidak pernah mengirim hasil method kembali ke respons webhook; setiap panggilan berikutnya tetap lewat transport seperti biasa. Inisialisasi `getMe` malas tidak pernah mengklaim slot tersebut. Berbeda dengan Telegraf, fitur ini opt-in agar deployment webhook yang sudah ada mempertahankan perilakunya persis.
1227
-
1228
- ### `createWebhookHandler(bot, options?)`
1229
-
1230
- ```ts
1231
- createWebhookHandler<S extends object>(
1232
- bot: Bot<S>,
1233
- options?: WebhookOptions,
1234
- ): (request: Request) => Promise<Response>
1235
- ```
1236
-
1237
- Handler menerima Request standar Web `Request` dan mengembalikan `Response`.
1238
-
1239
- | Kondisi | Response |
1240
- |---|---|
1241
- | Metode bukan POST | `405 Method Not Allowed`, header `allow: POST` |
1242
- | Header secret tidak cocok | `401 Unauthorized` |
1243
- | Header `Content-Length` atau body melebihi batas | `413 Payload Too Large` |
1244
- | JSON tidak valid atau `update_id` bukan integer | `400 Bad Request` untuk update id; exception saat parsing menghasilkan `500` |
1245
- | `bot.handleUpdate` sukses | `200 OK` dengan body `OK` |
1246
- | Pengecualian lain | `500 Internal Server Error` dan `onError` dipanggil |
1247
-
1248
- Nilai default `maxBodyBytes` adalah `1_048_576` bytes. Token rahasia Telegram dibaca dari header `x-telegram-bot-api-secret-token`.
1249
-
1250
- ```ts
1251
- import { Bot, createWebhookHandler } from "@xbibzlibrary/telebibz";
1252
-
1253
- const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!);
1254
- const handler = createWebhookHandler(bot, {
1255
- secretToken: process.env.TELEGRAM_WEBHOOK_SECRET,
1256
- });
1257
-
1258
- export default { fetch: handler };
1259
- ```
1260
-
1261
- ---
1262
-
1263
- ## 11. Percakapan, wizard, formulir, dan menu
1264
-
1265
- ### Percakapan
1266
-
1267
- ```ts
1268
- interface ConversationState {
1269
- name: string;
1270
- step: number;
1271
- values: Record<string, unknown>;
1272
- status: "active" | "completed" | "cancelled";
1273
- updatedAt: number;
1274
- }
1275
- ```
1276
-
1277
- #### `ConversationFlow<S>`
1278
-
1279
- ```ts
1280
- new ConversationFlow(ctx: Context<S>, state: ConversationState)
1281
- ```
1282
-
1283
- | Method/property | Signature | Deskripsi |
1284
- |---|---|---|
1285
- | `ctx` | `Context<S>` | Context update saat ini. |
1286
- | `state` | `ConversationState` | State percakapan mutable. |
1287
- | `values` | `Record<string, unknown>` | Alias ke `state.values`. |
1288
- | `set` | `set<T>(key, value): this` | Menyimpan value dan memperbarui `updatedAt`. |
1289
- | `get` | `get<T>(key): T \| undefined` | Mengambil typed value. |
1290
- | `next` | `next(): this` | Menaikkan step satu. |
1291
- | `previous` | `previous(): this` | Menurunkan step dengan minimum 0. |
1292
- | `complete` | `complete(): void` | Status menjadi `completed`. |
1293
- | `cancel` | `cancel(): void` | Status menjadi `cancelled`. |
1294
-
1295
- #### `ConversationManager<S>`
1296
-
1297
- ```ts
1298
- new ConversationManager<S>(): ConversationManager<S>
1299
- ```
1300
-
1301
- | Method | Signature | Deskripsi |
1302
- |---|---|---|
1303
- | `start` | `start(key, name, values?): ConversationState` | Membuat atau mengganti conversation state. |
1304
- | `get` | `get(key): ConversationState \| undefined` | Mengambil state aktif. |
1305
- | `cancel` | `cancel(key): boolean` | Menandai cancelled jika ada. |
1306
- | `clearExpired` | `clearExpired(maxAgeMs): number` | Menghapus state yang `updatedAt` lebih lama dari threshold. |
1307
- | `run` | `run(ctx, key, name, steps): Promise<ConversationState>` | Menjalankan step sesuai `state.step`; jika tidak ada step, status completed. |
1308
-
1309
- ```ts
1310
- const conversations = new ConversationManager();
1311
- await conversations.run(ctx, "chat:1", "profile", [
1312
- async (flow) => {
1313
- flow.set("name", ctx.message?.text);
1314
- flow.next();
1315
- },
1316
- async (flow) => {
1317
- await flow.ctx.reply(`Nama: ${flow.get<string>("name")}`);
1318
- flow.complete();
1319
- },
1320
- ]);
1321
- ```
1322
-
1323
- #### `Wizard<S>` dan `WizardStep<S>`
1324
-
1325
- ```ts
1326
- interface WizardStep<S> {
1327
- id: string;
1328
- run: (flow: ConversationFlow<S>) => void | Promise<void>;
1329
- optional?: boolean;
1330
- }
1331
-
1332
- new Wizard<S>()
1333
- ```
1334
-
1335
- | Method/property | Deskripsi |
1336
- |---|---|
1337
- | `step(definition)` | Menambahkan step dan mengembalikan wizard. `optional` disimpan dalam definition tetapi belum diproses khusus oleh runner. |
1338
- | `run(ctx, key, manager?)` | Menjalankan step wizard melalui `ConversationManager` dengan name `"wizard"`. |
1339
- | `steps` | Daftar step read-only. |
1340
-
1341
- ### Formulir
1342
-
1343
- ```ts
1344
- interface ValidationIssue {
1345
- path: string;
1346
- message: string;
1347
- code?: string;
1348
- }
1349
-
1350
- interface Field<T> {
1351
- name: string;
1352
- parse: (input: unknown) => T;
1353
- validate?: (value: T) => string | undefined | Promise<string | undefined>;
1354
- transform?: (value: T) => T | Promise<T>;
1355
- required?: boolean;
1356
- }
1357
- ```
1358
-
1359
- #### `Form<T>`
1360
-
1361
- ```ts
1362
- new Form<T extends Record<string, unknown>>(): Form<T>
1363
- ```
1364
-
1365
- | Method | Deskripsi |
1366
- |---|---|
1367
- | `field(definition)` | Mendaftarkan field typed berdasarkan `name`. |
1368
- | `parse(input)` | Memproses seluruh field. Return union success atau issues. Urutan: required check, parse, transform, validate. |
1369
- | `reset()` | Menghapus data hasil parse yang tersimpan internal. |
1370
-
1371
- Hasil parse:
1372
-
1373
- ```ts
1374
- type FormResult<T> =
1375
- | { success: true; data: T }
1376
- | { success: false; issues: ValidationIssue[] };
1377
- ```
1378
-
1379
- Issue memakai code `required`, `parse`, atau `invalid`.
1380
-
1381
- #### `validators`
1382
-
1383
- | Validator | Input | Hasil/eror |
1384
- |---|---|---|
1385
- | `validators.string` | `unknown` | String; selain itu `TypeError("Expected string")`. |
1386
- | `validators.number` | `unknown` | Number finite, termasuk numeric string; selain itu `TypeError("Expected number")`. |
1387
- | `validators.integer` | `unknown` | Integer; selain itu `TypeError("Expected integer")`. |
1388
- | `validators.email` | `unknown` | String dengan pola email sederhana; selain itu `TypeError("Expected email")`. |
1389
- | `validators.url` | `unknown` | String yang diterima constructor `URL`; selain itu `TypeError("Expected URL")`. |
1390
-
1391
- ### Pagination dan menu
1392
-
1393
- #### `Page<T>`
1394
-
1395
- ```ts
1396
- interface Page<T> {
1397
- items: T[];
1398
- page: number;
1399
- pageCount: number;
1400
- hasPrevious: boolean;
1401
- hasNext: boolean;
1402
- }
1403
- ```
1404
-
1405
- #### `paginate(items, page, pageSize)`
1406
-
1407
- ```ts
1408
- paginate<T>(
1409
- items: readonly T[],
1410
- page: number,
1411
- pageSize: number,
1412
- ): Page<T>
1413
- ```
1414
-
1415
- Page memakai index berbasis 0. Page yang melebihi batas di-clamp ke halaman terakhir. Collection kosong tetap memiliki `pageCount: 1`. `page` negatif/non-integer atau `pageSize < 1` melempar `RangeError`.
1416
-
1417
- #### `paginationButtons(page, prefix)`
1418
-
1419
- ```ts
1420
- paginationButtons(
1421
- page: Page<unknown>,
1422
- prefix: string,
1423
- ): InlineKeyboardButton[]
1424
- ```
1425
-
1426
- Menghasilkan button `Previous`, indicator `${page + 1}/${pageCount}` dengan callback `${prefix}:noop`, dan `Next` sesuai flag page.
1427
-
1428
- #### `MenuItem`
1429
-
1430
- ```ts
1431
- interface MenuItem {
1432
- id: string;
1433
- label: string;
1434
- callbackData?: string;
1435
- url?: string;
1436
- visible?: boolean | ((context: MenuContext, item: MenuItem) => boolean | Promise<boolean>);
1437
- permission?: string | ((context: MenuContext, item: MenuItem) => boolean | Promise<boolean>);
1438
- }
1439
- ```
1440
-
1441
- #### `Menu`
1442
-
1443
- ```ts
1444
- new Menu(id: string): Menu
1445
- ```
1446
-
1447
- | Method/property | Deskripsi |
1448
- |---|---|
1449
- | `item(item)` | Menambah item dan mendukung chaining. |
1450
- | `breadcrumb(label)` | Menambah label breadcrumb. |
1451
- | `build()` | Menunggu predicate visibility, melewati item invisible, lalu menghasilkan `InlineKeyboard`. URL diprioritaskan dibanding callback. |
1452
- | `breadcrumbs` | Array breadcrumb read-only. |
1453
-
1454
- `permission` hanya disimpan sebagai metadata item; `Menu.build()` tidak melakukan authorization otomatis.
1455
-
1456
- ---
1457
-
1458
- ## 12. Logging Terminal
1459
-
1460
- 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>`.
1461
-
1462
- Setiap update yang ditangani bot dicatat dalam baris yang mudah dibaca:
1463
-
1464
- ```text
1465
- [ => ] Message From 123456789 John Doe 29/08/2026 15:04:05
1466
- ↳ Text: /start
1467
- [ => ] Callback From 123456789 John Doe 29/08/2026 15:04:07
1468
- ↳ Data: menu:open
1469
- ```
1470
-
1471
- 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.
1472
-
1473
- Helper branding tambahan yang diekspor untuk aplikasi: `runStartupSequence()`, `startTeleBibzBanner()`, `printTeleBibzBanner()`, `paintRainbow()`, dan `printStatusLine()`.
1474
-
1475
- ## 13. Utilitas Teks
1476
-
1477
- ### `escapeMarkdownV2(value)`
1478
-
1479
- ```ts
1480
- escapeMarkdownV2(value: string): string
1481
- ```
1482
-
1483
- Meng-escape karakter MarkdownV2 Telegram: `\\_ * [ ] ( ) ~ ` > # + - = | { } . !`.
1484
-
1485
- ### `escapeHtml(value)`
1486
-
1487
- ```ts
1488
- escapeHtml(value: string): string
1489
- ```
1490
-
1491
- Mengubah `&`, `<`, `>`, dan `"` menjadi HTML entities.
1492
-
1493
- ### `md`
1494
-
1495
- Object helper MarkdownV2 berikut tersedia:
1496
-
1497
- | Method | Output konseptual |
1498
- |---|---|
1499
- | `md.bold(value)` | `*escaped value*` |
1500
- | `md.italic(value)` | `_escaped value_` |
1501
- | `md.link(label, url)` | `[escaped label](escaped url)` |
1502
- | `md.code(value)` | Inline code dengan backtick yang di-escape. |
1503
- | `md.pre(value, language?)` | Code block dengan language label opsional. |
1504
- | `md.escape(value)` | Alias `escapeMarkdownV2`. |
1505
-
1506
- ### `splitMessage(text, options?)`
1507
-
1508
- ```ts
1509
- splitMessage(
1510
- text: string,
1511
- options?: {
1512
- limit?: number;
1513
- parseMode?: "Markdown" | "MarkdownV2" | "HTML";
1514
- },
1515
- ): string[]
1516
- ```
1517
-
1518
- Memecah teks menjadi potongan dengan batas default `4096` karakter. Jika memungkinkan, pemotongan memilih batas paragraf, baris baru, atau spasi; batas hanya dipakai jika terletak lebih dari separuh jendela. `parseMode` diterima sebagai opsi API tetapi belum mengubah algoritma pemotongan.
1519
-
1520
- Limit kurang dari 1 akan melempar `RangeError`.
1521
-
1522
- ### `splitCaption(text)`
1523
-
1524
- ```ts
1525
- splitCaption(text: string): string[]
1526
- ```
1527
-
1528
- Alias `splitMessage(text, { limit: 1024 })`.
1529
-
1530
- ### `template(templateText, values)`
1531
-
1532
- ```ts
1533
- template(
1534
- templateText: string,
1535
- values: Record<string, unknown>,
1536
- ): string
1537
- ```
1538
-
1539
- Mengganti placeholder `{{ key }}` dan nested path seperti `{{ user.name }}`. Nilai `null` atau `undefined` diganti string kosong; nilai lain dikonversi dengan `String()`.
1540
-
1541
- ```ts
1542
- template("Halo {{ user.name }}", { user: { name: "Ayu" } });
1543
- // "Halo Ayu"
1544
- ```
1545
-
1546
- ---
1547
-
1548
- ### `validateUpload(upload, rules)`
1549
-
1550
- ```ts
1551
- validateUpload(upload: UploadLike, rules: UploadRules): UploadValidationIssue[]
1552
- ```
1553
-
1554
- Memvalidasi unggahan sebelum dikirim: `maxBytes` (batas ukuran), `allowedMimeTypes` (persis atau wildcard seperti `image/*`), dan `allowedExtensions` (case-insensitive, dengan atau tanpa titik awal). Mengembalikan semua pelanggaran yang ditemukan — array kosong berarti unggahan diterima.
1555
-
1556
- ### `assertValidUpload(upload, rules)`
1557
-
1558
- Aturan yang sama, tetapi melempar `UploadValidationError` (dengan seluruh `issues` terlampir) alih-alih mengembalikannya.
1559
-
1560
- ```ts
1561
- import { assertValidUpload } from "@xbibzlibrary/telebibz";
1562
-
1563
- assertValidUpload(
1564
- { sizeBytes: fileBytes.length, mimeType: "image/png", fileName: "logo.png" },
1565
- { maxBytes: 5_000_000, allowedMimeTypes: ["image/png", "image/jpeg"], allowedExtensions: [".png", ".jpg"] },
1566
- );
1567
- ```
1568
-
1569
- ## 14. Testing utilities
1570
-
1571
- Import dari `@xbibzlibrary/telebibz/testing` atau root package.
1572
-
1573
- ### `MockTransport`
1574
-
1575
- ```ts
1576
- new MockTransport(): MockTransport
1577
- ```
1578
-
1579
- | API | Deskripsi |
1580
- |---|---|
1581
- | `calls` | Array semua `TransportRequest` yang diterima. |
1582
- | `respond(method, response)` | Mengatur response statis atau callback berdasarkan payload dan mengembalikan transport. |
1583
- | `request(request)` | Mencatat request dan mengembalikan response mock. Response default adalah `{ ok: true, result: true }`. |
1584
-
1585
- Status mock adalah `200` bila `ok: true`, atau `error_code`/`500` bila `ok: false`.
1586
-
1587
- ```ts
1588
- const transport = new MockTransport()
1589
- .respond("getMe", {
1590
- ok: true,
1591
- result: { id: 1, is_bot: true, first_name: "Test" },
1592
- });
1593
- ```
1594
-
1595
- `MockTransport` juga mengimplementasikan member download opsional: `download(filePath)` mencatat path ke `downloads` dan mengembalikan `downloadBytes` (default: encoding UTF-8 dari path), serta `fileUrl(filePath)` mengembalikan `mock://files/<filePath>` — sehingga `bot.downloadFile()` sepenuhnya bisa dites tanpa jaringan.
1596
-
1597
- ### `createMockUpdate(overrides?)`
1598
-
1599
- ```ts
1600
- createMockUpdate(overrides?: Partial<Update>): Update
1601
- ```
1602
-
1603
- Membuat update message default dengan `update_id: 1`, chat private id `1`, user id `2`, dan text `/start`. Object `overrides` digabung shallow dengan default.
1604
-
1605
- ### `createTestBot()`
1606
-
1607
- ```ts
1608
- createTestBot(): { bot: Bot; transport: MockTransport }
1609
- ```
1610
-
1611
- Membuat bot dengan token test `123456:TEST_TOKEN`, mock `getMe()` yang menghasilkan bot id `99`, dan transport yang dapat diperiksa melalui `transport.calls`.
1612
-
1613
- ### `createMockContext(bot, update?)`
1614
-
1615
- ```ts
1616
- createMockContext(
1617
- bot: Bot,
1618
- update?: Update,
1619
- ): Context
1620
- ```
1621
-
1622
- Membuat context menggunakan API bot, session kosong, dan services kosong.
1623
-
1624
- ---
1625
-
1626
-
1627
- ## 15. Namespace metode Telegram yang dihasilkan
1628
-
1629
- `generated/api.ts` adalah source internal generator yang mendefinisikan:
1630
-
1631
- ```ts
1632
- const TELEGRAM_API_VERSION = "10.2";
1633
- const TELEGRAM_METHOD_NAMES: readonly string[];
1634
- type TelegramMethodName = typeof TELEGRAM_METHOD_NAMES[number];
1635
- type GeneratedMethodSpec = {
1636
- params: Record<string, unknown>;
1637
- result: unknown;
1638
- };
1639
- type GeneratedTelegramMethodMap = {
1640
- [K in TelegramMethodName]: GeneratedMethodSpec;
1641
- };
1642
- const GENERATED_METHODS: Record<TelegramMethodName, TelegramMethodName>;
1643
- ```
1644
-
1645
- `TELEGRAM_METHOD_NAMES` berisi 184 nama method pada source generator. Namespace tersebut menjadi dasar proxy `api.methods`, `api.call`, dan `api.request`, tetapi file generated tidak diekspor sebagai package subpath publik pada release ini. Parameter/result yang belum dipetakan khusus dapat dipanggil dengan `api.raw()` atau dengan cast parameter pada TypeScript.
1646
-
1647
- Untuk daftar canonical tanpa pengelompokan, nama method yang tersedia pada generated runtime namespace adalah:
1648
-
1649
- ```text
1650
- addStickerToSet,
1651
- answerCallbackQuery,
1652
- answerChatJoinRequestQuery,
1653
- answerGuestQuery,
1654
- answerInlineQuery,
1655
- answerPreCheckoutQuery,
1656
- answerShippingQuery,
1657
- answerWebAppQuery,
1658
- approveChatJoinRequest,
1659
- approveSuggestedPost,
1660
- banChatMember,
1661
- banChatSenderChat,
1662
- close,
1663
- closeForumTopic,
1664
- closeGeneralForumTopic,
1665
- convertGiftToStars,
1666
- copyMessage,
1667
- copyMessages,
1668
- createChatInviteLink,
1669
- createChatSubscriptionInviteLink,
1670
- createForumTopic,
1671
- createInvoiceLink,
1672
- createNewStickerSet,
1673
- declineChatJoinRequest,
1674
- declineSuggestedPost,
1675
- deleteAllMessageReactions,
1676
- deleteBusinessMessages,
1677
- deleteChatPhoto,
1678
- deleteChatStickerSet,
1679
- deleteEphemeralMessage,
1680
- deleteForumTopic,
1681
- deleteMessage,
1682
- deleteMessageReaction,
1683
- deleteMessages,
1684
- deleteMyCommands,
1685
- deleteStickerFromSet,
1686
- deleteStickerSet,
1687
- deleteStory,
1688
- deleteWebhook,
1689
- editChatInviteLink,
1690
- editChatSubscriptionInviteLink,
1691
- editEphemeralMessageCaption,
1692
- editEphemeralMessageMedia,
1693
- editEphemeralMessageReplyMarkup,
1694
- editEphemeralMessageText,
1695
- editForumTopic,
1696
- editGeneralForumTopic,
1697
- editMessageCaption,
1698
- editMessageChecklist,
1699
- editMessageLiveLocation,
1700
- editMessageMedia,
1701
- editMessageReplyMarkup,
1702
- editMessageText,
1703
- editStory,
1704
- editUserStarSubscription,
1705
- exportChatInviteLink,
1706
- forwardMessage,
1707
- forwardMessages,
1708
- getAvailableGifts,
1709
- getBusinessAccountGifts,
1710
- getBusinessAccountStarBalance,
1711
- getBusinessConnection,
1712
- getChat,
1713
- getChatAdministrators,
1714
- getChatGifts,
1715
- getChatMember,
1716
- getChatMemberCount,
1717
- getChatMenuButton,
1718
- getCustomEmojiStickers,
1719
- getFile,
1720
- getForumTopicIconStickers,
1721
- getGameHighScores,
1722
- getManagedBotAccessSettings,
1723
- getManagedBotToken,
1724
- getMe,
1725
- getMyCommands,
1726
- getMyDefaultAdministratorRights,
1727
- getMyDescription,
1728
- getMyName,
1729
- getMyShortDescription,
1730
- getMyStarBalance,
1731
- getStarTransactions,
1732
- getStickerSet,
1733
- getUpdates,
1734
- getUserChatBoosts,
1735
- getUserGifts,
1736
- getUserPersonalChatMessages,
1737
- getUserProfileAudios,
1738
- getUserProfilePhotos,
1739
- getWebhookInfo,
1740
- giftPremiumSubscription,
1741
- hideGeneralForumTopic,
1742
- leaveChat,
1743
- logOut,
1744
- pinChatMessage,
1745
- postStory,
1746
- promoteChatMember,
1747
- readBusinessMessage,
1748
- refundStarPayment,
1749
- removeBusinessAccountProfilePhoto,
1750
- removeChatVerification,
1751
- removeMyProfilePhoto,
1752
- removeUserVerification,
1753
- reopenForumTopic,
1754
- reopenGeneralForumTopic,
1755
- replaceManagedBotToken,
1756
- replaceStickerInSet,
1757
- repostStory,
1758
- restrictChatMember,
1759
- revokeChatInviteLink,
1760
- savePreparedInlineMessage,
1761
- savePreparedKeyboardButton,
1762
- sendAnimation,
1763
- sendAudio,
1764
- sendChatAction,
1765
- sendChatJoinRequestWebApp,
1766
- sendChecklist,
1767
- sendContact,
1768
- sendDice,
1769
- sendDocument,
1770
- sendGame,
1771
- sendGift,
1772
- sendInvoice,
1773
- sendLivePhoto,
1774
- sendLocation,
1775
- sendMediaGroup,
1776
- sendMessage,
1777
- sendMessageDraft,
1778
- sendPaidMedia,
1779
- sendPhoto,
1780
- sendPoll,
1781
- sendRichMessage,
1782
- sendRichMessageDraft,
1783
- sendSticker,
1784
- sendVenue,
1785
- sendVideo,
1786
- sendVideoNote,
1787
- sendVoice,
1788
- setBusinessAccountBio,
1789
- setBusinessAccountGiftSettings,
1790
- setBusinessAccountName,
1791
- setBusinessAccountProfilePhoto,
1792
- setBusinessAccountUsername,
1793
- setChatAdministratorCustomTitle,
1794
- setChatDescription,
1795
- setChatMemberTag,
1796
- setChatMenuButton,
1797
- setChatPermissions,
1798
- setChatPhoto,
1799
- setChatStickerSet,
1800
- setChatTitle,
1801
- setCustomEmojiStickerSetThumbnail,
1802
- setGameScore,
1803
- setManagedBotAccessSettings,
1804
- setMessageReaction,
1805
- setMyCommands,
1806
- setMyDefaultAdministratorRights,
1807
- setMyDescription,
1808
- setMyName,
1809
- setMyProfilePhoto,
1810
- setMyShortDescription,
1811
- setPassportDataErrors,
1812
- setStickerEmojiList,
1813
- setStickerKeywords,
1814
- setStickerMaskPosition,
1815
- setStickerPositionInSet,
1816
- setStickerSetThumbnail,
1817
- setStickerSetTitle,
1818
- setUserEmojiStatus,
1819
- setWebhook,
1820
- stopMessageLiveLocation,
1821
- stopPoll,
1822
- transferBusinessAccountStars,
1823
- transferGift,
1824
- unbanChatMember,
1825
- unbanChatSenderChat,
1826
- unhideGeneralForumTopic,
1827
- unpinAllChatMessages,
1828
- unpinAllForumTopicMessages,
1829
- unpinAllGeneralForumTopicMessages,
1830
- unpinChatMessage,
1831
- upgradeGift,
1832
- uploadStickerFile,
1833
- verifyChat
1834
- ```
1835
-
1836
- > Daftar di atas mengikuti generated source. Jika Telegram menambahkan method baru, jalankan `npm run update:telegram` atau `telebibz generate` setelah schema diperbarui.
1837
-
1838
- ---
1839
-
1840
- ## 16. CLI
1841
-
1842
- Binary package adalah `telebibz`.
1843
-
1844
- ```bash
1845
- npx telebibz <command>
1846
- ```
1847
-
1848
- | Command | Perilaku |
1849
- |---|---|
1850
- | `telebibz init [directory]` | Membuat directory, `index.ts` minimal, dan `.env.example`. Default directory `my-telebibz-bot`. |
1851
- | `telebibz doctor` | Menampilkan Node version, presence `TELEGRAM_BOT_TOKEN`, cwd, package name, lalu health API jika token tersedia. Exit code menjadi 1 jika API tidak reachable. |
1852
- | `telebibz generate` | Menjalankan generator method dari `scripts/generate-api.mjs`. |
1853
- | `telebibz build` | Menjalankan `npm run build`. |
1854
- | `telebibz test` | Menjalankan `npm test`. |
1855
- | `telebibz webhook` | Memeriksa `TELEGRAM_BOT_TOKEN`, memakai `TELEGRAM_WEBHOOK_SECRET` bila ada, membuat handler, dan mencetak kesiapan. Command ini tidak membuat HTTP server. |
1856
- | `telebibz inspect` | Menampilkan cwd dan Node version. |
1857
- | tanpa command | Menampilkan daftar command bantuan. |
1858
-
1859
- Environment variable yang dipakai CLI adalah `TELEGRAM_BOT_TOKEN` dan `TELEGRAM_WEBHOOK_SECRET`.
1860
-
1861
- ---
1862
-
1863
- ## 17. Tipe Telegram utama
1864
-
1865
- Paket mengekspor tipe data yang paling sering digunakan secara langsung.
1866
-
1867
- | Tipe | Isi penting |
1868
- |---|---|
1869
- | `User` | ID, penanda bot, nama, username, bahasa, dan penanda kemampuan. |
1870
- | `Chat` | ID, tipe, title/username/nama, penanda forum/pesan langsung. |
1871
- | `Message` | ID, tanggal, chat, pengirim, teks/caption, entity, reply, markup, plus index signature untuk field Telegram tambahan. |
1872
- | `Update` | Semua field update yang didukung sumber, termasuk message, callback, inline, poll, member, join request, reaction, boost, business, dan field ekstensi. |
1873
- | `CallbackQuery` | ID, from, message/inline message id, chat instance, data. |
1874
- | `InlineQuery` | ID, from, query, offset, tipe chat, lokasi. |
1875
- | `Poll`, `PollAnswer` | Data poll dan jawaban. |
1876
- | `ChatMemberUpdated`, `ChatJoinRequest` | Perubahan anggota dan permintaan bergabung. |
1877
- | `InlineKeyboardMarkup`, `ReplyKeyboardMarkup`, `ReplyKeyboardRemove`, `ForceReply` | Bentuk reply markup Telegram. |
1878
- | `MessageEntity`, `ReplyParameters`, `LinkPreviewOptions` | Metadata entity, reply, dan pratinjau tautan. |
1879
- | `BotCommand`, `BotCommandScope`, `WebhookInfo`, `File`, `UserProfilePhotos`, `ChatMember`, `ChatAdministratorRights` | Tipe hasil/parameter untuk helper API. |
1880
-
1881
- ---
1882
-
1883
- ## 18. Persistence, cron lengkap, menu, dan deklarasi Telegram lengkap
1884
-
1885
- ### Adapter storage persistent
1886
-
1887
- Semua adapter mengimplementasikan kontrak `Storage<K, V>` yang sama. Package inti tetap tidak memiliki runtime dependency vendor; adapter Redis, SQL, dan Mongo menerima driver kecil yang disediakan aplikasi atau client vendor pilihan aplikasi.
1888
-
1889
- | Class | Konstruktor | Tujuan |
1890
- |---|---|---|
1891
- | `MemoryStorage<K, V>` | `new MemoryStorage()` | Storage in-memory cepat dengan TTL dan `update()` atomik per key. |
1892
- | `JsonFileStorage<V>` | `new JsonFileStorage(filePath)` | Persistensi JSON atomik untuk deployment single-process. |
1893
- | `RedisStorage<V>` | `new RedisStorage(client, prefix?)` | Storage Redis melalui `RedisLikeClient`, termasuk TTL dan namespace. |
1894
- | `SqlStorage<V>` | `new SqlStorage(driver)` | Storage SQL melalui `SqlStorageDriver` milik aplikasi. |
1895
- | `MongoStorage<V>` | `new MongoStorage(collection)` | Storage Mongo melalui `MongoStorageCollection` milik aplikasi. |
1896
-
1897
- `BotOptions.session` menerima `Storage<string, S>`, sehingga session dapat memakai adapter apa pun. `ConversationManager` menerima abstraction yang sama dan menyediakan `getAsync()`, `cancelAsync()`, serta `clearExpiredAsync()` untuk state conversation durable.
1898
-
1899
- ```ts
1900
- const session = new JsonFileStorage<Record<string, unknown>>("./data/sessions.json");
1901
- const bot = new Bot({ token: process.env.TELEGRAM_BOT_TOKEN!, session });
1902
- ```
1903
-
1904
- ### Cron lima field lengkap
1905
-
1906
- `parseCronExpression()` mendukung lima field standar `minute hour day-of-month month day-of-week`, termasuk wildcard, list, range, dan step seperti `*/15 9-17 1,15 * 1-5`. `nextCronOccurrence()` menghitung occurrence lokal berikutnya. `Scheduler.cron()` memakai timer one-shot dan menjadwalkan ulang setelah setiap eksekusi; kegagalan task dikirim ke `Scheduler({ onError })`, bukan menjadi unhandled promise rejection.
1907
-
1908
- ### Mode matching router
1909
-
1910
- `new Router()` menggunakan **first-match secara default** untuk mencegah double reply. Gunakan `new Router({ matchMode: "all" })` hanya untuk fan-out yang disengaja. Matcher RegExp mereset `lastIndex`, sehingga expression global atau sticky dapat digunakan ulang dengan aman.
1911
-
1912
- ### MenuController dan permission
1913
-
1914
- `Menu` mendukung item berbasis permission, predicate visibility/permission asynchronous, breadcrumb, dan layout multi-kolom. `MenuController` menambahkan render halaman stateful dan dispatch callback untuk `select`, `page`, `noop`, serta callback asing.
1915
-
1916
- ### Namespace deklarasi Telegram lengkap
1917
-
1918
- Package memvendorkan declaration Telegram berlisensi MIT dan mengeksposnya sebagai type-only export melalui `TelegramTypes`, serta alias seperti `TelegramUser`, `TelegramMessage`, `TelegramUpdate`, dan `TelegramApiMethods`. CLI memakai kotak Unicode berwarna dengan attribution `Library Bot Telegram By @xbibzofficial`. `Logger` menghasilkan output terminal atau JSON terstruktur dengan level, redaction, ringkasan update, dan opt-in untuk isi pesan user/callback. Declaration ini mencakup surface object, union, enum, dan method tanpa runtime dependency tambahan. Map method inti telebibz tetap khusus untuk method yang memiliki pemetaan parameter/result langsung.
1919
-
1920
- ---
1921
-
1922
- ## 19. Kompatibilitas dan batasan yang perlu diketahui
1923
-
1924
- 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.
1925
-
1926
- 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.
1927
-
1928
- State session dan primitive in-memory lainnya hilang saat proses dimulai ulang kecuali aplikasi menyediakan adapter persistent. `BotOptions.session` menerima kontrak generic `Storage<string, S>`.
1929
-
1930
- ---
1931
-
1932
- ## Referensi
1933
-
1934
- [1]: https://core.telegram.org/bots/api "Telegram Bot API — dokumentasi resmi"
1935
- [2]: https://www.npmjs.com/package/@xbibzlibrary/telebibz "@xbibzlibrary/telebibz di npm"