@xbibzlibrary/telebibz 0.4.5 → 3.1.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 +53 -138
- package/LICENSE +1 -1
- package/NOTICE.md +9 -4
- package/README.id.md +542 -206
- package/README.md +539 -213
- package/examples/01-quickstart.js +13 -0
- package/examples/02-menu-tombol.js +22 -0
- package/examples/03-wizard.js +58 -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 +106 -0
- package/index.js +44 -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 +193 -0
- package/lib/wizard.js +247 -0
- package/package.json +35 -97
- package/test/all.test.js +439 -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.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/API.zh-CN.md
DELETED
|
@@ -1,1929 +0,0 @@
|
|
|
1
|
-
# telebibz API 参考 — 简体中文
|
|
2
|
-
|
|
3
|
-
[English](API.md) · [Bahasa Indonesia](API.id.md) · **简体中文**
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
本文件是当前发布的 `@xbibzlibrary/telebibz` API 参考。此处描述的所有签名和行为均映射自该包导出的 TypeScript 源代码。如果某个 Telegram 类型尚未有特定的参数/结果映射,该包仍通过动态 API 提供运行时访问,但其参数类型仍为通用类型。
|
|
8
|
-
|
|
9
|
-
> **实现状态。** 本文档说明当前版本中可用的功能。`JsonFileStorage`、基于 driver 的 Redis/SQL/Mongo storage、基于 Storage 的 session/conversation、完整五字段 cron、`MenuController`、带 branding 的 terminal status output、带 redaction 的 structured logging、Web App 验证、`PaymentsClient` 和 `TelegramTypes` declaration 均已提供。core method map 仍主要为特定 request/result inference 提供类型,未来 Telegram method 可通过 `api.raw()` 访问。
|
|
10
|
-
|
|
11
|
-
## 安装与导入
|
|
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
|
-
可用的子路径导出如下:
|
|
36
|
-
|
|
37
|
-
| 子路径 | 内容 |
|
|
38
|
-
|---|---|
|
|
39
|
-
| `@xbibzlibrary/telebibz` | 来自 `src/index.ts` 的全部主要公共 API |
|
|
40
|
-
| `@xbibzlibrary/telebibz/api` | Client、transport、errors,以及所有 Telegram API 类型 |
|
|
41
|
-
| `@xbibzlibrary/telebibz/keyboard` | `InlineKeyboard`、`ReplyKeyboard` 以及键盘辅助函数 |
|
|
42
|
-
| `@xbibzlibrary/telebibz/testing` | `MockTransport` 以及测试工厂 |
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## 1. 核心 Bot
|
|
47
|
-
|
|
48
|
-
### `BotStatus`
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
type BotStatus =
|
|
52
|
-
| "created"
|
|
53
|
-
| "initialized"
|
|
54
|
-
| "starting"
|
|
55
|
-
| "running"
|
|
56
|
-
| "stopping"
|
|
57
|
-
| "stopped"
|
|
58
|
-
| "error";
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### `BotOptions<S>`
|
|
62
|
-
|
|
63
|
-
| 属性 | 类型 | 默认 | 说明 |
|
|
64
|
-
|---|---|---:|---|
|
|
65
|
-
| `token` | `string` | 必需 | Token BotFather dengan format `<digits>:<token>`. |
|
|
66
|
-
| `apiBaseUrl` | `string` | `https://api.telegram.org` | Telegram API 的基础 URL。结尾的 `/` 会被自动移除。 |
|
|
67
|
-
| `transport` | `Transport` | `FetchTransport` | 用于 mock、proxy 或其他实现的自定义 transport。 |
|
|
68
|
-
| `transportOptions` | `Omit<FetchTransportOptions, "baseUrl">` | `{}` | 超时、重试、退避、jitter、headers 和 fetch 实现。 |
|
|
69
|
-
| `session` | `Storage<string, S>` | 新的存储 | 基于 chat/user key 的会话存储,可使用持久化适配器。 |
|
|
70
|
-
| `services` | `Record<string, unknown>` | `{}` | 通过 `ctx.services` 可用的依赖/服务。 |
|
|
71
|
-
| `branding` | `boolean` | `true` | 终端启动体验:打字效果、glass 进度条、动画彩虹 `Tele Bibz` 横幅以及易读的 update 日志行。仅在交互式 TTY 上渲染。 |
|
|
72
|
-
| `polling.timeout` | `number` | `30` | 用于 `getUpdates` 的长轮询超时(秒)。 |
|
|
73
|
-
| `polling.limit` | `number` | `100` | 每次轮询请求的最大 update 数量。 |
|
|
74
|
-
| `polling.allowedUpdates` | `string[]` | `[]` | Telegram 更新过滤器。 |
|
|
75
|
-
| `polling.retryDelayMs` | `number` | `500` | 轮询失败时的初始延迟(毫秒)。 |
|
|
76
|
-
| `polling.maxRetryDelayMs` | `number` | `30000` | 重连延迟的最大值(毫秒)。 |
|
|
77
|
-
| `updates.concurrency` | `number` | `Infinity` | 同时处理的 update 数量上限。不同 chat 的 update 始终并行,同一 chat 内保持顺序,因此 1000+ 条消息的突发可一次性处理。 |
|
|
78
|
-
| `handlerTimeout` | `number` | `90000` | 单个 update 的处理超时(毫秒,`Infinity` 表示禁用)。超时后走 update 错误流程(`update:error`、`bot:error`、`catch()` 边界),`handleUpdate()` 以 `UpdateTimeoutError` 拒绝,而 handler 仍在后台继续运行直至完成。 |
|
|
79
|
-
| `contextType` | `new (options: ContextOptions<S>) => Context<S>` | `Context` | 为每个 update 实例化的自定义 `Context` 子类(Telegraf 的 `contextType`)。 |
|
|
80
|
-
|
|
81
|
-
### `Bot` constructor
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
new Bot<S extends object = Record<string, unknown>>(
|
|
85
|
-
options: string | BotOptions<S>,
|
|
86
|
-
): Bot<S>
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Jika argumen berupa string, string tersebut dianggap sebagai token. Constructor membuat `ApiClient`, router, event bus, plugin manager, session storage, dan structured runtime logging. Constructor langsung memancarkan event `bot:created` secara asynchronous.
|
|
90
|
-
|
|
91
|
-
Constructor melempar `Error` jika token kosong atau tidak sesuai pola token Telegram.
|
|
92
|
-
|
|
93
|
-
### `Bot` 的属性和 getter
|
|
94
|
-
|
|
95
|
-
| API | 类型 | 描述 |
|
|
96
|
-
|---|---|---|
|
|
97
|
-
| `api` | `ApiClient` | 类型化/动态的 Telegram 客户端。 |
|
|
98
|
-
| `router` | `Router<Context<S>>` | bot 的主路由器。 |
|
|
99
|
-
| `events` | `EventBus<EventMap>` | 生命周期、update、API、webhook 和 polling 的事件总线。 |
|
|
100
|
-
| `plugins` | `PluginManager<Context<S>>` | 插件的生命周期管理器。 |
|
|
101
|
-
| `session` | `Storage<string, S>` | bot 的会话存储,可使用持久化适配器。 |
|
|
102
|
-
| `services` | `Record<string, unknown>` | 构造函数提供的 service 的拷贝。 |
|
|
103
|
-
| `token` | `string` | 客户端使用的 bot token。 |
|
|
104
|
-
| `status` | `BotStatus` | 当前生命周期状态。 |
|
|
105
|
-
| `botInfo` | `User \| undefined` | 最近一次 `getMe()` 的结果。 |
|
|
106
|
-
|
|
107
|
-
### `bot.use(...middleware)`
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
use(...middleware: Middleware<Context<S>>[]): this
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
添加全局 middleware。middleware 在每个 update 的 router 之前执行,按注册顺序。返回 bot 实例以便链式调用。
|
|
114
|
-
|
|
115
|
-
### `bot.command(name, handler)`
|
|
116
|
-
|
|
117
|
-
```ts
|
|
118
|
-
command(name: string, handler: Middleware<Context<S>>): this
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
注册 Telegram 命令,可带或不带前导 `/`。匹配时取 `/` 之后的第一个 token,并忽略 `@` 后的 bot mention。例如 `/start@my_bot` 会匹配 `"start"`。
|
|
122
|
-
|
|
123
|
-
### `bot.callback(pattern, handler)`
|
|
124
|
-
|
|
125
|
-
```ts
|
|
126
|
-
callback(pattern: string | RegExp, handler: Middleware<Context<S>>): this
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
callback query 路由的快捷方式。以 `*` 结尾的字符串表示前缀匹配;其他字符串必须完全相等。
|
|
130
|
-
|
|
131
|
-
### `bot.onText(text, handler)`
|
|
132
|
-
|
|
133
|
-
```ts
|
|
134
|
-
onText(text: string, handler: Middleware<Context<S>>): this
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
处理 `message.text` 与 `text` 完全相同的消息。
|
|
138
|
-
|
|
139
|
-
### `bot.onRegex(expression, handler)`
|
|
140
|
-
|
|
141
|
-
```ts
|
|
142
|
-
onRegex(expression: RegExp, handler: Middleware<Context<S>>): this
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
使用 `RegExp` 处理消息文本。路由参数不会自动提取到 `ctx.params`;如需提取请使用 predicate 或自定义 middleware。
|
|
146
|
-
|
|
147
|
-
### `bot.on(filter, handler)`
|
|
148
|
-
|
|
149
|
-
```ts
|
|
150
|
-
on(filter: UpdateFilter | UpdateFilter[], handler: Middleware<Context<S>>): this
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
按更新类型注册处理器,并可用 payload 字段收窄。示例:`"message"`、`"message:text"`、`"message:photo"`、`"edited_message"`、`"channel_post"`、`"callback_query"`、`"callback_query:data"`、`"inline_query"`、`"chat_member"`、`"message_reaction"`,或数组如 `["message:text", "callback_query:data"]`。无效的更新类型会在注册时抛出 `TypeError`。
|
|
154
|
-
|
|
155
|
-
### `bot.hears(trigger, handler)`
|
|
156
|
-
|
|
157
|
-
```ts
|
|
158
|
-
hears(trigger: string | RegExp, handler: Middleware<Context<S>>): this
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
处理完全匹配的文本(string)或匹配 `RegExp` 的消息文本。
|
|
162
|
-
|
|
163
|
-
### `bot.catch(handler)`
|
|
164
|
-
|
|
165
|
-
```ts
|
|
166
|
-
catch(handler: (error: unknown, ctx: Context<S>) => void | Promise<void>): this
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
注册更新处理器的错误边界。设置后,处理器失败会被记录、以 `update:error`/`bot:error` 事件发出,并转发给该 handler,而不是让 `handleUpdate()` 拒绝 —— webhook 返回 `200`,轮询继续。未设置边界时错误会被重新抛出。
|
|
170
|
-
|
|
171
|
-
### `bot.usePlugin(plugin)`
|
|
172
|
-
|
|
173
|
-
```ts
|
|
174
|
-
usePlugin(plugin: Plugin<Context<S>>): this
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
注册插件。插件名称必须唯一。
|
|
178
|
-
|
|
179
|
-
### `bot.init()`
|
|
180
|
-
|
|
181
|
-
```ts
|
|
182
|
-
init(): Promise<this>
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
调用 `getMe()`,保存 bot 信息,初始化插件,并返回可用于 polling 或手动处理 update 的 bot。
|
|
186
|
-
|
|
187
|
-
`init()` 在状态已为 `initialized` 或 `running` 时是幂等的。
|
|
188
|
-
|
|
189
|
-
### `bot.start()`
|
|
190
|
-
|
|
191
|
-
```ts
|
|
192
|
-
start(): Promise<void>
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
是 `launch({ mode: "polling" })` 的快捷方式。此方法开始 long polling 并等待直到轮询被停止或发生致命错误。
|
|
196
|
-
|
|
197
|
-
### `bot.launch(options?)`
|
|
198
|
-
|
|
199
|
-
```ts
|
|
200
|
-
launch(options?: {
|
|
201
|
-
mode: "polling";
|
|
202
|
-
timeout?: number;
|
|
203
|
-
allowedUpdates?: string[];
|
|
204
|
-
dropPendingUpdates?: boolean;
|
|
205
|
-
}): Promise<void>
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
以 polling 模式运行 bot。启动时生命周期依次变为 `starting` 然后 `running`,之后 `getUpdates()` 循环并发处理每一批 update:不同 chat 的 update 并行执行,同一 chat 的 update 保持到达顺序。轮询失败会触发 `polling:reconnect` 并使用指数退避。`dropPendingUpdates: true`(`bot.start()` 同样支持)会在第一次 `getUpdates` 之前丢弃 Telegram 为该 bot 持有的全部更新,使用与 Telegraf 相同的 `deleteWebhook({ drop_pending_updates: true })` 机制。
|
|
209
|
-
|
|
210
|
-
除 `"polling"` 外的模式会抛出错误,并建议对 webhook 使用 `createWebhookHandler()`。
|
|
211
|
-
|
|
212
|
-
### `bot.stop()`
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
stop(): Promise<void>
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
通过 `AbortController` 停止轮询,调用 `plugins.dispose()`,将状态设置为 `stopped`,并触发 stopping/stopped 事件。当状态为 `created` 或 `stopped` 时调用不会有任何效果。
|
|
219
|
-
|
|
220
|
-
### `bot.restart()`
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
restart(): Promise<void>
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
先运行 `stop()` 然后 `start()`。
|
|
227
|
-
|
|
228
|
-
### `bot.health()`
|
|
229
|
-
|
|
230
|
-
```ts
|
|
231
|
-
health(): Promise<HealthStatus>
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
调用 `getMe()` 检查 API 可达性。请求失败不会抛出错误;失败会以 `apiReachable: false` 和错误信息的形式返回。
|
|
235
|
-
|
|
236
|
-
```ts
|
|
237
|
-
interface HealthStatus {
|
|
238
|
-
status: BotStatus;
|
|
239
|
-
apiReachable: boolean;
|
|
240
|
-
bot?: User;
|
|
241
|
-
checkedAt: string; // ISO timestamp
|
|
242
|
-
error?: string;
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### `bot.getMe()`
|
|
247
|
-
|
|
248
|
-
```ts
|
|
249
|
-
getMe(): Promise<User>
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
从 Telegram 获取 bot 数据并更新 `botInfo`。
|
|
253
|
-
|
|
254
|
-
### `bot.setCommands(commands, scope?, languageCode?)`
|
|
255
|
-
|
|
256
|
-
```ts
|
|
257
|
-
setCommands(
|
|
258
|
-
commands: BotCommand[],
|
|
259
|
-
scope?: BotCommandScope,
|
|
260
|
-
languageCode?: string,
|
|
261
|
-
): Promise<true>
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
相当于 `setMyCommands` 的快捷方式。`languageCode` 会映射为 Telegram 的 `language_code` 字段。
|
|
265
|
-
|
|
266
|
-
### `bot.deleteCommands(scope?, languageCode?)`
|
|
267
|
-
|
|
268
|
-
```ts
|
|
269
|
-
deleteCommands(
|
|
270
|
-
scope?: BotCommandScope,
|
|
271
|
-
languageCode?: string,
|
|
272
|
-
): Promise<true>
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
相当于 `deleteMyCommands` 的快捷方式。
|
|
276
|
-
|
|
277
|
-
### `bot.downloadFile(fileId, options?)`
|
|
278
|
-
|
|
279
|
-
```ts
|
|
280
|
-
downloadFile(
|
|
281
|
-
fileId: string,
|
|
282
|
-
options?: { signal?: AbortSignal; destination?: string },
|
|
283
|
-
): Promise<DownloadedFile>
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
通过 `getFile` 解析 `fileId`,再经由 transport 的下载端点获取原始字节。传入 `destination` 可同时把字节保存到本地文件路径(结果中的 `savedTo` 会被填充)。当 Telegram 未返回 `file_path` 或 transport 不支持下载时抛出 `TelegramError`(kind 为 `validation`);下载失败时抛出 `TelegramNetworkError`。Telegram 限制单次下载 20 MB;返回的 `url` 至少一小时内有效。
|
|
287
|
-
|
|
288
|
-
```ts
|
|
289
|
-
const file = await bot.downloadFile(photoFileId, { destination: "downloads/photo.jpg" });
|
|
290
|
-
console.log(file.fileName, file.sizeBytes, file.url, file.savedTo);
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### `bot.handleUpdate(update)`
|
|
294
|
-
|
|
295
|
-
```ts
|
|
296
|
-
handleUpdate(update: Update, options?: { webhookReply?: WebhookReplySink }): Promise<void>
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
手动处理单个 update。该方法根据 `chat.id` 和 `from.id` 确定会话 key,创建 `Context`(由配置的 `contextType` 实例化),触发 `update` 和 `message` 事件,执行 middleware 然后路由器,并在流水线完成后保存会话。
|
|
300
|
-
|
|
301
|
-
不同 chat 的 update 并行处理;同一 chat 的 update 按到达顺序串行处理,因此会话、wizard 和 conversation 永远不会交错,会话写入也不会丢失。并发的 update 突发只会触发一次 `getMe` 初始化。整个单 update 流程受 `handlerTimeout` 保护(默认 90 秒,与 Telegraf 一致):超时后错误会流经 `update:error`/`bot:error` 和 `catch()` 边界,`handleUpdate()` 以 `UpdateTimeoutError` 拒绝,而 handler 仍在后台继续运行。
|
|
302
|
-
|
|
303
|
-
`options.webhookReply` 安装一个 Telegraf 风格的响应器:该 update 期间第一个外发 API 调用通过 webhook HTTP 响应本身来应答(而不是单独发请求),并且以 `true` resolve(Telegram 从不把方法结果发回 webhook 响应)。
|
|
304
|
-
|
|
305
|
-
流水线错误会将 bot 状态置为 `error`,触发 `bot:error`,然后重新抛出错误。
|
|
306
|
-
|
|
307
|
-
### `bot.handleUpdates(updates)`
|
|
308
|
-
|
|
309
|
-
```ts
|
|
310
|
-
handleUpdates(updates: readonly Update[]): Promise<void>
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
一次性处理整批 update:批次中的每个 chat 立即处理——跨 chat 并行、同一 chat 内按序——因此 1000 条消息的突发绝不会卡在某个慢速 handler 后面。单个 handler 的失败会记录日志、以 `update:error` 触发事件并交给 `catch()` 错误边界;它们永远不会让该 promise 被 reject。轮询循环对每个 `getUpdates` 批次都使用此方法。
|
|
314
|
-
|
|
315
|
-
### `bot.broadcast(chatIds, send, options?)`
|
|
316
|
-
|
|
317
|
-
```ts
|
|
318
|
-
broadcast(
|
|
319
|
-
chatIds: readonly ChatId[],
|
|
320
|
-
send: (chatId: ChatId) => Promise<unknown>,
|
|
321
|
-
options?: BroadcastOptions,
|
|
322
|
-
): Promise<BroadcastReport>
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
并行向大量 chat 发送消息——专为向 1000+ 用户广播而设计。没有主动冷却:所有 chat(至多 `options.concurrency`,默认 `Infinity`)同时尝试发送。当 Telegram 返回 429 时,会严格按照 Telegram 指定的 `retry_after` 延迟自动重试(至多 `options.maxAttempts` 次,默认 `10`),因此突发流量会完整送达而不是失败。不可重试的错误(例如 bot 无法发送的 chat)会按 chat 记录在返回的报告中。
|
|
326
|
-
|
|
327
|
-
```ts
|
|
328
|
-
const report = await bot.broadcast(
|
|
329
|
-
subscriberIds,
|
|
330
|
-
(chatId) => bot.api.methods.sendMessage({ chat_id: chatId, text: "Newsletter #42" }),
|
|
331
|
-
{ onProgress: (progress) => console.log(`${progress.delivered}/${progress.total} delivered`) },
|
|
332
|
-
);
|
|
333
|
-
console.log(`Delivered ${report.delivered} of ${report.total} in ${report.durationMs}ms`);
|
|
334
|
-
for (const failure of report.failures) console.warn(`Failed: ${failure.chatId} — ${failure.error}`);
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
#### `BroadcastOptions` 和 `BroadcastReport`
|
|
338
|
-
|
|
339
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
340
|
-
|---|---|---:|---|
|
|
341
|
-
| `BroadcastOptions.concurrency` | `number` | `Infinity` | 同时向多少个 chat 发送消息。 |
|
|
342
|
-
| `BroadcastOptions.maxAttempts` | `number` | `10` | Telegram 返回 429 时每个 chat 的尝试次数。 |
|
|
343
|
-
| `BroadcastOptions.onProgress` | `(progress: BroadcastProgress) => void` | — | 每个 chat 结束后调用。 |
|
|
344
|
-
| `BroadcastOptions.signal` | `AbortSignal` | — | 中止待发送的消息;已送达的消息保持送达。 |
|
|
345
|
-
| `BroadcastReport.total` | `number` | — | 本次广播的 chat 总数。 |
|
|
346
|
-
| `BroadcastReport.delivered` | `number` | — | 成功收到消息的 chat 数。 |
|
|
347
|
-
| `BroadcastReport.failed` | `number` | — | 未收到消息的 chat 数。 |
|
|
348
|
-
| `BroadcastReport.durationMs` | `number` | — | 本次广播的实际耗时(毫秒)。 |
|
|
349
|
-
| `BroadcastReport.failures` | `BroadcastFailure[]` | — | 按 chat 记录的 `{ chatId, attempts, error, errorKind }`。 |
|
|
350
|
-
|
|
351
|
-
### `UpdateTimeoutError` 与 webhook-reply 辅助函数
|
|
352
|
-
|
|
353
|
-
```ts
|
|
354
|
-
class UpdateTimeoutError extends Error {
|
|
355
|
-
readonly name = "UpdateTimeoutError";
|
|
356
|
-
readonly updateId: number;
|
|
357
|
-
}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
当单个 update 超过 `handlerTimeout` 时由 `handleUpdate()` 拒绝抛出。handler 本身继续运行;错误同样会流经 `update:error`、`bot:error` 和 `catch()` 边界。
|
|
361
|
-
|
|
362
|
-
```ts
|
|
363
|
-
type WebhookReplySink = (payload: Record<string, unknown>) => void;
|
|
364
|
-
runWithWebhookReply(sink, fn): Promise<T> // 为 fn 内的所有 API 调用设置响应器
|
|
365
|
-
runWithoutWebhookReply(fn): Promise<T> // 永不占用槽位的库内部调用
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
导出这些函数,让自定义 webhook 服务器能够以与 `createWebhookHandler` 相同的方式接入 webhook 应答。
|
|
369
|
-
|
|
370
|
-
### 最小 bot 示例
|
|
371
|
-
|
|
372
|
-
```ts
|
|
373
|
-
import { Bot, InlineKeyboard } from "@xbibzlibrary/telebibz";
|
|
374
|
-
|
|
375
|
-
const bot = new Bot({
|
|
376
|
-
token: process.env.TELEGRAM_BOT_TOKEN!,
|
|
377
|
-
polling: { allowedUpdates: ["message", "callback_query"] },
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
bot.command("start", async (ctx) => {
|
|
381
|
-
await ctx.reply("来自 telebibz 的问候", {
|
|
382
|
-
reply_markup: new InlineKeyboard()
|
|
383
|
-
.text("状态", "status")
|
|
384
|
-
.build(),
|
|
385
|
-
});
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
bot.callback("status", async (ctx) => {
|
|
389
|
-
await ctx.answerCallbackQuery("机器人已激活");
|
|
390
|
-
await ctx.reply("状态:running");
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
await bot.start();
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
---
|
|
397
|
-
|
|
398
|
-
## 2. 事件总线
|
|
399
|
-
|
|
400
|
-
### `EventMap`
|
|
401
|
-
|
|
402
|
-
| 事件 | 负载 |
|
|
403
|
-
|---|---|
|
|
404
|
-
| `bot:created` | `{ bot: unknown }` |
|
|
405
|
-
| `bot:initialized` | `{ bot: unknown }` |
|
|
406
|
-
| `bot:starting` | `{ bot: unknown }` |
|
|
407
|
-
| `bot:started` | `{ bot: unknown }` |
|
|
408
|
-
| `bot:stopping` | `{ bot: unknown }` |
|
|
409
|
-
| `bot:stopped` | `{ bot: unknown }` |
|
|
410
|
-
| `bot:error` | `{ bot: unknown; error: unknown }` |
|
|
411
|
-
| `update` | `{ update: unknown }` |
|
|
412
|
-
| `message` | `{ message: unknown }` |
|
|
413
|
-
| `command` | `{ name: string; update: unknown }` |
|
|
414
|
-
| `callback` | `{ data: string; update: unknown }` |
|
|
415
|
-
| `api:request` | `{ method: string; payload: unknown }` |
|
|
416
|
-
| `api:response` | `{ method: string; durationMs: number; response: unknown }` |
|
|
417
|
-
| `api:error` | `{ method: string; durationMs: number; error: unknown }` |
|
|
418
|
-
| `webhook:request` | `{ update: unknown }` |
|
|
419
|
-
| `polling:reconnect` | `{ error: unknown; attempt: number }` |
|
|
420
|
-
|
|
421
|
-
### `EventBus<Events>`
|
|
422
|
-
|
|
423
|
-
```ts
|
|
424
|
-
new EventBus<Events extends Record<string, unknown> = EventMap>()
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
| 方法 | 签名 | 行为 |
|
|
428
|
-
|---|---|---|
|
|
429
|
-
| `on` | `on<K>(event: K, listener: (payload: Events[K]) => void \| Promise<void>): () => void` | 添加监听器并返回取消订阅函数。 |
|
|
430
|
-
| `once` | `once<K>(event: K, listener: ...): () => void` | 监听器仅调用一次,然后被移除。 |
|
|
431
|
-
| `off` | `off<K>(event: K, listener: ...): void` | 移除指定的监听器。 |
|
|
432
|
-
| `emit` | `emit<K>(event: K, payload: Events[K]): Promise<void>` | 依次调用监听器并等待每个完成。 |
|
|
433
|
-
| `removeAllListeners` | `removeAllListeners(): void` | 移除所有监听器。 |
|
|
434
|
-
| `listenerCount` | `listenerCount<K>(event: K): number` | 返回事件的监听器数量。 |
|
|
435
|
-
|
|
436
|
-
```ts
|
|
437
|
-
const unsubscribe = bot.events.on("bot:error", ({ error }) => {
|
|
438
|
-
console.error(error);
|
|
439
|
-
});
|
|
440
|
-
unsubscribe();
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
---
|
|
444
|
-
|
|
445
|
-
## 3. API 客户端、传输 与 错误
|
|
446
|
-
|
|
447
|
-
### 基本类型
|
|
448
|
-
|
|
449
|
-
```ts
|
|
450
|
-
type ChatId = number | string;
|
|
451
|
-
type ParseMode = "Markdown" | "MarkdownV2" | "HTML";
|
|
452
|
-
type InputFile =
|
|
453
|
-
| string
|
|
454
|
-
| Uint8Array
|
|
455
|
-
| ArrayBuffer
|
|
456
|
-
| Blob
|
|
457
|
-
| NodeJS.ReadableStream
|
|
458
|
-
| { source: string | Uint8Array | ArrayBuffer | Blob | NodeJS.ReadableStream; filename?: string };
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
`InputFile` string 可以是普通字符串,或在作为 upload 对象中的 `source` 时为文件路径。在 Node.js 中,绝对路径、`./...` 和 `../...` 会被 `FetchTransport` 读取,然后作为 multipart 文件发送。
|
|
462
|
-
|
|
463
|
-
### `TelegramResponse<T>`
|
|
464
|
-
|
|
465
|
-
```ts
|
|
466
|
-
interface TelegramResponse<T> {
|
|
467
|
-
ok: boolean;
|
|
468
|
-
result?: T;
|
|
469
|
-
description?: string;
|
|
470
|
-
error_code?: number;
|
|
471
|
-
parameters?: ResponseParameters;
|
|
472
|
-
}
|
|
473
|
-
```
|
|
474
|
-
|
|
475
|
-
### `TransportRequest`, `TransportResponse`, 和 `Transport`
|
|
476
|
-
|
|
477
|
-
```ts
|
|
478
|
-
interface TransportRequest {
|
|
479
|
-
method: string;
|
|
480
|
-
payload?: Record<string, unknown>;
|
|
481
|
-
signal?: AbortSignal;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
interface TransportResponse<T = unknown> {
|
|
485
|
-
status: number;
|
|
486
|
-
headers: Headers;
|
|
487
|
-
data: TelegramResponse<T>;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
interface Transport {
|
|
491
|
-
request<T>(request: TransportRequest): Promise<TransportResponse<T>>;
|
|
492
|
-
}
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
### `FetchTransportOptions`
|
|
496
|
-
|
|
497
|
-
| 属性 | 默认 | 描述 |
|
|
498
|
-
|---|---:|---|
|
|
499
|
-
| `baseUrl` | `https://api.telegram.org` | 在 `/<method>` 之前的 URL 前缀。 |
|
|
500
|
-
| `fetch` | `globalThis.fetch` | 自定义 fetch 实现。 |
|
|
501
|
-
| `timeoutMs` | `30000` | 每次尝试的超时(毫秒)。 |
|
|
502
|
-
| `retries` | `2` | 初始尝试后的网络错误重试次数。 |
|
|
503
|
-
| `backoffMs` | `250` | 初始指数退避延迟(毫秒)。 |
|
|
504
|
-
| `maxBackoffMs` | `8000` | 传输延迟上限(毫秒)。 |
|
|
505
|
-
| `jitter` | `0.2` | 对指数延迟的随机抖动,范围为 ±20%。 |
|
|
506
|
-
| `floodGate` | `true` | 当 Telegram 返回 429 时,暂停新的请求直到 Telegram 指定的 `retry_after` 窗口结束。这不是主动冷却——唯一的等待就是 Telegram 自己要求的等待。 |
|
|
507
|
-
| `headers` | `{}` | 额外的请求头。 |
|
|
508
|
-
|
|
509
|
-
### `new FetchTransport(options?)`
|
|
510
|
-
|
|
511
|
-
```ts
|
|
512
|
-
new FetchTransport(options?: FetchTransportOptions): FetchTransport
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
内置的 transport 基于 `fetch`。不含上传的 payload 会以 JSON 发送。包含 `Uint8Array`、`ArrayBuffer`、`Blob` 或嵌套上传的 payload 会使用 `FormData` 作为 `multipart/form-data` 发送。
|
|
516
|
-
|
|
517
|
-
### `fetchTransport.request(request)`
|
|
518
|
-
|
|
519
|
-
```ts
|
|
520
|
-
request<T>(request: TransportRequest): Promise<TransportResponse<T>>
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
发送 POST 到 `${baseUrl}/${method}`。以 `/` 开头的 method 会被正规化。外部的 AbortSignal 会转发到内部的 controller。被判定为可重试的网络错误会按指数退避并加抖动进行重试;当重试耗尽时,错误会被封装为 `TelegramNetworkError`。
|
|
524
|
-
|
|
525
|
-
### `ApiHookContext`, `ApiClientOptions`, dan `ApiMethods`
|
|
526
|
-
|
|
527
|
-
```ts
|
|
528
|
-
interface ApiHookContext {
|
|
529
|
-
method: string;
|
|
530
|
-
payload: unknown;
|
|
531
|
-
startedAt: number;
|
|
532
|
-
durationMs?: number;
|
|
533
|
-
response?: TelegramResponse<unknown>;
|
|
534
|
-
error?: unknown;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
interface ApiClientOptions {
|
|
538
|
-
transport: Transport;
|
|
539
|
-
hooks?: {
|
|
540
|
-
onRequest?: (context: ApiHookContext) => void | Promise<void>;
|
|
541
|
-
onResponse?: (context: ApiHookContext) => void | Promise<void>;
|
|
542
|
-
onError?: (context: ApiHookContext) => void | Promise<void>;
|
|
543
|
-
};
|
|
544
|
-
}
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
`ApiMethods` 是基于 184 个 `TelegramMethodName` 的映射类型:
|
|
548
|
-
|
|
549
|
-
```ts
|
|
550
|
-
type ApiMethods = {
|
|
551
|
-
[M in TelegramMethodName]:
|
|
552
|
-
(...args: ApiCallArgs<M>) => Promise<ApiResult<M>>;
|
|
553
|
-
};
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### `new ApiClient(options)`
|
|
557
|
-
|
|
558
|
-
```ts
|
|
559
|
-
new ApiClient(options: ApiClientOptions): ApiClient
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
在 `client.methods` 上创建动态方法代理。Hook `onRequest` 在调用 transport 之前触发,`onResponse` 在收到响应后触发,`onError` 在请求失败或 Telegram 返回 `ok: false` 时触发。
|
|
563
|
-
|
|
564
|
-
### `api.methods.<method>(params?)`
|
|
565
|
-
|
|
566
|
-
动态方法可以直接调用。无参数的方法(如 `getMe()`)无需传入参数;其他方法接受单个对象参数。
|
|
567
|
-
|
|
568
|
-
```ts
|
|
569
|
-
const me = await bot.api.methods.getMe();
|
|
570
|
-
const chat = await bot.api.methods.getChat({ chat_id: "@channel" });
|
|
571
|
-
const message = await bot.api.methods.sendMessage({
|
|
572
|
-
chat_id: 123456789,
|
|
573
|
-
text: "你好",
|
|
574
|
-
});
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### `api.call(method, ...args)`
|
|
578
|
-
|
|
579
|
-
```ts
|
|
580
|
-
call<M extends TelegramMethodName>(
|
|
581
|
-
method: M,
|
|
582
|
-
...args: ApiCallArgs<M>
|
|
583
|
-
): Promise<ApiResult<M>>
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
用于基于字符串字面量调用方法的类型化形式。
|
|
587
|
-
|
|
588
|
-
### `api.request(method, payload?, signal?)`
|
|
589
|
-
|
|
590
|
-
```ts
|
|
591
|
-
request<M extends TelegramMethodName>(
|
|
592
|
-
method: M,
|
|
593
|
-
payload?: ApiParams<M>,
|
|
594
|
-
signal?: AbortSignal,
|
|
595
|
-
): Promise<ApiResult<M>>
|
|
596
|
-
```
|
|
597
|
-
|
|
598
|
-
低层请求方法,允许显式传入 `AbortSignal`。
|
|
599
|
-
|
|
600
|
-
### `api.raw(method, payload?, signal?)`
|
|
601
|
-
|
|
602
|
-
```ts
|
|
603
|
-
raw(
|
|
604
|
-
method: string,
|
|
605
|
-
payload?: Record<string, unknown>,
|
|
606
|
-
signal?: AbortSignal,
|
|
607
|
-
): Promise<unknown>
|
|
608
|
-
```
|
|
609
|
-
|
|
610
|
-
在 transport 上调用任意字符串方法。用于调用尚未包含在 `TelegramMethodMap` 的新的 Telegram 方法或参数。即使响应为 `ok: false`,也会被转换为 `TelegramError`。
|
|
611
|
-
|
|
612
|
-
### `api.downloadFile(fileId, options?)`
|
|
613
|
-
|
|
614
|
-
```ts
|
|
615
|
-
downloadFile(fileId: string, options?: { signal?: AbortSignal }): Promise<DownloadedFile>
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
`bot.downloadFile` 的 API 客户端核心:解析 `getFile`、校验返回了 `file_path`,然后通过 transport 下载字节。
|
|
619
|
-
|
|
620
|
-
### `DownloadedFile`
|
|
621
|
-
|
|
622
|
-
```ts
|
|
623
|
-
interface DownloadedFile {
|
|
624
|
-
file: File; // getFile 返回的 Telegram File 对象
|
|
625
|
-
bytes: Uint8Array; // 原始字节(Telegram 上限 20 MB)
|
|
626
|
-
filePath: string; // 用于下载的 file_path
|
|
627
|
-
url: string; // 直接下载 URL,至少一小时内有效
|
|
628
|
-
fileName: string; // filePath 的最后一段
|
|
629
|
-
sizeBytes: number; // bytes 的字节长度
|
|
630
|
-
savedTo?: string; // 当 Bot.downloadFile 把文件写入磁盘时填充
|
|
631
|
-
}
|
|
632
|
-
```
|
|
633
|
-
|
|
634
|
-
### `fetchTransport.fileUrl(filePath)` 与 `fetchTransport.download(filePath, signal?)`
|
|
635
|
-
|
|
636
|
-
```ts
|
|
637
|
-
fileUrl(filePath: string): string
|
|
638
|
-
download(filePath: string, signal?: AbortSignal): Promise<Uint8Array>
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
`FetchTransport` 把 `/bot<token>` 基础 URL 映射为 `/file/bot<token>` 下载端点;`download` 以 GET 获取字节(大文件的 timeout 下限为 120 秒),HTTP 失败时抛出 `TelegramNetworkError`。两者都是 `Transport` 接口的可选成员,自定义 transport 可以省略 —— 此时 `downloadFile` 会以明确的校验错误失败,而不是崩溃。
|
|
642
|
-
|
|
643
|
-
### 可用的类型化参数和返回值
|
|
644
|
-
|
|
645
|
-
以下类型在此发布版本中已被映射:
|
|
646
|
-
|
|
647
|
-
| 方法 | 参数 | 返回值 |
|
|
648
|
-
|---|---|---|
|
|
649
|
-
| `getMe` | 无 | `User` |
|
|
650
|
-
| `getUpdates` | `GetUpdatesParams` | `Update[]` |
|
|
651
|
-
| `setWebhook` | `SetWebhookParams` | `boolean` |
|
|
652
|
-
| `deleteWebhook` | `{ drop_pending_updates?: boolean }` | `boolean` |
|
|
653
|
-
| `getWebhookInfo` | 无 | `WebhookInfo` |
|
|
654
|
-
| `sendMessage` | `SendMessageParams` | `Message` |
|
|
655
|
-
| `editMessageText` | `EditMessageTextParams` | `Message \| true` |
|
|
656
|
-
| `deleteMessage` | `DeleteMessageParams` | `true` |
|
|
657
|
-
| `answerCallbackQuery` | `AnswerCallbackQueryParams` | `true` |
|
|
658
|
-
| `getChat` | `GetChatParams` | `Chat` |
|
|
659
|
-
| `getFile` | `GetFileParams` | `File` |
|
|
660
|
-
| `getUserProfilePhotos` | `{ user_id: number; offset?: number; limit?: number }` | `UserProfilePhotos` |
|
|
661
|
-
| `sendPhoto` | `SendPhotoParams` | `Message` |
|
|
662
|
-
| `sendDocument` | `SendDocumentParams` | `Message` |
|
|
663
|
-
|
|
664
|
-
可用的附加参数类型包括 `ReplyParameters`、`LinkPreviewOptions`、`InlineKeyboardButton`、`ReplyMarkup`、`BotCommand`、`BotCommandScope`,以及从 `api/types.ts` 导出的所有 Telegram update 类型。
|
|
665
|
-
|
|
666
|
-
### API 错误
|
|
667
|
-
|
|
668
|
-
```ts
|
|
669
|
-
type TelegramErrorKind =
|
|
670
|
-
| "retryable"
|
|
671
|
-
| "rate-limit"
|
|
672
|
-
| "authentication"
|
|
673
|
-
| "validation"
|
|
674
|
-
| "network"
|
|
675
|
-
| "server"
|
|
676
|
-
| "unknown";
|
|
677
|
-
```
|
|
678
|
-
|
|
679
|
-
#### `TelegramError`
|
|
680
|
-
|
|
681
|
-
```ts
|
|
682
|
-
new TelegramError(message: string, options: {
|
|
683
|
-
method: string;
|
|
684
|
-
payload: unknown;
|
|
685
|
-
errorCode?: number;
|
|
686
|
-
parameters?: ResponseParameters;
|
|
687
|
-
status?: number;
|
|
688
|
-
kind?: TelegramErrorKind;
|
|
689
|
-
cause?: unknown;
|
|
690
|
-
})
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
公开属性有 `kind`、`errorCode`、`parameters`、`method`、`payload` 和 `status`。Getter `retryAfter` 读取 `parameters.retry_after`;getter `migrateToChatId` 读取 `parameters.migrate_to_chat_id`。
|
|
694
|
-
|
|
695
|
-
#### 错误子类
|
|
696
|
-
|
|
697
|
-
| 类 | `name` | 强制的 `kind` |
|
|
698
|
-
|---|---|---|
|
|
699
|
-
| `TelegramRateLimitError` | `TelegramRateLimitError` | `rate-limit` |
|
|
700
|
-
| `TelegramAuthError` | `TelegramAuthError` | `authentication` |
|
|
701
|
-
| `TelegramValidationError` | `TelegramValidationError` | `validation` |
|
|
702
|
-
| `TelegramNetworkError` | `TelegramNetworkError` | `network` |
|
|
703
|
-
|
|
704
|
-
这四个子类使用与 `TelegramError` 相同的构造器选项。
|
|
705
|
-
|
|
706
|
-
#### `classifyTelegramError(errorCode?, status?)`
|
|
707
|
-
|
|
708
|
-
```ts
|
|
709
|
-
classifyTelegramError(
|
|
710
|
-
errorCode?: number,
|
|
711
|
-
status?: number,
|
|
712
|
-
): TelegramErrorKind
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
实际分类:`429` 映射为 `rate-limit`;错误 `401` 或 HTTP `401/403` 映射为 `authentication`;错误代码 `400–499` 映射为 `validation`;HTTP `500+` 映射为 `server`;其他为 `unknown`。
|
|
716
|
-
|
|
717
|
-
#### `telegramErrorFromResponse(response, context)`
|
|
718
|
-
|
|
719
|
-
```ts
|
|
720
|
-
telegramErrorFromResponse<T>(
|
|
721
|
-
response: TelegramResponse<T>,
|
|
722
|
-
context: { method: string; payload: unknown; status?: number },
|
|
723
|
-
): TelegramError
|
|
724
|
-
```
|
|
725
|
-
|
|
726
|
-
将失败的 Telegram 响应转换为相应的子类。`429`、认证和验证错误会产生相应的子类;其他错误会生成普通的 `TelegramError`。
|
|
727
|
-
|
|
728
|
-
---
|
|
729
|
-
|
|
730
|
-
## 4. 上下文
|
|
731
|
-
|
|
732
|
-
### `ContextOptions<S>`
|
|
733
|
-
|
|
734
|
-
```ts
|
|
735
|
-
interface ContextOptions<S extends object = Record<string, unknown>> {
|
|
736
|
-
update: Update;
|
|
737
|
-
api: ApiClient;
|
|
738
|
-
session: S;
|
|
739
|
-
services: Record<string, unknown>;
|
|
740
|
-
}
|
|
741
|
-
```
|
|
742
|
-
|
|
743
|
-
### `Context<S>` 属性
|
|
744
|
-
|
|
745
|
-
| 属性 | 内容 |
|
|
746
|
-
|---|---|
|
|
747
|
-
| `update` | Telegram 的原始 Update. |
|
|
748
|
-
| `api` | 机器人 `ApiClient`. |
|
|
749
|
-
| `session` | 当前 update key 的可变 session 对象. |
|
|
750
|
-
| `state` | 每个上下文的临时对象,不会自动保存到 session. |
|
|
751
|
-
| `services` | 通过 `BotOptions.services` 提供的服务. |
|
|
752
|
-
| `params` | 路由参数对象;内置路由器目前不会自动填充. |
|
|
753
|
-
| `message` | 来自 message/edited/channel/business/guest 更新的主要 message. |
|
|
754
|
-
| `chat` | 若可用则为 `message.chat`. |
|
|
755
|
-
| `from` / `sender` | 来自 message、callback query 或 inline query 的用户. |
|
|
756
|
-
| `callbackQuery` | `update.callback_query`. |
|
|
757
|
-
| `inlineQuery` | `update.inline_query`. |
|
|
758
|
-
| `poll` | `update.poll`. |
|
|
759
|
-
| `pollAnswer` | `update.poll_answer`. |
|
|
760
|
-
| `chatMember` | `update.chat_member`. |
|
|
761
|
-
| `myChatMember` | `update.my_chat_member`. |
|
|
762
|
-
| `chatJoinRequest` | `update.chat_join_request`. |
|
|
763
|
-
| `reaction` | `update.message_reaction`. |
|
|
764
|
-
| `boost` | `chat_boost` 或 `removed_chat_boost`. |
|
|
765
|
-
|
|
766
|
-
### `new Context(options)`
|
|
767
|
-
|
|
768
|
-
```ts
|
|
769
|
-
new Context<S>(options: ContextOptions<S>): Context<S>
|
|
770
|
-
```
|
|
771
|
-
|
|
772
|
-
### Context 消息方法
|
|
773
|
-
|
|
774
|
-
| 方法 | 签名 | 行为 |
|
|
775
|
-
|---|---|---|
|
|
776
|
-
| `reply` | `reply(text, extra?): Promise<Message>` | 将消息发送到更新的聊天,并在存在消息时设置 `reply_parameters.message_id`. |
|
|
777
|
-
| `send` | `send(text, extra?): Promise<Message>` | 向更新的聊天发送消息,不带回复引用. |
|
|
778
|
-
| `edit` | `edit(text, extra?): Promise<Message \| true>` | 使用 `editMessageText` 编辑更新的消息. |
|
|
779
|
-
| `delete` | `delete(): Promise<true>` | 删除更新的消息. |
|
|
780
|
-
| `replyWithHTML` | `replyWithHTML(text, extra?): Promise<Message>` | 以 `parse_mode: "HTML"` 回复. |
|
|
781
|
-
| `replyWithMarkdown` | `replyWithMarkdown(text, extra?): Promise<Message>` | 以 `parse_mode: "MarkdownV2"` 回复. |
|
|
782
|
-
| `replyWithPhoto` | `replyWithPhoto(photo, extra?): Promise<Message>` | 发送 `sendPhoto`,自动引用回复. |
|
|
783
|
-
| `replyWithDocument` | `replyWithDocument(document, extra?): Promise<Message>` | 发送 `sendDocument`,自动引用回复. |
|
|
784
|
-
| `replyWithAudio` | `replyWithAudio(audio, extra?): Promise<Message>` | 发送 `sendAudio`,自动引用回复. |
|
|
785
|
-
| `replyWithVideo` | `replyWithVideo(video, extra?): Promise<Message>` | 发送 `sendVideo`,自动引用回复. |
|
|
786
|
-
| `replyWithVoice` | `replyWithVoice(voice, extra?): Promise<Message>` | 发送 `sendVoice`,自动引用回复. |
|
|
787
|
-
| `replyWithAnimation` | `replyWithAnimation(animation, extra?): Promise<Message>` | 发送 `sendAnimation`,自动引用回复. |
|
|
788
|
-
| `replyWithVideoNote` | `replyWithVideoNote(videoNote, extra?): Promise<Message>` | 发送 `sendVideoNote`,自动引用回复. |
|
|
789
|
-
| `replyWithSticker` | `replyWithSticker(sticker, extra?): Promise<Message>` | 发送 `sendSticker`,自动引用回复. |
|
|
790
|
-
| `replyWithMediaGroup` | `replyWithMediaGroup(media, extra?): Promise<Message[]>` | 通过 `sendMediaGroup` 发送相册,自动引用回复. |
|
|
791
|
-
| `replyWithLocation` | `replyWithLocation(latitude, longitude, extra?): Promise<Message>` | 发送 `sendLocation`,自动引用回复. |
|
|
792
|
-
| `replyWithVenue` | `replyWithVenue(latitude, longitude, title, address, extra?): Promise<Message>` | 发送 `sendVenue`,自动引用回复. |
|
|
793
|
-
| `replyWithContact` | `replyWithContact(phoneNumber, firstName, extra?): Promise<Message>` | 发送 `sendContact`,自动引用回复. |
|
|
794
|
-
| `replyWithPoll` | `replyWithPoll(question, options, extra?): Promise<Message>` | 发送 `sendPoll`,自动引用回复. |
|
|
795
|
-
| `replyWithDice` | `replyWithDice(emoji?, extra?): Promise<Message>` | 发送 `sendDice`,自动引用回复. |
|
|
796
|
-
| `copy` | `copy(fromChatId, messageId, extra?): Promise<unknown>` | 向上下文聊天调用 `copyMessage`. |
|
|
797
|
-
| `forward` | `forward(fromChatId, messageId, extra?): Promise<Message>` | 向上下文聊天调用 `forwardMessage`. |
|
|
798
|
-
| `pin` | `pin(messageId?, extra?): Promise<true>` | 调用 `pinChatMessage`,默认消息 ID 来自上下文. |
|
|
799
|
-
| `unpin` | `unpin(messageId?, extra?): Promise<true>` | 调用 `unpinChatMessage`,默认消息 ID 来自上下文. |
|
|
800
|
-
| `react` | `react(reaction, extra?): Promise<true>` | 调用 `setMessageReaction`. |
|
|
801
|
-
| `answerCallbackQuery` | `answerCallbackQuery(text?, extra?): Promise<true>` | 回答活动的回调查询。如果不是回调更新则抛出错误. |
|
|
802
|
-
| `answerInlineQuery` | `answerInlineQuery(results, extra?): Promise<true>` | 回答活动的内联查询。如果不是内联更新则抛出错误. |
|
|
803
|
-
| `getChat` | `getChat(): Promise<Chat>` | 获取上下文聊天的详细信息. |
|
|
804
|
-
| `getUserProfilePhotos` | `getUserProfilePhotos(userId?, extra?): Promise<unknown>` | 获取上下文用户的头像照片. |
|
|
805
|
-
| `getFile` | `getFile(fileId): Promise<unknown>` | 根据 ID 获取文件. |
|
|
806
|
-
| `withReplyMarkup` | `withReplyMarkup(markup): this` | 将标记保存到 `ctx.state.reply_markup` 并返回上下文。此方法不会自动发送消息. |
|
|
807
|
-
|
|
808
|
-
`reply`、`send`、`getChat` 以及其他一些辅助方法在更新缺少所需聊天时会抛出错误。`edit` 和 `delete` 需要同时有聊天和消息。
|
|
809
|
-
|
|
810
|
-
### Context 管理员、聊天与论坛方法(与 Telegraf 完全对齐)
|
|
811
|
-
|
|
812
|
-
以下方法均作用于本次更新的聊天(`ctx.chat`),并通过 `extra` 接受原生 Telegram 参数;当更新没有聊天时都会抛出清晰的错误。要操作其他聊天请使用 `ctx.api.methods.*`。
|
|
813
|
-
|
|
814
|
-
| 分组 | 方法 |
|
|
815
|
-
|---|---|
|
|
816
|
-
| 管理/封禁 | `banChatMember(userId, untilDate?, extra?)`、`unbanChatMember(userId, onlyIfBanned?, extra?)`、`restrictChatMember(userId, permissions, untilDate?, extra?)`、`promoteChatMember(userId, extra?)`、`banChatSenderChat(senderChatId, extra?)`、`unbanChatSenderChat(senderChatId, extra?)` |
|
|
817
|
-
| 聊天管理 | `setChatTitle(title)`、`setChatDescription(description?)`、`setChatPhoto(photo)`、`deleteChatPhoto()`、`setChatPermissions(permissions, extra?)`、`leaveChat()`、`unpinAllChatMessages(extra?)`、`setChatStickerSet(name)`、`deleteChatStickerSet()` |
|
|
818
|
-
| 聊天与成员信息 | `getChatAdministrators(): Promise<ChatMember[]>`、`getChatMemberCount(): Promise<number>`、`getChatMember(userId): Promise<ChatMember>` |
|
|
819
|
-
| 邀请链接 | `exportChatInviteLink(): Promise<string>`、`createChatInviteLink(extra?)`、`editChatInviteLink(inviteLink, extra?)`、`revokeChatInviteLink(inviteLink)` |
|
|
820
|
-
| 加群申请 | `approveChatJoinRequest(userId)`、`declineChatJoinRequest(userId)` |
|
|
821
|
-
| 投票与实时位置 | `replyWithQuiz(question, options, extra?)`(`type: "quiz"` 的 sendPoll)、`stopPoll(messageId?, extra?)`、`editMessageLiveLocation(latitude?, longitude?, extra?)`、`stopMessageLiveLocation(extra?)` |
|
|
822
|
-
| 游戏与支付 | `replyWithGame(gameShortName, extra?)`、`setGameScore(userId, score, extra?)`、`getGameHighScores(userId?, extra?)`、`replyWithInvoice(title, description, payload, providerToken, currency, prices, extra?)` |
|
|
823
|
-
| 论坛主题 | `createForumTopic(name, extra?)`、`editForumTopic(extra?)`、`closeForumTopic(threadId?)`、`reopenForumTopic(threadId?)`、`deleteForumTopic(threadId?)`、`unpinAllForumTopicMessages(threadId?)`、`getForumTopicIconStickers()`、`editGeneralForumTopic(name)`、`closeGeneralForumTopic()`、`reopenGeneralForumTopic()`、`hideGeneralForumTopic()`、`unhideGeneralForumTopic()` |
|
|
824
|
-
|
|
825
|
-
`threadId` 默认取上下文消息的 `message_thread_id`。`replyWithQuiz`、`replyWithGame` 和 `replyWithInvoice` 与所有 `replyWith*` 发送者一样自动引用回复。
|
|
826
|
-
|
|
827
|
-
---
|
|
828
|
-
|
|
829
|
-
## 5. 中间件与路由器
|
|
830
|
-
|
|
831
|
-
### Middleware types
|
|
832
|
-
|
|
833
|
-
```ts
|
|
834
|
-
type Next = () => Promise<void>;
|
|
835
|
-
type Middleware<Context> = (ctx: Context, next: Next) => void | Promise<void>;
|
|
836
|
-
```
|
|
837
|
-
|
|
838
|
-
以洋葱模型(onion pattern)组合中间件。`next()` 会执行下一个中间件。如果同一个中间件多次调用 `next()`,`compose` 会抛出 `Error("next() called multiple times")`。
|
|
839
|
-
|
|
840
|
-
### `middleware(handler)`
|
|
841
|
-
|
|
842
|
-
```ts
|
|
843
|
-
middleware<Context>(handler: Middleware<Context>): Middleware<Context>
|
|
844
|
-
```
|
|
845
|
-
|
|
846
|
-
用于为中间件提供注解/类型推导的标识辅助函数。
|
|
847
|
-
|
|
848
|
-
### `RoutableContext`
|
|
849
|
-
|
|
850
|
-
路由器所需的最小上下文:`update`、`message`、`callbackQuery` 和 `params`。
|
|
851
|
-
|
|
852
|
-
### `Router<Context>`
|
|
853
|
-
|
|
854
|
-
```ts
|
|
855
|
-
new Router<Context extends RoutableContext>(): Router<Context>
|
|
856
|
-
```
|
|
857
|
-
|
|
858
|
-
路由按照优先级和注册顺序处理。匹配的路由不会自动阻止后续路由;所有匹配的路由都可以被执行。如果没有任何路由匹配,则在 `handle` 上的 `terminal` 会被调用。
|
|
859
|
-
|
|
860
|
-
| 方法 | 签名 | 匹配 |
|
|
861
|
-
|---|---|---|
|
|
862
|
-
| `use` | `use(...middleware): this` | 全局路由中间件,具有最高优先级,先执行。 |
|
|
863
|
-
| `route` | `route(matcher, ...middleware): this` | 布尔或异步的自定义匹配器。 |
|
|
864
|
-
| `command` | `command(name: string \| RegExp, ...middleware): this` | 以 `/` 开头的消息文本的第一个命令。 |
|
|
865
|
-
| `text` | `text(value: string, ...middleware): this` | 精确文本匹配。 |
|
|
866
|
-
| `regex` | `regex(expression: RegExp, ...middleware): this` | 对消息文本或空字符串使用 `RegExp.test`。 |
|
|
867
|
-
| `callback` | `callback(pattern: string \| RegExp, ...middleware): this` | 对 callback 数据进行精确匹配、以 `*` 结尾作为前缀匹配,或使用正则匹配。 |
|
|
868
|
-
| `chat` | `chat(chatId: number \| string, ...middleware): this` | 匹配 `message.chat.id`,数值或字符串等价。 |
|
|
869
|
-
| `predicate` | `predicate(matcher, ...middleware): this` | 自定义 matcher 的语义别名。 |
|
|
870
|
-
| `nest` | `nest(child: Router<Context>): this` | 将子路由作为嵌套路由运行。 |
|
|
871
|
-
| `handle` | `handle(ctx, terminal?): Promise<void>` | 评估并执行所有匹配的路由。 |
|
|
872
|
-
|
|
873
|
-
```ts
|
|
874
|
-
const router = new Router<Context>();
|
|
875
|
-
router.use(async (ctx, next) => {
|
|
876
|
-
console.log("before");
|
|
877
|
-
await next();
|
|
878
|
-
});
|
|
879
|
-
router.callback("page:*", async (ctx) => {
|
|
880
|
-
await ctx.answerCallbackQuery();
|
|
881
|
-
});
|
|
882
|
-
router.predicate((ctx) => Boolean(ctx.from?.id), async (ctx) => {
|
|
883
|
-
await ctx.reply("Authenticated update");
|
|
884
|
-
});
|
|
885
|
-
```
|
|
886
|
-
|
|
887
|
-
**RegExp 注意事项。** 路由器直接调用 `.test()`。对于带有 `g` 或 `y` 标志的表达式,JavaScript 中有状态的 `lastIndex` 可能会影响重复匹配。
|
|
888
|
-
|
|
889
|
-
---
|
|
890
|
-
|
|
891
|
-
## 6. Keyboard builders
|
|
892
|
-
|
|
893
|
-
### `InlineKeyboard`
|
|
894
|
-
|
|
895
|
-
```ts
|
|
896
|
-
new InlineKeyboard(): InlineKeyboard
|
|
897
|
-
InlineKeyboard.from(rows: InlineKeyboardButton[][]): InlineKeyboard
|
|
898
|
-
```
|
|
899
|
-
|
|
900
|
-
构建器以可变方式保存 `rows`,并且所有构建器方法都返回 `this`。
|
|
901
|
-
|
|
902
|
-
| Method | Signature | 描述 |
|
|
903
|
-
|---|---|---|
|
|
904
|
-
| `from` | `static from(rows): InlineKeyboard` | 从 `rows` 创建键盘并复制每一行。 |
|
|
905
|
-
| `text` | `text(text, callbackData): this` | 回调按钮。 |
|
|
906
|
-
| `url` | `url(text, url): this` | URL 按钮。 |
|
|
907
|
-
| `webApp` | `webApp(text, url): this` | Web 应用按钮。 |
|
|
908
|
-
| `pay` | `pay(text = "Pay"): this` | 支付按钮。 |
|
|
909
|
-
| `copy` | `copy(text, copiedText): this` | 复制文本按钮。 |
|
|
910
|
-
| `button` | `button(button): this` | 将一个按钮添加到最后一行,或创建第一行。 |
|
|
911
|
-
| `row` | `row(...buttons): this` | 添加新行。 |
|
|
912
|
-
| `conditional` | `conditional(condition, factory): this` | 仅当 condition 为 true 时执行 factory。 |
|
|
913
|
-
| `grid` | `grid(buttons, columns): this` | 将按钮按列数分配到各行。 |
|
|
914
|
-
| `build` | `build(): InlineKeyboardMarkup` | 生成新的 markup。 |
|
|
915
|
-
| `asReplyMarkup` | `asReplyMarkup(): InlineKeyboardMarkup` | `build` 的别名。 |
|
|
916
|
-
|
|
917
|
-
每个 inline 按钮必须有 text 并且恰好一个 action。回调数据限制为最多 64 字节 UTF-8;超出会抛出 `RangeError`。
|
|
918
|
-
|
|
919
|
-
```ts
|
|
920
|
-
const keyboard = new InlineKeyboard()
|
|
921
|
-
.text("允许", "approve:123")
|
|
922
|
-
.url("文档", "https://example.com")
|
|
923
|
-
.row(
|
|
924
|
-
{ text: "A", callback_data: "a" },
|
|
925
|
-
{ text: "B", callback_data: "b" },
|
|
926
|
-
)
|
|
927
|
-
.build();
|
|
928
|
-
```
|
|
929
|
-
|
|
930
|
-
### `ReplyKeyboard`
|
|
931
|
-
|
|
932
|
-
```ts
|
|
933
|
-
new ReplyKeyboard(): ReplyKeyboard
|
|
934
|
-
```
|
|
935
|
-
|
|
936
|
-
| Method | Signature | 描述 |
|
|
937
|
-
|---|---|---|
|
|
938
|
-
| `text` | `text(text): this` | 普通文本按钮。 |
|
|
939
|
-
| `contact` | `contact(text): this` | 请求联系人。 |
|
|
940
|
-
| `location` | `location(text): this` | 请求位置。 |
|
|
941
|
-
| `poll` | `poll(text, type?): this` | 请求投票,类型为 `quiz` 或 `regular`。 |
|
|
942
|
-
| `webApp` | `webApp(text, url): this` | Web 应用按钮。 |
|
|
943
|
-
| `button` | `button(button): this` | 将一个按钮添加到最后一行。 |
|
|
944
|
-
| `row` | `row(...buttons): this` | 添加新行。 |
|
|
945
|
-
| `grid` | `grid(buttons, columns): this` | 将按钮划分为网格。 |
|
|
946
|
-
| `build` | `build(options?): ReplyKeyboardMarkup` | 生成 markup 并合并 options。 |
|
|
947
|
-
| `asReplyMarkup` | `asReplyMarkup(): ReplyKeyboardMarkup` | `build()` 的别名,不带 options。 |
|
|
948
|
-
|
|
949
|
-
columns 必须为正整数;否则,`grid` 会抛出 `RangeError`。
|
|
950
|
-
|
|
951
|
-
### `removeKeyboard(selective?)`
|
|
952
|
-
|
|
953
|
-
```ts
|
|
954
|
-
removeKeyboard(selective = false): ReplyMarkup
|
|
955
|
-
```
|
|
956
|
-
|
|
957
|
-
生成 `{ remove_keyboard: true }`,如果请求则包含 `selective: true`。
|
|
958
|
-
|
|
959
|
-
### `forceReply(placeholder?, selective?)`
|
|
960
|
-
|
|
961
|
-
```ts
|
|
962
|
-
forceReply(placeholder?: string, selective = false): ReplyMarkup
|
|
963
|
-
```
|
|
964
|
-
|
|
965
|
-
生成 ForceReply。仅当 placeholder 为 truthy 时才会添加占位符。
|
|
966
|
-
|
|
967
|
-
---
|
|
968
|
-
|
|
969
|
-
## 7. 存储与缓存
|
|
970
|
-
|
|
971
|
-
### `Storage<K, V>`
|
|
972
|
-
|
|
973
|
-
```ts
|
|
974
|
-
interface Storage<K, V> {
|
|
975
|
-
get(key: K): Promise<V | undefined>;
|
|
976
|
-
set(key: K, value: V, options?: { ttlMs?: number }): Promise<void>;
|
|
977
|
-
delete(key: K): Promise<boolean>;
|
|
978
|
-
has(key: K): Promise<boolean>;
|
|
979
|
-
clear(): Promise<void>;
|
|
980
|
-
keys(): AsyncIterable<K>;
|
|
981
|
-
values(): AsyncIterable<V>;
|
|
982
|
-
entries(): AsyncIterable<[K, V]>;
|
|
983
|
-
update<T extends V>(
|
|
984
|
-
key: K,
|
|
985
|
-
updater: (current: V | undefined) => T | Promise<T>,
|
|
986
|
-
options?: { ttlMs?: number },
|
|
987
|
-
): Promise<T>;
|
|
988
|
-
}
|
|
989
|
-
```
|
|
990
|
-
|
|
991
|
-
### `MemoryStorage<K, V>`
|
|
992
|
-
|
|
993
|
-
```ts
|
|
994
|
-
new MemoryStorage<K, V>(): MemoryStorage<K, V>
|
|
995
|
-
```
|
|
996
|
-
|
|
997
|
-
基于 `Map` 的内存实现。TTL 在读取或迭代键时惰性清理;没有后台定时器。`update` 使得每个键的 updater 操作串行执行,从而避免对同一键的并发更新出现意外覆盖。
|
|
998
|
-
|
|
999
|
-
```ts
|
|
1000
|
-
const sessions = new MemoryStorage<string, { count: number }>();
|
|
1001
|
-
await sessions.set("user:1", { count: 0 }, { ttlMs: 60_000 });
|
|
1002
|
-
await sessions.update("user:1", (current) => ({
|
|
1003
|
-
count: (current?.count ?? 0) + 1,
|
|
1004
|
-
}));
|
|
1005
|
-
```
|
|
1006
|
-
|
|
1007
|
-
### `Cache<K, V>`
|
|
1008
|
-
|
|
1009
|
-
```ts
|
|
1010
|
-
interface Cache<K = string, V = unknown> {
|
|
1011
|
-
get(key: K): Promise<V | undefined>;
|
|
1012
|
-
set(key: K, value: V, ttlMs?: number): Promise<void>;
|
|
1013
|
-
delete(key: K): Promise<boolean>;
|
|
1014
|
-
invalidate(prefix?: string): Promise<void>;
|
|
1015
|
-
getOrSet(key: K, factory: () => V | Promise<V>, ttlMs?: number): Promise<V>;
|
|
1016
|
-
}
|
|
1017
|
-
```
|
|
1018
|
-
|
|
1019
|
-
### `MemoryCache`
|
|
1020
|
-
|
|
1021
|
-
```ts
|
|
1022
|
-
new MemoryCache(namespace = "telebibz"): MemoryCache
|
|
1023
|
-
```
|
|
1024
|
-
|
|
1025
|
-
对字符串键的缓存,会对每个 key 在内部添加命名空间。
|
|
1026
|
-
|
|
1027
|
-
| Method | 行为 |
|
|
1028
|
-
|---|---|
|
|
1029
|
-
| `get` | 获取 value 或 `undefined`。 |
|
|
1030
|
-
| `set` | 存储 value 并可选 TTL。 |
|
|
1031
|
-
| `delete` | 删除 key 并返回布尔值。 |
|
|
1032
|
-
| `invalidate(prefix = "")` | 删除命名空间中以 prefix 开头的所有 key。 |
|
|
1033
|
-
| `getOrSet` | 返回缓存命中;若未命中,执行 factory,存储结果后返回。 |
|
|
1034
|
-
|
|
1035
|
-
`getOrSet` 不使用去重锁;当 key 还不存在且并发调用时,factory 可能会被执行多次。
|
|
1036
|
-
|
|
1037
|
-
### `RateLimitResult`
|
|
1038
|
-
|
|
1039
|
-
```ts
|
|
1040
|
-
interface RateLimitResult {
|
|
1041
|
-
allowed: boolean;
|
|
1042
|
-
remaining: number;
|
|
1043
|
-
resetAt: number;
|
|
1044
|
-
retryAfterMs?: number;
|
|
1045
|
-
}
|
|
1046
|
-
```
|
|
1047
|
-
|
|
1048
|
-
### `TokenBucketLimiter`
|
|
1049
|
-
|
|
1050
|
-
```ts
|
|
1051
|
-
new TokenBucketLimiter(
|
|
1052
|
-
capacity: number,
|
|
1053
|
-
refillPerSecond: number,
|
|
1054
|
-
): TokenBucketLimiter
|
|
1055
|
-
```
|
|
1056
|
-
|
|
1057
|
-
构造函数在任一参数非正时抛出 `RangeError`。`consume(key, cost = 1)` 在有可用令牌时扣减;若令牌不足,返回 `allowed: false` 并给出估计的 `retryAfterMs`。`clear(key?)` 删除单个桶或全部桶。
|
|
1058
|
-
|
|
1059
|
-
---
|
|
1060
|
-
|
|
1061
|
-
## 8. 队列和调度器
|
|
1062
|
-
|
|
1063
|
-
### `Job<T>` 和 `QueueOptions`
|
|
1064
|
-
|
|
1065
|
-
```ts
|
|
1066
|
-
interface Job<T = unknown> {
|
|
1067
|
-
id: string;
|
|
1068
|
-
data: T;
|
|
1069
|
-
attempts: number;
|
|
1070
|
-
priority: number;
|
|
1071
|
-
runAt: number;
|
|
1072
|
-
status: "queued" | "running" | "completed" | "failed" | "cancelled";
|
|
1073
|
-
error?: unknown;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
interface QueueOptions {
|
|
1077
|
-
concurrency?: number;
|
|
1078
|
-
retries?: number;
|
|
1079
|
-
backoffMs?: number;
|
|
1080
|
-
maxBackoffMs?: number;
|
|
1081
|
-
}
|
|
1082
|
-
```
|
|
1083
|
-
|
|
1084
|
-
### `TaskQueue<T>`
|
|
1085
|
-
|
|
1086
|
-
```ts
|
|
1087
|
-
new TaskQueue<T>(
|
|
1088
|
-
worker: (job: Job<T>, signal: AbortSignal) => Promise<void>,
|
|
1089
|
-
options?: QueueOptions,
|
|
1090
|
-
): TaskQueue<T>
|
|
1091
|
-
```
|
|
1092
|
-
|
|
1093
|
-
| 方法 | 签名 | 描述 |
|
|
1094
|
-
|---|---|---|
|
|
1095
|
-
| `add` | `add(data, options?): Job<T>` | 添加作业;options 包含 `id`, `priority`, `delayMs`。作业会立即被调度。 |
|
|
1096
|
-
| `get` | `get(id): Job<T> \| undefined` | 返回作业状态的副本。 |
|
|
1097
|
-
| `cancel` | `cancel(id): boolean` | 取消处于 queued 或 running 状态的作业并中止 worker 的 signal。 |
|
|
1098
|
-
| `onIdle` | `onIdle(): Promise<void>` | 等待直到 pending 和 active 为空。 |
|
|
1099
|
-
| `close` | `close(): Promise<void>` | 停止新的排空并取消活动的控制器。 |
|
|
1100
|
-
|
|
1101
|
-
优先级更高的作业先执行;若相同,则较早的 `runAt` 先执行。重试会一直进行直到超过 `retries`。重试延迟为指数增长,`maxBackoffMs` 的默认上限为 30 秒。
|
|
1102
|
-
|
|
1103
|
-
### `ScheduledJob`
|
|
1104
|
-
|
|
1105
|
-
```ts
|
|
1106
|
-
interface ScheduledJob {
|
|
1107
|
-
id: string;
|
|
1108
|
-
cancel: () => void;
|
|
1109
|
-
}
|
|
1110
|
-
```
|
|
1111
|
-
|
|
1112
|
-
### `Scheduler`
|
|
1113
|
-
|
|
1114
|
-
```ts
|
|
1115
|
-
new Scheduler(): Scheduler
|
|
1116
|
-
```
|
|
1117
|
-
|
|
1118
|
-
| 方法 | 签名 | 描述 |
|
|
1119
|
-
|---|---|---|
|
|
1120
|
-
| `every` | `every(id, intervalMs, task): ScheduledJob` | 使用 `setInterval` 运行任务。使用相同 id 替换定时器。 |
|
|
1121
|
-
| `after` | `after(id, delayMs, task): ScheduledJob` | 使用 `setTimeout` 执行一次任务。 |
|
|
1122
|
-
| `cron` | `cron(id, expression, task): ScheduledJob` | 支持分钟字段的简单格式 `*/N`,等同于间隔 `N * 60_000`。 |
|
|
1123
|
-
| `cancel` | `cancel(id): boolean` | 取消定时器。 |
|
|
1124
|
-
| `clear` | `clear(): void` | 取消所有定时器。 |
|
|
1125
|
-
|
|
1126
|
-
内置调度器不支持完整的 cron 格式。除 `*/N` 外的表达式会抛出 `Error`。
|
|
1127
|
-
|
|
1128
|
-
### `Limiter` 和 `mapWithConcurrency`
|
|
1129
|
-
|
|
1130
|
-
```ts
|
|
1131
|
-
new Limiter(limit: number): Limiter
|
|
1132
|
-
|
|
1133
|
-
mapWithConcurrency<T, R>(
|
|
1134
|
-
items: readonly T[],
|
|
1135
|
-
limit: number,
|
|
1136
|
-
worker: (item: T, index: number) => Promise<R>,
|
|
1137
|
-
): Promise<R[]>
|
|
1138
|
-
```
|
|
1139
|
-
|
|
1140
|
-
`Limiter` 是一个 promise 信号量:有空闲槽位时任务立即执行,超出后按 FIFO 排队。`limit` 接受任意正整数或 `Infinity`(完全并行——即本库的默认值)。`mapWithConcurrency` 以相同的并发上限让 item 通过 async worker 映射,同时保持结果顺序。这些原语自身不会添加任何延迟——它们只限制同时运行的任务数量。`Limiter` 暴露 `activeCount` 和 `queuedCount` 用于可观测性。
|
|
1141
|
-
|
|
1142
|
-
---
|
|
1143
|
-
|
|
1144
|
-
## 9. 插件与服务
|
|
1145
|
-
|
|
1146
|
-
### `Plugin<Context>`
|
|
1147
|
-
|
|
1148
|
-
```ts
|
|
1149
|
-
interface Plugin<Context = unknown> {
|
|
1150
|
-
name: string;
|
|
1151
|
-
version?: string;
|
|
1152
|
-
install?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
1153
|
-
setup?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
1154
|
-
onStart?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
1155
|
-
onUpdate?: (context: Context) => void | Promise<void>;
|
|
1156
|
-
onStop?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
1157
|
-
dispose?: (api: PluginApi<Context>) => void | Promise<void>;
|
|
1158
|
-
}
|
|
1159
|
-
```
|
|
1160
|
-
|
|
1161
|
-
### `PluginApi<Context>`
|
|
1162
|
-
|
|
1163
|
-
```ts
|
|
1164
|
-
interface PluginApi<Context> {
|
|
1165
|
-
bot: unknown;
|
|
1166
|
-
services: ServiceContainer;
|
|
1167
|
-
registerMiddleware: (middleware: unknown) => void;
|
|
1168
|
-
registerRoute: (route: unknown) => void;
|
|
1169
|
-
}
|
|
1170
|
-
```
|
|
1171
|
-
|
|
1172
|
-
在此发行版中,`registerMiddleware` 和 `registerRoute` 可作为 hook API 提供,但实现管理器尚未将它们自动连接到 bot/router。插件可以直接使用 `api.bot` 和 `api.services`。
|
|
1173
|
-
|
|
1174
|
-
### `ServiceContainer`
|
|
1175
|
-
|
|
1176
|
-
```ts
|
|
1177
|
-
new ServiceContainer(): ServiceContainer
|
|
1178
|
-
```
|
|
1179
|
-
|
|
1180
|
-
| 方法 | 签名 | 描述 |
|
|
1181
|
-
|---|---|---|
|
|
1182
|
-
| `register` | `register<T>(name: string \| symbol, value: T): this` | 存储服务并支持链式调用。 |
|
|
1183
|
-
| `get` | `get<T>(name: string \| symbol): T` | 获取服务;如果未注册则抛出错误。 |
|
|
1184
|
-
| `has` | `has(name: string \| symbol): boolean` | 检查服务是否存在。 |
|
|
1185
|
-
| `delete` | `delete(name: string \| symbol): boolean` | 删除服务。 |
|
|
1186
|
-
|
|
1187
|
-
### `PluginManager<Context>`
|
|
1188
|
-
|
|
1189
|
-
```ts
|
|
1190
|
-
new PluginManager<Context>(bot: unknown): PluginManager<Context>
|
|
1191
|
-
```
|
|
1192
|
-
|
|
1193
|
-
| 方法 | 行为 |
|
|
1194
|
-
|---|---|
|
|
1195
|
-
| `use(plugin)` | 添加插件;重复名称会抛出错误。 |
|
|
1196
|
-
| `setup()` | 对每个插件先执行 `install` 然后 `setup`。 |
|
|
1197
|
-
| `start()` | 按注册顺序执行 `onStart`。 |
|
|
1198
|
-
| `update(context)` | 按注册顺序执行 `onUpdate`。 |
|
|
1199
|
-
| `stop()` | 执行 `onStop`。 |
|
|
1200
|
-
| `dispose()` | 按相反的注册顺序执行 `dispose`。 |
|
|
1201
|
-
| `list()` | 返回只读的插件列表。 |
|
|
1202
|
-
|
|
1203
|
-
`Bot.handleUpdate()` 在此发行版中不会自动调用 `plugins.update()`;如果插件需要 update 生命周期,请显式调用管理器。
|
|
1204
|
-
|
|
1205
|
-
---
|
|
1206
|
-
|
|
1207
|
-
## 10. Webhook
|
|
1208
|
-
|
|
1209
|
-
### `WebhookOptions`
|
|
1210
|
-
|
|
1211
|
-
```ts
|
|
1212
|
-
interface WebhookOptions {
|
|
1213
|
-
secretToken?: string;
|
|
1214
|
-
maxBodyBytes?: number;
|
|
1215
|
-
onError?: (error: unknown) => void | Promise<void>;
|
|
1216
|
-
webhookReply?: boolean;
|
|
1217
|
-
}
|
|
1218
|
-
```
|
|
1219
|
-
|
|
1220
|
-
`webhookReply`(默认 `false`)启用 Telegraf 风格的 webhook 应答:处理 update 期间,第一个外发 API 调用直接通过 webhook HTTP 响应本身应答(`{"method":"sendMessage", ...}`),Telegram 因此无需第二次请求即可执行该方法。该调用以 `true` resolve,因为 Telegram 从不把方法结果发回 webhook 响应;之后的每个调用都照常走 transport。懒加载的 `getMe` 初始化永远不会占用该槽位。与 Telegraf 不同,此功能为 opt-in,已有的 webhook 部署行为保持完全不变。
|
|
1221
|
-
|
|
1222
|
-
### `createWebhookHandler(bot, options?)`
|
|
1223
|
-
|
|
1224
|
-
```ts
|
|
1225
|
-
createWebhookHandler<S extends object>(
|
|
1226
|
-
bot: Bot<S>,
|
|
1227
|
-
options?: WebhookOptions,
|
|
1228
|
-
): (request: Request) => Promise<Response>
|
|
1229
|
-
```
|
|
1230
|
-
|
|
1231
|
-
处理程序接收标准的 Web `Request` 并返回 `Response`。
|
|
1232
|
-
|
|
1233
|
-
| 情况 | 响应 |
|
|
1234
|
-
|---|---|
|
|
1235
|
-
| 方法不是 POST | `405 Method Not Allowed`, header `allow: POST` |
|
|
1236
|
-
| Secret 头部不匹配 | `401 Unauthorized` |
|
|
1237
|
-
| `Content-Length` 或 body 超过限制 | `413 Payload Too Large` |
|
|
1238
|
-
| JSON 无效或 `update_id` 不是整数 | 对于 update id 返回 `400 Bad Request`; 解析时的异常返回 `500` |
|
|
1239
|
-
| `bot.handleUpdate` 成功 | `200 OK`,body 为 `OK` |
|
|
1240
|
-
| 其他异常 | `500 Internal Server Error` 并调用 `onError` |
|
|
1241
|
-
|
|
1242
|
-
默认 `maxBodyBytes` 为 `1_048_576` bytes。Telegram 的 secret 从 header `x-telegram-bot-api-secret-token` 读取。
|
|
1243
|
-
|
|
1244
|
-
```ts
|
|
1245
|
-
import { Bot, createWebhookHandler } from "@xbibzlibrary/telebibz";
|
|
1246
|
-
|
|
1247
|
-
const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN!);
|
|
1248
|
-
const handler = createWebhookHandler(bot, {
|
|
1249
|
-
secretToken: process.env.TELEGRAM_WEBHOOK_SECRET,
|
|
1250
|
-
});
|
|
1251
|
-
|
|
1252
|
-
export default { fetch: handler };
|
|
1253
|
-
```
|
|
1254
|
-
|
|
1255
|
-
---
|
|
1256
|
-
|
|
1257
|
-
## 11. 会话、向导、表单和菜单
|
|
1258
|
-
|
|
1259
|
-
### Conversation
|
|
1260
|
-
|
|
1261
|
-
```ts
|
|
1262
|
-
interface ConversationState {
|
|
1263
|
-
name: string;
|
|
1264
|
-
step: number;
|
|
1265
|
-
values: Record<string, unknown>;
|
|
1266
|
-
status: "active" | "completed" | "cancelled";
|
|
1267
|
-
updatedAt: number;
|
|
1268
|
-
}
|
|
1269
|
-
```
|
|
1270
|
-
|
|
1271
|
-
#### `ConversationFlow<S>`
|
|
1272
|
-
|
|
1273
|
-
```ts
|
|
1274
|
-
new ConversationFlow(ctx: Context<S>, state: ConversationState)
|
|
1275
|
-
```
|
|
1276
|
-
|
|
1277
|
-
| 方法/属性 | 签名 | 描述 |
|
|
1278
|
-
|---|---|---|
|
|
1279
|
-
| `ctx` | `Context<S>` | 当前的更新上下文。 |
|
|
1280
|
-
| `state` | `ConversationState` | 可变的会话状态。 |
|
|
1281
|
-
| `values` | `Record<string, unknown>` | 是 `state.values` 的别名。 |
|
|
1282
|
-
| `set` | `set<T>(key, value): this` | 保存值并更新 `updatedAt`。 |
|
|
1283
|
-
| `get` | `get<T>(key): T \| undefined` | 获取类型化的值。 |
|
|
1284
|
-
| `next` | `next(): this` | 将 step 增加 1。 |
|
|
1285
|
-
| `previous` | `previous(): this` | 将 step 减少,但最低为 0。 |
|
|
1286
|
-
| `complete` | `complete(): void` | 将状态置为 `completed`。 |
|
|
1287
|
-
| `cancel` | `cancel(): void` | 将状态置为 `cancelled`。 |
|
|
1288
|
-
|
|
1289
|
-
#### `ConversationManager<S>`
|
|
1290
|
-
|
|
1291
|
-
```ts
|
|
1292
|
-
new ConversationManager<S>(): ConversationManager<S>
|
|
1293
|
-
```
|
|
1294
|
-
|
|
1295
|
-
| 方法 | 签名 | 描述 |
|
|
1296
|
-
|---|---|---|
|
|
1297
|
-
| `start` | `start(key, name, values?): ConversationState` | 创建或替换会话状态。 |
|
|
1298
|
-
| `get` | `get(key): ConversationState \| undefined` | 获取活动状态。 |
|
|
1299
|
-
| `cancel` | `cancel(key): boolean` | 如果存在则标记为 cancelled。 |
|
|
1300
|
-
| `clearExpired` | `clearExpired(maxAgeMs): number` | 删除 `updatedAt` 早于阈值的状态。 |
|
|
1301
|
-
| `run` | `run(ctx, key, name, steps): Promise<ConversationState>` | 根据 `state.step` 运行对应的步骤;如果没有步骤,则状态为 completed。 |
|
|
1302
|
-
|
|
1303
|
-
```ts
|
|
1304
|
-
const conversations = new ConversationManager();
|
|
1305
|
-
await conversations.run(ctx, "chat:1", "profile", [
|
|
1306
|
-
async (flow) => {
|
|
1307
|
-
flow.set("name", ctx.message?.text);
|
|
1308
|
-
flow.next();
|
|
1309
|
-
},
|
|
1310
|
-
async (flow) => {
|
|
1311
|
-
await flow.ctx.reply(`Nama: ${flow.get<string>("name")}`);
|
|
1312
|
-
flow.complete();
|
|
1313
|
-
},
|
|
1314
|
-
]);
|
|
1315
|
-
```
|
|
1316
|
-
|
|
1317
|
-
#### `Wizard<S>` dan `WizardStep<S>`
|
|
1318
|
-
|
|
1319
|
-
```ts
|
|
1320
|
-
interface WizardStep<S> {
|
|
1321
|
-
id: string;
|
|
1322
|
-
run: (flow: ConversationFlow<S>) => void | Promise<void>;
|
|
1323
|
-
optional?: boolean;
|
|
1324
|
-
}
|
|
1325
|
-
|
|
1326
|
-
new Wizard<S>()
|
|
1327
|
-
```
|
|
1328
|
-
|
|
1329
|
-
| 方法/属性 | 描述 |
|
|
1330
|
-
|---|---|
|
|
1331
|
-
| `step(definition)` | 添加步骤并返回 wizard。`optional` 保存在定义中,但 runner 还没有特殊处理。 |
|
|
1332
|
-
| `run(ctx, key, manager?)` | 通过 `ConversationManager` 使用 name 为 `"wizard"` 运行 wizard 的步骤。 |
|
|
1333
|
-
| `steps` | 只读的步骤列表。 |
|
|
1334
|
-
|
|
1335
|
-
### 表单
|
|
1336
|
-
|
|
1337
|
-
```ts
|
|
1338
|
-
interface ValidationIssue {
|
|
1339
|
-
path: string;
|
|
1340
|
-
message: string;
|
|
1341
|
-
code?: string;
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
interface Field<T> {
|
|
1345
|
-
name: string;
|
|
1346
|
-
parse: (input: unknown) => T;
|
|
1347
|
-
validate?: (value: T) => string | undefined | Promise<string | undefined>;
|
|
1348
|
-
transform?: (value: T) => T | Promise<T>;
|
|
1349
|
-
required?: boolean;
|
|
1350
|
-
}
|
|
1351
|
-
```
|
|
1352
|
-
|
|
1353
|
-
#### `Form<T>`
|
|
1354
|
-
|
|
1355
|
-
```ts
|
|
1356
|
-
new Form<T extends Record<string, unknown>>(): Form<T>
|
|
1357
|
-
```
|
|
1358
|
-
|
|
1359
|
-
| 方法 | 描述 |
|
|
1360
|
-
|---|---|
|
|
1361
|
-
| `field(definition)` | 根据 `name` 注册类型化字段。 |
|
|
1362
|
-
| `parse(input)` | 处理所有字段。返回 success 或 issues 的联合结果。顺序:required 检查、parse、transform、validate。 |
|
|
1363
|
-
| `reset()` | 清除内部保存的解析数据。 |
|
|
1364
|
-
|
|
1365
|
-
Result parse:
|
|
1366
|
-
|
|
1367
|
-
```ts
|
|
1368
|
-
type FormResult<T> =
|
|
1369
|
-
| { success: true; data: T }
|
|
1370
|
-
| { success: false; issues: ValidationIssue[] };
|
|
1371
|
-
```
|
|
1372
|
-
|
|
1373
|
-
Issue 使用 code `required`、`parse` 或 `invalid`。
|
|
1374
|
-
|
|
1375
|
-
#### `validators`
|
|
1376
|
-
|
|
1377
|
-
| 验证器 | 输入 | 结果/错误 |
|
|
1378
|
-
|---|---|---|
|
|
1379
|
-
| `validators.string` | `unknown` | String;否则 `TypeError("Expected string")`. |
|
|
1380
|
-
| `validators.number` | `unknown` | 有限的 Number,包括数字字符串;否则 `TypeError("Expected number")`. |
|
|
1381
|
-
| `validators.integer` | `unknown` | Integer;否则 `TypeError("Expected integer")`. |
|
|
1382
|
-
| `validators.email` | `unknown` | 符合简易 email 模式的 String;否则 `TypeError("Expected email")`. |
|
|
1383
|
-
| `validators.url` | `unknown` | 可被 `URL` 构造函数接受的 String;否则 `TypeError("Expected URL")`. |
|
|
1384
|
-
|
|
1385
|
-
### 分页与菜单
|
|
1386
|
-
|
|
1387
|
-
#### `Page<T>`
|
|
1388
|
-
|
|
1389
|
-
```ts
|
|
1390
|
-
interface Page<T> {
|
|
1391
|
-
items: T[];
|
|
1392
|
-
page: number;
|
|
1393
|
-
pageCount: number;
|
|
1394
|
-
hasPrevious: boolean;
|
|
1395
|
-
hasNext: boolean;
|
|
1396
|
-
}
|
|
1397
|
-
```
|
|
1398
|
-
|
|
1399
|
-
#### `paginate(items, page, pageSize)`
|
|
1400
|
-
|
|
1401
|
-
```ts
|
|
1402
|
-
paginate<T>(
|
|
1403
|
-
items: readonly T[],
|
|
1404
|
-
page: number,
|
|
1405
|
-
pageSize: number,
|
|
1406
|
-
): Page<T>
|
|
1407
|
-
```
|
|
1408
|
-
|
|
1409
|
-
Page 使用 0 为基的索引。超出范围的 page 会被夹取到最后一页。空集合仍然具有 `pageCount: 1`。负数或非整数的 `page` 或 `pageSize < 1` 会抛出 `RangeError`。
|
|
1410
|
-
|
|
1411
|
-
#### `paginationButtons(page, prefix)`
|
|
1412
|
-
|
|
1413
|
-
```ts
|
|
1414
|
-
paginationButtons(
|
|
1415
|
-
page: Page<unknown>,
|
|
1416
|
-
prefix: string,
|
|
1417
|
-
): InlineKeyboardButton[]
|
|
1418
|
-
```
|
|
1419
|
-
|
|
1420
|
-
生成 `Previous` 按钮、带回调 `${prefix}:noop` 的指示器 `${page + 1}/${pageCount}`,以及根据 page 标志显示 `Next`。
|
|
1421
|
-
|
|
1422
|
-
#### `MenuItem`
|
|
1423
|
-
|
|
1424
|
-
```ts
|
|
1425
|
-
interface MenuItem {
|
|
1426
|
-
id: string;
|
|
1427
|
-
label: string;
|
|
1428
|
-
callbackData?: string;
|
|
1429
|
-
url?: string;
|
|
1430
|
-
visible?: boolean | ((context: MenuContext, item: MenuItem) => boolean | Promise<boolean>);
|
|
1431
|
-
permission?: string | ((context: MenuContext, item: MenuItem) => boolean | Promise<boolean>);
|
|
1432
|
-
}
|
|
1433
|
-
```
|
|
1434
|
-
|
|
1435
|
-
#### `Menu`
|
|
1436
|
-
|
|
1437
|
-
```ts
|
|
1438
|
-
new Menu(id: string): Menu
|
|
1439
|
-
```
|
|
1440
|
-
|
|
1441
|
-
| 方法/属性 | 描述 |
|
|
1442
|
-
|---|---|
|
|
1443
|
-
| `item(item)` | 添加 item 并支持链式调用。 |
|
|
1444
|
-
| `breadcrumb(label)` | 添加 breadcrumb 标签。 |
|
|
1445
|
-
| `build()` | 等待可见性谓词,跳过不可见项,然后生成 `InlineKeyboard`。URL 优先于 callback。 |
|
|
1446
|
-
| `breadcrumbs` | `breadcrumbs` 只读的 breadcrumb 数组。 |
|
|
1447
|
-
|
|
1448
|
-
`permission` 仅作为 item 的元数据存储;`Menu.build()` 不会自动执行授权。
|
|
1449
|
-
|
|
1450
|
-
---
|
|
1451
|
-
|
|
1452
|
-
## 12. Terminal Logging
|
|
1453
|
-
|
|
1454
|
-
当 stdout 是交互式 TTY 时,每次 `bot.start()` / `bot.launch()` 都会播放启动序列:`Installing Dependencies......` 打字效果、带扫过高光的 glass 进度条,以及动画彩虹 ASCII 横幅 `Tele Bibz`(figlet `Speed` 字体)——持续流动直到 bot 连接成功,随后定格为 `✓ Connected as @<username>`。
|
|
1455
|
-
|
|
1456
|
-
bot 处理的每条 update 都会以易读的行格式记录:
|
|
1457
|
-
|
|
1458
|
-
```text
|
|
1459
|
-
[ => ] Message From 123456789 John Doe 29/08/2026 15:04:05
|
|
1460
|
-
↳ Text: /start
|
|
1461
|
-
[ => ] Callback From 123456789 John Doe 29/08/2026 15:04:07
|
|
1462
|
-
↳ Data: menu:open
|
|
1463
|
-
```
|
|
1464
|
-
|
|
1465
|
-
普通消息与命令文本截断为 50 个字符;回调按钮数据完整显示。错误以红色打印并附带完整堆栈。向 `Bot` 传入 `branding: false` 可关闭启动序列;设置 `logger.format: "json"` 时,进入的 update 会作为结构化 `update.received` entry 输出。非交互 stdout(管道、Docker、CI)自动回退为无动画的纯文本。
|
|
1466
|
-
|
|
1467
|
-
面向应用导出的附加 branding helper:`runStartupSequence()`、`startTeleBibzBanner()`、`printTeleBibzBanner()`、`paintRainbow()` 和 `printStatusLine()`。
|
|
1468
|
-
|
|
1469
|
-
## 13. 文本工具
|
|
1470
|
-
|
|
1471
|
-
### `escapeMarkdownV2(value)`
|
|
1472
|
-
|
|
1473
|
-
```ts
|
|
1474
|
-
escapeMarkdownV2(value: string): string
|
|
1475
|
-
```
|
|
1476
|
-
|
|
1477
|
-
对 Telegram MarkdownV2 字符进行转义:`\\_ * [ ] ( ) ~ ` > # + - = | { } . !`.
|
|
1478
|
-
|
|
1479
|
-
### `escapeHtml(value)`
|
|
1480
|
-
|
|
1481
|
-
```ts
|
|
1482
|
-
escapeHtml(value: string): string
|
|
1483
|
-
```
|
|
1484
|
-
|
|
1485
|
-
将 `&`、`<`、`>` 和 `"` 转换为 HTML 实体。
|
|
1486
|
-
|
|
1487
|
-
### `md`
|
|
1488
|
-
|
|
1489
|
-
下面可用的 MarkdownV2 辅助对象:
|
|
1490
|
-
|
|
1491
|
-
| Method | 输出(概念) |
|
|
1492
|
-
|---|---|
|
|
1493
|
-
| `md.bold(value)` | `*escaped value*` |
|
|
1494
|
-
| `md.italic(value)` | `_escaped value_` |
|
|
1495
|
-
| `md.link(label, url)` | `[escaped label](escaped url)` |
|
|
1496
|
-
| `md.code(value)` | 使用已转义反引号的行内代码。 |
|
|
1497
|
-
| `md.pre(value, language?)` | 带可选语言标签的代码块。 |
|
|
1498
|
-
| `md.escape(value)` | 别名 `escapeMarkdownV2`. |
|
|
1499
|
-
|
|
1500
|
-
### `splitMessage(text, options?)`
|
|
1501
|
-
|
|
1502
|
-
```ts
|
|
1503
|
-
splitMessage(
|
|
1504
|
-
text: string,
|
|
1505
|
-
options?: {
|
|
1506
|
-
limit?: number;
|
|
1507
|
-
parseMode?: "Markdown" | "MarkdownV2" | "HTML";
|
|
1508
|
-
},
|
|
1509
|
-
): string[]
|
|
1510
|
-
```
|
|
1511
|
-
|
|
1512
|
-
将文本拆分为片段,默认字符限制为 `4096`。在可能的情况下,拆分会优先选择段落边界、换行或空格;仅当边界位于窗口的一半以上时才使用该边界。`parseMode` 作为 API 的选项被接受,但目前并不会改变拆分算法。
|
|
1513
|
-
|
|
1514
|
-
小于 1 的 limit 会抛出 `RangeError`。
|
|
1515
|
-
|
|
1516
|
-
### `splitCaption(text)`
|
|
1517
|
-
|
|
1518
|
-
```ts
|
|
1519
|
-
splitCaption(text: string): string[]
|
|
1520
|
-
```
|
|
1521
|
-
|
|
1522
|
-
`splitMessage(text, { limit: 1024 })` 的快捷方式。
|
|
1523
|
-
|
|
1524
|
-
### `template(templateText, values)`
|
|
1525
|
-
|
|
1526
|
-
```ts
|
|
1527
|
-
template(
|
|
1528
|
-
templateText: string,
|
|
1529
|
-
values: Record<string, unknown>,
|
|
1530
|
-
): string
|
|
1531
|
-
```
|
|
1532
|
-
|
|
1533
|
-
替换占位符 `{{ key }}` 以及类似 `{{ user.name }}` 的嵌套路径。`null` 或 `undefined` 的值会被替换为空字符串;其他值将使用 `String()` 转换。
|
|
1534
|
-
|
|
1535
|
-
```ts
|
|
1536
|
-
template("你好 {{ user.name }}", { user: { name: "Ayu" } });
|
|
1537
|
-
// "你好 Ayu"
|
|
1538
|
-
```
|
|
1539
|
-
|
|
1540
|
-
---
|
|
1541
|
-
|
|
1542
|
-
### `validateUpload(upload, rules)`
|
|
1543
|
-
|
|
1544
|
-
```ts
|
|
1545
|
-
validateUpload(upload: UploadLike, rules: UploadRules): UploadValidationIssue[]
|
|
1546
|
-
```
|
|
1547
|
-
|
|
1548
|
-
在发送前校验上传:`maxBytes`(大小上限)、`allowedMimeTypes`(精确或通配符如 `image/*`)、`allowedExtensions`(大小写不敏感,带不带前导点均可)。返回找到的全部违规 —— 空数组表示上传可接受。
|
|
1549
|
-
|
|
1550
|
-
### `assertValidUpload(upload, rules)`
|
|
1551
|
-
|
|
1552
|
-
规则相同,但不返回结果而是抛出 `UploadValidationError`(附带全部 `issues`)。
|
|
1553
|
-
|
|
1554
|
-
```ts
|
|
1555
|
-
import { assertValidUpload } from "@xbibzlibrary/telebibz";
|
|
1556
|
-
|
|
1557
|
-
assertValidUpload(
|
|
1558
|
-
{ sizeBytes: fileBytes.length, mimeType: "image/png", fileName: "logo.png" },
|
|
1559
|
-
{ maxBytes: 5_000_000, allowedMimeTypes: ["image/png", "image/jpeg"], allowedExtensions: [".png", ".jpg"] },
|
|
1560
|
-
);
|
|
1561
|
-
```
|
|
1562
|
-
|
|
1563
|
-
## 14. Testing utilities
|
|
1564
|
-
|
|
1565
|
-
Import dari `@xbibzlibrary/telebibz/testing` atau root package.
|
|
1566
|
-
|
|
1567
|
-
### `MockTransport`
|
|
1568
|
-
|
|
1569
|
-
```ts
|
|
1570
|
-
new MockTransport(): MockTransport
|
|
1571
|
-
```
|
|
1572
|
-
|
|
1573
|
-
| API | Deskripsi |
|
|
1574
|
-
|---|---|
|
|
1575
|
-
| `calls` | Array semua `TransportRequest` yang diterima. |
|
|
1576
|
-
| `respond(method, response)` | Mengatur response statis atau callback berdasarkan payload dan mengembalikan transport. |
|
|
1577
|
-
| `request(request)` | Mencatat request dan mengembalikan response mock. Response default adalah `{ ok: true, result: true }`. |
|
|
1578
|
-
|
|
1579
|
-
Status mock adalah `200` bila `ok: true`, atau `error_code`/`500` bila `ok: false`.
|
|
1580
|
-
|
|
1581
|
-
```ts
|
|
1582
|
-
const transport = new MockTransport()
|
|
1583
|
-
.respond("getMe", {
|
|
1584
|
-
ok: true,
|
|
1585
|
-
result: { id: 1, is_bot: true, first_name: "Test" },
|
|
1586
|
-
});
|
|
1587
|
-
```
|
|
1588
|
-
|
|
1589
|
-
`MockTransport` 同样实现了可选的下载成员:`download(filePath)` 把路径记录进 `downloads` 并返回 `downloadBytes`(默认为路径的 UTF-8 编码),`fileUrl(filePath)` 返回 `mock://files/<filePath>` —— 因此 `bot.downloadFile()` 无需网络即可完整测试。
|
|
1590
|
-
|
|
1591
|
-
### `createMockUpdate(overrides?)`
|
|
1592
|
-
|
|
1593
|
-
```ts
|
|
1594
|
-
createMockUpdate(overrides?: Partial<Update>): Update
|
|
1595
|
-
```
|
|
1596
|
-
|
|
1597
|
-
Membuat update message default dengan `update_id: 1`, chat private id `1`, user id `2`, dan text `/start`. Object `overrides` digabung shallow dengan default.
|
|
1598
|
-
|
|
1599
|
-
### `createTestBot()`
|
|
1600
|
-
|
|
1601
|
-
```ts
|
|
1602
|
-
createTestBot(): { bot: Bot; transport: MockTransport }
|
|
1603
|
-
```
|
|
1604
|
-
|
|
1605
|
-
Membuat bot dengan token test `123456:TEST_TOKEN`, mock `getMe()` yang menghasilkan bot id `99`, dan transport yang dapat diperiksa melalui `transport.calls`.
|
|
1606
|
-
|
|
1607
|
-
### `createMockContext(bot, update?)`
|
|
1608
|
-
|
|
1609
|
-
```ts
|
|
1610
|
-
createMockContext(
|
|
1611
|
-
bot: Bot,
|
|
1612
|
-
update?: Update,
|
|
1613
|
-
): Context
|
|
1614
|
-
```
|
|
1615
|
-
|
|
1616
|
-
Membuat context menggunakan API bot, session kosong, dan services kosong.
|
|
1617
|
-
|
|
1618
|
-
---
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
## 15. 生成的 Telegram 方法命名空间
|
|
1622
|
-
|
|
1623
|
-
`generated/api.ts` 是生成器的内部源,定义了:
|
|
1624
|
-
|
|
1625
|
-
```ts
|
|
1626
|
-
const TELEGRAM_API_VERSION = "10.2";
|
|
1627
|
-
const TELEGRAM_METHOD_NAMES: readonly string[];
|
|
1628
|
-
type TelegramMethodName = typeof TELEGRAM_METHOD_NAMES[number];
|
|
1629
|
-
type GeneratedMethodSpec = {
|
|
1630
|
-
params: Record<string, unknown>;
|
|
1631
|
-
result: unknown;
|
|
1632
|
-
};
|
|
1633
|
-
type GeneratedTelegramMethodMap = {
|
|
1634
|
-
[K in TelegramMethodName]: GeneratedMethodSpec;
|
|
1635
|
-
};
|
|
1636
|
-
const GENERATED_METHODS: Record<TelegramMethodName, TelegramMethodName>;
|
|
1637
|
-
```
|
|
1638
|
-
|
|
1639
|
-
`TELEGRAM_METHOD_NAMES` 包含生成器源中的 184 个方法名。该命名空间是 `api.methods`、`api.call` 和 `api.request` 的代理基础,但在此版本中生成的文件并未作为公共包子路径导出。尚未映射的特定参数/返回值可以通过 `api.raw()` 调用,或在 TypeScript 中通过类型转换传参。
|
|
1640
|
-
|
|
1641
|
-
以下为未经分组的规范方法列表,生成时运行时命名空间中可用的方法名为:
|
|
1642
|
-
|
|
1643
|
-
```text
|
|
1644
|
-
addStickerToSet,
|
|
1645
|
-
answerCallbackQuery,
|
|
1646
|
-
answerChatJoinRequestQuery,
|
|
1647
|
-
answerGuestQuery,
|
|
1648
|
-
answerInlineQuery,
|
|
1649
|
-
answerPreCheckoutQuery,
|
|
1650
|
-
answerShippingQuery,
|
|
1651
|
-
answerWebAppQuery,
|
|
1652
|
-
approveChatJoinRequest,
|
|
1653
|
-
approveSuggestedPost,
|
|
1654
|
-
banChatMember,
|
|
1655
|
-
banChatSenderChat,
|
|
1656
|
-
close,
|
|
1657
|
-
closeForumTopic,
|
|
1658
|
-
closeGeneralForumTopic,
|
|
1659
|
-
convertGiftToStars,
|
|
1660
|
-
copyMessage,
|
|
1661
|
-
copyMessages,
|
|
1662
|
-
createChatInviteLink,
|
|
1663
|
-
createChatSubscriptionInviteLink,
|
|
1664
|
-
createForumTopic,
|
|
1665
|
-
createInvoiceLink,
|
|
1666
|
-
createNewStickerSet,
|
|
1667
|
-
declineChatJoinRequest,
|
|
1668
|
-
declineSuggestedPost,
|
|
1669
|
-
deleteAllMessageReactions,
|
|
1670
|
-
deleteBusinessMessages,
|
|
1671
|
-
deleteChatPhoto,
|
|
1672
|
-
deleteChatStickerSet,
|
|
1673
|
-
deleteEphemeralMessage,
|
|
1674
|
-
deleteForumTopic,
|
|
1675
|
-
deleteMessage,
|
|
1676
|
-
deleteMessageReaction,
|
|
1677
|
-
deleteMessages,
|
|
1678
|
-
deleteMyCommands,
|
|
1679
|
-
deleteStickerFromSet,
|
|
1680
|
-
deleteStickerSet,
|
|
1681
|
-
deleteStory,
|
|
1682
|
-
deleteWebhook,
|
|
1683
|
-
editChatInviteLink,
|
|
1684
|
-
editChatSubscriptionInviteLink,
|
|
1685
|
-
editEphemeralMessageCaption,
|
|
1686
|
-
editEphemeralMessageMedia,
|
|
1687
|
-
editEphemeralMessageReplyMarkup,
|
|
1688
|
-
editEphemeralMessageText,
|
|
1689
|
-
editForumTopic,
|
|
1690
|
-
editGeneralForumTopic,
|
|
1691
|
-
editMessageCaption,
|
|
1692
|
-
editMessageChecklist,
|
|
1693
|
-
editMessageLiveLocation,
|
|
1694
|
-
editMessageMedia,
|
|
1695
|
-
editMessageReplyMarkup,
|
|
1696
|
-
editMessageText,
|
|
1697
|
-
editStory,
|
|
1698
|
-
editUserStarSubscription,
|
|
1699
|
-
exportChatInviteLink,
|
|
1700
|
-
forwardMessage,
|
|
1701
|
-
forwardMessages,
|
|
1702
|
-
getAvailableGifts,
|
|
1703
|
-
getBusinessAccountGifts,
|
|
1704
|
-
getBusinessAccountStarBalance,
|
|
1705
|
-
getBusinessConnection,
|
|
1706
|
-
getChat,
|
|
1707
|
-
getChatAdministrators,
|
|
1708
|
-
getChatGifts,
|
|
1709
|
-
getChatMember,
|
|
1710
|
-
getChatMemberCount,
|
|
1711
|
-
getChatMenuButton,
|
|
1712
|
-
getCustomEmojiStickers,
|
|
1713
|
-
getFile,
|
|
1714
|
-
getForumTopicIconStickers,
|
|
1715
|
-
getGameHighScores,
|
|
1716
|
-
getManagedBotAccessSettings,
|
|
1717
|
-
getManagedBotToken,
|
|
1718
|
-
getMe,
|
|
1719
|
-
getMyCommands,
|
|
1720
|
-
getMyDefaultAdministratorRights,
|
|
1721
|
-
getMyDescription,
|
|
1722
|
-
getMyName,
|
|
1723
|
-
getMyShortDescription,
|
|
1724
|
-
getMyStarBalance,
|
|
1725
|
-
getStarTransactions,
|
|
1726
|
-
getStickerSet,
|
|
1727
|
-
getUpdates,
|
|
1728
|
-
getUserChatBoosts,
|
|
1729
|
-
getUserGifts,
|
|
1730
|
-
getUserPersonalChatMessages,
|
|
1731
|
-
getUserProfileAudios,
|
|
1732
|
-
getUserProfilePhotos,
|
|
1733
|
-
getWebhookInfo,
|
|
1734
|
-
giftPremiumSubscription,
|
|
1735
|
-
hideGeneralForumTopic,
|
|
1736
|
-
leaveChat,
|
|
1737
|
-
logOut,
|
|
1738
|
-
pinChatMessage,
|
|
1739
|
-
postStory,
|
|
1740
|
-
promoteChatMember,
|
|
1741
|
-
readBusinessMessage,
|
|
1742
|
-
refundStarPayment,
|
|
1743
|
-
removeBusinessAccountProfilePhoto,
|
|
1744
|
-
removeChatVerification,
|
|
1745
|
-
removeMyProfilePhoto,
|
|
1746
|
-
removeUserVerification,
|
|
1747
|
-
reopenForumTopic,
|
|
1748
|
-
reopenGeneralForumTopic,
|
|
1749
|
-
replaceManagedBotToken,
|
|
1750
|
-
replaceStickerInSet,
|
|
1751
|
-
repostStory,
|
|
1752
|
-
restrictChatMember,
|
|
1753
|
-
revokeChatInviteLink,
|
|
1754
|
-
savePreparedInlineMessage,
|
|
1755
|
-
savePreparedKeyboardButton,
|
|
1756
|
-
sendAnimation,
|
|
1757
|
-
sendAudio,
|
|
1758
|
-
sendChatAction,
|
|
1759
|
-
sendChatJoinRequestWebApp,
|
|
1760
|
-
sendChecklist,
|
|
1761
|
-
sendContact,
|
|
1762
|
-
sendDice,
|
|
1763
|
-
sendDocument,
|
|
1764
|
-
sendGame,
|
|
1765
|
-
sendGift,
|
|
1766
|
-
sendInvoice,
|
|
1767
|
-
sendLivePhoto,
|
|
1768
|
-
sendLocation,
|
|
1769
|
-
sendMediaGroup,
|
|
1770
|
-
sendMessage,
|
|
1771
|
-
sendMessageDraft,
|
|
1772
|
-
sendPaidMedia,
|
|
1773
|
-
sendPhoto,
|
|
1774
|
-
sendPoll,
|
|
1775
|
-
sendRichMessage,
|
|
1776
|
-
sendRichMessageDraft,
|
|
1777
|
-
sendSticker,
|
|
1778
|
-
sendVenue,
|
|
1779
|
-
sendVideo,
|
|
1780
|
-
sendVideoNote,
|
|
1781
|
-
sendVoice,
|
|
1782
|
-
setBusinessAccountBio,
|
|
1783
|
-
setBusinessAccountGiftSettings,
|
|
1784
|
-
setBusinessAccountName,
|
|
1785
|
-
setBusinessAccountProfilePhoto,
|
|
1786
|
-
setBusinessAccountUsername,
|
|
1787
|
-
setChatAdministratorCustomTitle,
|
|
1788
|
-
setChatDescription,
|
|
1789
|
-
setChatMemberTag,
|
|
1790
|
-
setChatMenuButton,
|
|
1791
|
-
setChatPermissions,
|
|
1792
|
-
setChatPhoto,
|
|
1793
|
-
setChatStickerSet,
|
|
1794
|
-
setChatTitle,
|
|
1795
|
-
setCustomEmojiStickerSetThumbnail,
|
|
1796
|
-
setGameScore,
|
|
1797
|
-
setManagedBotAccessSettings,
|
|
1798
|
-
setMessageReaction,
|
|
1799
|
-
setMyCommands,
|
|
1800
|
-
setMyDefaultAdministratorRights,
|
|
1801
|
-
setMyDescription,
|
|
1802
|
-
setMyName,
|
|
1803
|
-
setMyProfilePhoto,
|
|
1804
|
-
setMyShortDescription,
|
|
1805
|
-
setPassportDataErrors,
|
|
1806
|
-
setStickerEmojiList,
|
|
1807
|
-
setStickerKeywords,
|
|
1808
|
-
setStickerMaskPosition,
|
|
1809
|
-
setStickerPositionInSet,
|
|
1810
|
-
setStickerSetThumbnail,
|
|
1811
|
-
setStickerSetTitle,
|
|
1812
|
-
setUserEmojiStatus,
|
|
1813
|
-
setWebhook,
|
|
1814
|
-
stopMessageLiveLocation,
|
|
1815
|
-
stopPoll,
|
|
1816
|
-
transferBusinessAccountStars,
|
|
1817
|
-
transferGift,
|
|
1818
|
-
unbanChatMember,
|
|
1819
|
-
unbanChatSenderChat,
|
|
1820
|
-
unhideGeneralForumTopic,
|
|
1821
|
-
unpinAllChatMessages,
|
|
1822
|
-
unpinAllForumTopicMessages,
|
|
1823
|
-
unpinAllGeneralForumTopicMessages,
|
|
1824
|
-
unpinChatMessage,
|
|
1825
|
-
upgradeGift,
|
|
1826
|
-
uploadStickerFile,
|
|
1827
|
-
verifyChat
|
|
1828
|
-
```
|
|
1829
|
-
|
|
1830
|
-
> 上述列表遵循生成的源代码。如果 Telegram 添加了新方法,请在 schema 更新后运行 `npm run update:telegram` 或 `telebibz generate`。
|
|
1831
|
-
|
|
1832
|
-
---
|
|
1833
|
-
|
|
1834
|
-
## 16. 命令行界面 (CLI)
|
|
1835
|
-
|
|
1836
|
-
二进制包为 `telebibz`。
|
|
1837
|
-
|
|
1838
|
-
```bash
|
|
1839
|
-
npx telebibz <command>
|
|
1840
|
-
```
|
|
1841
|
-
|
|
1842
|
-
| 命令 | 行为 |
|
|
1843
|
-
|---|---|
|
|
1844
|
-
| `telebibz init [directory]` | 创建目录、最小的 `index.ts`,以及 `.env.example`。默认目录 `my-telebibz-bot`。 |
|
|
1845
|
-
| `telebibz doctor` | 显示 Node 版本、是否存在 `TELEGRAM_BOT_TOKEN`、cwd、包名,然后如果存在 token 则检查健康检查 API。如果 API 无法访问则退出码为 1。 |
|
|
1846
|
-
| `telebibz generate` | 运行 `scripts/generate-api.mjs` 中的生成器方法。 |
|
|
1847
|
-
| `telebibz build` | 运行 `npm run build`。 |
|
|
1848
|
-
| `telebibz test` | 运行 `npm test`。 |
|
|
1849
|
-
| `telebibz webhook` | 检查 `TELEGRAM_BOT_TOKEN`,如有则使用 `TELEGRAM_WEBHOOK_SECRET`,创建处理器并打印就绪状态。此命令不会创建 HTTP 服务器。 |
|
|
1850
|
-
| `telebibz inspect` | 显示 cwd 和 Node 版本。 |
|
|
1851
|
-
| 无命令 | 显示帮助命令列表。 |
|
|
1852
|
-
|
|
1853
|
-
CLI 使用的环境变量是 `TELEGRAM_BOT_TOKEN` 和 `TELEGRAM_WEBHOOK_SECRET`。
|
|
1854
|
-
|
|
1855
|
-
---
|
|
1856
|
-
|
|
1857
|
-
## 17. 主要 Telegram 类型
|
|
1858
|
-
|
|
1859
|
-
该包直接导出最常用的数据类型。
|
|
1860
|
-
|
|
1861
|
-
| Type | 重要内容 |
|
|
1862
|
-
|---|---|
|
|
1863
|
-
| `User` | ID、机器人标志、姓名、用户名、语言和能力标志。 |
|
|
1864
|
-
| `Chat` | ID、类型、标题/用户名/名称、论坛/私信 标志。 |
|
|
1865
|
-
| `Message` | ID、日期、聊天、发送者、文本/说明、实体、回复、标记,以及用于额外 Telegram 字段的索引签名。 |
|
|
1866
|
-
| `Update` | 包含源支持的所有更新字段,包括消息、回调、内联、投票、成员、加入请求、反应、提升、业务和扩展字段。 |
|
|
1867
|
-
| `CallbackQuery` | ID、来自用户、消息/内联消息 ID、聊天实例、数据。 |
|
|
1868
|
-
| `InlineQuery` | ID、来自用户、查询、偏移、聊天类型、位置。 |
|
|
1869
|
-
| `Poll`, `PollAnswer` | 投票数据和答案。 |
|
|
1870
|
-
| `ChatMemberUpdated`, `ChatJoinRequest` | 成员变更和加入请求。 |
|
|
1871
|
-
| `InlineKeyboardMarkup`, `ReplyKeyboardMarkup`, `ReplyKeyboardRemove`, `ForceReply` | Telegram 的回复标记(reply markup)格式。 |
|
|
1872
|
-
| `MessageEntity`, `ReplyParameters`, `LinkPreviewOptions` | 实体元数据、回复参数和链接预览选项。 |
|
|
1873
|
-
| `BotCommand`, `BotCommandScope`, `WebhookInfo`, `File`, `UserProfilePhotos`, `ChatMember`, `ChatAdministratorRights` | API 的结果/参数辅助类型。 |
|
|
1874
|
-
|
|
1875
|
-
---
|
|
1876
|
-
|
|
1877
|
-
## 18. 持久化、完整 cron、菜单和完整 Telegram 声明
|
|
1878
|
-
|
|
1879
|
-
### 持久化 storage 适配器
|
|
1880
|
-
|
|
1881
|
-
所有适配器都实现相同的 `Storage<K, V>` contract。核心 package 不包含 vendor runtime dependency;Redis、SQL 和 Mongo 适配器接收由应用或所选 vendor client 提供的小型 driver interface。
|
|
1882
|
-
|
|
1883
|
-
| Class | 构造函数 | 用途 |
|
|
1884
|
-
|---|---|---|
|
|
1885
|
-
| `MemoryStorage<K, V>` | `new MemoryStorage()` | 带 TTL 和按 key 原子 `update()` 的快速内存 storage。 |
|
|
1886
|
-
| `JsonFileStorage<V>` | `new JsonFileStorage(filePath)` | 适用于单进程部署的原子 JSON 文件持久化。 |
|
|
1887
|
-
| `RedisStorage<V>` | `new RedisStorage(client, prefix?)` | 通过 `RedisLikeClient` 使用 Redis storage,包含 TTL 和 namespace。 |
|
|
1888
|
-
| `SqlStorage<V>` | `new SqlStorage(driver)` | 通过应用提供的 `SqlStorageDriver` 使用 SQL storage。 |
|
|
1889
|
-
| `MongoStorage<V>` | `new MongoStorage(collection)` | 通过应用提供的 `MongoStorageCollection` 使用 Mongo storage。 |
|
|
1890
|
-
|
|
1891
|
-
`BotOptions.session` 接受 `Storage<string, S>`,因此 session 可以使用任意适配器。`ConversationManager` 接受相同的 storage abstraction,并提供 `getAsync()`、`cancelAsync()` 和 `clearExpiredAsync()` 来持久化 conversation state。
|
|
1892
|
-
|
|
1893
|
-
```ts
|
|
1894
|
-
const session = new JsonFileStorage<Record<string, unknown>>("./data/sessions.json");
|
|
1895
|
-
const bot = new Bot({ token: process.env.TELEGRAM_BOT_TOKEN!, session });
|
|
1896
|
-
```
|
|
1897
|
-
|
|
1898
|
-
### 完整五字段 cron
|
|
1899
|
-
|
|
1900
|
-
`parseCronExpression()` 支持标准五字段 `minute hour day-of-month month day-of-week`,包括 wildcard、list、range 和 step,例如 `*/15 9-17 1,15 * 1-5`。`nextCronOccurrence()` 计算下一个本地 occurrence。`Scheduler.cron()` 使用 one-shot timer,并在每次执行后重新调度;任务错误会交给 `Scheduler({ onError })`,不会成为未处理的 promise rejection。
|
|
1901
|
-
|
|
1902
|
-
### Router matching mode
|
|
1903
|
-
|
|
1904
|
-
`new Router()` 默认使用 **first-match**,防止意外的 double reply。只有在确实需要 fan-out 时才使用 `new Router({ matchMode: "all" })`。RegExp matcher 在测试前会重置 `lastIndex`,因此 global 或 sticky expression 可以安全复用。
|
|
1905
|
-
|
|
1906
|
-
### MenuController 和 permission
|
|
1907
|
-
|
|
1908
|
-
`Menu` 支持基于 permission 的 item、异步 visibility/permission predicate、breadcrumb 和多列布局。`MenuController` 增加 stateful page rendering,以及对 `select`、`page`、`noop` 和外部 callback data 的 dispatch。
|
|
1909
|
-
|
|
1910
|
-
### 完整 Telegram declaration namespace
|
|
1911
|
-
|
|
1912
|
-
Package 内置 MIT 许可的 Telegram declaration,并通过 type-only export 暴露 `TelegramTypes`,同时提供 `TelegramUser`、`TelegramMessage`、`TelegramUpdate` 和 `TelegramApiMethods` 等 alias。CLI 使用带颜色的 Unicode box,并包含 attribution `Library Bot Telegram By @xbibzofficial`。`Logger` 输出带 level、redaction、update summary 的 terminal 或 JSON 结构化日志,并支持 opt-in 记录 user message/callback content。这些 declaration 覆盖完整的 object、union、enum 和 method surface,不增加 runtime dependency。telebibz core method map 仍专门为具有直接参数/结果映射的 method 提供类型。
|
|
1913
|
-
|
|
1914
|
-
---
|
|
1915
|
-
|
|
1916
|
-
## 19. 兼容性和需要注意的限制
|
|
1917
|
-
|
|
1918
|
-
本库面向 Node.js `>=22`,使用 ESM 作为主要模块,并提供 CommonJS 构建。Webhook 需要运行时提供 Web `Request`、`Response`、`Headers`、`FormData`、`Blob` 和 `AbortController`;现代 Node.js 原生提供了这些。
|
|
1919
|
-
|
|
1920
|
-
API 生成的方法列表(generated method list)和 API 方法映射(API method map)并不相同。`TelegramMethodName` 包含 184 个运行时名称,但 `TelegramMethodMap` 仅对 API 客户端部分列出的子集提供了带类型的参数/结果。对于其他方法,使用 `api.raw()` 或在应用端添加类型声明。
|
|
1921
|
-
|
|
1922
|
-
Session 状态和其他内存 primitive 会在进程重启时丢失,除非应用提供持久化适配器。`BotOptions.session` 接受 generic `Storage<string, S>` contract。
|
|
1923
|
-
|
|
1924
|
-
---
|
|
1925
|
-
|
|
1926
|
-
## 参考文献
|
|
1927
|
-
|
|
1928
|
-
[1]: https://core.telegram.org/bots/api "Telegram Bot API — 官方文档"
|
|
1929
|
-
[2]: https://www.npmjs.com/package/@xbibzlibrary/telebibz "@xbibzlibrary/telebibz 在 npm 上"
|