@xbibzlibrary/telebibz 0.4.5 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -138
- package/LICENSE +1 -1
- package/NOTICE.md +9 -4
- package/README.md +173 -256
- package/examples/01-quickstart.js +13 -0
- package/examples/02-menu-tombol.js +22 -0
- package/examples/03-wizard.js +29 -0
- package/examples/04-broadcast.js +25 -0
- package/examples/05-kirim-file.js +19 -0
- package/examples/06-menu.js +35 -0
- package/examples/07-inline-query.js +18 -0
- package/index.d.ts +81 -0
- package/index.js +42 -0
- package/lib/api.js +154 -0
- package/lib/broadcast.js +35 -0
- package/lib/composer.js +174 -0
- package/lib/context.js +190 -0
- package/lib/errors.js +35 -0
- package/lib/file.js +41 -0
- package/lib/inline-query.js +28 -0
- package/lib/keyboard.js +83 -0
- package/lib/logger.js +38 -0
- package/lib/menus.js +90 -0
- package/lib/net.js +115 -0
- package/lib/ratelimit.js +61 -0
- package/lib/runner.js +45 -0
- package/lib/session.js +36 -0
- package/lib/telebibz.js +190 -0
- package/lib/wizard.js +81 -0
- package/package.json +35 -97
- package/test/all.test.js +334 -0
- package/CODE_OF_CONDUCT.md +0 -37
- package/CONTRIBUTING.md +0 -59
- package/CONTRIBUTION_RULES.md +0 -41
- package/GOVERNANCE.md +0 -47
- package/README.id.md +0 -306
- package/README.zh-CN.md +0 -306
- package/RELEASE_AUTOMATION.md +0 -78
- package/RELEASE_POLICY.md +0 -32
- package/SECURITY.md +0 -47
- package/SHOWCASE.md +0 -29
- package/SUPPORT.md +0 -30
- package/assets/readme-preview.html +0 -75
- package/assets/telebibz-logo.png +0 -0
- package/assets/telebibz-readme-preview.png +0 -0
- package/bin/telebibz.mjs +0 -3
- package/dist/generated/api.d.ts +0 -13
- package/dist/generated/api.d.ts.map +0 -1
- package/dist/generated/api.js +0 -192
- package/dist/generated/api.js.map +0 -1
- package/dist/src/api/client.d.ts +0 -62
- package/dist/src/api/client.d.ts.map +0 -1
- package/dist/src/api/client.js +0 -104
- package/dist/src/api/client.js.map +0 -1
- package/dist/src/api/errors.d.ts +0 -45
- package/dist/src/api/errors.d.ts.map +0 -1
- package/dist/src/api/errors.js +0 -65
- package/dist/src/api/errors.js.map +0 -1
- package/dist/src/api/index.d.ts +0 -6
- package/dist/src/api/index.d.ts.map +0 -1
- package/dist/src/api/index.js +0 -6
- package/dist/src/api/index.js.map +0 -1
- package/dist/src/api/telegram-types/LICENSE +0 -21
- package/dist/src/api/telegram-types/api.d.ts +0 -22
- package/dist/src/api/telegram-types/checklist.d.ts +0 -72
- package/dist/src/api/telegram-types/inline.d.ts +0 -692
- package/dist/src/api/telegram-types/langs.d.ts +0 -193
- package/dist/src/api/telegram-types/manage.d.ts +0 -1144
- package/dist/src/api/telegram-types/markup.d.ts +0 -268
- package/dist/src/api/telegram-types/message.d.ts +0 -1537
- package/dist/src/api/telegram-types/methods.d.ts +0 -2870
- package/dist/src/api/telegram-types/mod.d.ts +0 -14
- package/dist/src/api/telegram-types/passport.d.ts +0 -163
- package/dist/src/api/telegram-types/payment.d.ts +0 -570
- package/dist/src/api/telegram-types/rich.d.ts +0 -1010
- package/dist/src/api/telegram-types/settings.d.ts +0 -120
- package/dist/src/api/telegram-types/story.d.ts +0 -89
- package/dist/src/api/telegram-types/update.d.ts +0 -84
- package/dist/src/api/telegram.d.ts +0 -7
- package/dist/src/api/telegram.d.ts.map +0 -1
- package/dist/src/api/telegram.js +0 -2
- package/dist/src/api/telegram.js.map +0 -1
- package/dist/src/api/transport.d.ts +0 -68
- package/dist/src/api/transport.d.ts.map +0 -1
- package/dist/src/api/transport.js +0 -264
- package/dist/src/api/transport.js.map +0 -1
- package/dist/src/api/types.d.ts +0 -466
- package/dist/src/api/types.d.ts.map +0 -1
- package/dist/src/api/types.js +0 -2
- package/dist/src/api/types.js.map +0 -1
- package/dist/src/branding/terminal.d.ts +0 -77
- package/dist/src/branding/terminal.d.ts.map +0 -1
- package/dist/src/branding/terminal.js +0 -328
- package/dist/src/branding/terminal.js.map +0 -1
- package/dist/src/broadcast/broadcast.d.ts +0 -50
- package/dist/src/broadcast/broadcast.d.ts.map +0 -1
- package/dist/src/broadcast/broadcast.js +0 -56
- package/dist/src/broadcast/broadcast.js.map +0 -1
- package/dist/src/cache/cache.d.ts +0 -34
- package/dist/src/cache/cache.d.ts.map +0 -1
- package/dist/src/cache/cache.js +0 -41
- package/dist/src/cache/cache.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -84
- package/dist/src/cli.js.map +0 -1
- package/dist/src/context/context.d.ts +0 -124
- package/dist/src/context/context.d.ts.map +0 -1
- package/dist/src/context/context.js +0 -302
- package/dist/src/context/context.js.map +0 -1
- package/dist/src/core/bot.d.ts +0 -204
- package/dist/src/core/bot.d.ts.map +0 -1
- package/dist/src/core/bot.js +0 -506
- package/dist/src/core/bot.js.map +0 -1
- package/dist/src/core/events.d.ts +0 -75
- package/dist/src/core/events.d.ts.map +0 -1
- package/dist/src/core/events.js +0 -35
- package/dist/src/core/events.js.map +0 -1
- package/dist/src/core/webhook-reply.d.ts +0 -34
- package/dist/src/core/webhook-reply.d.ts.map +0 -1
- package/dist/src/core/webhook-reply.js +0 -37
- package/dist/src/core/webhook-reply.js.map +0 -1
- package/dist/src/index.d.ts +0 -24
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -24
- package/dist/src/index.js.map +0 -1
- package/dist/src/keyboard/index.d.ts +0 -46
- package/dist/src/keyboard/index.d.ts.map +0 -1
- package/dist/src/keyboard/index.js +0 -55
- package/dist/src/keyboard/index.js.map +0 -1
- package/dist/src/middleware/compose.d.ts +0 -5
- package/dist/src/middleware/compose.d.ts.map +0 -1
- package/dist/src/middleware/compose.js +0 -17
- package/dist/src/middleware/compose.js.map +0 -1
- package/dist/src/observability/logger.d.ts +0 -78
- package/dist/src/observability/logger.d.ts.map +0 -1
- package/dist/src/observability/logger.js +0 -285
- package/dist/src/observability/logger.js.map +0 -1
- package/dist/src/plugins/plugin.d.ts +0 -38
- package/dist/src/plugins/plugin.d.ts.map +0 -1
- package/dist/src/plugins/plugin.js +0 -59
- package/dist/src/plugins/plugin.js.map +0 -1
- package/dist/src/queue/queue.d.ts +0 -77
- package/dist/src/queue/queue.d.ts.map +0 -1
- package/dist/src/queue/queue.js +0 -213
- package/dist/src/queue/queue.js.map +0 -1
- package/dist/src/router/router.d.ts +0 -61
- package/dist/src/router/router.d.ts.map +0 -1
- package/dist/src/router/router.js +0 -183
- package/dist/src/router/router.js.map +0 -1
- package/dist/src/state/conversation.d.ts +0 -56
- package/dist/src/state/conversation.d.ts.map +0 -1
- package/dist/src/state/conversation.js +0 -133
- package/dist/src/state/conversation.js.map +0 -1
- package/dist/src/state/forms.d.ts +0 -34
- package/dist/src/state/forms.d.ts.map +0 -1
- package/dist/src/state/forms.js +0 -44
- package/dist/src/state/forms.js.map +0 -1
- package/dist/src/state/menu.d.ts +0 -78
- package/dist/src/state/menu.d.ts.map +0 -1
- package/dist/src/state/menu.js +0 -127
- package/dist/src/state/menu.js.map +0 -1
- package/dist/src/storage/storage.d.ts +0 -146
- package/dist/src/storage/storage.d.ts.map +0 -1
- package/dist/src/storage/storage.js +0 -195
- package/dist/src/storage/storage.js.map +0 -1
- package/dist/src/telegram-features.d.ts +0 -33
- package/dist/src/telegram-features.d.ts.map +0 -1
- package/dist/src/telegram-features.js +0 -71
- package/dist/src/telegram-features.js.map +0 -1
- package/dist/src/testing.d.ts +0 -24
- package/dist/src/testing.d.ts.map +0 -1
- package/dist/src/testing.js +0 -38
- package/dist/src/testing.js.map +0 -1
- package/dist/src/utils/concurrency.d.ts +0 -25
- package/dist/src/utils/concurrency.d.ts.map +0 -1
- package/dist/src/utils/concurrency.js +0 -52
- package/dist/src/utils/concurrency.js.map +0 -1
- package/dist/src/utils/files.d.ts +0 -45
- package/dist/src/utils/files.d.ts.map +0 -1
- package/dist/src/utils/files.js +0 -53
- package/dist/src/utils/files.js.map +0 -1
- package/dist/src/utils/text.d.ts +0 -39
- package/dist/src/utils/text.d.ts.map +0 -1
- package/dist/src/utils/text.js +0 -56
- package/dist/src/utils/text.js.map +0 -1
- package/dist/src/webhook/handler.d.ts +0 -19
- package/dist/src/webhook/handler.d.ts.map +0 -1
- package/dist/src/webhook/handler.js +0 -141
- package/dist/src/webhook/handler.js.map +0 -1
- package/dist-cjs/generated/api.js +0 -194
- package/dist-cjs/package.json +0 -3
- package/dist-cjs/src/api/client.js +0 -107
- package/dist-cjs/src/api/errors.js +0 -74
- package/dist-cjs/src/api/index.js +0 -21
- package/dist-cjs/src/api/telegram-types/LICENSE +0 -21
- package/dist-cjs/src/api/telegram-types/api.d.ts +0 -22
- package/dist-cjs/src/api/telegram-types/checklist.d.ts +0 -72
- package/dist-cjs/src/api/telegram-types/inline.d.ts +0 -692
- package/dist-cjs/src/api/telegram-types/langs.d.ts +0 -193
- package/dist-cjs/src/api/telegram-types/manage.d.ts +0 -1144
- package/dist-cjs/src/api/telegram-types/markup.d.ts +0 -268
- package/dist-cjs/src/api/telegram-types/message.d.ts +0 -1537
- package/dist-cjs/src/api/telegram-types/methods.d.ts +0 -2870
- package/dist-cjs/src/api/telegram-types/mod.d.ts +0 -14
- package/dist-cjs/src/api/telegram-types/passport.d.ts +0 -163
- package/dist-cjs/src/api/telegram-types/payment.d.ts +0 -570
- package/dist-cjs/src/api/telegram-types/rich.d.ts +0 -1010
- package/dist-cjs/src/api/telegram-types/settings.d.ts +0 -120
- package/dist-cjs/src/api/telegram-types/story.d.ts +0 -89
- package/dist-cjs/src/api/telegram-types/update.d.ts +0 -84
- package/dist-cjs/src/api/telegram.js +0 -2
- package/dist-cjs/src/api/transport.js +0 -267
- package/dist-cjs/src/api/types.js +0 -2
- package/dist-cjs/src/branding/terminal.js +0 -338
- package/dist-cjs/src/broadcast/broadcast.js +0 -58
- package/dist-cjs/src/cache/cache.js +0 -45
- package/dist-cjs/src/cli.js +0 -86
- package/dist-cjs/src/context/context.js +0 -305
- package/dist-cjs/src/core/bot.js +0 -510
- package/dist-cjs/src/core/events.js +0 -38
- package/dist-cjs/src/core/webhook-reply.js +0 -42
- package/dist-cjs/src/index.js +0 -47
- package/dist-cjs/src/keyboard/index.js +0 -61
- package/dist-cjs/src/middleware/compose.js +0 -20
- package/dist-cjs/src/observability/logger.js +0 -293
- package/dist-cjs/src/plugins/plugin.js +0 -63
- package/dist-cjs/src/queue/queue.js +0 -219
- package/dist-cjs/src/router/router.js +0 -186
- package/dist-cjs/src/state/conversation.js +0 -139
- package/dist-cjs/src/state/forms.js +0 -47
- package/dist-cjs/src/state/menu.js +0 -133
- package/dist-cjs/src/storage/storage.js +0 -202
- package/dist-cjs/src/telegram-features.js +0 -76
- package/dist-cjs/src/testing.js +0 -45
- package/dist-cjs/src/utils/concurrency.js +0 -57
- package/dist-cjs/src/utils/files.js +0 -58
- package/dist-cjs/src/utils/text.js +0 -63
- package/dist-cjs/src/webhook/handler.js +0 -144
- package/docs/API.id.md +0 -1935
- package/docs/API.md +0 -1969
- package/docs/API.zh-CN.md +0 -1929
- package/docs/COOKBOOK.id.md +0 -321
- package/docs/COOKBOOK.md +0 -321
- package/docs/COOKBOOK.zh-CN.md +0 -321
- package/docs/ERRORS.id.md +0 -194
- package/docs/ERRORS.md +0 -194
- package/docs/ERRORS.zh-CN.md +0 -194
- package/docs/FILES.id.md +0 -243
- package/docs/FILES.md +0 -243
- package/docs/FILES.zh-CN.md +0 -243
- package/docs/GETTING_STARTED.id.md +0 -89
- package/docs/GETTING_STARTED.md +0 -89
- package/docs/GETTING_STARTED.zh-CN.md +0 -89
- package/docs/GITHUB_PACKAGES.id.md +0 -82
- package/docs/GITHUB_PACKAGES.md +0 -82
- package/docs/GITHUB_PACKAGES.zh-CN.md +0 -82
- package/docs/MIGRATION_TELEGRAF.id.md +0 -147
- package/docs/MIGRATION_TELEGRAF.md +0 -154
- package/docs/MIGRATION_TELEGRAF.zh-CN.md +0 -147
- package/docs/README.md +0 -67
- package/docs/STORAGE.id.md +0 -105
- package/docs/STORAGE.md +0 -105
- package/docs/STORAGE.zh-CN.md +0 -105
- package/docs/TESTING.id.md +0 -203
- package/docs/TESTING.md +0 -203
- package/docs/TESTING.zh-CN.md +0 -203
- package/docs/WEBHOOK.id.md +0 -212
- package/docs/WEBHOOK.md +0 -215
- package/docs/WEBHOOK.zh-CN.md +0 -212
- package/examples/README.md +0 -37
- package/examples/files.ts +0 -35
- package/examples/minimal.ts +0 -12
- package/examples/tsconfig.json +0 -9
- package/examples/webhook.ts +0 -42
- package/examples/wizard-registration.ts +0 -42
package/docs/WEBHOOK.id.md
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# Webhook (Bahasa Indonesia)
|
|
2
|
-
|
|
3
|
-
Cara melayani update lewat webhook — untuk platform tanpa long-polling (serverless, container), atau saat butuh endpoint terbuka HTTPS.
|
|
4
|
-
|
|
5
|
-
## Daftar isi
|
|
6
|
-
|
|
7
|
-
1. [Polling vs webhook](#1-polling-vs-webhook)
|
|
8
|
-
2. [Handler: `createWebhookHandler()`](#2-handler-createwebhookhandler)
|
|
9
|
-
3. [Framework populer](#3-framework-populer)
|
|
10
|
-
4. [Mendaftarkan URL ke Telegram](#4-mendaftarkan-url-ke-telegram)
|
|
11
|
-
5. [Secret token](#5-secret-token)
|
|
12
|
-
6. [Mode `webhookReply`](#6-mode-webhookreply)
|
|
13
|
-
7. [Local development dengan tunnel](#7-local-development-dengan-tunnel)
|
|
14
|
-
8. [Checklist produksi](#8-checklist-produksi)
|
|
15
|
-
9. [Troubleshooting](#9-troubleshooting)
|
|
16
|
-
|
|
17
|
-
## 1. Polling vs webhook
|
|
18
|
-
|
|
19
|
-
| | Polling (`bot.start()`) | Webhook |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| Menghubungi Telegram | Ya (long-polling) | Tidak — Telegram yang menghubungi Anda |
|
|
22
|
-
| Butuh domain + HTTPS publik | Tidak | Ya |
|
|
23
|
-
| Cocok untuk | Skrip lokal, development, VPS | Serverless (Lambda/Workers/Cloud Functions), container, k8s |
|
|
24
|
-
| Konkurensi | Pipeline per-update yang sama | Pipeline per-update yang sama |
|
|
25
|
-
| Menerima `POST /<path>` Anda sendiri | — | Ya — handler mengembalikan `Response`, routing tetap milik Anda |
|
|
26
|
-
|
|
27
|
-
Hanya satu yang aktif: Telegram mengirim update ke webhook terdaftar dan mengabaikan `getUpdates` selama webhook aktif.
|
|
28
|
-
|
|
29
|
-
## 2. Handler: `createWebhookHandler()`
|
|
30
|
-
|
|
31
|
-
Handler menerima **Web-standard `Request`** dan mengembalikan **`Response`** — berjalan di Node, Bun, Deno, dan edge runtime:
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { createWebhookHandler } from "@xbibzlibrary/telebibz";
|
|
35
|
-
|
|
36
|
-
const handleUpdate = createWebhookHandler(bot, {
|
|
37
|
-
secretToken: process.env.TELEGRAM_WEBHOOK_SECRET, // verifikasi X-Telegram-Bot-Api-Secret-Token
|
|
38
|
-
webhookReply: true, // jawab API via body respons (opsional)
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
export default {
|
|
42
|
-
async fetch(request: Request): Promise<Response> {
|
|
43
|
-
if (request.method === "POST" && new URL(request.url).pathname === "/telegram") {
|
|
44
|
-
return handleUpdate(request);
|
|
45
|
-
}
|
|
46
|
-
return new Response("Not Found", { status: 404 });
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
- Body > 1 MB → `413 Payload Too Large` (atur `maxBodyBytes`).
|
|
52
|
-
- Secret salah → `401 Unauthorized`.
|
|
53
|
-
- Method non-POST → `405 Method Not Allowed`.
|
|
54
|
-
- Update diproses via pipeline normal — error handler, session, conversation, semuanya bekerja.
|
|
55
|
-
|
|
56
|
-
Untuk server Node ala Express (objek req/res, bukan `Request`), gunakan `webhookCallback()` — lihat [Framework populer](#3-framework-populer).
|
|
57
|
-
|
|
58
|
-
## 3. Framework populer
|
|
59
|
-
|
|
60
|
-
### Express
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
import express from "express";
|
|
64
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
65
|
-
|
|
66
|
-
const app = express();
|
|
67
|
-
app.use(express.json({ limit: "1mb" }));
|
|
68
|
-
app.post("/telegram", webhookCallback(bot, "express", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET }));
|
|
69
|
-
app.listen(3000);
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Node `http`
|
|
73
|
-
|
|
74
|
-
```ts
|
|
75
|
-
import http from "node:http";
|
|
76
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
77
|
-
|
|
78
|
-
const callback = webhookCallback(bot, "http", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET });
|
|
79
|
-
http
|
|
80
|
-
.createServer(async (req, res) => {
|
|
81
|
-
if (req.method === "POST" && req.url === "/telegram") return callback(req, res);
|
|
82
|
-
res.writeHead(404).end();
|
|
83
|
-
})
|
|
84
|
-
.listen(3000);
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Fastify
|
|
88
|
-
|
|
89
|
-
```ts
|
|
90
|
-
import Fastify from "fastify";
|
|
91
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
92
|
-
|
|
93
|
-
const fastify = Fastify({ bodyLimit: 1_048_576 });
|
|
94
|
-
fastify.post("/telegram", (req, reply) => webhookCallback(bot, "fastify", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET })(req, reply));
|
|
95
|
-
await fastify.listen({ port: 3000 });
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Koa (dengan `koa-bodyparser` agar `ctx.request.body` terisi)
|
|
99
|
-
|
|
100
|
-
```ts
|
|
101
|
-
import Koa from "koa";
|
|
102
|
-
import bodyParser from "koa-bodyparser";
|
|
103
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
104
|
-
|
|
105
|
-
const app = new Koa();
|
|
106
|
-
app.use(bodyParser());
|
|
107
|
-
app.use(async (ctx) => {
|
|
108
|
-
if (ctx.method === "POST" && ctx.path === "/telegram") {
|
|
109
|
-
await webhookCallback(bot, "koa", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET })(ctx.request, ctx);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
ctx.status = 404;
|
|
113
|
-
});
|
|
114
|
-
app.listen(3000);
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## 4. Mendaftarkan URL ke Telegram
|
|
118
|
-
|
|
119
|
-
Webhook hanya mengirim ke URL yang Anda daftarkan. Setelah server jalan di URL publik, panggil `setWebhook` sekali:
|
|
120
|
-
|
|
121
|
-
```ts
|
|
122
|
-
const secret = process.env.TELEGRAM_WEBHOOK_SECRET;
|
|
123
|
-
|
|
124
|
-
await bot.api.methods.setWebhook({
|
|
125
|
-
url: "https://bot.example.com/telegram",
|
|
126
|
-
secret_token: secret, // sama persis dengan secretToken handler
|
|
127
|
-
max_connections: 40, // default 40; sesuaikan dengan kapasitas
|
|
128
|
-
allowed_updates: ["message", "callback_query"], // opsional: kurangi trafik
|
|
129
|
-
drop_pending_updates: false,
|
|
130
|
-
});
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Menghentikan webhook** — dua opsi:
|
|
134
|
-
|
|
135
|
-
```ts
|
|
136
|
-
await bot.api.methods.deleteWebhook({ drop_pending_updates: true }); // kembali ke polling
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
Saat bot Anda berjalan di local Bot API server, `setWebhook` juga menerima `ip_address` untuk menghindari resolusi DNS publik.
|
|
140
|
-
|
|
141
|
-
## 5. Secret token
|
|
142
|
-
|
|
143
|
-
Tanpa secret, siapa pun yang tahu URL bisa mengirim update palsu. Secret memverifikasi bahwa request berasal dari Telegram:
|
|
144
|
-
|
|
145
|
-
```bash
|
|
146
|
-
openssl rand -hex 32
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Simpan sebagai environment variable dan berikan **nilai yang sama persis** ke `setWebhook` (parameter `secret_token`) dan ke handler (opsi `secretToken`). Perbandingan dilakukan constant-time — tidak bisa diTiming-attack. Aturan: 1–256 karakter dari `A-Z a-z 0-9 _ -`.
|
|
150
|
-
|
|
151
|
-
Perhatikan `webhookReply` **tidak** terkait secret — mode itu memilih *bagaimana* respons API dikirim, bukan siapa pengirimnya.
|
|
152
|
-
|
|
153
|
-
## 6. Mode `webhookReply`
|
|
154
|
-
|
|
155
|
-
Telegram mengizinkan bot menjawab satu panggilan API langsung di body respons webhook. Mengaktifkan mode ini menghilangkan satu round-trip per balasan — sangat berguna di serverless:
|
|
156
|
-
|
|
157
|
-
```ts
|
|
158
|
-
const handleUpdate = createWebhookHandler(bot, { webhookReply: true });
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Hanya **satu** panggilan API per update yang mendapat manfaat ini — panggilan pertama yang selesai menang; sisanya dikirim sebagai request HTTP normal. Ketika body respons sudah dipakai, handler mengembalikan `{}` (Telegram tetap menganggapnya sukses).
|
|
162
|
-
|
|
163
|
-
Telegraf menyebutnya `telegram.webhookReply`; konsep dan default-nya sama persi di telebibz.
|
|
164
|
-
|
|
165
|
-
## 7. Local development dengan tunnel
|
|
166
|
-
|
|
167
|
-
Telegram hanya mengirim ke URL **publik HTTPS**. Saat development, arahkan URL publik ke localhost:
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
# cloudflared (tanpa akun)
|
|
171
|
-
cloudflared tunnel --url http://localhost:3000
|
|
172
|
-
|
|
173
|
-
# atau ngrok
|
|
174
|
-
ngrok http 3000
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Lalu daftarkan URL yang dihasilkan:
|
|
178
|
-
|
|
179
|
-
```bash
|
|
180
|
-
TOKEN="…"
|
|
181
|
-
URL="https://random-words.loca.lt" # dari output tunnel
|
|
182
|
-
SECRET="…"
|
|
183
|
-
curl "https://api.telegram.org/bot$TOKEN/setWebhook" \
|
|
184
|
-
-d "url=$URL/telegram" -d "secret_token=$SECRET"
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Lepas webhook setelah selesai (`deleteWebhook`) agar `bot.start()` berfungsi kembali.
|
|
188
|
-
|
|
189
|
-
## 8. Checklist produksi
|
|
190
|
-
|
|
191
|
-
- [ ] HTTPS publik + sertifikat valid (Telegram menolak self-signed)
|
|
192
|
-
- [ ] `secret_token` ter-set dan cocok di kedua sisi
|
|
193
|
-
- [ ] `max_connections` disetel (default 40)
|
|
194
|
-
- [ ] Body parser limit ≥ 1 MB (`express.json({ limit: "1mb" })` dkk.)
|
|
195
|
-
- [ ] Timeout upstream > `handlerTimeout` (agar `UpdateTimeoutError` sempat mengambil alih, bukan 504 load balancer)
|
|
196
|
-
- [ ] `drop_pending_updates` dipertimbangkan saat redeploy
|
|
197
|
-
- [ ] Error ter-observasi: `bot.catch()` + `update:error`
|
|
198
|
-
- [ ] Graceful shutdown: `bot.stop()` sebelum exit
|
|
199
|
-
|
|
200
|
-
## 9. Troubleshooting
|
|
201
|
-
|
|
202
|
-
| Gejala | Penyebab | Perbaikan |
|
|
203
|
-
|---|---|---|
|
|
204
|
-
| Telegram selalu timeout (baris `getUpdates` kosong) | Webhook aktif — Telegram mengabaikan polling | `deleteWebhook` atau gunakan handler |
|
|
205
|
-
| 401 di setiap request | Secret handler ≠ `secret_token` yang terdaftar | Samakan nilainya di `setWebhook` dan handler |
|
|
206
|
-
| 413 Payload Too Large | Body melebihi `maxBodyBytes` (default 1 MB) | Naikkan `maxBodyBytes` + limit body parser |
|
|
207
|
-
| 502 dari proxy | Webhook mengirim `content-type: application/json` — proxy menolak | Hapus rewrite content-type; handler menerima JSON |
|
|
208
|
-
| `409 Conflict` saat `getUpdates` | Webhook masih terdaftar | `deleteWebhook` dulu |
|
|
209
|
-
| Update diterima lalu menggantung | Handler menunggu network call yang lambat | Turunkan `handlerTimeout`; pastikan observabilitas via `update:error` |
|
|
210
|
-
| Serverless: jawaban tidak pernah sampai | Terlalu banyak await di satu handler | Aktifkan `webhookReply` agar panggilan pertama menumpang respons |
|
|
211
|
-
|
|
212
|
-
English: [WEBHOOK.md](WEBHOOK.md) · 简体中文: [WEBHOOK.zh-CN.md](WEBHOOK.zh-CN.md)
|
package/docs/WEBHOOK.md
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
# Webhook deployment guide (English)
|
|
2
|
-
|
|
3
|
-
Everything needed to run telebibz behind a webhook: choosing polling vs webhook, the four framework integrations, secret tokens, registering the webhook, webhook replies, and local development tunnels.
|
|
4
|
-
|
|
5
|
-
## Contents
|
|
6
|
-
|
|
7
|
-
1. [Polling or webhook?](#1-polling-or-webhook)
|
|
8
|
-
2. [The Web-standard handler](#2-the-web-standard-handler)
|
|
9
|
-
3. [Express, Koa, Fastify, and Node http](#3-express-koa-fastify-and-node-http)
|
|
10
|
-
4. [Registering the webhook](#4-registering-the-webhook)
|
|
11
|
-
5. [Secret tokens](#5-secret-tokens)
|
|
12
|
-
6. [Webhook replies (Telegraf-style)](#6-webhook-replies-telegraf-style)
|
|
13
|
-
7. [Local development with a tunnel](#7-local-development-with-a-tunnel)
|
|
14
|
-
8. [Production checklist](#8-production-checklist)
|
|
15
|
-
9. [Troubleshooting](#9-troubleshooting)
|
|
16
|
-
|
|
17
|
-
## 1. Polling or webhook?
|
|
18
|
-
|
|
19
|
-
| | Long polling (`bot.start()`) | Webhook |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| Setup | zero | needs HTTPS endpoint |
|
|
22
|
-
| Works behind NAT/laptop | ✅ | needs tunnel |
|
|
23
|
-
| Best for | development, small bots | production, serverless, high volume |
|
|
24
|
-
| Update delivery | bot pulls | Telegram pushes |
|
|
25
|
-
|
|
26
|
-
Both share the exact same update pipeline (parallel across chats, ordered per chat). Switch freely — handlers do not change.
|
|
27
|
-
|
|
28
|
-
## 2. The Web-standard handler
|
|
29
|
-
|
|
30
|
-
`createWebhookHandler()` takes a Web `Request` and returns a `Response` — it works on Node 22 (via `node:http` bridging below), Bun, Deno, and edge runtimes:
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
import { Bot, createWebhookHandler } from "@xbibzlibrary/telebibz";
|
|
34
|
-
|
|
35
|
-
const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!);
|
|
36
|
-
bot.on("message", async (ctx) => { await ctx.reply("hello"); });
|
|
37
|
-
|
|
38
|
-
export const handler = createWebhookHandler(bot, {
|
|
39
|
-
secretToken: process.env.TELEGRAM_WEBHOOK_SECRET, // verifies X-Telegram-Bot-Api-Secret-Token
|
|
40
|
-
maxBodyBytes: 1_048_576, // reject bodies over 1 MB (default)
|
|
41
|
-
webhookReply: false, // see section 6
|
|
42
|
-
});
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
The handler verifies, in order: HTTP method and path, secret token header, body size, JSON parsing, and update shape — answering each failure with the right status code before your handlers ever run.
|
|
46
|
-
|
|
47
|
-
## 3. Express, Koa, Fastify, and Node http
|
|
48
|
-
|
|
49
|
-
`webhookCallback()` adapts the handler to each framework's request/response style:
|
|
50
|
-
|
|
51
|
-
**Express**
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
import express from "express";
|
|
55
|
-
import { Bot, webhookCallback } from "@xbibzlibrary/telebibz";
|
|
56
|
-
|
|
57
|
-
const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!);
|
|
58
|
-
bot.on("message", async (ctx) => { await ctx.reply("hello"); });
|
|
59
|
-
|
|
60
|
-
const app = express();
|
|
61
|
-
app.use(express.json({ limit: "1mb" }));
|
|
62
|
-
app.post("/telegram", webhookCallback(bot, "express", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET }));
|
|
63
|
-
app.get("/healthz", (_req, res) => res.json({ ok: true }));
|
|
64
|
-
|
|
65
|
-
app.listen(3000);
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
**Node http (no framework)**
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
import { createServer } from "node:http";
|
|
72
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
73
|
-
|
|
74
|
-
const callback = webhookCallback(bot, "http", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET });
|
|
75
|
-
createServer((req, res) => {
|
|
76
|
-
if (req.method === "POST" && req.url === "/telegram") return void callback(req, res);
|
|
77
|
-
res.writeHead(404).end();
|
|
78
|
-
}).listen(3000);
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
**Fastify**
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
import Fastify from "fastify";
|
|
85
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
86
|
-
|
|
87
|
-
const fastify = Fastify({ logger: true });
|
|
88
|
-
fastify.post("/telegram", (req, reply) => webhookCallback(bot, "fastify", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET })(req, reply));
|
|
89
|
-
await fastify.listen({ port: 3000, host: "0.0.0.0" });
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
**Koa** (with `koa-bodyparser` so `ctx.request.body` is parsed)
|
|
93
|
-
|
|
94
|
-
```ts
|
|
95
|
-
import Koa from "koa";
|
|
96
|
-
import bodyParser from "koa-bodyparser";
|
|
97
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
98
|
-
|
|
99
|
-
const app = new Koa();
|
|
100
|
-
app.use(bodyParser());
|
|
101
|
-
app.use(async (ctx) => {
|
|
102
|
-
if (ctx.method === "POST" && ctx.path === "/telegram") {
|
|
103
|
-
await webhookCallback(bot, "koa", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET })(ctx.request, ctx);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
ctx.status = 404;
|
|
107
|
-
});
|
|
108
|
-
app.listen(3000);
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
Framework ids: `"express" | "http" | "fastify" | "koa"`.
|
|
112
|
-
|
|
113
|
-
## 4. Registering the webhook
|
|
114
|
-
|
|
115
|
-
Point Telegram at your endpoint once (not on every boot):
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
await bot.api.methods.setWebhook({
|
|
119
|
-
url: "https://bot.example.com/telegram",
|
|
120
|
-
secret_token: process.env.TELEGRAM_WEBHOOK_SECRET,
|
|
121
|
-
max_connections: 40, // 1–100, default 40
|
|
122
|
-
drop_pending_updates: true, // optional: discard updates queued while down
|
|
123
|
-
allowed_updates: ["message", "callback_query"],
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Check registration and tear it down:
|
|
128
|
-
|
|
129
|
-
```ts
|
|
130
|
-
const info = await bot.api.methods.getWebhookInfo();
|
|
131
|
-
await bot.api.methods.deleteWebhook({ drop_pending_updates: false });
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
A small CLI-style script makes this repeatable:
|
|
135
|
-
|
|
136
|
-
```ts
|
|
137
|
-
// scripts/register-webhook.ts — run with: npx tsx scripts/register-webhook.ts
|
|
138
|
-
import { Bot } from "@xbibzlibrary/telebibz";
|
|
139
|
-
const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!);
|
|
140
|
-
await bot.api.methods.setWebhook({
|
|
141
|
-
url: process.env.WEBHOOK_URL!,
|
|
142
|
-
secret_token: process.env.TELEGRAM_WEBHOOK_SECRET,
|
|
143
|
-
max_connections: 40,
|
|
144
|
-
});
|
|
145
|
-
console.log("webhook registered:", process.env.WEBHOOK_URL);
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## 5. Secret tokens
|
|
149
|
-
|
|
150
|
-
Always set a secret token. Telegram then sends it in the `X-Telegram-Bot-Api-Secret-Token` header on every update; the handler rejects anything that does not match with `401 Unauthorized`. Generate one with `openssl rand -hex 32`. Two rules:
|
|
151
|
-
|
|
152
|
-
- Between 1 and 256 characters of `A-Z, a-z, 0-9, _` and `-`.
|
|
153
|
-
- Pass the **same value** to `setWebhook` (`secret_token`) and to `createWebhookHandler`/`webhookCallback` (`secretToken`).
|
|
154
|
-
|
|
155
|
-
## 6. Webhook replies (Telegraf-style)
|
|
156
|
-
|
|
157
|
-
With `webhookReply: true`, the **first** API call while handling an update is answered through the webhook HTTP response itself — Telegram executes the method for you and you save one round trip:
|
|
158
|
-
|
|
159
|
-
```ts
|
|
160
|
-
const handler = createWebhookHandler(bot, { webhookReply: true });
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
- Only the first call is claimed; later calls go through the transport as usual.
|
|
164
|
-
- The claimed call resolves with `true` (Telegram never sends the method result back through the webhook response).
|
|
165
|
-
- The lazy `getMe` initialization never claims the slot.
|
|
166
|
-
- Per-update override: `bot.handleUpdate(update, { webhookReply: sink })` for fully custom servers.
|
|
167
|
-
|
|
168
|
-
Most bots should keep it off — the default reply-then-200 flow is simpler to reason about, and the transport's connection reuse already keeps latency low.
|
|
169
|
-
|
|
170
|
-
## 7. Local development with a tunnel
|
|
171
|
-
|
|
172
|
-
Telegram must reach your endpoint over HTTPS. For local development, expose your port through a tunnel and register the tunnel URL:
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
# Option A: cloudflared (no account, no install to project)
|
|
176
|
-
cloudflared tunnel --url http://localhost:3000
|
|
177
|
-
# → https://random-name.trycloudflare.com
|
|
178
|
-
|
|
179
|
-
# Option B: ngrok
|
|
180
|
-
ngrok http 3000
|
|
181
|
-
# → https://random-name.ngrok-free.app
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Then:
|
|
185
|
-
|
|
186
|
-
```bash
|
|
187
|
-
WEBHOOK_URL=https://random-name.trycloudflare.com npx tsx scripts/register-webhook.ts
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Tunnel URLs change on restart — re-register after each restart, or keep using polling during development and switch to webhooks only in staging/production (the handler code is identical).
|
|
191
|
-
|
|
192
|
-
## 8. Production checklist
|
|
193
|
-
|
|
194
|
-
- [ ] HTTPS endpoint with a valid certificate (Telegram rejects self-signed certs unless you upload `certificate`)
|
|
195
|
-
- [ ] Secret token set on both `setWebhook` and the handler
|
|
196
|
-
- [ ] `max_connections` tuned (default 40; range 1–100)
|
|
197
|
-
- [ ] Health endpoint (`/healthz`) for your load balancer
|
|
198
|
-
- [ ] Graceful shutdown: `process.on("SIGTERM", () => bot.stop())` — drains in-flight handlers before plugins are disposed
|
|
199
|
-
- [ ] Body limit enforced (the handler rejects oversized bodies, but the framework's own limit should match)
|
|
200
|
-
- [ ] Logging: `logger: { format: "json" }` for structured ingestion
|
|
201
|
-
- [ ] Monitoring: subscribe to `update:error` and `bot:error` events
|
|
202
|
-
- [ ] `getWebhookInfo()` polled by your ops dashboard (watch `pending_update_count`)
|
|
203
|
-
|
|
204
|
-
## 9. Troubleshooting
|
|
205
|
-
|
|
206
|
-
| Symptom | Cause | Fix |
|
|
207
|
-
|---|---|---|
|
|
208
|
-
| Telegram never calls the endpoint | Webhook not registered / wrong URL | `getWebhookInfo()` shows the registered URL and the last error |
|
|
209
|
-
| Every update answers 401 | Secret token mismatch | Same value in `setWebhook` and the handler |
|
|
210
|
-
| 404 from Telegram | Wrong path | Register the exact path you serve (`/telegram`) |
|
|
211
|
-
| Updates arrive twice | Both polling and webhook active | `deleteWebhook()` or stop calling `bot.start()` |
|
|
212
|
-
| `ai_response`/ssl errors in `getWebhookInfo` | Invalid certificate | Use a CA-signed cert or upload the self-signed one as `certificate` |
|
|
213
|
-
| Handler never sees large bodies | Framework body limit below Telegram's payload | Raise the framework's JSON limit (e.g. `express.json({ limit: "1mb" })`) |
|
|
214
|
-
|
|
215
|
-
Bahasa Indonesia: [WEBHOOK.id.md](WEBHOOK.id.md) · 简体中文: [WEBHOOK.zh-CN.md](WEBHOOK.zh-CN.md)
|
package/docs/WEBHOOK.zh-CN.md
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
# Webhook 指南(简体中文)
|
|
2
|
-
|
|
3
|
-
通过 webhook 提供更新服务 —— 适用于无长轮询的平台(serverless、容器),或需要公网 HTTPS 端点的场景。
|
|
4
|
-
|
|
5
|
-
## 目录
|
|
6
|
-
|
|
7
|
-
1. [轮询 vs webhook](#1-轮询-vs-webhook)
|
|
8
|
-
2. [处理器:`createWebhookHandler()`](#2-处理器createwebhookhandler)
|
|
9
|
-
3. [常见框架](#3-常见框架)
|
|
10
|
-
4. [向 Telegram 注册 URL](#4-向-telegram-注册-url)
|
|
11
|
-
5. [Secret token](#5-secret-token)
|
|
12
|
-
6. [`webhookReply` 模式](#6-webhookreply-模式)
|
|
13
|
-
7. [本地开发:隧道](#7-本地开发隧道)
|
|
14
|
-
8. [生产检查清单](#8-生产检查清单)
|
|
15
|
-
9. [故障排查](#9-故障排查)
|
|
16
|
-
|
|
17
|
-
## 1. 轮询 vs webhook
|
|
18
|
-
|
|
19
|
-
| | 轮询(`bot.start()`) | Webhook |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| 主动连接 Telegram | 是(长轮询) | 否 —— Telegram 主动连接你 |
|
|
22
|
-
| 需要公网域名 + HTTPS | 否 | 是 |
|
|
23
|
-
| 适合 | 本地脚本、开发、VPS | Serverless(Lambda/Workers/Cloud Functions)、容器、k8s |
|
|
24
|
-
| 并发 | 相同的逐更新流水线 | 相同的逐更新流水线 |
|
|
25
|
-
| 自定义 `POST /<path>` 路由 | — | 是 —— 处理器返回 `Response`,路由仍归你 |
|
|
26
|
-
|
|
27
|
-
同一时间只有一种生效:webhook 注册后 Telegram 把更新推到该 URL,并忽略 `getUpdates`。
|
|
28
|
-
|
|
29
|
-
## 2. 处理器:`createWebhookHandler()`
|
|
30
|
-
|
|
31
|
-
处理器接受 **Web 标准 `Request`** 并返回 **`Response`** —— 可运行于 Node、Bun、Deno 与 edge runtime:
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { createWebhookHandler } from "@xbibzlibrary/telebibz";
|
|
35
|
-
|
|
36
|
-
const handleUpdate = createWebhookHandler(bot, {
|
|
37
|
-
secretToken: process.env.TELEGRAM_WEBHOOK_SECRET, // 校验 X-Telegram-Bot-Api-Secret-Token
|
|
38
|
-
webhookReply: true, // 通过响应体应答 API(可选)
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
export default {
|
|
42
|
-
async fetch(request: Request): Promise<Response> {
|
|
43
|
-
if (request.method === "POST" && new URL(request.url).pathname === "/telegram") {
|
|
44
|
-
return handleUpdate(request);
|
|
45
|
-
}
|
|
46
|
-
return new Response("Not Found", { status: 404 });
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
- Body 超过 1 MB → `413 Payload Too Large`(可用 `maxBodyBytes` 调整)。
|
|
52
|
-
- Secret 不匹配 → `401 Unauthorized`。
|
|
53
|
-
- 非 POST 方法 → `405 Method Not Allowed`。
|
|
54
|
-
- 更新经正常流水线处理 —— 错误处理器、session、conversation 全部照常工作。
|
|
55
|
-
|
|
56
|
-
对 Express 风格的 Node 服务器(req/res 对象而非 `Request`),使用 `webhookCallback()` —— 见[常见框架](#3-常见框架)。
|
|
57
|
-
|
|
58
|
-
## 3. 常见框架
|
|
59
|
-
|
|
60
|
-
### Express
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
import express from "express";
|
|
64
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
65
|
-
|
|
66
|
-
const app = express();
|
|
67
|
-
app.use(express.json({ limit: "1mb" }));
|
|
68
|
-
app.post("/telegram", webhookCallback(bot, "express", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET }));
|
|
69
|
-
app.listen(3000);
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Node `http`
|
|
73
|
-
|
|
74
|
-
```ts
|
|
75
|
-
import http from "node:http";
|
|
76
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
77
|
-
|
|
78
|
-
const callback = webhookCallback(bot, "http", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET });
|
|
79
|
-
http
|
|
80
|
-
.createServer(async (req, res) => {
|
|
81
|
-
if (req.method === "POST" && req.url === "/telegram") return callback(req, res);
|
|
82
|
-
res.writeHead(404).end();
|
|
83
|
-
})
|
|
84
|
-
.listen(3000);
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### Fastify
|
|
88
|
-
|
|
89
|
-
```ts
|
|
90
|
-
import Fastify from "fastify";
|
|
91
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
92
|
-
|
|
93
|
-
const fastify = Fastify({ bodyLimit: 1_048_576 });
|
|
94
|
-
fastify.post("/telegram", (req, reply) => webhookCallback(bot, "fastify", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET })(req, reply));
|
|
95
|
-
await fastify.listen({ port: 3000 });
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
### Koa(配合 `koa-bodyparser` 使 `ctx.request.body` 被解析)
|
|
99
|
-
|
|
100
|
-
```ts
|
|
101
|
-
import Koa from "koa";
|
|
102
|
-
import bodyParser from "koa-bodyparser";
|
|
103
|
-
import { webhookCallback } from "@xbibzlibrary/telebibz";
|
|
104
|
-
|
|
105
|
-
const app = new Koa();
|
|
106
|
-
app.use(bodyParser());
|
|
107
|
-
app.use(async (ctx) => {
|
|
108
|
-
if (ctx.method === "POST" && ctx.path === "/telegram") {
|
|
109
|
-
await webhookCallback(bot, "koa", { secretToken: process.env.TELEGRAM_WEBHOOK_SECRET })(ctx.request, ctx);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
ctx.status = 404;
|
|
113
|
-
});
|
|
114
|
-
app.listen(3000);
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## 4. 向 Telegram 注册 URL
|
|
118
|
-
|
|
119
|
-
webhook 只会推送到你注册的 URL。服务器在公网地址就绪后,调用一次 `setWebhook`:
|
|
120
|
-
|
|
121
|
-
```ts
|
|
122
|
-
const secret = process.env.TELEGRAM_WEBHOOK_SECRET;
|
|
123
|
-
|
|
124
|
-
await bot.api.methods.setWebhook({
|
|
125
|
-
url: "https://bot.example.com/telegram",
|
|
126
|
-
secret_token: secret, // 与处理器的 secretToken 完全一致
|
|
127
|
-
max_connections: 40, // 默认 40;按容量调整
|
|
128
|
-
allowed_updates: ["message", "callback_query"], // 可选:减少流量
|
|
129
|
-
drop_pending_updates: false,
|
|
130
|
-
});
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**撤销 webhook** —— 两种选择:
|
|
134
|
-
|
|
135
|
-
```ts
|
|
136
|
-
await bot.api.methods.deleteWebhook({ drop_pending_updates: true }); // 回到轮询
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
当 bot 运行在本地 Bot API 服务器上时,`setWebhook` 还接受 `ip_address`,避免公网 DNS 解析。
|
|
140
|
-
|
|
141
|
-
## 5. Secret token
|
|
142
|
-
|
|
143
|
-
没有 secret,任何知道 URL 的人都能伪造更新。secret 用于确认请求确实来自 Telegram:
|
|
144
|
-
|
|
145
|
-
```bash
|
|
146
|
-
openssl rand -hex 32
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
保存为环境变量,并把**完全相同的值**传给 `setWebhook`(`secret_token` 参数)和处理器(`secretToken` 选项)。比较采用常数时间 —— 无法计时攻击。规则:1–256 个字符,仅限 `A-Z a-z 0-9 _ -`。
|
|
150
|
-
|
|
151
|
-
注意 `webhookReply` 与 secret 无关 —— 该模式决定 API 响应*如何*发送,而非请求来自谁。
|
|
152
|
-
|
|
153
|
-
## 6. `webhookReply` 模式
|
|
154
|
-
|
|
155
|
-
Telegram 允许 bot 直接在 webhook 响应体里应答一次 API 调用。开启后每次回复省去一次往返 —— 在 serverless 上尤其有价值:
|
|
156
|
-
|
|
157
|
-
```ts
|
|
158
|
-
const handleUpdate = createWebhookHandler(bot, { webhookReply: true });
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
每个 update 只有**一次** API 调用能享受此机制 —— 最先完成的那次获胜;其余照常走 HTTP 请求。响应体被占用后,处理器返回 `{}`(Telegram 仍视为成功)。
|
|
162
|
-
|
|
163
|
-
Telegraf 中称为 `telegram.webhookReply`;概念与默认值在 telebibz 中完全一致。
|
|
164
|
-
|
|
165
|
-
## 7. 本地开发:隧道
|
|
166
|
-
|
|
167
|
-
Telegram 只向**公网 HTTPS** URL 推送。开发时把公网 URL 隧道到 localhost:
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
# cloudflared(无需账号)
|
|
171
|
-
cloudflared tunnel --url http://localhost:3000
|
|
172
|
-
|
|
173
|
-
# 或 ngrok
|
|
174
|
-
ngrok http 3000
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
然后注册得到的 URL:
|
|
178
|
-
|
|
179
|
-
```bash
|
|
180
|
-
TOKEN="…"
|
|
181
|
-
URL="https://random-words.loca.lt" # 来自隧道输出
|
|
182
|
-
SECRET="…"
|
|
183
|
-
curl "https://api.telegram.org/bot$TOKEN/setWebhook" \
|
|
184
|
-
-d "url=$URL/telegram" -d "secret_token=$SECRET"
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
开发完成后记得 `deleteWebhook`,让 `bot.start()` 重新可用。
|
|
188
|
-
|
|
189
|
-
## 8. 生产检查清单
|
|
190
|
-
|
|
191
|
-
- [ ] 公网 HTTPS + 有效证书(Telegram 拒绝自签名)
|
|
192
|
-
- [ ] `secret_token` 已设置且两端一致
|
|
193
|
-
- [ ] `max_connections` 已调整(默认 40)
|
|
194
|
-
- [ ] Body parser 上限 ≥ 1 MB(`express.json({ limit: "1mb" })` 等)
|
|
195
|
-
- [ ] 上游超时 > `handlerTimeout`(让 `UpdateTimeoutError` 先接管,而不是负载均衡器返回 504)
|
|
196
|
-
- [ ] 重新部署时考虑 `drop_pending_updates`
|
|
197
|
-
- [ ] 错误可观测:`bot.catch()` + `update:error`
|
|
198
|
-
- [ ] 优雅关机:退出前 `bot.stop()`
|
|
199
|
-
|
|
200
|
-
## 9. 故障排查
|
|
201
|
-
|
|
202
|
-
| 症状 | 原因 | 修复 |
|
|
203
|
-
|---|---|---|
|
|
204
|
-
| Telegram 一直超时(`getUpdates` 空转) | webhook 处于激活状态 —— Telegram 忽略轮询 | `deleteWebhook` 或改用处理器 |
|
|
205
|
-
| 每个请求都 401 | 处理器 secret ≠ 已注册的 `secret_token` | 两端改为一致 |
|
|
206
|
-
| 413 Payload Too Large | Body 超过 `maxBodyBytes`(默认 1 MB) | 调大 `maxBodyBytes` 与 body parser 上限 |
|
|
207
|
-
| 代理返回 502 | webhook 发送 `content-type: application/json` —— 代理拒绝 | 移除 content-type 改写;处理器接受 JSON |
|
|
208
|
-
| `getUpdates` 报 `409 Conflict` | webhook 仍注册着 | 先 `deleteWebhook` |
|
|
209
|
-
| 更新收到后挂起 | handler 在等待缓慢的网络调用 | 降低 `handlerTimeout`;通过 `update:error` 保证可观测 |
|
|
210
|
-
| Serverless:回复从未送达 | 单个 handler 里 await 过多 | 开启 `webhookReply`,让第一次调用搭响应的便车 |
|
|
211
|
-
|
|
212
|
-
English: [WEBHOOK.md](WEBHOOK.md) · Bahasa Indonesia: [WEBHOOK.id.md](WEBHOOK.id.md)
|
package/examples/README.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Telebibz examples
|
|
2
|
-
|
|
3
|
-
These examples are intentionally small, runnable starting points for developers evaluating Telebibz. They use environment variables for secrets and do not contain real Telegram credentials.
|
|
4
|
-
|
|
5
|
-
## Run the examples
|
|
6
|
-
|
|
7
|
-
From the repository root:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install
|
|
11
|
-
export TELEGRAM_BOT_TOKEN="<your-bot-token>"
|
|
12
|
-
npx tsx examples/minimal.ts
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
The official npm package can be used from a separate project by replacing the local import path with `@xbibzlibrary/telebibz` and installing the package from npm.
|
|
16
|
-
|
|
17
|
-
## Available starters
|
|
18
|
-
|
|
19
|
-
| Example | Demonstrates | Start command |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| `minimal.ts` | Commands, text routing, and a minimal long-polling bot. | `npx tsx examples/minimal.ts` |
|
|
22
|
-
| `wizard-registration.ts` | A two-step name/age conversation with automatic continuation across messages. | `npx tsx examples/wizard-registration.ts` |
|
|
23
|
-
| `webhook.ts` | A Node.js HTTP server, webhook secret validation, and Telegram update handling. | `TELEGRAM_WEBHOOK_SECRET=<secret> npx tsx examples/webhook.ts` |
|
|
24
|
-
|
|
25
|
-
The wizard example starts the flow with `/register`, asks for a name, then asks for an age. Send `/cancel` to cancel an active flow. The webhook example requires both `TELEGRAM_BOT_TOKEN` and `TELEGRAM_WEBHOOK_SECRET`; expose the server through HTTPS in a deployment environment and configure the matching Telegram webhook secret.
|
|
26
|
-
|
|
27
|
-
## Production checklist
|
|
28
|
-
|
|
29
|
-
Use a secret manager or deployment environment for `TELEGRAM_BOT_TOKEN` and `TELEGRAM_WEBHOOK_SECRET`. Do not commit `.env` files or literal credentials. Run `npm run test:examples` before submitting changes. For production deployments, prefer a managed HTTPS endpoint, structured JSON logs, health checks, graceful shutdown, and a persistent storage adapter when conversations or sessions must survive restarts.
|
|
30
|
-
|
|
31
|
-
## More documentation
|
|
32
|
-
|
|
33
|
-
- [English API reference](../docs/API.md)
|
|
34
|
-
- [Bahasa Indonesia API reference](../docs/API.id.md)
|
|
35
|
-
- [简体中文 API reference](../docs/API.zh-CN.md)
|
|
36
|
-
- [GitHub Packages guide](../docs/GITHUB_PACKAGES.md)
|
|
37
|
-
- [Contributing guide](../CONTRIBUTING.md)
|