qqbot-elysia 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sibuxiangx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ <p align="center">
2
+ <h1 align="center">🦊 qqbot-elysia</h1>
3
+ <p align="center">
4
+ <strong>基于 Bun + Elysia 的 QQ 开放平台 Bot SDK</strong>
5
+ </p>
6
+ <p align="center">
7
+ 装饰器路由 · 依赖注入 · 模块化开发
8
+ </p>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://github.com/Sibuxiangx/qqbot-elysia/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License" /></a>
13
+ <a href="https://www.npmjs.com/package/qqbot-elysia"><img src="https://img.shields.io/npm/v/qqbot-elysia.svg" alt="npm version" /></a>
14
+ <a href="https://bun.sh"><img src="https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun" alt="Bun" /></a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ ## ✨ 特性
20
+
21
+ - **🚀 极速** — 基于 [Bun](https://bun.sh) 运行时 + [Elysia](https://elysiajs.com) Web 框架
22
+ - **🎨 装饰器路由** — 用 `@OnCommand("ping")` 声明式地注册消息处理器
23
+ - **💉 依赖注入** — 在处理器参数中直接声明你需要的数据(`Content`, `AttachmentList`, `AuthorInfo`…)
24
+ - **📦 模块化** — 继承 `BotModule` 即可创建可插拔的功能模块
25
+ - **🔐 Webhook** — 原生支持 QQ 开放平台 Webhook 验签(Ed25519)
26
+ - **🌐 多场景** — 群聊、私聊 (C2C)、频道、频道私信全覆盖
27
+ - **📋 日志** — 内置漂亮的结构化日志输出
28
+
29
+ ## 📦 安装
30
+
31
+ ```bash
32
+ bun add qqbot-elysia
33
+ ```
34
+
35
+ > **前置要求**: [Bun](https://bun.sh) >= 1.0, TypeScript >= 5
36
+
37
+ ## 🚀 快速开始
38
+
39
+ ```typescript
40
+ import { Bot, BotModule, OnCommand, Content } from "qqbot-elysia";
41
+ import type { MessageContext } from "qqbot-elysia";
42
+
43
+ class PingModule extends BotModule {
44
+ @OnCommand("ping")
45
+ async onPing(ctx: MessageContext) {
46
+ await ctx.reply("pong! 🏓");
47
+ }
48
+
49
+ @OnCommand("echo")
50
+ async onEcho(ctx: MessageContext, content: Content) {
51
+ await ctx.reply(content || "你没有输入任何内容");
52
+ }
53
+ }
54
+
55
+ const bot = new Bot({
56
+ appId: process.env.APP_ID!,
57
+ clientSecret: process.env.CLIENT_SECRET!,
58
+ port: 2099,
59
+ isSandbox: true,
60
+ debug: true,
61
+ });
62
+
63
+ await bot.use(new PingModule());
64
+ await bot.start();
65
+ ```
66
+
67
+ ## 📖 核心概念
68
+
69
+ ### 模块 (Module)
70
+
71
+ 所有功能都封装在 `BotModule` 子类中:
72
+
73
+ ```typescript
74
+ class MyModule extends BotModule {
75
+ override async onLoad() {
76
+ // 模块加载时触发
77
+ }
78
+
79
+ override async onUnload() {
80
+ // 模块卸载时触发
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### 装饰器 (Decorators)
86
+
87
+ | 装饰器 | 说明 |
88
+ | :--- | :--- |
89
+ | `@OnCommand("cmd")` | 匹配指定命令(群 + 私聊) |
90
+ | `@OnPrefix("前缀")` | 匹配消息前缀 |
91
+ | `@OnSuffix("后缀")` | 匹配消息后缀 |
92
+ | `@OnKeyword("关键词")` | 匹配消息关键词 |
93
+ | `@OnGroup` | 仅匹配群消息 |
94
+ | `@OnC2C` | 仅匹配私聊消息 |
95
+ | `@OnChannel` | 仅匹配频道消息 |
96
+ | `@OnDirect` | 仅匹配频道私信 |
97
+ | `@OnEvent(EventType.xxx)` | 匹配指定事件类型 |
98
+
99
+ 装饰器可以**组合使用**:
100
+
101
+ ```typescript
102
+ @OnGroup // 仅群聊
103
+ @OnCommand("admin-only") // 匹配 "admin-only" 命令
104
+ async onAdminCmd(ctx: GroupMessageContext) {
105
+ await ctx.reply("仅群聊可用的管理命令");
106
+ }
107
+ ```
108
+
109
+ ### 依赖注入 (DI)
110
+
111
+ 处理器参数会根据类型标记自动注入对应的数据:
112
+
113
+ | 类型 | 说明 | 备注 |
114
+ | :--- | :--- | :--- |
115
+ | `Content` | 匹配后的消息内容 | 去除了触发词后的剩余文本 |
116
+ | `RawContent` | 原始完整消息内容 | 用户输入的全部文本 |
117
+ | `AttachmentList` | 附件列表 | 若无附件则**跳过执行** |
118
+ | `AuthorInfo` | 发送者信息 | 包含 openid、用户名等 |
119
+ | `GroupInfo` | 群信息 | 仅群聊有效 |
120
+ | `MemberInfo` | 群成员信息 | 仅群聊有效 |
121
+
122
+ ```typescript
123
+ import { Content, AttachmentList, AuthorInfo } from "qqbot-elysia";
124
+
125
+ @OnCommand("save-image")
126
+ async onSaveImage(ctx: MessageContext, attachments: AttachmentList) {
127
+ // attachments 保证不为空
128
+ // 若消息无附件,本处理器会被自动跳过
129
+ await ctx.reply(`收到 ${attachments.length} 张图片!`);
130
+ }
131
+ ```
132
+
133
+ > **⚠️ 重要**:DI 标记类(`Content`, `AttachmentList` 等)必须用普通 `import` 导入,**不要**使用 `import type`,否则运行时元数据会丢失。
134
+
135
+ #### 自定义注入器
136
+
137
+ 你可以注册自己的注入器来扩展系统:
138
+
139
+ ```typescript
140
+ // 定义标记类
141
+ class Database {}
142
+
143
+ // 注册注入逻辑
144
+ bot.registerInjector("Database", (ctx) => myDatabaseInstance);
145
+
146
+ // 在处理器中使用
147
+ @OnCommand("db-test")
148
+ async test(ctx: MessageContext, db: Database) {
149
+ // db === myDatabaseInstance
150
+ }
151
+ ```
152
+
153
+ ### 消息发送
154
+
155
+ ```typescript
156
+ // 纯文本
157
+ await ctx.reply("Hello!");
158
+
159
+ // 图片
160
+ await ctx.reply("看看这张图", {
161
+ image: "https://example.com/image.png",
162
+ });
163
+
164
+ // Markdown (模板)
165
+ await ctx.reply("", {
166
+ markdown: {
167
+ custom_template_id: "your_template_id",
168
+ params: [{ key: "title", values: ["Hello"] }],
169
+ },
170
+ });
171
+
172
+ // 主动消息
173
+ await bot.sendGroupMessage(groupOpenid, "主动推送消息");
174
+ await bot.sendC2CMessage(userOpenid, "主动私聊消息");
175
+ ```
176
+
177
+ ### 事件监听
178
+
179
+ ```typescript
180
+ import { OnEvent, EventType } from "qqbot-elysia";
181
+
182
+ @OnEvent(EventType.GROUP_ADD_ROBOT)
183
+ async onBotAdded(ctx: GroupRobotEventContext) {
184
+ console.log(`Bot 被添加到群: ${ctx.groupOpenid}`);
185
+ }
186
+
187
+ @OnEvent(EventType.FRIEND_ADD)
188
+ async onFriendAdd(ctx: FriendEventContext) {
189
+ console.log(`新好友: ${ctx.userOpenid}`);
190
+ }
191
+ ```
192
+
193
+ ## 🔧 配置
194
+
195
+ ```typescript
196
+ const bot = new Bot({
197
+ appId: "你的 AppID",
198
+ clientSecret: "你的 ClientSecret",
199
+ port: 2099, // Webhook 监听端口 (默认 2099)
200
+ host: "0.0.0.0", // 监听地址 (默认 0.0.0.0)
201
+ postEventPath: "/postevent", // Webhook 路径
202
+ isSandbox: false, // 沙箱模式
203
+ randomMsgSeq: true, // 随机 msg_seq
204
+ debug: false, // 调试日志
205
+ });
206
+ ```
207
+
208
+ ## 📁 项目结构
209
+
210
+ ```
211
+ qqbot-elysia/
212
+ ├── src/
213
+ │ ├── index.ts # 统一导出
214
+ │ ├── bot.ts # Bot 核心类
215
+ │ ├── module.ts # BotModule 基类
216
+ │ ├── decorators.ts # 装饰器定义
217
+ │ ├── matchers.ts # 消息匹配器
218
+ │ ├── events.ts # 事件系统
219
+ │ ├── api.ts # QQ OpenAPI 客户端
220
+ │ ├── auth.ts # Token 管理
221
+ │ ├── sign.ts # Ed25519 验签
222
+ │ ├── logger.ts # 日志系统
223
+ │ ├── di/
224
+ │ │ ├── markers.ts # DI 标记类
225
+ │ │ └── registry.ts # 注入器注册表
226
+ │ └── types/
227
+ │ └── types.ts # TypeScript 类型定义
228
+ ├── docs/
229
+ │ └── di.md # DI 系统详细文档
230
+ ├── package.json
231
+ ├── tsconfig.json
232
+ ├── LICENSE
233
+ └── README.md
234
+ ```
235
+
236
+ ## 📝 TypeScript 配置
237
+
238
+ 你的 `tsconfig.json` **必须**包含以下设置:
239
+
240
+ ```json
241
+ {
242
+ "compilerOptions": {
243
+ "experimentalDecorators": true,
244
+ "emitDecoratorMetadata": true
245
+ }
246
+ }
247
+ ```
248
+
249
+ ## 📄 License
250
+
251
+ [MIT](LICENSE) © Sibuxiangx
package/docs/di.md ADDED
@@ -0,0 +1,69 @@
1
+ # 依赖注入 (DI) 系统指南
2
+
3
+ 本 SDK 提供了一套基于装饰器和 `reflect-metadata` 的依赖注入系统,允许你在事件处理器中声明式地获取参数。
4
+
5
+ ## 1. 核心概念
6
+
7
+ 在模块化的处理器中,你可以直接通过参数类型声明来注入你需要的对象,而不需要从完整的 `ctx` 中手动提取。
8
+
9
+ ```typescript
10
+ @OnCommand("echo")
11
+ async onEcho(ctx: MessageContext, content: Content) {
12
+ // content 会被自动注入为匹配后的文本内容
13
+ await ctx.reply(content);
14
+ }
15
+ ```
16
+
17
+ ## 2. 内置注入类型
18
+
19
+ 你可以从 `@src` (或项目根目录的 `./src`) 导入以下标记类:
20
+
21
+ | 类型 | 说明 | 注入内容 | 备注 |
22
+ | :--- | :--- | :--- | :--- |
23
+ | `Content` | 匹配后的内容 | `string` (包装类) | 去除了触发指令/前缀后的剩余文本 |
24
+ | `RawContent` | 原始完整内容 | `string` (包装类) | 原始用户输入的完整字符串 |
25
+ | `AttachmentList` | 消息附件列表 | `Attachment[]` | 若消息中无附件,且参数非可选,将**跳过**逻辑执行 |
26
+ | `AuthorInfo` | 发送者信息 | `Author` | 包含 `username`, `avatar`, `openid` 等 |
27
+ | `GroupInfo` | 群信息 | `GroupInfo` | 仅在群聊事件中有效 |
28
+ | `MemberInfo` | 群成员信息 | `GroupMember` | 仅在群聊事件中有效 (openid) |
29
+ | `MessageContext` | 原始上下文 | `Context` 对象 | 完整的事件上下文 |
30
+
31
+ ## 3. 运行逻辑与 "Skip Logic"
32
+
33
+ - **参数顺序**:注入器会扫描你的方法签名,根据参数的类型 (Type) 进行匹配,顺序不限。
34
+ - **Skip Logic (跳过逻辑)**:对于部分类型 (如 `AttachmentList`),如果当前事件中不包含这些数据,SDK 会认为该处理器不满足执行条件,**自动跳过**该处理器的调用。这可以让你免去 `if (!ctx.attachments) return` 之类的样板代码。
35
+
36
+ ## 4. 自定义注入器
37
+
38
+ 你可以通过 `bot.registerInjector` 扩展系统,注入自己的服务或对象:
39
+
40
+ ### 示例:注入数据库连接
41
+
42
+ 1. **定义标识类 (Markers)**:
43
+ ```typescript
44
+ class Database { } // 仅作为标识,不需要实现
45
+ ```
46
+
47
+ 2. **注册注入器**:
48
+ ```typescript
49
+ bot.registerInjector("Database", (ctx) => {
50
+ return myDbInstance; // 返回你想注入的实际对象
51
+ });
52
+ ```
53
+
54
+ 3. **在处理器中使用**:
55
+ ```typescript
56
+ class MyModule extends BotModule {
57
+ @OnCommand("db-test")
58
+ async test(ctx: MessageContext, db: Database) {
59
+ // db 将会是 myDbInstance
60
+ const data = await db.query(...);
61
+ await ctx.reply("查询成功");
62
+ }
63
+ }
64
+ ```
65
+
66
+ ## 5. 提示
67
+
68
+ - 确保你的 `tsconfig.json` 开启了 `emitDecoratorMetadata` 和 `experimentalDecorators`。
69
+ - 自定义注入时的名称字符串必须与你在参数中声明的**类名**完全一致。
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "qqbot-elysia",
3
+ "version": "0.1.0",
4
+ "description": "⚡ 基于 Bun + Elysia 的 QQ 开放平台 Bot SDK,支持装饰器路由、依赖注入与模块化开发",
5
+ "type": "module",
6
+ "main": "src/index.ts",
7
+ "module": "src/index.ts",
8
+ "types": "src/index.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./src/index.ts",
12
+ "types": "./src/index.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "src",
17
+ "docs",
18
+ "LICENSE",
19
+ "README.md"
20
+ ],
21
+ "scripts": {
22
+ "dev": "bun run index.ts",
23
+ "check": "bun x tsc --noEmit"
24
+ },
25
+ "keywords": [
26
+ "qq",
27
+ "qqbot",
28
+ "qq-bot",
29
+ "bot",
30
+ "elysia",
31
+ "bun",
32
+ "sdk",
33
+ "webhook",
34
+ "decorator",
35
+ "dependency-injection"
36
+ ],
37
+ "author": "Sibuxiangx",
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/Sibuxiangx/qqbot-elysia.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/Sibuxiangx/qqbot-elysia/issues"
45
+ },
46
+ "homepage": "https://github.com/Sibuxiangx/qqbot-elysia#readme",
47
+ "engines": {
48
+ "bun": ">=1.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "@types/bun": "latest"
52
+ },
53
+ "peerDependencies": {
54
+ "typescript": "^5"
55
+ },
56
+ "dependencies": {
57
+ "@noble/ed25519": "^3.0.0",
58
+ "elysia": "^1.4.25",
59
+ "log4js": "^6.9.1",
60
+ "reflect-metadata": "^0.2.2"
61
+ }
62
+ }