@xbibzlibrary/telebibz 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/README.id.md +158 -0
- package/README.md +53 -33
- package/README.zh-CN.md +158 -0
- package/RELEASE_AUTOMATION.md +66 -0
- package/RELEASE_POLICY.md +2 -2
- package/assets/readme-preview.html +75 -0
- package/assets/telebibz-readme-preview.png +0 -0
- package/dist/src/api/index.d.ts +1 -0
- package/dist/src/api/index.d.ts.map +1 -1
- package/dist/src/api/index.js +1 -0
- package/dist/src/api/index.js.map +1 -1
- package/dist/src/api/telegram-types/LICENSE +21 -0
- package/dist/src/api/telegram-types/api.d.ts +22 -0
- package/dist/src/api/telegram-types/checklist.d.ts +72 -0
- package/dist/src/api/telegram-types/inline.d.ts +692 -0
- package/dist/src/api/telegram-types/langs.d.ts +193 -0
- package/dist/src/api/telegram-types/manage.d.ts +1144 -0
- package/dist/src/api/telegram-types/markup.d.ts +268 -0
- package/dist/src/api/telegram-types/message.d.ts +1537 -0
- package/dist/src/api/telegram-types/methods.d.ts +2870 -0
- package/dist/src/api/telegram-types/mod.d.ts +14 -0
- package/dist/src/api/telegram-types/passport.d.ts +163 -0
- package/dist/src/api/telegram-types/payment.d.ts +570 -0
- package/dist/src/api/telegram-types/rich.d.ts +1010 -0
- package/dist/src/api/telegram-types/settings.d.ts +120 -0
- package/dist/src/api/telegram-types/story.d.ts +89 -0
- package/dist/src/api/telegram-types/update.d.ts +84 -0
- package/dist/src/api/telegram.d.ts +7 -0
- package/dist/src/api/telegram.d.ts.map +1 -0
- package/dist/src/api/telegram.js +2 -0
- package/dist/src/api/telegram.js.map +1 -0
- package/dist/src/approval/approval.d.ts +8 -0
- package/dist/src/approval/approval.d.ts.map +1 -1
- package/dist/src/approval/approval.js +9 -0
- package/dist/src/approval/approval.js.map +1 -1
- package/dist/src/cache/cache.d.ts +6 -5
- package/dist/src/cache/cache.d.ts.map +1 -1
- package/dist/src/cache/cache.js +7 -3
- package/dist/src/cache/cache.js.map +1 -1
- package/dist/src/context/context.d.ts.map +1 -1
- package/dist/src/context/context.js +26 -3
- package/dist/src/context/context.js.map +1 -1
- package/dist/src/core/bot.d.ts +6 -4
- package/dist/src/core/bot.d.ts.map +1 -1
- package/dist/src/core/bot.js +48 -7
- package/dist/src/core/bot.js.map +1 -1
- package/dist/src/core/events.d.ts +4 -0
- package/dist/src/core/events.d.ts.map +1 -1
- package/dist/src/core/events.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/queue/queue.d.ts +25 -0
- package/dist/src/queue/queue.d.ts.map +1 -1
- package/dist/src/queue/queue.js +175 -51
- package/dist/src/queue/queue.js.map +1 -1
- package/dist/src/router/router.d.ts +8 -1
- package/dist/src/router/router.d.ts.map +1 -1
- package/dist/src/router/router.js +75 -17
- package/dist/src/router/router.js.map +1 -1
- package/dist/src/state/conversation.d.ts +6 -0
- package/dist/src/state/conversation.d.ts.map +1 -1
- package/dist/src/state/conversation.js +79 -11
- package/dist/src/state/conversation.js.map +1 -1
- package/dist/src/state/menu.d.ts +53 -5
- package/dist/src/state/menu.d.ts.map +1 -1
- package/dist/src/state/menu.js +116 -17
- package/dist/src/state/menu.js.map +1 -1
- package/dist/src/storage/storage.d.ts +115 -12
- package/dist/src/storage/storage.d.ts.map +1 -1
- package/dist/src/storage/storage.js +130 -4
- package/dist/src/storage/storage.js.map +1 -1
- package/dist/src/telegram-features.d.ts +33 -0
- package/dist/src/telegram-features.d.ts.map +1 -0
- package/dist/src/telegram-features.js +69 -0
- package/dist/src/telegram-features.js.map +1 -0
- package/dist/src/testing.d.ts +1 -0
- package/dist/src/testing.d.ts.map +1 -1
- package/dist/src/testing.js +16 -0
- package/dist/src/testing.js.map +1 -1
- package/dist-cjs/src/api/index.js +1 -0
- package/dist-cjs/src/api/telegram-types/LICENSE +21 -0
- package/dist-cjs/src/api/telegram-types/api.d.ts +22 -0
- package/dist-cjs/src/api/telegram-types/checklist.d.ts +72 -0
- package/dist-cjs/src/api/telegram-types/inline.d.ts +692 -0
- package/dist-cjs/src/api/telegram-types/langs.d.ts +193 -0
- package/dist-cjs/src/api/telegram-types/manage.d.ts +1144 -0
- package/dist-cjs/src/api/telegram-types/markup.d.ts +268 -0
- package/dist-cjs/src/api/telegram-types/message.d.ts +1537 -0
- package/dist-cjs/src/api/telegram-types/methods.d.ts +2870 -0
- package/dist-cjs/src/api/telegram-types/mod.d.ts +14 -0
- package/dist-cjs/src/api/telegram-types/passport.d.ts +163 -0
- package/dist-cjs/src/api/telegram-types/payment.d.ts +570 -0
- package/dist-cjs/src/api/telegram-types/rich.d.ts +1010 -0
- package/dist-cjs/src/api/telegram-types/settings.d.ts +120 -0
- package/dist-cjs/src/api/telegram-types/story.d.ts +89 -0
- package/dist-cjs/src/api/telegram-types/update.d.ts +84 -0
- package/dist-cjs/src/api/telegram.js +2 -0
- package/dist-cjs/src/approval/approval.js +11 -1
- package/dist-cjs/src/cache/cache.js +7 -3
- package/dist-cjs/src/context/context.js +26 -3
- package/dist-cjs/src/core/bot.js +48 -7
- package/dist-cjs/src/index.js +1 -0
- package/dist-cjs/src/queue/queue.js +177 -51
- package/dist-cjs/src/router/router.js +75 -17
- package/dist-cjs/src/state/conversation.js +79 -11
- package/dist-cjs/src/state/menu.js +118 -18
- package/dist-cjs/src/storage/storage.js +135 -5
- package/dist-cjs/src/telegram-features.js +74 -0
- package/dist-cjs/src/testing.js +17 -0
- package/docs/API.id.md +1800 -0
- package/docs/API.md +1799 -0
- package/docs/API.zh-CN.md +1794 -0
- package/docs/README.md +26 -15
- package/package.json +13 -2
package/docs/API.id.md
ADDED
|
@@ -0,0 +1,1800 @@
|
|
|
1
|
+
# Referensi API telebibz — Bahasa Indonesia
|
|
2
|
+
|
|
3
|
+
[English](API.md) · **Bahasa Indonesia** · [简体中文](API.zh-CN.md)
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Dokumen ini adalah referensi API untuk `@xbibzlibrary/telebibz@0.1.2`. 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 benar-benar tersedia pada rilis saat ini. `MemoryStorage`, `MemoryCache`, `TaskQueue`, dan `Scheduler` adalah primitif in-memory; adapter terdistribusi, persistensi eksternal, dan pengetikan skema penuh untuk seluruh Telegram Bot API belum termasuk dalam rilis ini.
|
|
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
|
+
| "awaiting-approval"
|
|
55
|
+
| "starting"
|
|
56
|
+
| "running"
|
|
57
|
+
| "stopping"
|
|
58
|
+
| "stopped"
|
|
59
|
+
| "error";
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `BotOptions<S>`
|
|
63
|
+
|
|
64
|
+
| Properti | Tipe | Default | Keterangan |
|
|
65
|
+
|---|---|---:|---|
|
|
66
|
+
| `token` | `string` | wajib | Token BotFather dengan format `<digits>:<token>`. |
|
|
67
|
+
| `apiBaseUrl` | `string` | `https://api.telegram.org` | Base URL API Telegram. Akhiran `/` dihapus secara otomatis. |
|
|
68
|
+
| `transport` | `Transport` | `FetchTransport` | Transport kustom untuk mock, proxy, atau implementasi lain. |
|
|
69
|
+
| `transportOptions` | `Omit<FetchTransportOptions, "baseUrl">` | `{}` | Timeout, retry, backoff, jitter, headers, dan fetch implementation. |
|
|
70
|
+
| `session` | `Storage<string, S>` | storage baru | Penyimpanan session berdasarkan kunci chat/user; dapat memakai adapter persistent. |
|
|
71
|
+
| `services` | `Record<string, unknown>` | `{}` | Dependency/service yang tersedia melalui `ctx.services`. |
|
|
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
|
+
| `approval` | `ApprovalOptions` | disabled | Mengaktifkan gerbang persetujuan (approval) untuk pemilik. |
|
|
78
|
+
|
|
79
|
+
### Konstruktor `Bot`
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
new Bot<S extends object = Record<string, unknown>>(
|
|
83
|
+
options: string | BotOptions<S>,
|
|
84
|
+
): Bot<S>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Jika argumen berupa string, string tersebut dianggap sebagai token. Konstruktor membuat `ApiClient`, router, event bus, plugin manager, session storage, dan approval gate bila dikonfigurasi. Konstruktor langsung memancarkan event `bot:created` secara asinkron.
|
|
88
|
+
|
|
89
|
+
Konstruktor melempar `Error` jika token kosong atau tidak sesuai pola token Telegram.
|
|
90
|
+
|
|
91
|
+
### Properti dan getter `Bot`
|
|
92
|
+
|
|
93
|
+
| API | Tipe | Deskripsi |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `api` | `ApiClient` | Client Telegram typed/dynamic. |
|
|
96
|
+
| `router` | `Router<Context<S>>` | Router utama bot. |
|
|
97
|
+
| `events` | `EventBus<EventMap>` | Event bus untuk lifecycle, update, API, webhook, dan polling. |
|
|
98
|
+
| `plugins` | `PluginManager<Context<S>>` | Manajer lifecycle plugin. |
|
|
99
|
+
| `session` | `Storage<string, S>` | Session bot; dapat memakai adapter persistent. |
|
|
100
|
+
| `services` | `Record<string, unknown>` | Salinan service yang diberikan saat konstruktor. |
|
|
101
|
+
| `approval` | `ApprovalGate \| undefined` | Approval gate jika `approval` dikonfigurasi. |
|
|
102
|
+
| `token` | `string` | Token bot yang dipakai client. |
|
|
103
|
+
| `status` | `BotStatus` | Status lifecycle terkini. |
|
|
104
|
+
| `botInfo` | `User \| undefined` | Hasil `getMe()` terakhir yang tersimpan. |
|
|
105
|
+
|
|
106
|
+
### `bot.use(...middleware)`
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
use(...middleware: Middleware<Context<S>>[]): this
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Menambahkan middleware global. Middleware dijalankan sebelum router pada setiap update, sesuai urutan registrasi. Mengembalikan instance bot untuk chaining.
|
|
113
|
+
|
|
114
|
+
### `bot.command(name, handler)`
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
command(name: string, handler: Middleware<Context<S>>): this
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
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"`.
|
|
121
|
+
|
|
122
|
+
### `bot.callback(pattern, handler)`
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
callback(pattern: string | RegExp, handler: Middleware<Context<S>>): this
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Jalan pintas untuk route callback query. String yang berakhiran `*` berarti pencocokan prefix; string lain harus sama persis.
|
|
129
|
+
|
|
130
|
+
### `bot.onText(text, handler)`
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
onText(text: string, handler: Middleware<Context<S>>): this
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Menangani message yang `message.text`-nya sama persis dengan `text`.
|
|
137
|
+
|
|
138
|
+
### `bot.onRegex(expression, handler)`
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
onRegex(expression: RegExp, handler: Middleware<Context<S>>): this
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Menangani message text menggunakan `RegExp`. Parameter route tidak diekstrak otomatis ke `ctx.params`; gunakan predicate atau middleware custom jika memerlukan ekstraksi.
|
|
145
|
+
|
|
146
|
+
### `bot.usePlugin(plugin)`
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
usePlugin(plugin: Plugin<Context<S>>): this
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Mendaftarkan plugin. Nama plugin harus unik.
|
|
153
|
+
|
|
154
|
+
### `bot.init()`
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
init(): Promise<this>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Memanggil `getMe()`, menyimpan informasi bot, memproses approval gate jika aktif, lalu menjalankan lifecycle plugin `setup()` dan `start()`.
|
|
161
|
+
|
|
162
|
+
Jika approval belum diberikan, method mengubah status menjadi `"awaiting-approval"`, mengirim notifikasi ke pemilik melalui `ApprovalGate`, dan mengembalikan bot tanpa mengaktifkan status `initialized`. Panggilan berikutnya tetap dapat dipakai setelah pemilik memberikan persetujuan.
|
|
163
|
+
|
|
164
|
+
`init()` idempoten ketika status sudah `initialized` atau `running`.
|
|
165
|
+
|
|
166
|
+
### `bot.start()`
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
start(): Promise<void>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Jalan pintas untuk `launch({ mode: "polling" })`. Method ini menjalankan long polling dan menunggu sampai polling dihentikan atau gagal secara fatal.
|
|
173
|
+
|
|
174
|
+
### `bot.launch(options?)`
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
launch(options?: {
|
|
178
|
+
mode: "polling";
|
|
179
|
+
timeout?: number;
|
|
180
|
+
allowedUpdates?: string[];
|
|
181
|
+
}): Promise<void>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Menjalankan bot dalam mode polling. Saat mulai, lifecycle berpindah melalui `starting` lalu `running`, kemudian loop `getUpdates()` memproses setiap update secara berurutan. Kegagalan polling memancarkan `polling:reconnect` dan menggunakan backoff eksponensial.
|
|
185
|
+
|
|
186
|
+
Mode selain `"polling"` melempar error dan menyarankan penggunaan `createWebhookHandler()` untuk webhook.
|
|
187
|
+
|
|
188
|
+
### `bot.stop()`
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
stop(): Promise<void>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
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.
|
|
195
|
+
|
|
196
|
+
### `bot.restart()`
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
restart(): Promise<void>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Menjalankan `stop()` lalu `start()`.
|
|
203
|
+
|
|
204
|
+
### `bot.health()`
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
health(): Promise<HealthStatus>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Memanggil `getMe()` untuk memeriksa keterjangkauan API. Tidak melempar error untuk kegagalan request; kegagalan dikembalikan sebagai `apiReachable: false` dan pesan error.
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
interface HealthStatus {
|
|
214
|
+
status: BotStatus;
|
|
215
|
+
apiReachable: boolean;
|
|
216
|
+
bot?: User;
|
|
217
|
+
checkedAt: string; // ISO timestamp
|
|
218
|
+
error?: string;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### `bot.getMe()`
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
getMe(): Promise<User>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Mengambil data bot dari Telegram dan memperbarui `botInfo`.
|
|
229
|
+
|
|
230
|
+
### `bot.setCommands(commands, scope?, languageCode?)`
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
setCommands(
|
|
234
|
+
commands: BotCommand[],
|
|
235
|
+
scope?: BotCommandScope,
|
|
236
|
+
languageCode?: string,
|
|
237
|
+
): Promise<true>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Jalan pintas ke `setMyCommands`. `languageCode` dipetakan menjadi field Telegram `language_code`.
|
|
241
|
+
|
|
242
|
+
### `bot.deleteCommands(scope?, languageCode?)`
|
|
243
|
+
|
|
244
|
+
```ts
|
|
245
|
+
deleteCommands(
|
|
246
|
+
scope?: BotCommandScope,
|
|
247
|
+
languageCode?: string,
|
|
248
|
+
): Promise<true>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Jalan pintas ke `deleteMyCommands`.
|
|
252
|
+
|
|
253
|
+
### `bot.handleUpdate(update)`
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
handleUpdate(update: Update): Promise<void>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Memproses satu update secara manual. Method menentukan kunci session dari `chat.id` dan `from.id`, membuat `Context`, memancarkan event `update` dan `message`, menjalankan middleware lalu router, dan menyimpan session setelah pipeline selesai.
|
|
260
|
+
|
|
261
|
+
Jika approval aktif dan bot belum diizinkan, update biasa dihentikan. Callback approval tetap diteruskan ke `ApprovalGate.handleCallback()`.
|
|
262
|
+
|
|
263
|
+
Error pipeline mengubah status bot menjadi `error`, memancarkan `bot:error`, lalu dilempar kembali.
|
|
264
|
+
|
|
265
|
+
### Contoh bot minimal
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
import { Bot, InlineKeyboard } from "@xbibzlibrary/telebibz";
|
|
269
|
+
|
|
270
|
+
const bot = new Bot({
|
|
271
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
272
|
+
polling: { allowedUpdates: ["message", "callback_query"] },
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
bot.command("start", async (ctx) => {
|
|
276
|
+
await ctx.reply("Halo dari telebibz", {
|
|
277
|
+
reply_markup: new InlineKeyboard()
|
|
278
|
+
.text("Status", "status")
|
|
279
|
+
.build(),
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
bot.callback("status", async (ctx) => {
|
|
284
|
+
await ctx.answerCallbackQuery("Bot aktif");
|
|
285
|
+
await ctx.reply("Status: running");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
await bot.start();
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 2. Bus peristiwa
|
|
294
|
+
|
|
295
|
+
### `EventMap`
|
|
296
|
+
|
|
297
|
+
| Peristiwa | Muatan |
|
|
298
|
+
|---|---|
|
|
299
|
+
| `bot:created` | `{ bot: unknown }` |
|
|
300
|
+
| `bot:initialized` | `{ bot: unknown }` |
|
|
301
|
+
| `bot:starting` | `{ bot: unknown }` |
|
|
302
|
+
| `bot:started` | `{ bot: unknown }` |
|
|
303
|
+
| `bot:stopping` | `{ bot: unknown }` |
|
|
304
|
+
| `bot:stopped` | `{ bot: unknown }` |
|
|
305
|
+
| `bot:error` | `{ bot: unknown; error: unknown }` |
|
|
306
|
+
| `update` | `{ update: unknown }` |
|
|
307
|
+
| `message` | `{ message: unknown }` |
|
|
308
|
+
| `command` | `{ name: string; update: unknown }` |
|
|
309
|
+
| `callback` | `{ data: string; update: unknown }` |
|
|
310
|
+
| `api:request` | `{ method: string; payload: unknown }` |
|
|
311
|
+
| `api:response` | `{ method: string; durationMs: number; response: unknown }` |
|
|
312
|
+
| `api:error` | `{ method: string; durationMs: number; error: unknown }` |
|
|
313
|
+
| `webhook:request` | `{ update: unknown }` |
|
|
314
|
+
| `polling:reconnect` | `{ error: unknown; attempt: number }` |
|
|
315
|
+
|
|
316
|
+
### `EventBus<Events>`
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
new EventBus<Events extends Record<string, unknown> = EventMap>()
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
| Metode | Tanda tangan | Perilaku |
|
|
323
|
+
|---|---|---|
|
|
324
|
+
| `on` | `on<K>(event: K, listener: (payload: Events[K]) => void \| Promise<void>): () => void` | Menambah listener dan mengembalikan fungsi unsubscribe. |
|
|
325
|
+
| `once` | `once<K>(event: K, listener: ...): () => void` | Listener hanya dipanggil sekali, lalu dilepas. |
|
|
326
|
+
| `off` | `off<K>(event: K, listener: ...): void` | Melepas listener tertentu. |
|
|
327
|
+
| `emit` | `emit<K>(event: K, payload: Events[K]): Promise<void>` | Memanggil listener secara berurutan dan menunggu masing-masing. |
|
|
328
|
+
| `removeAllListeners` | `removeAllListeners(): void` | Menghapus semua listener. |
|
|
329
|
+
| `listenerCount` | `listenerCount<K>(event: K): number` | Mengembalikan jumlah listener event. |
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
const unsubscribe = bot.events.on("bot:error", ({ error }) => {
|
|
333
|
+
console.error(error);
|
|
334
|
+
});
|
|
335
|
+
unsubscribe();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 3. API client, transport, dan error
|
|
341
|
+
|
|
342
|
+
### Tipe dasar
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
type ChatId = number | string;
|
|
346
|
+
type ParseMode = "Markdown" | "MarkdownV2" | "HTML";
|
|
347
|
+
type InputFile =
|
|
348
|
+
| string
|
|
349
|
+
| Uint8Array
|
|
350
|
+
| ArrayBuffer
|
|
351
|
+
| Blob
|
|
352
|
+
| NodeJS.ReadableStream
|
|
353
|
+
| { source: string | Uint8Array | ArrayBuffer | Blob | NodeJS.ReadableStream; filename?: string };
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
`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.
|
|
357
|
+
|
|
358
|
+
### `TelegramResponse<T>`
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
interface TelegramResponse<T> {
|
|
362
|
+
ok: boolean;
|
|
363
|
+
result?: T;
|
|
364
|
+
description?: string;
|
|
365
|
+
error_code?: number;
|
|
366
|
+
parameters?: ResponseParameters;
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### `TransportRequest`, `TransportResponse`, dan `Transport`
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
interface TransportRequest {
|
|
374
|
+
method: string;
|
|
375
|
+
payload?: Record<string, unknown>;
|
|
376
|
+
signal?: AbortSignal;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
interface TransportResponse<T = unknown> {
|
|
380
|
+
status: number;
|
|
381
|
+
headers: Headers;
|
|
382
|
+
data: TelegramResponse<T>;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
interface Transport {
|
|
386
|
+
request<T>(request: TransportRequest): Promise<TransportResponse<T>>;
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### `FetchTransportOptions`
|
|
391
|
+
|
|
392
|
+
| Properti | Default | Deskripsi |
|
|
393
|
+
|---|---:|---|
|
|
394
|
+
| `baseUrl` | `https://api.telegram.org` | Prefix URL sebelum `/<method>`. |
|
|
395
|
+
| `fetch` | `globalThis.fetch` | Implementasi fetch custom. |
|
|
396
|
+
| `timeoutMs` | `30000` | Timeout per attempt. |
|
|
397
|
+
| `retries` | `2` | Jumlah retry network error setelah attempt awal. |
|
|
398
|
+
| `backoffMs` | `250` | Delay exponential awal. |
|
|
399
|
+
| `maxBackoffMs` | `8000` | Batas delay transport. |
|
|
400
|
+
| `jitter` | `0.2` | Variasi acak ±20% dari exponential delay. |
|
|
401
|
+
| `headers` | `{}` | Header tambahan. |
|
|
402
|
+
|
|
403
|
+
### `new FetchTransport(options?)`
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
new FetchTransport(options?: FetchTransportOptions): FetchTransport
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
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`.
|
|
410
|
+
|
|
411
|
+
### `fetchTransport.request(request)`
|
|
412
|
+
|
|
413
|
+
```ts
|
|
414
|
+
request<T>(request: TransportRequest): Promise<TransportResponse<T>>
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
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`.
|
|
418
|
+
|
|
419
|
+
### `ApiHookContext`, `ApiClientOptions`, dan `ApiMethods`
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
interface ApiHookContext {
|
|
423
|
+
method: string;
|
|
424
|
+
payload: unknown;
|
|
425
|
+
startedAt: number;
|
|
426
|
+
durationMs?: number;
|
|
427
|
+
response?: TelegramResponse<unknown>;
|
|
428
|
+
error?: unknown;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
interface ApiClientOptions {
|
|
432
|
+
transport: Transport;
|
|
433
|
+
hooks?: {
|
|
434
|
+
onRequest?: (context: ApiHookContext) => void | Promise<void>;
|
|
435
|
+
onResponse?: (context: ApiHookContext) => void | Promise<void>;
|
|
436
|
+
onError?: (context: ApiHookContext) => void | Promise<void>;
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
`ApiMethods` adalah mapped type dari 184 `TelegramMethodName`:
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
type ApiMethods = {
|
|
445
|
+
[M in TelegramMethodName]:
|
|
446
|
+
(...args: ApiCallArgs<M>) => Promise<ApiResult<M>>;
|
|
447
|
+
};
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
### `new ApiClient(options)`
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
new ApiClient(options: ApiClientOptions): ApiClient
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
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`.
|
|
457
|
+
|
|
458
|
+
### `api.methods.<method>(params?)`
|
|
459
|
+
|
|
460
|
+
Method dinamis dapat dipanggil langsung. Method yang memiliki parameter kosong seperti `getMe()` dipanggil tanpa argumen; method lain menerima satu object parameter.
|
|
461
|
+
|
|
462
|
+
```ts
|
|
463
|
+
const me = await bot.api.methods.getMe();
|
|
464
|
+
const chat = await bot.api.methods.getChat({ chat_id: "@channel" });
|
|
465
|
+
const message = await bot.api.methods.sendMessage({
|
|
466
|
+
chat_id: 123456789,
|
|
467
|
+
text: "Hello",
|
|
468
|
+
});
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### `api.call(method, ...args)`
|
|
472
|
+
|
|
473
|
+
```ts
|
|
474
|
+
call<M extends TelegramMethodName>(
|
|
475
|
+
method: M,
|
|
476
|
+
...args: ApiCallArgs<M>
|
|
477
|
+
): Promise<ApiResult<M>>
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Bentuk bertipe untuk pemanggilan method berdasarkan string literal.
|
|
481
|
+
|
|
482
|
+
### `api.request(method, payload?, signal?)`
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
request<M extends TelegramMethodName>(
|
|
486
|
+
method: M,
|
|
487
|
+
payload?: ApiParams<M>,
|
|
488
|
+
signal?: AbortSignal,
|
|
489
|
+
): Promise<ApiResult<M>>
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Method request tingkat rendah yang memungkinkan `AbortSignal` eksplisit.
|
|
493
|
+
|
|
494
|
+
### `api.raw(method, payload?, signal?)`
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
raw(
|
|
498
|
+
method: string,
|
|
499
|
+
payload?: Record<string, unknown>,
|
|
500
|
+
signal?: AbortSignal,
|
|
501
|
+
): Promise<unknown>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
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`.
|
|
505
|
+
|
|
506
|
+
### Parameter dan hasil bertipe yang tersedia
|
|
507
|
+
|
|
508
|
+
Tipe berikut dipetakan khusus pada rilis ini.
|
|
509
|
+
|
|
510
|
+
| Method | Parameter | Result |
|
|
511
|
+
|---|---|---|
|
|
512
|
+
| `getMe` | tidak ada | `User` |
|
|
513
|
+
| `getUpdates` | `GetUpdatesParams` | `Update[]` |
|
|
514
|
+
| `setWebhook` | `SetWebhookParams` | `boolean` |
|
|
515
|
+
| `deleteWebhook` | `{ drop_pending_updates?: boolean }` | `boolean` |
|
|
516
|
+
| `getWebhookInfo` | tidak ada | `WebhookInfo` |
|
|
517
|
+
| `sendMessage` | `SendMessageParams` | `Message` |
|
|
518
|
+
| `editMessageText` | `EditMessageTextParams` | `Message \| true` |
|
|
519
|
+
| `deleteMessage` | `DeleteMessageParams` | `true` |
|
|
520
|
+
| `answerCallbackQuery` | `AnswerCallbackQueryParams` | `true` |
|
|
521
|
+
| `getChat` | `GetChatParams` | `Chat` |
|
|
522
|
+
| `getFile` | `GetFileParams` | `File` |
|
|
523
|
+
| `getUserProfilePhotos` | `{ user_id: number; offset?: number; limit?: number }` | `UserProfilePhotos` |
|
|
524
|
+
| `sendPhoto` | `SendPhotoParams` | `Message` |
|
|
525
|
+
| `sendDocument` | `SendDocumentParams` | `Message` |
|
|
526
|
+
|
|
527
|
+
Tipe parameter tambahan yang tersedia adalah `ReplyParameters`, `LinkPreviewOptions`, `InlineKeyboardButton`, `ReplyMarkup`, `BotCommand`, `BotCommandScope`, dan seluruh tipe update Telegram yang diekspor dari `api/types.ts`.
|
|
528
|
+
|
|
529
|
+
### Error API
|
|
530
|
+
|
|
531
|
+
```ts
|
|
532
|
+
type TelegramErrorKind =
|
|
533
|
+
| "retryable"
|
|
534
|
+
| "rate-limit"
|
|
535
|
+
| "authentication"
|
|
536
|
+
| "validation"
|
|
537
|
+
| "network"
|
|
538
|
+
| "server"
|
|
539
|
+
| "unknown";
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
#### `TelegramError`
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
new TelegramError(message: string, options: {
|
|
546
|
+
method: string;
|
|
547
|
+
payload: unknown;
|
|
548
|
+
errorCode?: number;
|
|
549
|
+
parameters?: ResponseParameters;
|
|
550
|
+
status?: number;
|
|
551
|
+
kind?: TelegramErrorKind;
|
|
552
|
+
cause?: unknown;
|
|
553
|
+
})
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Properti publik adalah `kind`, `errorCode`, `parameters`, `method`, `payload`, dan `status`. Getter `retryAfter` membaca `parameters.retry_after`; getter `migrateToChatId` membaca `parameters.migrate_to_chat_id`.
|
|
557
|
+
|
|
558
|
+
#### Subclass error
|
|
559
|
+
|
|
560
|
+
| Class | `name` | `kind` paksa |
|
|
561
|
+
|---|---|---|
|
|
562
|
+
| `TelegramRateLimitError` | `TelegramRateLimitError` | `rate-limit` |
|
|
563
|
+
| `TelegramAuthError` | `TelegramAuthError` | `authentication` |
|
|
564
|
+
| `TelegramValidationError` | `TelegramValidationError` | `validation` |
|
|
565
|
+
| `TelegramNetworkError` | `TelegramNetworkError` | `network` |
|
|
566
|
+
|
|
567
|
+
Keempat subclass memakai constructor options yang sama seperti `TelegramError`.
|
|
568
|
+
|
|
569
|
+
#### `classifyTelegramError(errorCode?, status?)`
|
|
570
|
+
|
|
571
|
+
```ts
|
|
572
|
+
classifyTelegramError(
|
|
573
|
+
errorCode?: number,
|
|
574
|
+
status?: number,
|
|
575
|
+
): TelegramErrorKind
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
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`.
|
|
579
|
+
|
|
580
|
+
#### `telegramErrorFromResponse(response, context)`
|
|
581
|
+
|
|
582
|
+
```ts
|
|
583
|
+
telegramErrorFromResponse<T>(
|
|
584
|
+
response: TelegramResponse<T>,
|
|
585
|
+
context: { method: string; payload: unknown; status?: number },
|
|
586
|
+
): TelegramError
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Mengubah response Telegram gagal menjadi subclass yang sesuai. Error `429`, auth, dan validation menghasilkan subclass khusus; error lain menghasilkan `TelegramError` biasa.
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## 4. Konteks
|
|
594
|
+
|
|
595
|
+
### `ContextOptions<S>`
|
|
596
|
+
|
|
597
|
+
```ts
|
|
598
|
+
interface ContextOptions<S extends object = Record<string, unknown>> {
|
|
599
|
+
update: Update;
|
|
600
|
+
api: ApiClient;
|
|
601
|
+
session: S;
|
|
602
|
+
services: Record<string, unknown>;
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### `Context<S>` properties
|
|
607
|
+
|
|
608
|
+
| Properti | Isi |
|
|
609
|
+
|---|---|
|
|
610
|
+
| `update` | Update mentah Telegram. |
|
|
611
|
+
| `api` | `ApiClient` bot. |
|
|
612
|
+
| `session` | Objek session yang dapat diubah milik update key saat ini. |
|
|
613
|
+
| `state` | Objek transient per-konteks, tidak otomatis disimpan ke session. |
|
|
614
|
+
| `services` | Layanan yang dikirim melalui `BotOptions.services`. |
|
|
615
|
+
| `params` | Objek parameter route; router bawaan saat ini tidak mengisi otomatis. |
|
|
616
|
+
| `message` | Message utama dari message/edited/channel/business/guest update. |
|
|
617
|
+
| `chat` | `message.chat` bila tersedia. |
|
|
618
|
+
| `from` / `sender` | Pengguna dari message, callback query, atau inline query. |
|
|
619
|
+
| `callbackQuery` | `update.callback_query`. |
|
|
620
|
+
| `inlineQuery` | `update.inline_query`. |
|
|
621
|
+
| `poll` | `update.poll`. |
|
|
622
|
+
| `pollAnswer` | `update.poll_answer`. |
|
|
623
|
+
| `chatMember` | `update.chat_member`. |
|
|
624
|
+
| `myChatMember` | `update.my_chat_member`. |
|
|
625
|
+
| `chatJoinRequest` | `update.chat_join_request`. |
|
|
626
|
+
| `reaction` | `update.message_reaction`. |
|
|
627
|
+
| `boost` | `chat_boost` atau `removed_chat_boost`. |
|
|
628
|
+
|
|
629
|
+
### `new Context(options)`
|
|
630
|
+
|
|
631
|
+
```ts
|
|
632
|
+
new Context<S>(options: ContextOptions<S>): Context<S>
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
### Metode pesan Context
|
|
636
|
+
|
|
637
|
+
| Method | Signature | Perilaku |
|
|
638
|
+
|---|---|---|
|
|
639
|
+
| `reply` | `reply(text, extra?): Promise<Message>` | Mengirim message ke chat update dan mengisi `reply_parameters.message_id` bila ada message. |
|
|
640
|
+
| `send` | `send(text, extra?): Promise<Message>` | Mengirim message ke chat update tanpa reply reference. |
|
|
641
|
+
| `edit` | `edit(text, extra?): Promise<Message \| true>` | Mengedit message update menggunakan `editMessageText`. |
|
|
642
|
+
| `delete` | `delete(): Promise<true>` | Menghapus message update. |
|
|
643
|
+
| `copy` | `copy(fromChatId, messageId, extra?): Promise<unknown>` | Memanggil `copyMessage` ke chat context. |
|
|
644
|
+
| `forward` | `forward(fromChatId, messageId, extra?): Promise<Message>` | Memanggil `forwardMessage` ke chat context. |
|
|
645
|
+
| `pin` | `pin(messageId?, extra?): Promise<true>` | Memanggil `pinChatMessage`, default message id dari context. |
|
|
646
|
+
| `unpin` | `unpin(messageId?, extra?): Promise<true>` | Memanggil `unpinChatMessage`, default message id dari context. |
|
|
647
|
+
| `react` | `react(reaction, extra?): Promise<true>` | Memanggil `setMessageReaction`. |
|
|
648
|
+
| `answerCallbackQuery` | `answerCallbackQuery(text?, extra?): Promise<true>` | Menjawab callback query aktif. Error jika bukan callback update. |
|
|
649
|
+
| `answerInlineQuery` | `answerInlineQuery(results, extra?): Promise<true>` | Menjawab inline query aktif. Error jika bukan inline update. |
|
|
650
|
+
| `getChat` | `getChat(): Promise<Chat>` | Mengambil detail chat context. |
|
|
651
|
+
| `getUserProfilePhotos` | `getUserProfilePhotos(userId?, extra?): Promise<unknown>` | Mengambil foto profil user context. |
|
|
652
|
+
| `getFile` | `getFile(fileId): Promise<unknown>` | Mengambil file berdasarkan id. |
|
|
653
|
+
| `withReplyMarkup` | `withReplyMarkup(markup): this` | Menyimpan markup di `ctx.state.reply_markup` dan mengembalikan context. Metode ini tidak otomatis mengirim message. |
|
|
654
|
+
|
|
655
|
+
`reply`, `send`, `getChat`, dan beberapa helper lain melempar error ketika update tidak memiliki chat yang diperlukan. `edit` dan `delete` membutuhkan chat serta message.
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
## 5. Middleware dan router
|
|
660
|
+
|
|
661
|
+
### Jenis middleware
|
|
662
|
+
|
|
663
|
+
```ts
|
|
664
|
+
type Next = () => Promise<void>;
|
|
665
|
+
type Middleware<Context> = (ctx: Context, next: Next) => void | Promise<void>;
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### `compose(middleware)`
|
|
669
|
+
|
|
670
|
+
```ts
|
|
671
|
+
compose<Context>(
|
|
672
|
+
middleware: readonly Middleware<Context>[],
|
|
673
|
+
): (ctx: Context) => Promise<void>
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
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")`.
|
|
677
|
+
|
|
678
|
+
### `middleware(handler)`
|
|
679
|
+
|
|
680
|
+
```ts
|
|
681
|
+
middleware<Context>(handler: Middleware<Context>): Middleware<Context>
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
Pembantu identitas untuk memberi anotasi/inferensi tipe pada middleware.
|
|
685
|
+
|
|
686
|
+
### `RoutableContext`
|
|
687
|
+
|
|
688
|
+
Context minimal yang dibutuhkan router: `update`, `message`, `callbackQuery`, dan `params`.
|
|
689
|
+
|
|
690
|
+
### `Router<Context>`
|
|
691
|
+
|
|
692
|
+
```ts
|
|
693
|
+
new Router<Context extends RoutableContext>(): Router<Context>
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
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.
|
|
697
|
+
|
|
698
|
+
| Metode | Tanda tangan | Pencocokan |
|
|
699
|
+
|---|---|---|
|
|
700
|
+
| `use` | `use(...middleware): this` | Middleware global router dengan priority paling tinggi untuk dijalankan lebih awal. |
|
|
701
|
+
| `route` | `route(matcher, ...middleware): this` | Matcher boolean atau async custom. |
|
|
702
|
+
| `command` | `command(name: string \| RegExp, ...middleware): this` | Command pertama dari message text yang diawali `/`. |
|
|
703
|
+
| `text` | `text(value: string, ...middleware): this` | Pencocokan teks persis. |
|
|
704
|
+
| `regex` | `regex(expression: RegExp, ...middleware): this` | `RegExp.test` atas message text atau string kosong. |
|
|
705
|
+
| `callback` | `callback(pattern: string \| RegExp, ...middleware): this` | Exact, prefix dengan suffix `*`, atau regex atas callback data. |
|
|
706
|
+
| `chat` | `chat(chatId: number \| string, ...middleware): this` | Cocokkan `message.chat.id`, numeric atau string-equivalent. |
|
|
707
|
+
| `predicate` | `predicate(matcher, ...middleware): this` | Alias semantik untuk custom matcher. |
|
|
708
|
+
| `nest` | `nest(child: Router<Context>): this` | Menjalankan router child sebagai nested route. |
|
|
709
|
+
| `handle` | `handle(ctx, terminal?): Promise<void>` | Mengevaluasi dan menjalankan seluruh route yang cocok. |
|
|
710
|
+
|
|
711
|
+
```ts
|
|
712
|
+
const router = new Router<Context>();
|
|
713
|
+
router.use(async (ctx, next) => {
|
|
714
|
+
console.log("before");
|
|
715
|
+
await next();
|
|
716
|
+
});
|
|
717
|
+
router.callback("page:*", async (ctx) => {
|
|
718
|
+
await ctx.answerCallbackQuery();
|
|
719
|
+
});
|
|
720
|
+
router.predicate((ctx) => Boolean(ctx.from?.id), async (ctx) => {
|
|
721
|
+
await ctx.reply("Authenticated update");
|
|
722
|
+
});
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
**Catatan RegExp.** Router memanggil `.test()` langsung. Untuk ekspresi dengan flag `g` atau `y`, sifat stateful `lastIndex` JavaScript dapat memengaruhi pencocokan berulang.
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
## 6. Pembuat keyboard
|
|
730
|
+
|
|
731
|
+
### `InlineKeyboard`
|
|
732
|
+
|
|
733
|
+
```ts
|
|
734
|
+
new InlineKeyboard(): InlineKeyboard
|
|
735
|
+
InlineKeyboard.from(rows: InlineKeyboardButton[][]): InlineKeyboard
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Builder menyimpan baris secara dapat diubah (mutable) dan seluruh metode builder mengembalikan `this`.
|
|
739
|
+
|
|
740
|
+
| Method | Signature | Deskripsi |
|
|
741
|
+
|---|---|---|
|
|
742
|
+
| `from` | `static from(rows): InlineKeyboard` | Membuat keyboard dari rows dan menyalin setiap row. |
|
|
743
|
+
| `text` | `text(text, callbackData): this` | Tombol callback. |
|
|
744
|
+
| `url` | `url(text, url): this` | Tombol URL. |
|
|
745
|
+
| `webApp` | `webApp(text, url): this` | Tombol Web App. |
|
|
746
|
+
| `pay` | `pay(text = "Pay"): this` | Tombol pembayaran. |
|
|
747
|
+
| `copy` | `copy(text, copiedText): this` | Tombol salin teks. |
|
|
748
|
+
| `button` | `button(button): this` | Menambahkan satu tombol ke baris terakhir atau membuat baris pertama. |
|
|
749
|
+
| `row` | `row(...buttons): this` | Menambahkan baris baru. |
|
|
750
|
+
| `conditional` | `conditional(condition, factory): this` | Menjalankan factory hanya jika kondisi bernilai true. |
|
|
751
|
+
| `grid` | `grid(buttons, columns): this` | Membagi tombol ke baris berdasarkan jumlah kolom. |
|
|
752
|
+
| `build` | `build(): InlineKeyboardMarkup` | Menghasilkan markup baru. |
|
|
753
|
+
| `asReplyMarkup` | `asReplyMarkup(): InlineKeyboardMarkup` | Alias dari `build`. |
|
|
754
|
+
|
|
755
|
+
Setiap inline button wajib memiliki text dan tepat satu action. Callback data dibatasi maksimum 64 bytes UTF-8; pelanggaran melempar `RangeError`.
|
|
756
|
+
|
|
757
|
+
```ts
|
|
758
|
+
const keyboard = new InlineKeyboard()
|
|
759
|
+
.text("Izinkan", "approve:123")
|
|
760
|
+
.url("Dokumentasi", "https://example.com")
|
|
761
|
+
.row(
|
|
762
|
+
{ text: "A", callback_data: "a" },
|
|
763
|
+
{ text: "B", callback_data: "b" },
|
|
764
|
+
)
|
|
765
|
+
.build();
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### `ReplyKeyboard`
|
|
769
|
+
|
|
770
|
+
```ts
|
|
771
|
+
new ReplyKeyboard(): ReplyKeyboard
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
| Method | Signature | Deskripsi |
|
|
775
|
+
|---|---|---|
|
|
776
|
+
| `text` | `text(text): this` | Tombol teks biasa. |
|
|
777
|
+
| `contact` | `contact(text): this` | Meminta kontak. |
|
|
778
|
+
| `location` | `location(text): this` | Meminta lokasi. |
|
|
779
|
+
| `poll` | `poll(text, type?): this` | Meminta poll `quiz` atau `regular`. |
|
|
780
|
+
| `webApp` | `webApp(text, url): this` | Tombol Web App. |
|
|
781
|
+
| `button` | `button(button): this` | Tambah satu tombol ke baris terakhir. |
|
|
782
|
+
| `row` | `row(...buttons): this` | Tambah baris baru. |
|
|
783
|
+
| `grid` | `grid(buttons, columns): this` | Membagi tombol menjadi grid. |
|
|
784
|
+
| `build` | `build(options?): ReplyKeyboardMarkup` | Menghasilkan markup dan menggabungkan opsi. |
|
|
785
|
+
| `asReplyMarkup` | `asReplyMarkup(): ReplyKeyboardMarkup` | Alias dari `build()` tanpa opsi. |
|
|
786
|
+
|
|
787
|
+
`columns` harus integer positif; jika tidak, `grid` melempar `RangeError`.
|
|
788
|
+
|
|
789
|
+
### `removeKeyboard(selective?)`
|
|
790
|
+
|
|
791
|
+
```ts
|
|
792
|
+
removeKeyboard(selective = false): ReplyMarkup
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
Menghasilkan `{ remove_keyboard: true }`, dengan `selective: true` bila diminta.
|
|
796
|
+
|
|
797
|
+
### `forceReply(placeholder?, selective?)`
|
|
798
|
+
|
|
799
|
+
```ts
|
|
800
|
+
forceReply(placeholder?: string, selective = false): ReplyMarkup
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
Menghasilkan ForceReply. Placeholder hanya ditambahkan jika bernilai truthy.
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
## 7. Storage dan cache
|
|
808
|
+
|
|
809
|
+
### `Storage<K, V>`
|
|
810
|
+
|
|
811
|
+
```ts
|
|
812
|
+
interface Storage<K, V> {
|
|
813
|
+
get(key: K): Promise<V | undefined>;
|
|
814
|
+
set(key: K, value: V, options?: { ttlMs?: number }): Promise<void>;
|
|
815
|
+
delete(key: K): Promise<boolean>;
|
|
816
|
+
has(key: K): Promise<boolean>;
|
|
817
|
+
clear(): Promise<void>;
|
|
818
|
+
keys(): AsyncIterable<K>;
|
|
819
|
+
values(): AsyncIterable<V>;
|
|
820
|
+
entries(): AsyncIterable<[K, V]>;
|
|
821
|
+
update<T extends V>(
|
|
822
|
+
key: K,
|
|
823
|
+
updater: (current: V | undefined) => T | Promise<T>,
|
|
824
|
+
options?: { ttlMs?: number },
|
|
825
|
+
): Promise<T>;
|
|
826
|
+
}
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
### `MemoryStorage<K, V>`
|
|
830
|
+
|
|
831
|
+
```ts
|
|
832
|
+
new MemoryStorage<K, V>(): MemoryStorage<K, V>
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
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.
|
|
836
|
+
|
|
837
|
+
```ts
|
|
838
|
+
const sessions = new MemoryStorage<string, { count: number }>();
|
|
839
|
+
await sessions.set("user:1", { count: 0 }, { ttlMs: 60_000 });
|
|
840
|
+
await sessions.update("user:1", (current) => ({
|
|
841
|
+
count: (current?.count ?? 0) + 1,
|
|
842
|
+
}));
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### `Cache<K, V>`
|
|
846
|
+
|
|
847
|
+
```ts
|
|
848
|
+
interface Cache<K = string, V = unknown> {
|
|
849
|
+
get(key: K): Promise<V | undefined>;
|
|
850
|
+
set(key: K, value: V, ttlMs?: number): Promise<void>;
|
|
851
|
+
delete(key: K): Promise<boolean>;
|
|
852
|
+
invalidate(prefix?: string): Promise<void>;
|
|
853
|
+
getOrSet(key: K, factory: () => V | Promise<V>, ttlMs?: number): Promise<V>;
|
|
854
|
+
}
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
### `MemoryCache`
|
|
858
|
+
|
|
859
|
+
```ts
|
|
860
|
+
new MemoryCache(namespace = "telebibz"): MemoryCache
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Cache yang menggunakan string sebagai kunci dan menerapkan namespace internal pada setiap key.
|
|
864
|
+
|
|
865
|
+
| Method | Perilaku |
|
|
866
|
+
|---|---|
|
|
867
|
+
| `get` | Mengambil value atau `undefined`. |
|
|
868
|
+
| `set` | Menyimpan value dengan TTL opsional. |
|
|
869
|
+
| `delete` | Menghapus key dan mengembalikan boolean. |
|
|
870
|
+
| `invalidate(prefix = "")` | Menghapus semua key dalam namespace yang diawali oleh prefix. |
|
|
871
|
+
| `getOrSet` | Mengembalikan nilai dari cache jika ada; jika tidak ada, menjalankan factory, menyimpan hasilnya, lalu mengembalikannya. |
|
|
872
|
+
|
|
873
|
+
`getOrSet` tidak menggunakan lock deduplikasi; factory dapat dijalankan lebih dari sekali bila dipanggil konkuren saat key belum tersedia.
|
|
874
|
+
|
|
875
|
+
### `RateLimitResult`
|
|
876
|
+
|
|
877
|
+
```ts
|
|
878
|
+
interface RateLimitResult {
|
|
879
|
+
allowed: boolean;
|
|
880
|
+
remaining: number;
|
|
881
|
+
resetAt: number;
|
|
882
|
+
retryAfterMs?: number;
|
|
883
|
+
}
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
### `TokenBucketLimiter`
|
|
887
|
+
|
|
888
|
+
```ts
|
|
889
|
+
new TokenBucketLimiter(
|
|
890
|
+
capacity: number,
|
|
891
|
+
refillPerSecond: number,
|
|
892
|
+
): TokenBucketLimiter
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
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.
|
|
896
|
+
|
|
897
|
+
---
|
|
898
|
+
|
|
899
|
+
## 8. Queue dan scheduler
|
|
900
|
+
|
|
901
|
+
### `Job<T>` dan `QueueOptions`
|
|
902
|
+
|
|
903
|
+
```ts
|
|
904
|
+
interface Job<T = unknown> {
|
|
905
|
+
id: string;
|
|
906
|
+
data: T;
|
|
907
|
+
attempts: number;
|
|
908
|
+
priority: number;
|
|
909
|
+
runAt: number;
|
|
910
|
+
status: "queued" | "running" | "completed" | "failed" | "cancelled";
|
|
911
|
+
error?: unknown;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
interface QueueOptions {
|
|
915
|
+
concurrency?: number;
|
|
916
|
+
retries?: number;
|
|
917
|
+
backoffMs?: number;
|
|
918
|
+
maxBackoffMs?: number;
|
|
919
|
+
}
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
### `TaskQueue<T>`
|
|
923
|
+
|
|
924
|
+
```ts
|
|
925
|
+
new TaskQueue<T>(
|
|
926
|
+
worker: (job: Job<T>, signal: AbortSignal) => Promise<void>,
|
|
927
|
+
options?: QueueOptions,
|
|
928
|
+
): TaskQueue<T>
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
| Method | Signature | Deskripsi |
|
|
932
|
+
|---|---|---|
|
|
933
|
+
| `add` | `add(data, options?): Job<T>` | Menambah job; options `id`, `priority`, `delayMs`. Job langsung dijadwalkan. |
|
|
934
|
+
| `get` | `get(id): Job<T> \| undefined` | Mengembalikan salinan status job. |
|
|
935
|
+
| `cancel` | `cancel(id): boolean` | Membatalkan queued/running job dan abort signal worker. |
|
|
936
|
+
| `onIdle` | `onIdle(): Promise<void>` | Menunggu sampai pending dan active kosong. |
|
|
937
|
+
| `close` | `close(): Promise<void>` | Menghentikan draining baru dan membatalkan controller aktif. |
|
|
938
|
+
|
|
939
|
+
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.
|
|
940
|
+
|
|
941
|
+
### `ScheduledJob`
|
|
942
|
+
|
|
943
|
+
```ts
|
|
944
|
+
interface ScheduledJob {
|
|
945
|
+
id: string;
|
|
946
|
+
cancel: () => void;
|
|
947
|
+
}
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
### `Scheduler`
|
|
951
|
+
|
|
952
|
+
```ts
|
|
953
|
+
new Scheduler(): Scheduler
|
|
954
|
+
```
|
|
955
|
+
|
|
956
|
+
| Method | Signature | Deskripsi |
|
|
957
|
+
|---|---|---|
|
|
958
|
+
| `every` | `every(id, intervalMs, task): ScheduledJob` | Menjalankan task menggunakan `setInterval`. Mengganti timer dengan id sama. |
|
|
959
|
+
| `after` | `after(id, delayMs, task): ScheduledJob` | Menjalankan task sekali menggunakan `setTimeout`. |
|
|
960
|
+
| `cron` | `cron(id, expression, task): ScheduledJob` | Mendukung format sederhana `*/N` pada field menit, setara interval `N * 60_000`. |
|
|
961
|
+
| `cancel` | `cancel(id): boolean` | Membatalkan timer. |
|
|
962
|
+
| `clear` | `clear(): void` | Membatalkan semua timer. |
|
|
963
|
+
|
|
964
|
+
Format cron penuh tidak didukung oleh built-in scheduler. Ekspresi selain `*/N` melempar `Error`.
|
|
965
|
+
|
|
966
|
+
## 9. Plugin dan services
|
|
967
|
+
|
|
968
|
+
### `Plugin<Context>`
|
|
969
|
+
|
|
970
|
+
```ts
|
|
971
|
+
interface Plugin<Context = unknown> {
|
|
972
|
+
name: string;
|
|
973
|
+
version?: string;
|
|
974
|
+
install?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
975
|
+
setup?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
976
|
+
onStart?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
977
|
+
onUpdate?: (context: Context) => void | Promise<void>;
|
|
978
|
+
onStop?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
979
|
+
dispose?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
980
|
+
}
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
### `PluginApi<Context>`
|
|
984
|
+
|
|
985
|
+
```ts
|
|
986
|
+
interface PluginApi<Context> {
|
|
987
|
+
bot: unknown;
|
|
988
|
+
services: ServiceContainer;
|
|
989
|
+
registerMiddleware: (middleware: unknown) => void;
|
|
990
|
+
registerRoute: (route: unknown) => void;
|
|
991
|
+
}
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
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.
|
|
995
|
+
|
|
996
|
+
### `ServiceContainer`
|
|
997
|
+
|
|
998
|
+
```ts
|
|
999
|
+
new ServiceContainer(): ServiceContainer
|
|
1000
|
+
```
|
|
1001
|
+
|
|
1002
|
+
| Method | Signature | Deskripsi |
|
|
1003
|
+
|---|---|---|
|
|
1004
|
+
| `register` | `register<T>(name: string \| symbol, value: T): this` | Menyimpan service dan mendukung chaining. |
|
|
1005
|
+
| `get` | `get<T>(name: string \| symbol): T` | Mengambil service; melempar jika belum terdaftar. |
|
|
1006
|
+
| `has` | `has(name: string \| symbol): boolean` | Memeriksa keberadaan service. |
|
|
1007
|
+
| `delete` | `delete(name: string \| symbol): boolean` | Menghapus service. |
|
|
1008
|
+
|
|
1009
|
+
### `PluginManager<Context>`
|
|
1010
|
+
|
|
1011
|
+
```ts
|
|
1012
|
+
new PluginManager<Context>(bot: unknown): PluginManager<Context>
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
| Method | Perilaku |
|
|
1016
|
+
|---|---|
|
|
1017
|
+
| `use(plugin)` | Menambah plugin; nama duplikat melempar kesalahan. |
|
|
1018
|
+
| `setup()` | Untuk setiap plugin, menjalankan `install` lalu `setup`. |
|
|
1019
|
+
| `start()` | Menjalankan `onStart` sesuai urutan registrasi. |
|
|
1020
|
+
| `update(context)` | Menjalankan `onUpdate` sesuai urutan registrasi. |
|
|
1021
|
+
| `stop()` | Menjalankan `onStop`. |
|
|
1022
|
+
| `dispose()` | Menjalankan `dispose` dalam urutan pendaftaran terbalik. |
|
|
1023
|
+
| `list()` | Mengembalikan daftar plugin hanya baca. |
|
|
1024
|
+
|
|
1025
|
+
`Bot.handleUpdate()` pada rilis ini tidak memanggil `plugins.update()` secara otomatis; panggil manajer secara eksplisit bila plugin memerlukan siklus hidup pembaruan.
|
|
1026
|
+
|
|
1027
|
+
---
|
|
1028
|
+
|
|
1029
|
+
## 10. Webhook
|
|
1030
|
+
|
|
1031
|
+
### `WebhookOptions`
|
|
1032
|
+
|
|
1033
|
+
```ts
|
|
1034
|
+
interface WebhookOptions {
|
|
1035
|
+
secretToken?: string;
|
|
1036
|
+
maxBodyBytes?: number;
|
|
1037
|
+
onError?: (error: unknown) => void | Promise<void>;
|
|
1038
|
+
}
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
### `createWebhookHandler(bot, options?)`
|
|
1042
|
+
|
|
1043
|
+
```ts
|
|
1044
|
+
createWebhookHandler<S extends object>(
|
|
1045
|
+
bot: Bot<S>,
|
|
1046
|
+
options?: WebhookOptions,
|
|
1047
|
+
): (request: Request) => Promise<Response>
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
Handler menerima Request standar Web `Request` dan mengembalikan `Response`.
|
|
1051
|
+
|
|
1052
|
+
| Kondisi | Response |
|
|
1053
|
+
|---|---|
|
|
1054
|
+
| Metode bukan POST | `405 Method Not Allowed`, header `allow: POST` |
|
|
1055
|
+
| Header secret tidak cocok | `401 Unauthorized` |
|
|
1056
|
+
| Header `Content-Length` atau body melebihi batas | `413 Payload Too Large` |
|
|
1057
|
+
| JSON tidak valid atau `update_id` bukan integer | `400 Bad Request` untuk update id; exception saat parsing menghasilkan `500` |
|
|
1058
|
+
| `bot.handleUpdate` sukses | `200 OK` dengan body `OK` |
|
|
1059
|
+
| Pengecualian lain | `500 Internal Server Error` dan `onError` dipanggil |
|
|
1060
|
+
|
|
1061
|
+
Nilai default `maxBodyBytes` adalah `1_048_576` bytes. Token rahasia Telegram dibaca dari header `x-telegram-bot-api-secret-token`.
|
|
1062
|
+
|
|
1063
|
+
```ts
|
|
1064
|
+
import { Bot, createWebhookHandler } from "@xbibzlibrary/telebibz";
|
|
1065
|
+
|
|
1066
|
+
const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!);
|
|
1067
|
+
const handler = createWebhookHandler(bot, {
|
|
1068
|
+
secretToken: process.env.TELEGRAM_WEBHOOK_SECRET,
|
|
1069
|
+
});
|
|
1070
|
+
|
|
1071
|
+
export default { fetch: handler };
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
---
|
|
1075
|
+
|
|
1076
|
+
## 11. Percakapan, wizard, formulir, dan menu
|
|
1077
|
+
|
|
1078
|
+
### Percakapan
|
|
1079
|
+
|
|
1080
|
+
```ts
|
|
1081
|
+
interface ConversationState {
|
|
1082
|
+
name: string;
|
|
1083
|
+
step: number;
|
|
1084
|
+
values: Record<string, unknown>;
|
|
1085
|
+
status: "active" | "completed" | "cancelled";
|
|
1086
|
+
updatedAt: number;
|
|
1087
|
+
}
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
#### `ConversationFlow<S>`
|
|
1091
|
+
|
|
1092
|
+
```ts
|
|
1093
|
+
new ConversationFlow(ctx: Context<S>, state: ConversationState)
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
| Method/property | Signature | Deskripsi |
|
|
1097
|
+
|---|---|---|
|
|
1098
|
+
| `ctx` | `Context<S>` | Context update saat ini. |
|
|
1099
|
+
| `state` | `ConversationState` | State percakapan mutable. |
|
|
1100
|
+
| `values` | `Record<string, unknown>` | Alias ke `state.values`. |
|
|
1101
|
+
| `set` | `set<T>(key, value): this` | Menyimpan value dan memperbarui `updatedAt`. |
|
|
1102
|
+
| `get` | `get<T>(key): T \| undefined` | Mengambil typed value. |
|
|
1103
|
+
| `next` | `next(): this` | Menaikkan step satu. |
|
|
1104
|
+
| `previous` | `previous(): this` | Menurunkan step dengan minimum 0. |
|
|
1105
|
+
| `complete` | `complete(): void` | Status menjadi `completed`. |
|
|
1106
|
+
| `cancel` | `cancel(): void` | Status menjadi `cancelled`. |
|
|
1107
|
+
|
|
1108
|
+
#### `ConversationManager<S>`
|
|
1109
|
+
|
|
1110
|
+
```ts
|
|
1111
|
+
new ConversationManager<S>(): ConversationManager<S>
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
| Method | Signature | Deskripsi |
|
|
1115
|
+
|---|---|---|
|
|
1116
|
+
| `start` | `start(key, name, values?): ConversationState` | Membuat atau mengganti conversation state. |
|
|
1117
|
+
| `get` | `get(key): ConversationState \| undefined` | Mengambil state aktif. |
|
|
1118
|
+
| `cancel` | `cancel(key): boolean` | Menandai cancelled jika ada. |
|
|
1119
|
+
| `clearExpired` | `clearExpired(maxAgeMs): number` | Menghapus state yang `updatedAt` lebih lama dari threshold. |
|
|
1120
|
+
| `run` | `run(ctx, key, name, steps): Promise<ConversationState>` | Menjalankan step sesuai `state.step`; jika tidak ada step, status completed. |
|
|
1121
|
+
|
|
1122
|
+
```ts
|
|
1123
|
+
const conversations = new ConversationManager();
|
|
1124
|
+
await conversations.run(ctx, "chat:1", "profile", [
|
|
1125
|
+
async (flow) => {
|
|
1126
|
+
flow.set("name", ctx.message?.text);
|
|
1127
|
+
flow.next();
|
|
1128
|
+
},
|
|
1129
|
+
async (flow) => {
|
|
1130
|
+
await flow.ctx.reply(`Nama: ${flow.get<string>("name")}`);
|
|
1131
|
+
flow.complete();
|
|
1132
|
+
},
|
|
1133
|
+
]);
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
#### `Wizard<S>` dan `WizardStep<S>`
|
|
1137
|
+
|
|
1138
|
+
```ts
|
|
1139
|
+
interface WizardStep<S> {
|
|
1140
|
+
id: string;
|
|
1141
|
+
run: (flow: ConversationFlow<S>) => void | Promise<void>;
|
|
1142
|
+
optional?: boolean;
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
new Wizard<S>()
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
| Method/property | Deskripsi |
|
|
1149
|
+
|---|---|
|
|
1150
|
+
| `step(definition)` | Menambahkan step dan mengembalikan wizard. `optional` disimpan dalam definition tetapi belum diproses khusus oleh runner. |
|
|
1151
|
+
| `run(ctx, key, manager?)` | Menjalankan step wizard melalui `ConversationManager` dengan name `"wizard"`. |
|
|
1152
|
+
| `steps` | Daftar step read-only. |
|
|
1153
|
+
|
|
1154
|
+
### Formulir
|
|
1155
|
+
|
|
1156
|
+
```ts
|
|
1157
|
+
interface ValidationIssue {
|
|
1158
|
+
path: string;
|
|
1159
|
+
message: string;
|
|
1160
|
+
code?: string;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
interface Field<T> {
|
|
1164
|
+
name: string;
|
|
1165
|
+
parse: (input: unknown) => T;
|
|
1166
|
+
validate?: (value: T) => string | undefined | Promise<string | undefined>;
|
|
1167
|
+
transform?: (value: T) => T | Promise<T>;
|
|
1168
|
+
required?: boolean;
|
|
1169
|
+
}
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
#### `Form<T>`
|
|
1173
|
+
|
|
1174
|
+
```ts
|
|
1175
|
+
new Form<T extends Record<string, unknown>>(): Form<T>
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
| Method | Deskripsi |
|
|
1179
|
+
|---|---|
|
|
1180
|
+
| `field(definition)` | Mendaftarkan field typed berdasarkan `name`. |
|
|
1181
|
+
| `parse(input)` | Memproses seluruh field. Return union success atau issues. Urutan: required check, parse, transform, validate. |
|
|
1182
|
+
| `reset()` | Menghapus data hasil parse yang tersimpan internal. |
|
|
1183
|
+
|
|
1184
|
+
Hasil parse:
|
|
1185
|
+
|
|
1186
|
+
```ts
|
|
1187
|
+
type FormResult<T> =
|
|
1188
|
+
| { success: true; data: T }
|
|
1189
|
+
| { success: false; issues: ValidationIssue[] };
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
Issue memakai code `required`, `parse`, atau `invalid`.
|
|
1193
|
+
|
|
1194
|
+
#### `validators`
|
|
1195
|
+
|
|
1196
|
+
| Validator | Input | Hasil/eror |
|
|
1197
|
+
|---|---|---|
|
|
1198
|
+
| `validators.string` | `unknown` | String; selain itu `TypeError("Expected string")`. |
|
|
1199
|
+
| `validators.number` | `unknown` | Number finite, termasuk numeric string; selain itu `TypeError("Expected number")`. |
|
|
1200
|
+
| `validators.integer` | `unknown` | Integer; selain itu `TypeError("Expected integer")`. |
|
|
1201
|
+
| `validators.email` | `unknown` | String dengan pola email sederhana; selain itu `TypeError("Expected email")`. |
|
|
1202
|
+
| `validators.url` | `unknown` | String yang diterima constructor `URL`; selain itu `TypeError("Expected URL")`. |
|
|
1203
|
+
|
|
1204
|
+
### Pagination dan menu
|
|
1205
|
+
|
|
1206
|
+
#### `Page<T>`
|
|
1207
|
+
|
|
1208
|
+
```ts
|
|
1209
|
+
interface Page<T> {
|
|
1210
|
+
items: T[];
|
|
1211
|
+
page: number;
|
|
1212
|
+
pageCount: number;
|
|
1213
|
+
hasPrevious: boolean;
|
|
1214
|
+
hasNext: boolean;
|
|
1215
|
+
}
|
|
1216
|
+
```
|
|
1217
|
+
|
|
1218
|
+
#### `paginate(items, page, pageSize)`
|
|
1219
|
+
|
|
1220
|
+
```ts
|
|
1221
|
+
paginate<T>(
|
|
1222
|
+
items: readonly T[],
|
|
1223
|
+
page: number,
|
|
1224
|
+
pageSize: number,
|
|
1225
|
+
): Page<T>
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
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`.
|
|
1229
|
+
|
|
1230
|
+
#### `paginationButtons(page, prefix)`
|
|
1231
|
+
|
|
1232
|
+
```ts
|
|
1233
|
+
paginationButtons(
|
|
1234
|
+
page: Page<unknown>,
|
|
1235
|
+
prefix: string,
|
|
1236
|
+
): InlineKeyboardButton[]
|
|
1237
|
+
```
|
|
1238
|
+
|
|
1239
|
+
Menghasilkan button `Previous`, indicator `${page + 1}/${pageCount}` dengan callback `${prefix}:noop`, dan `Next` sesuai flag page.
|
|
1240
|
+
|
|
1241
|
+
#### `MenuItem`
|
|
1242
|
+
|
|
1243
|
+
```ts
|
|
1244
|
+
interface MenuItem {
|
|
1245
|
+
id: string;
|
|
1246
|
+
label: string;
|
|
1247
|
+
callbackData?: string;
|
|
1248
|
+
url?: string;
|
|
1249
|
+
visible?: boolean | (() => boolean | Promise<boolean>);
|
|
1250
|
+
permission?: string;
|
|
1251
|
+
}
|
|
1252
|
+
```
|
|
1253
|
+
|
|
1254
|
+
#### `Menu`
|
|
1255
|
+
|
|
1256
|
+
```ts
|
|
1257
|
+
new Menu(id: string): Menu
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
| Method/property | Deskripsi |
|
|
1261
|
+
|---|---|
|
|
1262
|
+
| `item(item)` | Menambah item dan mendukung chaining. |
|
|
1263
|
+
| `breadcrumb(label)` | Menambah label breadcrumb. |
|
|
1264
|
+
| `build()` | Menunggu predicate visibility, melewati item invisible, lalu menghasilkan `InlineKeyboard`. URL diprioritaskan dibanding callback. |
|
|
1265
|
+
| `breadcrumbs` | Array breadcrumb read-only. |
|
|
1266
|
+
|
|
1267
|
+
`permission` hanya disimpan sebagai metadata item; `Menu.build()` tidak melakukan authorization otomatis.
|
|
1268
|
+
|
|
1269
|
+
---
|
|
1270
|
+
|
|
1271
|
+
## 12. Gerbang persetujuan
|
|
1272
|
+
|
|
1273
|
+
Gerbang persetujuan mengirim notifikasi kepada owner ketika bot baru pertama kali menggunakan library. Pesan default memakai label `Dev Gantenggg`, menyertakan bot ID/username dan owner ID, lalu menyediakan tombol `Izinkan` dan `Tidak Diizinkan`.
|
|
1274
|
+
|
|
1275
|
+
### `ApprovalOptions`
|
|
1276
|
+
|
|
1277
|
+
| Properti | Tipe | Default | Deskripsi |
|
|
1278
|
+
|---|---|---:|---|
|
|
1279
|
+
| `ownerChatId` | `ChatId` | wajib | Chat tujuan notifikasi. |
|
|
1280
|
+
| `ownerUserId` | `number` | wajib | User ID yang boleh menekan tombol. |
|
|
1281
|
+
| `ownerLabel` | `string` | `Dev Gantenggg` | Label pada notifikasi. |
|
|
1282
|
+
| `requireApproval` | `boolean` | `true` | `false` menonaktifkan gate. |
|
|
1283
|
+
| `notificationCooldownMs` | `number` | `600000` | Cooldown notifikasi pending. |
|
|
1284
|
+
| `store` | `ApprovalStore` | `MemoryApprovalStore` | Penyimpanan approval custom. |
|
|
1285
|
+
|
|
1286
|
+
### Tipe persetujuan
|
|
1287
|
+
|
|
1288
|
+
```ts
|
|
1289
|
+
type ApprovalStatus = "pending" | "approved" | "denied";
|
|
1290
|
+
|
|
1291
|
+
interface ApprovalRecord {
|
|
1292
|
+
key: string;
|
|
1293
|
+
botId: number;
|
|
1294
|
+
botUsername?: string;
|
|
1295
|
+
ownerUserId?: number;
|
|
1296
|
+
status: ApprovalStatus;
|
|
1297
|
+
nonce: string;
|
|
1298
|
+
requestedAt: number;
|
|
1299
|
+
decidedAt?: number;
|
|
1300
|
+
decidedBy?: number;
|
|
1301
|
+
notificationMessageId?: number;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
interface ApprovalIdentity {
|
|
1305
|
+
bot: User;
|
|
1306
|
+
configuredOwnerUserId?: number;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
interface ApprovalCheck {
|
|
1310
|
+
allowed: boolean;
|
|
1311
|
+
status: ApprovalStatus | "disabled";
|
|
1312
|
+
record?: ApprovalRecord;
|
|
1313
|
+
}
|
|
1314
|
+
```
|
|
1315
|
+
|
|
1316
|
+
### `ApprovalStore`
|
|
1317
|
+
|
|
1318
|
+
```ts
|
|
1319
|
+
interface ApprovalStore {
|
|
1320
|
+
get(key: string): Promise<ApprovalRecord | undefined>;
|
|
1321
|
+
set(key: string, record: ApprovalRecord): Promise<void>;
|
|
1322
|
+
delete?(key: string): Promise<boolean>;
|
|
1323
|
+
}
|
|
1324
|
+
```
|
|
1325
|
+
|
|
1326
|
+
### `MemoryApprovalStore`
|
|
1327
|
+
|
|
1328
|
+
```ts
|
|
1329
|
+
new MemoryApprovalStore(): MemoryApprovalStore
|
|
1330
|
+
```
|
|
1331
|
+
|
|
1332
|
+
Penyimpanan in-memory yang mengembalikan salinan record saat `get` dan `set`.
|
|
1333
|
+
|
|
1334
|
+
### `ApprovalGate`
|
|
1335
|
+
|
|
1336
|
+
```ts
|
|
1337
|
+
new ApprovalGate(api: ApiClient, options: ApprovalOptions): ApprovalGate
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
| Method | Signature | Deskripsi |
|
|
1341
|
+
|---|---|---|
|
|
1342
|
+
| `check` | `check(identity): Promise<ApprovalCheck>` | Mengembalikan approved jika record berstatus approved; mengirim request baru jika belum ada atau cooldown habis. |
|
|
1343
|
+
| `handleCallback` | `handleCallback(callback): Promise<{ handled: boolean; status?: ApprovalStatus }>` | Memvalidasi nonce dan owner, lalu melakukan approve/deny. Callback yang tidak valid atau bukan approval dikembalikan sebagai `handled: false`. |
|
|
1344
|
+
| `isAllowed` | `isAllowed(botId): Promise<boolean>` | True jika approved atau gate dinonaktifkan. |
|
|
1345
|
+
| `revoke` | `revoke(botId): Promise<boolean>` | Menghapus record jika store mendukung delete. |
|
|
1346
|
+
|
|
1347
|
+
Callback hanya dapat diputuskan oleh `ownerUserId` yang dikonfigurasi. Nonce acak 16 karakter heksadesimal mencegah callback lama digunakan kembali. Callback kadaluarsa menghasilkan alert kedaluwarsa.
|
|
1348
|
+
|
|
1349
|
+
```ts
|
|
1350
|
+
const bot = new Bot({
|
|
1351
|
+
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
1352
|
+
approval: {
|
|
1353
|
+
ownerChatId: 7377733784,
|
|
1354
|
+
ownerUserId: 7377733784,
|
|
1355
|
+
ownerLabel: "Dev Gantenggg",
|
|
1356
|
+
},
|
|
1357
|
+
});
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
---
|
|
1361
|
+
|
|
1362
|
+
## 13. Utilitas Teks
|
|
1363
|
+
|
|
1364
|
+
### `escapeMarkdownV2(value)`
|
|
1365
|
+
|
|
1366
|
+
```ts
|
|
1367
|
+
escapeMarkdownV2(value: string): string
|
|
1368
|
+
```
|
|
1369
|
+
|
|
1370
|
+
Meng-escape karakter MarkdownV2 Telegram: `\\_ * [ ] ( ) ~ ` > # + - = | { } . !`.
|
|
1371
|
+
|
|
1372
|
+
### `escapeHtml(value)`
|
|
1373
|
+
|
|
1374
|
+
```ts
|
|
1375
|
+
escapeHtml(value: string): string
|
|
1376
|
+
```
|
|
1377
|
+
|
|
1378
|
+
Mengubah `&`, `<`, `>`, dan `"` menjadi HTML entities.
|
|
1379
|
+
|
|
1380
|
+
### `md`
|
|
1381
|
+
|
|
1382
|
+
Object helper MarkdownV2 berikut tersedia:
|
|
1383
|
+
|
|
1384
|
+
| Method | Output konseptual |
|
|
1385
|
+
|---|---|
|
|
1386
|
+
| `md.bold(value)` | `*escaped value*` |
|
|
1387
|
+
| `md.italic(value)` | `_escaped value_` |
|
|
1388
|
+
| `md.link(label, url)` | `[escaped label](escaped url)` |
|
|
1389
|
+
| `md.code(value)` | Inline code dengan backtick yang di-escape. |
|
|
1390
|
+
| `md.pre(value, language?)` | Code block dengan language label opsional. |
|
|
1391
|
+
| `md.escape(value)` | Alias `escapeMarkdownV2`. |
|
|
1392
|
+
|
|
1393
|
+
### `splitMessage(text, options?)`
|
|
1394
|
+
|
|
1395
|
+
```ts
|
|
1396
|
+
splitMessage(
|
|
1397
|
+
text: string,
|
|
1398
|
+
options?: {
|
|
1399
|
+
limit?: number;
|
|
1400
|
+
parseMode?: "Markdown" | "MarkdownV2" | "HTML";
|
|
1401
|
+
},
|
|
1402
|
+
): string[]
|
|
1403
|
+
```
|
|
1404
|
+
|
|
1405
|
+
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.
|
|
1406
|
+
|
|
1407
|
+
Limit kurang dari 1 akan melempar `RangeError`.
|
|
1408
|
+
|
|
1409
|
+
### `splitCaption(text)`
|
|
1410
|
+
|
|
1411
|
+
```ts
|
|
1412
|
+
splitCaption(text: string): string[]
|
|
1413
|
+
```
|
|
1414
|
+
|
|
1415
|
+
Alias `splitMessage(text, { limit: 1024 })`.
|
|
1416
|
+
|
|
1417
|
+
### `template(templateText, values)`
|
|
1418
|
+
|
|
1419
|
+
```ts
|
|
1420
|
+
template(
|
|
1421
|
+
templateText: string,
|
|
1422
|
+
values: Record<string, unknown>,
|
|
1423
|
+
): string
|
|
1424
|
+
```
|
|
1425
|
+
|
|
1426
|
+
Mengganti placeholder `{{ key }}` dan nested path seperti `{{ user.name }}`. Nilai `null` atau `undefined` diganti string kosong; nilai lain dikonversi dengan `String()`.
|
|
1427
|
+
|
|
1428
|
+
```ts
|
|
1429
|
+
template("Halo {{ user.name }}", { user: { name: "Ayu" } });
|
|
1430
|
+
// "Halo Ayu"
|
|
1431
|
+
```
|
|
1432
|
+
|
|
1433
|
+
---
|
|
1434
|
+
|
|
1435
|
+
## 14. Testing utilities
|
|
1436
|
+
|
|
1437
|
+
Import dari `@xbibzlibrary/telebibz/testing` atau root package.
|
|
1438
|
+
|
|
1439
|
+
### `MockTransport`
|
|
1440
|
+
|
|
1441
|
+
```ts
|
|
1442
|
+
new MockTransport(): MockTransport
|
|
1443
|
+
```
|
|
1444
|
+
|
|
1445
|
+
| API | Deskripsi |
|
|
1446
|
+
|---|---|
|
|
1447
|
+
| `calls` | Array semua `TransportRequest` yang diterima. |
|
|
1448
|
+
| `respond(method, response)` | Mengatur response statis atau callback berdasarkan payload dan mengembalikan transport. |
|
|
1449
|
+
| `request(request)` | Mencatat request dan mengembalikan response mock. Response default adalah `{ ok: true, result: true }`. |
|
|
1450
|
+
|
|
1451
|
+
Status mock adalah `200` bila `ok: true`, atau `error_code`/`500` bila `ok: false`.
|
|
1452
|
+
|
|
1453
|
+
```ts
|
|
1454
|
+
const transport = new MockTransport()
|
|
1455
|
+
.respond("getMe", {
|
|
1456
|
+
ok: true,
|
|
1457
|
+
result: { id: 1, is_bot: true, first_name: "Test" },
|
|
1458
|
+
});
|
|
1459
|
+
```
|
|
1460
|
+
|
|
1461
|
+
### `createMockUpdate(overrides?)`
|
|
1462
|
+
|
|
1463
|
+
```ts
|
|
1464
|
+
createMockUpdate(overrides?: Partial<Update>): Update
|
|
1465
|
+
```
|
|
1466
|
+
|
|
1467
|
+
Membuat update message default dengan `update_id: 1`, chat private id `1`, user id `2`, dan text `/start`. Object `overrides` digabung shallow dengan default.
|
|
1468
|
+
|
|
1469
|
+
### `createTestBot()`
|
|
1470
|
+
|
|
1471
|
+
```ts
|
|
1472
|
+
createTestBot(): { bot: Bot; transport: MockTransport }
|
|
1473
|
+
```
|
|
1474
|
+
|
|
1475
|
+
Membuat bot dengan token test `123456:TEST_TOKEN`, mock `getMe()` yang menghasilkan bot id `99`, dan transport yang dapat diperiksa melalui `transport.calls`.
|
|
1476
|
+
|
|
1477
|
+
### `createMockContext(bot, update?)`
|
|
1478
|
+
|
|
1479
|
+
```ts
|
|
1480
|
+
createMockContext(
|
|
1481
|
+
bot: Bot,
|
|
1482
|
+
update?: Update,
|
|
1483
|
+
): Context
|
|
1484
|
+
```
|
|
1485
|
+
|
|
1486
|
+
Membuat context menggunakan API bot, session kosong, dan services kosong.
|
|
1487
|
+
|
|
1488
|
+
---
|
|
1489
|
+
|
|
1490
|
+
|
|
1491
|
+
## 15. Namespace metode Telegram yang dihasilkan
|
|
1492
|
+
|
|
1493
|
+
`generated/api.ts` adalah source internal generator yang mendefinisikan:
|
|
1494
|
+
|
|
1495
|
+
```ts
|
|
1496
|
+
const TELEGRAM_API_VERSION = "10.2";
|
|
1497
|
+
const TELEGRAM_METHOD_NAMES: readonly string[];
|
|
1498
|
+
type TelegramMethodName = typeof TELEGRAM_METHOD_NAMES[number];
|
|
1499
|
+
type GeneratedMethodSpec = {
|
|
1500
|
+
params: Record<string, unknown>;
|
|
1501
|
+
result: unknown;
|
|
1502
|
+
};
|
|
1503
|
+
type GeneratedTelegramMethodMap = {
|
|
1504
|
+
[K in TelegramMethodName]: GeneratedMethodSpec;
|
|
1505
|
+
};
|
|
1506
|
+
const GENERATED_METHODS: Record<TelegramMethodName, TelegramMethodName>;
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
`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.
|
|
1510
|
+
|
|
1511
|
+
Untuk daftar canonical tanpa pengelompokan, nama method yang tersedia pada generated runtime namespace adalah:
|
|
1512
|
+
|
|
1513
|
+
```text
|
|
1514
|
+
addStickerToSet,
|
|
1515
|
+
answerCallbackQuery,
|
|
1516
|
+
answerChatJoinRequestQuery,
|
|
1517
|
+
answerGuestQuery,
|
|
1518
|
+
answerInlineQuery,
|
|
1519
|
+
answerPreCheckoutQuery,
|
|
1520
|
+
answerShippingQuery,
|
|
1521
|
+
answerWebAppQuery,
|
|
1522
|
+
approveChatJoinRequest,
|
|
1523
|
+
approveSuggestedPost,
|
|
1524
|
+
banChatMember,
|
|
1525
|
+
banChatSenderChat,
|
|
1526
|
+
close,
|
|
1527
|
+
closeForumTopic,
|
|
1528
|
+
closeGeneralForumTopic,
|
|
1529
|
+
convertGiftToStars,
|
|
1530
|
+
copyMessage,
|
|
1531
|
+
copyMessages,
|
|
1532
|
+
createChatInviteLink,
|
|
1533
|
+
createChatSubscriptionInviteLink,
|
|
1534
|
+
createForumTopic,
|
|
1535
|
+
createInvoiceLink,
|
|
1536
|
+
createNewStickerSet,
|
|
1537
|
+
declineChatJoinRequest,
|
|
1538
|
+
declineSuggestedPost,
|
|
1539
|
+
deleteAllMessageReactions,
|
|
1540
|
+
deleteBusinessMessages,
|
|
1541
|
+
deleteChatPhoto,
|
|
1542
|
+
deleteChatStickerSet,
|
|
1543
|
+
deleteEphemeralMessage,
|
|
1544
|
+
deleteForumTopic,
|
|
1545
|
+
deleteMessage,
|
|
1546
|
+
deleteMessageReaction,
|
|
1547
|
+
deleteMessages,
|
|
1548
|
+
deleteMyCommands,
|
|
1549
|
+
deleteStickerFromSet,
|
|
1550
|
+
deleteStickerSet,
|
|
1551
|
+
deleteStory,
|
|
1552
|
+
deleteWebhook,
|
|
1553
|
+
editChatInviteLink,
|
|
1554
|
+
editChatSubscriptionInviteLink,
|
|
1555
|
+
editEphemeralMessageCaption,
|
|
1556
|
+
editEphemeralMessageMedia,
|
|
1557
|
+
editEphemeralMessageReplyMarkup,
|
|
1558
|
+
editEphemeralMessageText,
|
|
1559
|
+
editForumTopic,
|
|
1560
|
+
editGeneralForumTopic,
|
|
1561
|
+
editMessageCaption,
|
|
1562
|
+
editMessageChecklist,
|
|
1563
|
+
editMessageLiveLocation,
|
|
1564
|
+
editMessageMedia,
|
|
1565
|
+
editMessageReplyMarkup,
|
|
1566
|
+
editMessageText,
|
|
1567
|
+
editStory,
|
|
1568
|
+
editUserStarSubscription,
|
|
1569
|
+
exportChatInviteLink,
|
|
1570
|
+
forwardMessage,
|
|
1571
|
+
forwardMessages,
|
|
1572
|
+
getAvailableGifts,
|
|
1573
|
+
getBusinessAccountGifts,
|
|
1574
|
+
getBusinessAccountStarBalance,
|
|
1575
|
+
getBusinessConnection,
|
|
1576
|
+
getChat,
|
|
1577
|
+
getChatAdministrators,
|
|
1578
|
+
getChatGifts,
|
|
1579
|
+
getChatMember,
|
|
1580
|
+
getChatMemberCount,
|
|
1581
|
+
getChatMenuButton,
|
|
1582
|
+
getCustomEmojiStickers,
|
|
1583
|
+
getFile,
|
|
1584
|
+
getForumTopicIconStickers,
|
|
1585
|
+
getGameHighScores,
|
|
1586
|
+
getManagedBotAccessSettings,
|
|
1587
|
+
getManagedBotToken,
|
|
1588
|
+
getMe,
|
|
1589
|
+
getMyCommands,
|
|
1590
|
+
getMyDefaultAdministratorRights,
|
|
1591
|
+
getMyDescription,
|
|
1592
|
+
getMyName,
|
|
1593
|
+
getMyShortDescription,
|
|
1594
|
+
getMyStarBalance,
|
|
1595
|
+
getStarTransactions,
|
|
1596
|
+
getStickerSet,
|
|
1597
|
+
getUpdates,
|
|
1598
|
+
getUserChatBoosts,
|
|
1599
|
+
getUserGifts,
|
|
1600
|
+
getUserPersonalChatMessages,
|
|
1601
|
+
getUserProfileAudios,
|
|
1602
|
+
getUserProfilePhotos,
|
|
1603
|
+
getWebhookInfo,
|
|
1604
|
+
giftPremiumSubscription,
|
|
1605
|
+
hideGeneralForumTopic,
|
|
1606
|
+
leaveChat,
|
|
1607
|
+
logOut,
|
|
1608
|
+
pinChatMessage,
|
|
1609
|
+
postStory,
|
|
1610
|
+
promoteChatMember,
|
|
1611
|
+
readBusinessMessage,
|
|
1612
|
+
refundStarPayment,
|
|
1613
|
+
removeBusinessAccountProfilePhoto,
|
|
1614
|
+
removeChatVerification,
|
|
1615
|
+
removeMyProfilePhoto,
|
|
1616
|
+
removeUserVerification,
|
|
1617
|
+
reopenForumTopic,
|
|
1618
|
+
reopenGeneralForumTopic,
|
|
1619
|
+
replaceManagedBotToken,
|
|
1620
|
+
replaceStickerInSet,
|
|
1621
|
+
repostStory,
|
|
1622
|
+
restrictChatMember,
|
|
1623
|
+
revokeChatInviteLink,
|
|
1624
|
+
savePreparedInlineMessage,
|
|
1625
|
+
savePreparedKeyboardButton,
|
|
1626
|
+
sendAnimation,
|
|
1627
|
+
sendAudio,
|
|
1628
|
+
sendChatAction,
|
|
1629
|
+
sendChatJoinRequestWebApp,
|
|
1630
|
+
sendChecklist,
|
|
1631
|
+
sendContact,
|
|
1632
|
+
sendDice,
|
|
1633
|
+
sendDocument,
|
|
1634
|
+
sendGame,
|
|
1635
|
+
sendGift,
|
|
1636
|
+
sendInvoice,
|
|
1637
|
+
sendLivePhoto,
|
|
1638
|
+
sendLocation,
|
|
1639
|
+
sendMediaGroup,
|
|
1640
|
+
sendMessage,
|
|
1641
|
+
sendMessageDraft,
|
|
1642
|
+
sendPaidMedia,
|
|
1643
|
+
sendPhoto,
|
|
1644
|
+
sendPoll,
|
|
1645
|
+
sendRichMessage,
|
|
1646
|
+
sendRichMessageDraft,
|
|
1647
|
+
sendSticker,
|
|
1648
|
+
sendVenue,
|
|
1649
|
+
sendVideo,
|
|
1650
|
+
sendVideoNote,
|
|
1651
|
+
sendVoice,
|
|
1652
|
+
setBusinessAccountBio,
|
|
1653
|
+
setBusinessAccountGiftSettings,
|
|
1654
|
+
setBusinessAccountName,
|
|
1655
|
+
setBusinessAccountProfilePhoto,
|
|
1656
|
+
setBusinessAccountUsername,
|
|
1657
|
+
setChatAdministratorCustomTitle,
|
|
1658
|
+
setChatDescription,
|
|
1659
|
+
setChatMemberTag,
|
|
1660
|
+
setChatMenuButton,
|
|
1661
|
+
setChatPermissions,
|
|
1662
|
+
setChatPhoto,
|
|
1663
|
+
setChatStickerSet,
|
|
1664
|
+
setChatTitle,
|
|
1665
|
+
setCustomEmojiStickerSetThumbnail,
|
|
1666
|
+
setGameScore,
|
|
1667
|
+
setManagedBotAccessSettings,
|
|
1668
|
+
setMessageReaction,
|
|
1669
|
+
setMyCommands,
|
|
1670
|
+
setMyDefaultAdministratorRights,
|
|
1671
|
+
setMyDescription,
|
|
1672
|
+
setMyName,
|
|
1673
|
+
setMyProfilePhoto,
|
|
1674
|
+
setMyShortDescription,
|
|
1675
|
+
setPassportDataErrors,
|
|
1676
|
+
setStickerEmojiList,
|
|
1677
|
+
setStickerKeywords,
|
|
1678
|
+
setStickerMaskPosition,
|
|
1679
|
+
setStickerPositionInSet,
|
|
1680
|
+
setStickerSetThumbnail,
|
|
1681
|
+
setStickerSetTitle,
|
|
1682
|
+
setUserEmojiStatus,
|
|
1683
|
+
setWebhook,
|
|
1684
|
+
stopMessageLiveLocation,
|
|
1685
|
+
stopPoll,
|
|
1686
|
+
transferBusinessAccountStars,
|
|
1687
|
+
transferGift,
|
|
1688
|
+
unbanChatMember,
|
|
1689
|
+
unbanChatSenderChat,
|
|
1690
|
+
unhideGeneralForumTopic,
|
|
1691
|
+
unpinAllChatMessages,
|
|
1692
|
+
unpinAllForumTopicMessages,
|
|
1693
|
+
unpinAllGeneralForumTopicMessages,
|
|
1694
|
+
unpinChatMessage,
|
|
1695
|
+
upgradeGift,
|
|
1696
|
+
uploadStickerFile,
|
|
1697
|
+
verifyChat
|
|
1698
|
+
```
|
|
1699
|
+
|
|
1700
|
+
> Daftar di atas mengikuti generated source. Jika Telegram menambahkan method baru, jalankan `npm run update:telegram` atau `telebibz generate` setelah schema diperbarui.
|
|
1701
|
+
|
|
1702
|
+
---
|
|
1703
|
+
|
|
1704
|
+
## 16. CLI
|
|
1705
|
+
|
|
1706
|
+
Binary package adalah `telebibz`.
|
|
1707
|
+
|
|
1708
|
+
```bash
|
|
1709
|
+
npx telebibz <command>
|
|
1710
|
+
```
|
|
1711
|
+
|
|
1712
|
+
| Command | Perilaku |
|
|
1713
|
+
|---|---|
|
|
1714
|
+
| `telebibz init [directory]` | Membuat directory, `index.ts` minimal, dan `.env.example`. Default directory `my-telebibz-bot`. |
|
|
1715
|
+
| `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. |
|
|
1716
|
+
| `telebibz generate` | Menjalankan generator method dari `scripts/generate-api.mjs`. |
|
|
1717
|
+
| `telebibz build` | Menjalankan `npm run build`. |
|
|
1718
|
+
| `telebibz test` | Menjalankan `npm test`. |
|
|
1719
|
+
| `telebibz webhook` | Memeriksa `TELEGRAM_BOT_TOKEN`, memakai `TELEGRAM_WEBHOOK_SECRET` bila ada, membuat handler, dan mencetak kesiapan. Command ini tidak membuat HTTP server. |
|
|
1720
|
+
| `telebibz inspect` | Menampilkan cwd dan Node version. |
|
|
1721
|
+
| tanpa command | Menampilkan daftar command bantuan. |
|
|
1722
|
+
|
|
1723
|
+
Environment variable yang dipakai CLI adalah `TELEGRAM_BOT_TOKEN` dan `TELEGRAM_WEBHOOK_SECRET`.
|
|
1724
|
+
|
|
1725
|
+
---
|
|
1726
|
+
|
|
1727
|
+
## 17. Tipe Telegram utama
|
|
1728
|
+
|
|
1729
|
+
Paket mengekspor tipe data yang paling sering digunakan secara langsung.
|
|
1730
|
+
|
|
1731
|
+
| Tipe | Isi penting |
|
|
1732
|
+
|---|---|
|
|
1733
|
+
| `User` | ID, penanda bot, nama, username, bahasa, dan penanda kemampuan. |
|
|
1734
|
+
| `Chat` | ID, tipe, title/username/nama, penanda forum/pesan langsung. |
|
|
1735
|
+
| `Message` | ID, tanggal, chat, pengirim, teks/caption, entity, reply, markup, plus index signature untuk field Telegram tambahan. |
|
|
1736
|
+
| `Update` | Semua field update yang didukung sumber, termasuk message, callback, inline, poll, member, join request, reaction, boost, business, dan field ekstensi. |
|
|
1737
|
+
| `CallbackQuery` | ID, from, message/inline message id, chat instance, data. |
|
|
1738
|
+
| `InlineQuery` | ID, from, query, offset, tipe chat, lokasi. |
|
|
1739
|
+
| `Poll`, `PollAnswer` | Data poll dan jawaban. |
|
|
1740
|
+
| `ChatMemberUpdated`, `ChatJoinRequest` | Perubahan anggota dan permintaan bergabung. |
|
|
1741
|
+
| `InlineKeyboardMarkup`, `ReplyKeyboardMarkup`, `ReplyKeyboardRemove`, `ForceReply` | Bentuk reply markup Telegram. |
|
|
1742
|
+
| `MessageEntity`, `ReplyParameters`, `LinkPreviewOptions` | Metadata entity, reply, dan pratinjau tautan. |
|
|
1743
|
+
| `BotCommand`, `BotCommandScope`, `WebhookInfo`, `File`, `UserProfilePhotos`, `ChatMember`, `ChatAdministratorRights` | Tipe hasil/parameter untuk helper API. |
|
|
1744
|
+
|
|
1745
|
+
---
|
|
1746
|
+
|
|
1747
|
+
## 18. Persistence, cron lengkap, menu, dan deklarasi Telegram lengkap
|
|
1748
|
+
|
|
1749
|
+
### Adapter storage persistent
|
|
1750
|
+
|
|
1751
|
+
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.
|
|
1752
|
+
|
|
1753
|
+
| Class | Konstruktor | Tujuan |
|
|
1754
|
+
|---|---|---|
|
|
1755
|
+
| `MemoryStorage<K, V>` | `new MemoryStorage()` | Storage in-memory cepat dengan TTL dan `update()` atomik per key. |
|
|
1756
|
+
| `JsonFileStorage<V>` | `new JsonFileStorage(filePath)` | Persistensi JSON atomik untuk deployment single-process. |
|
|
1757
|
+
| `RedisStorage<V>` | `new RedisStorage(client, prefix?)` | Storage Redis melalui `RedisLikeClient`, termasuk TTL dan namespace. |
|
|
1758
|
+
| `SqlStorage<V>` | `new SqlStorage(driver)` | Storage SQL melalui `SqlStorageDriver` milik aplikasi. |
|
|
1759
|
+
| `MongoStorage<V>` | `new MongoStorage(collection)` | Storage Mongo melalui `MongoStorageCollection` milik aplikasi. |
|
|
1760
|
+
| `StorageApprovalStore` | `new StorageApprovalStore(storage)` | Record approval persistent dari `Storage<string, ApprovalRecord>` apa pun. |
|
|
1761
|
+
|
|
1762
|
+
`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.
|
|
1763
|
+
|
|
1764
|
+
```ts
|
|
1765
|
+
const session = new JsonFileStorage<Record<string, unknown>>("./data/sessions.json");
|
|
1766
|
+
const bot = new Bot({ token: process.env.TELEGRAM_BOT_TOKEN!, session });
|
|
1767
|
+
```
|
|
1768
|
+
|
|
1769
|
+
### Cron lima field lengkap
|
|
1770
|
+
|
|
1771
|
+
`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.
|
|
1772
|
+
|
|
1773
|
+
### Mode matching router
|
|
1774
|
+
|
|
1775
|
+
`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.
|
|
1776
|
+
|
|
1777
|
+
### MenuController dan permission
|
|
1778
|
+
|
|
1779
|
+
`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.
|
|
1780
|
+
|
|
1781
|
+
### Namespace deklarasi Telegram lengkap
|
|
1782
|
+
|
|
1783
|
+
Package memvendorkan declaration Telegram berlisensi MIT dan mengeksposnya sebagai type-only export melalui `TelegramTypes`, serta alias seperti `TelegramUser`, `TelegramMessage`, `TelegramUpdate`, dan `TelegramApiMethods`. 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.
|
|
1784
|
+
|
|
1785
|
+
---
|
|
1786
|
+
|
|
1787
|
+
## 19. Kompatibilitas dan batasan yang perlu diketahui
|
|
1788
|
+
|
|
1789
|
+
Perpustakaan menargetkan Node.js `>=20`, menggunakan ESM sebagai module utama, serta menyediakan build CommonJS. Webhook membutuhkan runtime yang menyediakan Web `Request`, `Response`, `Headers`, `FormData`, `Blob`, dan `AbortController`; Node.js modern menyediakannya secara native.
|
|
1790
|
+
|
|
1791
|
+
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.
|
|
1792
|
+
|
|
1793
|
+
State persetujuan dan primitive in-memory lainnya hilang saat proses dimulai ulang kecuali aplikasi menyediakan adapter persistent. `BotOptions.session` menerima kontrak generic `Storage<string, S>`, dan `ApprovalGate` dapat memakai `StorageApprovalStore` atau `ApprovalStore` kustom.
|
|
1794
|
+
|
|
1795
|
+
---
|
|
1796
|
+
|
|
1797
|
+
## Referensi
|
|
1798
|
+
|
|
1799
|
+
[1]: https://core.telegram.org/bots/api "Telegram Bot API — dokumentasi resmi"
|
|
1800
|
+
[2]: https://www.npmjs.com/package/@xbibzlibrary/telebibz "@xbibzlibrary/telebibz di npm"
|