@zhin.js/adapter-qq 5.0.2 → 6.0.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.
Files changed (130) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +75 -409
  3. package/adapters/qq.ts +51 -0
  4. package/agent/tools/add_role.ts +2 -2
  5. package/agent/tools/channel_info.ts +2 -2
  6. package/agent/tools/create_role.ts +2 -2
  7. package/agent/tools/list_channels.ts +2 -2
  8. package/agent/tools/list_guilds.ts +2 -2
  9. package/agent/tools/list_roles.ts +2 -2
  10. package/agent/tools/member_detail.ts +2 -2
  11. package/agent/tools/remove_role.ts +2 -2
  12. package/commands/endpoint/add/[name:string=].ts +21 -0
  13. package/commands/endpoint/cancel.ts +15 -0
  14. package/commands/endpoint/list.ts +10 -0
  15. package/commands/endpoint/remove/[name:string].ts +15 -0
  16. package/lib/endpoint.d.ts +75 -0
  17. package/lib/endpoint.js +358 -0
  18. package/lib/index.d.ts +6 -0
  19. package/lib/index.js +6 -0
  20. package/lib/platform-permit.d.ts +16 -0
  21. package/lib/{src/platform-permit.js → platform-permit.js} +1 -2
  22. package/lib/protocol.d.ts +96 -0
  23. package/lib/protocol.js +129 -0
  24. package/lib/qq-agent-deps.d.ts +21 -0
  25. package/lib/qq-agent-deps.js +30 -0
  26. package/lib/qq-bind-api.d.ts +36 -0
  27. package/lib/{src/qq-bind-api.js → qq-bind-api.js} +0 -1
  28. package/lib/qq-bind-flow.d.ts +19 -0
  29. package/lib/{src/qq-bind-flow.js → qq-bind-flow.js} +0 -1
  30. package/lib/qq-bind-persist.d.ts +12 -0
  31. package/lib/{src/qq-bind-persist.js → qq-bind-persist.js} +4 -5
  32. package/lib/qq-endpoint-commands.d.ts +25 -0
  33. package/lib/qq-endpoint-commands.js +147 -0
  34. package/lib/qq-endpoint-config.d.ts +17 -0
  35. package/lib/qq-endpoint-config.js +110 -0
  36. package/lib/qq-runtime-state.d.ts +17 -0
  37. package/lib/qq-runtime-state.js +13 -0
  38. package/lib/webhook.d.ts +18 -0
  39. package/lib/webhook.js +100 -0
  40. package/lib/ws.d.ts +24 -0
  41. package/lib/ws.js +118 -0
  42. package/package.json +51 -40
  43. package/plugin.ts +16 -0
  44. package/schema.json +85 -0
  45. package/src/endpoint.ts +362 -596
  46. package/src/index.ts +56 -186
  47. package/src/platform-permit.ts +1 -1
  48. package/src/protocol.ts +229 -0
  49. package/src/qq-agent-deps.ts +49 -12
  50. package/src/qq-bind-persist.ts +4 -4
  51. package/src/qq-endpoint-commands.ts +174 -0
  52. package/src/qq-endpoint-config.ts +135 -0
  53. package/src/qq-runtime-state.ts +36 -0
  54. package/src/webhook.ts +127 -0
  55. package/src/ws.ts +172 -0
  56. package/client/Dashboard.tsx +0 -230
  57. package/client/index.tsx +0 -11
  58. package/client/tsconfig.json +0 -7
  59. package/client/utils/api.ts +0 -30
  60. package/dist/index.js +0 -30
  61. package/lib/agent/tools/add_role.js +0 -23
  62. package/lib/agent/tools/add_role.js.map +0 -1
  63. package/lib/agent/tools/channel_info.js +0 -19
  64. package/lib/agent/tools/channel_info.js.map +0 -1
  65. package/lib/agent/tools/create_role.js +0 -22
  66. package/lib/agent/tools/create_role.js.map +0 -1
  67. package/lib/agent/tools/list_channels.js +0 -20
  68. package/lib/agent/tools/list_channels.js.map +0 -1
  69. package/lib/agent/tools/list_guilds.js +0 -17
  70. package/lib/agent/tools/list_guilds.js.map +0 -1
  71. package/lib/agent/tools/list_roles.js +0 -20
  72. package/lib/agent/tools/list_roles.js.map +0 -1
  73. package/lib/agent/tools/member_detail.js +0 -20
  74. package/lib/agent/tools/member_detail.js.map +0 -1
  75. package/lib/agent/tools/remove_role.js +0 -23
  76. package/lib/agent/tools/remove_role.js.map +0 -1
  77. package/lib/package.json +0 -105
  78. package/lib/src/adapter.js +0 -81
  79. package/lib/src/adapter.js.map +0 -1
  80. package/lib/src/endpoint.js +0 -600
  81. package/lib/src/endpoint.js.map +0 -1
  82. package/lib/src/gateway-config.js +0 -32
  83. package/lib/src/gateway-config.js.map +0 -1
  84. package/lib/src/group-at-normalize.js +0 -68
  85. package/lib/src/group-at-normalize.js.map +0 -1
  86. package/lib/src/inbound-action.js +0 -57
  87. package/lib/src/inbound-action.js.map +0 -1
  88. package/lib/src/inbound-normalize.js +0 -47
  89. package/lib/src/inbound-normalize.js.map +0 -1
  90. package/lib/src/index.js +0 -194
  91. package/lib/src/index.js.map +0 -1
  92. package/lib/src/mention-enrich.js +0 -52
  93. package/lib/src/mention-enrich.js.map +0 -1
  94. package/lib/src/outbound-keyboard.js +0 -100
  95. package/lib/src/outbound-keyboard.js.map +0 -1
  96. package/lib/src/outbound-markdown.js +0 -189
  97. package/lib/src/outbound-markdown.js.map +0 -1
  98. package/lib/src/outbound-media.js +0 -51
  99. package/lib/src/outbound-media.js.map +0 -1
  100. package/lib/src/outbound-mixed-media.js +0 -78
  101. package/lib/src/outbound-mixed-media.js.map +0 -1
  102. package/lib/src/platform-permit.js.map +0 -1
  103. package/lib/src/qq-agent-deps.js +0 -10
  104. package/lib/src/qq-agent-deps.js.map +0 -1
  105. package/lib/src/qq-bind-api.js.map +0 -1
  106. package/lib/src/qq-bind-flow.js.map +0 -1
  107. package/lib/src/qq-bind-persist.js.map +0 -1
  108. package/lib/src/qq-endpoint-manager.js +0 -159
  109. package/lib/src/qq-endpoint-manager.js.map +0 -1
  110. package/lib/src/sdk-version.js +0 -19
  111. package/lib/src/sdk-version.js.map +0 -1
  112. package/lib/src/segment-mapper.js +0 -2
  113. package/lib/src/segment-mapper.js.map +0 -1
  114. package/lib/src/types.js +0 -2
  115. package/lib/src/types.js.map +0 -1
  116. package/plugin.yml +0 -3
  117. package/src/adapter.ts +0 -97
  118. package/src/gateway-config.ts +0 -48
  119. package/src/group-at-normalize.ts +0 -75
  120. package/src/inbound-action.ts +0 -87
  121. package/src/inbound-normalize.ts +0 -57
  122. package/src/mention-enrich.ts +0 -71
  123. package/src/outbound-keyboard.ts +0 -117
  124. package/src/outbound-markdown.ts +0 -212
  125. package/src/outbound-media.ts +0 -58
  126. package/src/outbound-mixed-media.ts +0 -101
  127. package/src/qq-endpoint-manager.ts +0 -199
  128. package/src/sdk-version.ts +0 -21
  129. package/src/segment-mapper.ts +0 -1
  130. package/src/types.ts +0 -52
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # @zhin.js/adapter-qq
2
2
 
3
+ ## 6.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3ea88d8: 恢复 legacy 的聊天命令添加 endpoint 能力(随适配器发布):`qq endpoint add [name]`(手机 QQ 扫码绑定 → 凭据写 `.env` 的 `QQ_<NAME>_APPID/SECRET` → 追加 `plugins.qq.endpoints` 配置,重启生效)、`qq endpoint cancel`(中止绑定)、`qq endpoint list`(运行中 + 配置内 endpoints)、`qq endpoint remove <name>`(移除配置项)。add/cancel/remove 受 `master` 限制(顶层或 `endpoints[i]` 声明时仅 master 可执行,未配置则放行);schema 新增顶层共享字段 `master`。绑定协议(create_bind_task / poll_bind_result / AES-256-GCM 解密)自 legacy 移植,二维码当前以链接文本下发(QQ 出站富媒体待迁移)。
8
+
9
+ ### Patch Changes
10
+
11
+ - 7db69c1: 命令前缀改为适配器配置项:`MessageDispatcher` 不再硬编码 `/`,默认按消息所属适配器实例 config 的 `commandPrefix` 解析(默认 `''` 无前缀,任意文本按命令匹配),`endpoints[i].commandPrefix` 逐项覆盖;`ImRuntime({ commandPrefix })` 仍可设全局静态前缀。全部 20 个平台适配器 schema 新增 `commandPrefix` 属性。
12
+
13
+ BREAKING(行为变化):未配置时命令不再需要 `/` 前缀——原 `/zt` 写法不再命中,直接发 `zt` 即可;需要斜杠风格的适配器请在配置里显式设 `commandPrefix: '/'`。
14
+
15
+ - e5c84ed: Adapter 多账号:插件实例 config 支持 `endpoints: [{name, ...覆盖}]` 数组,`expandEndpointConfigs` 将一个实例展开为多个 endpoint record(id 为 `<slotId>~<name>`,顶层字段共享、逐项覆盖),替代多 `instanceKey` 方案;Console `/api/plugins` 收敛为一个插件卡片 + 多 endpoint。icqq / qq schema 与 README 补 `endpoints` 配置。
16
+
17
+ Plugin Runtime Console Host:补 `/esm/*` React/router ESM 代理路由(legacy `consoleApiRouter` 对齐),TypeScriptClientBuilder 裸导入改写为 `/esm/<enc>.mjs`。
18
+
19
+ - 713445c: 适配器配置格式定稿(不兼容旧格式):`plugins.<adapter>` 顶层仅共享字段 + `commandPrefix`,`endpoints[i]` 携带 endpoint 级字段(`name` + 凭据,各 schema 已类型化),`endpoints` 为必填(icqq 另需顶层 `master`);icqq 新增 `trusted` 列表(顶层/逐项均可)。scaffold-wizard 全部字段式与自定义 configure() 产出改为新格式,examples(full-bot / qq-games-bot)与 20 个适配器 README 同步迁移。
20
+ - f32c424: 适配器配置统一为 endpoints 数组格式:`plugins.<adapter>` 为该 adapter 所有 endpoint 的通用配置,`plugins.<adapter>.endpoints[i]` 为单个 endpoint 的特殊配置(逐项覆盖,`name` 必填)。slack / github schema 新增 `endpoints` 属性;icqq / qq / slack 顶层必填改为 `anyOf`(单 endpoint 字段或 `endpoints` 数组二选一,兼容 Ajv strictRequired)。
21
+ - Updated dependencies [7db69c1]
22
+ - Updated dependencies [e5c84ed]
23
+ - Updated dependencies [3ea84a0]
24
+ - Updated dependencies [1ddcd70]
25
+ - Updated dependencies [ac9da66]
26
+ - @zhin.js/core@1.4.0
27
+ - @zhin.js/adapter@1.1.0
28
+ - @zhin.js/plugin-runtime@1.1.0
29
+ - @zhin.js/agent@1.0.5
30
+ - @zhin.js/host-http@1.0.2
31
+ - zhin.js@5.0.0
32
+ - @zhin.js/command@1.0.2
33
+
34
+ ## 5.0.3
35
+
36
+ ### Patch Changes
37
+
38
+ - cc5c94d: 约定式插件运行时迁移(breaking):插件与适配器由 `usePlugin()` / `extends Adapter` 迁移为 `definePlugin` / `defineAdapter` + `plugin.ts` + 约定目录(`adapters/`、`commands/`、`components/`、`tools/` 等)。
39
+
40
+ - 新增约定式运行时包:`@zhin.js/plugin-runtime`、`@zhin.js/adapter`、`@zhin.js/runtime`、`@zhin.js/host-http`(首版 1.0.0 走 init-publish,不在本 changeset 内 bump)。
41
+ - 全部 20 个平台适配器改为约定式 `defineAdapter`,旧 `usePlugin` / `extends Adapter` / `segment-mapper` 生产入口已删除;onebot11 反向 WSS、onebot12 webhook/wss、milky sse/webhook/wss、satori webhook、kook webhook、qq webhook/middleware 等 slice 1 推迟的连接模式已补齐。
42
+ - 游戏 / 工具 / 服务插件同步迁移到约定目录结构。
43
+ - CLI 增加 plugin-runtime host installer(http/database/outbound/schedule/console 等)。
44
+
45
+ 后续加固(同批):
46
+
47
+ - CLI:`zhin runtime start --daemon`(pidfile/崩溃拉起/风暴保护),orphan watchdog 防僵尸进程;legacy `zhin dev` / `zhin start` 已移除(含 `zhin restart`),`zhin stop` 兼容新 daemon。
48
+ - 安全:builtin 工具统一走 `security/policy-facade.ts` 的 `runToolPolicies`(声明式策略表,deny 优先);审计日志 close flush + 背压队列;`splitCompoundCommand` 引号感知、`extractCommandName` 去引号堵绕过。
49
+ - 日志:Logger 双堆栈修复、本地时区、`getLogger` 挂树(`setLevel` 递归生效)、第三方库(log4js/discord)桥接、启动人读总结。
50
+ - 结构:`plugins/games/shared` 迁为 `packages/game-kit`(`@zhin.js/game-kit`);死目录 `plugins/adapters/common` 删除。
51
+ - 脚手架:`create-zhin-app` / `zhin new` / scaffold-wizard 生成物改为 Plugin Runtime 形态(minimal-bot 同构,新配置格式)。
52
+ - Console:endpoint.list 真实名称与 phase、schema:get-all 按 instanceKey 映射、db:\* 接 DatabaseHost。
53
+
54
+ 注:按仓库发布惯例(见 1bb345dd2),本次 breaking 迁移统一使用 patch,避免 zhin.js 5.0 级联。
55
+
56
+ - 447f3e2: 迁移缺口修复(legacy 功能对齐):
57
+
58
+ - html 段出站规范化:经 `@zhin.js/html-renderer` 渲染为 image 段(sandbox 豁免、无渲染器时降级文本),修复真实平台 `[object Object]`。
59
+ - 群聊 @ 触发 AI:适配器入站标注 `metadata.mentioned`(icqq/qq/slack/onebot11/onebot12/napcat/milky/discord/telegram/kook/dingtalk/satori),`matchAiTrigger` 补齐 ignorePrefixes/respondToAt/respondToPrivate/keywords(默认值与 legacy 对齐)。
60
+ - im_transcripts 全量流水恢复写入(chat_history 工具可用);群聊旁听上下文回迁。
61
+ - `ai.trigger.timeout/thinkingMessage/errorTemplate` 生效;masters/trusted 角色解析对齐 legacy。
62
+ - `Message.sender` 统一为用户 ID(onebot11/12、napcat、milky 原误传显示名);quote_id 经 metadata 接入 AI 引用上下文。
63
+
64
+ - Updated dependencies [16ec4e8]
65
+ - Updated dependencies [cc5c94d]
66
+ - Updated dependencies [447f3e2]
67
+ - @zhin.js/core@1.3.5
68
+ - @zhin.js/agent@1.0.4
69
+ - @zhin.js/host-http@1.0.1
70
+ - zhin.js@4.1.3
71
+ - @zhin.js/logger@1.0.75
72
+ - @zhin.js/plugin-runtime@1.0.1
73
+ - @zhin.js/adapter@1.0.1
74
+
3
75
  ## 5.0.2
4
76
 
5
77
  ### Patch Changes
package/README.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # @zhin.js/adapter-qq
2
2
 
3
- Zhin.js QQ 官方机器人适配器,基于 QQ 官方机器人 API 开发,支持频道、群聊和私聊消息。
3
+ Zhin.js QQ 官方机器人适配器(Plugin Runtime),默认通过 **WebSocket Gateway**(`qq-official-bot`)收发消息(无需 host-router / host-http)。
4
+
5
+ ## 功能
6
+
7
+ - WebSocket Gateway 入站(默认;无需公网 HTTPS / host)
8
+ - 解析私聊 / 群 / 频道消息
9
+ - 出站 `send({ target, payload })` → QQ API(`private:` / `group:` / `channel:` / `direct:`)
10
+ - 约定式 `defineAdapter` / `definePlugin`(无需 `usePlugin`)
11
+ - Webhook / middleware 模式已实现(经 `httpHostToken` 注册 POST 路由)
12
+ - AI `@` 触发标注:群消息(GROUP_AT_MESSAGE_CREATE 仅 @ 时下发)与频道 `mentions[].bot` 会在入站 metadata 标 `mentioned: true`(新 Plugin Runtime 纯文本 content 经 metadata 传递 @)
4
13
 
5
14
  ## 安装
6
15
 
@@ -8,444 +17,101 @@ Zhin.js QQ 官方机器人适配器,基于 QQ 官方机器人 API 开发,支
8
17
  pnpm add @zhin.js/adapter-qq
9
18
  ```
10
19
 
11
- ## 扫码添加机器人(/endpoint)
12
-
13
- 无需手动复制 AppID/AppSecret:在 IM 中由 **master / trusted** 操作员发送命令,适配器会内联调用 QQ 开放平台绑定协议(等价 `@tencent-connect/qqbot-connector`,**不依赖**该 npm 包),扫码成功后:
14
-
15
- - 凭据写入项目根 **`.env`**(键名 `QQ_<name>_APPID` / `QQ_<name>_SECRET`)
16
- - `zhin.config.yml` 仅保留 **`${...}` 引用**(与脚手架其它 adapter 一致)
17
- - **`master`** 与 **`aiAccess.users`** 自动设为 `poll_bind_result` 返回的扫码者 OpenID(接口无该字段时回退为发起命令的用户 ID)
18
-
19
- | 命令 | 说明 |
20
- |------|------|
21
- | `/endpoint add qq [name]` | 发起扫码绑定;`name` 默认使用 AppID |
22
- | `/endpoint sync` | 将内存 endpoint 写回 `zhin.config.yml` |
23
- | `/endpoint cancel` | 取消进行中的绑定 |
24
- | `/endpoints` | 查看运行时 qq endpoints 在线状态 |
25
- | `/endpoint help` | Endpoint 管理帮助 |
26
-
27
- 二维码通过 IM `$reply` 发送 `segment.qrcode(url)`;各 Adapter 在 **`$sendMessage`** 内解析该段(IM → `image`,process → 终端 ASCII,GitHub 等 → 文本链接)。
28
-
29
- 二维码渲染使用 core 的 `segment.qrcode` / `GeneratedQrCode`(`import { segment } from 'zhin.js'` 或 `import { GeneratedQrCode } from 'zhin.js/qrcode'`)。
30
-
31
- ## AIGC 合规(AI 白名单)
32
-
33
- QQ 开放平台限制 AIGC 进入社群场景、禁止面向全量用户开放生成式能力。请配置 **`endpoints[].aiAccess`**(推荐)或全局 **`ai.access`**,仅 gate LLM 回复;斜杠命令、游戏插件等不受影响。
34
-
35
- ```yaml
36
- endpoints:
37
- - context: qq
38
- name: my-bot
39
- appid: ...
40
- secret: ...
41
- aiAccess:
42
- mode: whitelist
43
- users: ['QQ用户openid']
44
- groups: ['QQ群openid']
45
- denyMessage: 当前未开放 AI,请联系管理员。
46
- ```
47
-
48
- 多 Endpoint 时可分别设置(例如正式服 `whitelist`、沙箱 `open`)。未写 `endpoints[].aiAccess` 时回退到全局 `ai.access`。
49
-
50
- - 群/频道未放行:**静默**(不回复 AI)
51
- - 私聊未放行:回复 `denyMessage`
52
- - 白名单:`users` 与 `groups` **OR** 匹配
53
-
54
- 详见 [内容审查 / AI Access Gate](../../../docs/advanced/content-moderation.md)。
55
-
56
- ## 配置
57
-
58
- ### 基础配置
59
-
60
- ```typescript
61
- import { defineConfig } from 'zhin.js';
62
-
63
- export default defineConfig({
64
- endpoints: [
65
- {
66
- context: 'qq',
67
- name: 'my-qq-bot',
68
- appid: process.env.QQ_APPID,
69
- secret: process.env.QQ_SECRET,
70
- mode: 'middleware',
71
- application: 'koa',
72
- webhookPath: '/qq/webhook',
73
- sandbox: true,
74
- data_dir: './data',
75
- },
76
- ],
77
- plugins: [
78
- '@zhin.js/adapter-qq',
79
- ],
80
- })
81
- ```
20
+ ## Plugin Runtime
82
21
 
83
- ### 完整配置选项
84
-
85
- ```typescript
86
- const config: QQEndpointConfig = {
87
- context: 'qq',
88
- name: 'my-qq-bot',
89
- appid: 'YOUR_APPID', // 机器人 AppID(必需,小写 appid)
90
- secret: 'YOUR_SECRET', // 机器人 Secret(必需)
91
- mode: 'middleware', // websocket | webhook | middleware(推荐挂 host-router)
92
- application: 'koa', // middleware 模式必填
93
- webhookPath: '/qq/webhook', // middleware 回调路径(完整 URL: {host}:8086/qq/webhook,无 /api 前缀)
94
- // mode: 'webhook', // 独立 HTTP 端口模式
95
- // port: 8087,
96
- // path: '/qq/webhook',
97
- platform: 'qq', // 'qq' | 'qzone' 平台类型
98
- intents: [ // 事件订阅意图(qq-official-bot ^1.2 使用 GROUP_AND_C2C_EVENT)
99
- 'GROUP_AND_C2C_EVENT', // QQ 群 @ 消息 + C2C 私聊(必需)
100
- 'GUILDS',
101
- 'GUILD_MEMBERS',
102
- 'GUILD_MESSAGES', // 频道私域 @ 消息(公域用 PUBLIC_GUILD_MESSAGES)
103
- 'DIRECT_MESSAGE',
104
- 'PUBLIC_GUILD_MESSAGES',
105
- ],
106
- data_dir: './data', // 数据目录(可选)
107
- sandbox: false // 是否为沙箱环境(可选)
108
- }
109
- ```
22
+ - `@zhin.js/adapter` — 约定式 `adapters/qq.ts`(`defineAdapter`)
23
+ - `@zhin.js/core` — `messageGatewayToken` 入站/出站
24
+ - `@zhin.js/plugin-runtime` — `plugin.ts`(`definePlugin`)
25
+ - 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
26
+ - **无需** `@zhin.js/host-http` / `@zhin.js/host-router`(WebSocket 路径)
110
27
 
111
- ## 入站模式(二选一)
28
+ 入站:`gateway.receive({ adapter, target: 'group:…'|…, content, sender, metadata })`
29
+ 出站:`send({ target, payload })` → `sendPrivateMessage` / `sendGroupMessage` / `sendGuildMessage`
112
30
 
113
- | 模式 | 配置 | 说明 |
114
- |------|------|------|
115
- | **middleware**(推荐) | `mode: middleware` + `application: koa` + `webhookPath` | 挂在 `@zhin.js/host-router`,与 Host **同端口**(如 `8086`) |
116
- | **webhook**(独立端口) | `mode: webhook` + `port` + `path` | `qq-official-bot` 自建 HTTP 服务,需单独暴露端口 |
117
- | websocket | `mode: websocket` | 长连接,无需公网回调 |
31
+ ## 前置条件
118
32
 
119
- ### 自定义网关(qq-official-bot ^1.2.2)
33
+ | 要求 | 说明 |
34
+ |------|------|
35
+ | **AppID / Secret** | [QQ 开放平台](https://q.qq.com/) 创建机器人应用并获取 |
36
+ | **WebSocket(默认)** | `qq-official-bot` 正向连接;无需公网回调 |
37
+ | **host-http** | WebSocket **不需要**;Webhook / middleware 模式需要(经 `httpHostToken`) |
120
38
 
121
- 经代理或私有部署时,可覆盖官方 token / gateway 接口:
39
+ 必填字段(`endpoints[i]`):`name`、`appid`、`secret`。
122
40
 
123
- ```yaml
124
- # WebSocket 入站:gatewayUrl 与 accessTokenUrl 均生效
125
- - context: qq
126
- name: my-qq-bot
127
- mode: websocket
128
- appid: ${QQ_APPID}
129
- secret: ${QQ_SECRET}
130
- accessTokenUrl: https://your-proxy.example.com/app/getAppAccessToken
131
- gatewayUrl: https://your-proxy.example.com/gateway/bot
132
- intents:
133
- - GROUP_AND_C2C_EVENT
134
- ```
41
+ ## 最小配置
135
42
 
136
43
  ```yaml
137
- # middleware / webhook:仅 accessTokenUrl 影响出站鉴权(入站走 HTTP 回调,不用 gateway)
138
- - context: qq
139
- name: my-qq-bot
140
- mode: middleware
141
- application: koa
142
- webhookPath: /qq/webhook
143
- accessTokenUrl: https://your-proxy.example.com/app/getAppAccessToken
44
+ # zhin.config.yml(Plugin Runtime)
45
+ plugins:
46
+ qq:
47
+ # mode: websocket # 默认
48
+ endpoints:
49
+ - name: my-qq-bot
50
+ appid: ${QQ_APPID}
51
+ secret: ${QQ_SECRET}
144
52
  ```
145
53
 
146
- - `gatewayUrl` 可为完整 URL 或相对路径(默认 `/gateway/bot`,相对 `api.sgroup.qq.com` baseURL)
147
- - 连接时日志会输出 `op: qq_gateway` 及实际使用的地址
148
-
149
- ### middleware 回调地址(test-bot 默认)
150
-
151
- 1. 确保 `plugins` 含 `@zhin.js/host-router`(test-bot 已启用)
152
- 2. `zhin.config.yml` 示例见上文 `webhookPath: /qq/webhook`
153
- 3. 在 [QQ 开放平台](https://q.qq.com/bot) → **开发 → 回调配置** 填写:
154
-
155
- ```
156
- https://<你的公网域名>/qq/webhook
157
- ```
158
-
159
- 本地:`http://127.0.0.1:8086/qq/webhook`(经 ngrok / cloudflared 暴露公网即可)。
160
-
161
- > **注意**:Host 的 Console/API 走 `/api/*`(如 `/api/stats`),但适配器 webhook 直接挂在 Router 根路径,**不要**加 `/api` 前缀;填 `/api/qq/webhook` 会命中 Bearer 鉴权,QQ 平台回调会 401。
162
-
163
- 4. 勾选事件:**群事件**、**C2C 私聊**、**频道事件**(按需)
164
- 5. 保存时平台会发 `SIGN_VERIFY` 签名校验,适配器会自动响应
165
-
166
- > Host 对含 `/webhook` 的路径**免 Bearer 鉴权**,由 QQ 的 `X-Signature-Ed25519` 验签。
167
-
168
- ### webhook 独立端口
54
+ 多账号:一个插件实例挂多个 endpoint(`endpoints` 数组逐项覆盖顶层字段,`name` 必填):
169
55
 
170
56
  ```yaml
171
- mode: webhook
172
- port: 8087
173
- path: /qq/webhook
174
- ```
175
-
176
- 回调填 `https://<域名>:8087/qq/webhook`(或反代到该端口)。
177
-
178
- ## 获取配置信息
179
-
180
- ### 1. 注册 QQ 机器人
181
-
182
- 1. 访问 [QQ 开放平台](https://q.qq.com/bot)
183
- 2. 登录并创建机器人应用
184
- 3. 在「开发设置」中获取:
185
- - **AppID**(配置字段 `appid`): 机器人应用 ID
186
- - **Secret**: 机器人密钥(`secret`)
187
- - **Secret**: 机器人密钥
188
-
189
- ### 2. 配置权限
190
-
191
- 在机器人设置中:
192
- - 配置需要的事件订阅
193
- - 设置消息接收模式(公域/私域)
194
- - 添加频道/群聊白名单(如需要)
195
-
196
- ### 3. 事件订阅(Intents)
197
-
198
- 可订阅的事件类型:
199
- - `GUILDS` - 频道事件
200
- - `GUILD_MEMBERS` - 成员变动
201
- - `GUILD_MESSAGES` - 频道消息
202
- - `DIRECT_MESSAGE` - 私信消息
203
- - `GROUP_AND_C2C_EVENT` - QQ 群 @ 消息 + C2C 私聊(`GROUP_AT_MESSAGE_CREATE` / `C2C_MESSAGE_CREATE`)
204
- - `INTERACTION` - 互动事件
205
-
206
- ## 使用示例
207
-
208
- ### 基础消息处理
209
-
210
- ```typescript
211
- import { addCommand, MessageCommand } from 'zhin.js'
212
-
213
- addCommand(new MessageCommand('hello <name:text>')
214
- .action(async (message, result) => {
215
- return `你好,${result.params.name}!`
216
- })
217
- )
57
+ plugins:
58
+ qq:
59
+ mode: websocket
60
+ intents: [GUILDS, GROUP_AND_C2C_EVENT]
61
+ endpoints:
62
+ - name: main-bot
63
+ appid: ${QQ_APPID}
64
+ secret: ${QQ_SECRET}
65
+ - name: second-bot
66
+ appid: ${QQ_APPID_2}
67
+ secret: ${QQ_SECRET_2}
218
68
  ```
219
69
 
220
- ### 频道消息
70
+ 根插件 `zhin.plugins`(或项目图)需引用 `@zhin.js/adapter-qq`(`instanceKey: qq`)。
221
71
 
222
- ```typescript
223
- import { onMessage } from 'zhin.js'
224
-
225
- onMessage(async (message) => {
226
- // 仅处理频道消息
227
- if (message.$channel.type === 'channel') {
228
- console.log(`频道消息:${message.$raw}`)
229
- }
230
- })
231
- ```
72
+ ## Endpoint 管理命令
232
73
 
233
- ### 群聊消息
234
-
235
- ```typescript
236
- import { onGroupMessage } from 'zhin.js'
237
-
238
- onGroupMessage(async (message) => {
239
- console.log(`群聊消息来自:${message.$sender.name}`)
240
- console.log(`消息内容:${message.$raw}`)
241
- })
242
- ```
243
-
244
- ### 私聊消息
245
-
246
- ```typescript
247
- import { onPrivateMessage } from 'zhin.js'
248
-
249
- onPrivateMessage(async (message) => {
250
- await message.$reply('收到你的私信了!')
251
- })
252
- ```
253
-
254
- ### 发送不同类型消息
255
-
256
- ```typescript
257
- addCommand(new MessageCommand('card')
258
- .action(async (message) => {
259
- // 发送 Ark 模板卡片
260
- return {
261
- type: 'ark',
262
- template_id: 23,
263
- kv: [
264
- { key: '#TITLE#', value: '标题' },
265
- { key: '#DESC#', value: '描述' },
266
- { key: '#PROMPT#', value: '提示' }
267
- ]
268
- }
269
- })
270
- )
271
-
272
- addCommand(new MessageCommand('embed')
273
- .action(async (message) => {
274
- // 发送 Embed 消息
275
- return {
276
- type: 'embed',
277
- title: 'Embed 标题',
278
- prompt: '消息提示',
279
- thumbnail: { url: 'https://example.com/image.png' },
280
- fields: [
281
- { name: '字段1', value: '值1' }
282
- ]
283
- }
284
- })
285
- )
286
- ```
74
+ 适配器自带 `qq endpoint` 命令组(聊天内直接使用,默认无前缀;受 `commandPrefix` 影响):
287
75
 
288
- ## 消息类型支持
289
-
290
- ### 接收消息类型
291
-
292
- - ✅ 文本消息
293
- - ✅ @ 提及
294
- - ✅ 图片消息
295
- - ✅ 表情消息
296
- - ✅ Ark 模板消息
297
- - ✅ Embed 消息
298
- - ✅ Markdown 消息
299
-
300
- ### 发送消息类型
301
-
302
- - ✅ 文本消息
303
- - ✅ 图片消息
304
- - ✅ Ark 模板消息
305
- - ✅ Embed 富文本消息
306
- - ✅ Markdown 消息
307
- - ✅ 消息引用(回复)
308
-
309
- ### AI 出站 Markdown(默认开启)
310
-
311
- 官方已支持全量 Markdown 后,适配器会在 `$sendMessage` 出站前自动把 AI 的纯文本段转为 `msg_type=2` 的 Markdown 消息(保留 leading `reply`)。配置项 `outboundMarkdown`:
312
-
313
- | 值 | 行为 |
314
- |---|---|
315
- | `auto`(默认) | 正文含 `**`、列表、代码块等 Markdown 语法时才转换 |
316
- | `true` | 纯文本也走 Markdown |
317
- | `false` | 始终发纯文本(与旧行为一致) |
318
-
319
- ```yaml
320
- endpoints:
321
- - context: qq
322
- name: my-bot
323
- outboundMarkdown: auto # 或 true / false
324
- ```
325
-
326
- 含图片/文件等富媒体的多段消息不会合并,避免破坏分条发送。
327
-
328
- **图文混排**(文本 + 图片,典型如二维码说明 + `segment.qrcode`)会自动处理:
329
-
330
- | 图片类型 | 行为 |
331
- |---|---|
332
- | 公网 `https` URL | 合并为单条 Markdown(`msg_type=2`),图片用 `![说明 #宽 #高](url)` |
333
- | 内联 base64 / data URI(如二维码渲染) | 群聊/私聊先上传 `file_info`,再以 `msg_type=7` 图文混排同条发送 |
334
-
335
- `outboundMarkdown: false` 时关闭上述合并,恢复分条发送。
336
-
337
- ## API 方法
338
-
339
- ### 基础方法
340
-
341
- ```typescript
342
- const endpoint = app.adapters.get('qq')?.endpoints.get('你的机器人ID')
343
-
344
- // 发送私信
345
- await endpoint.sendPrivateMessage(userId, '消息内容')
346
-
347
- // 发送群消息
348
- await endpoint.sendGroupMessage(groupId, '消息内容')
349
-
350
- // 发送频道消息
351
- await endpoint.sendGuildMessage(channelId, '消息内容')
352
-
353
- // 撤回消息
354
- await endpoint.$recallMessage(messageId)
355
- ```
356
-
357
- ## 消息 ID 格式
358
-
359
- 本适配器使用特殊的消息 ID 格式来区分不同类型的消息:
360
-
361
- - 私信:`private-{userId}:{messageId}`
362
- - 群聊:`group-{groupId}:{messageId}`
363
- - 频道:`channel-{channelId}:{messageId}`
364
- - 私域频道:`direct-{guildId}:{messageId}`
365
-
366
- ## 注意事项
367
-
368
- ### 接收模式
369
-
370
- - **公域模式 (public)**: 仅接收 @ 机器人的消息
371
- - **私域模式 (private)**: 可接收频道内所有消息(需要申请权限)
372
-
373
- ### 频率限制
374
-
375
- QQ 机器人有严格的频率限制:
376
- - 主动消息:每个用户每天 5 条
377
- - 被动消息(回复):无限制
378
- - 建议在被动模式下使用(用户 @ 后回复)
379
-
380
- ### 沙箱环境
381
-
382
- 开发时可以使用沙箱环境测试:
383
-
384
- ```typescript
385
- {
386
- context: 'qq',
387
- sandbox: true, // 启用沙箱
388
- // ...其他配置
389
- }
390
- ```
391
-
392
- ## 常见问题
393
-
394
- ### Q: 机器人无法收到消息?
395
-
396
- A: 检查以下几点:
397
- 1. AppID、Secret 是否正确;`intents` 必须含 **`GROUP_AND_C2C_EVENT`**(不是旧名 `GROUP_AT_MESSAGE_CREATE`)
398
- 2. **沙箱环境**:在 [QQ 开放平台](https://q.qq.com/bot) → 开发设置 → 沙箱配置,把测试 **QQ 群** 加入沙箱白名单(私聊能通不代表群已配置)
399
- 3. 机器人是否已被拉入该 QQ 群(日志应出现 `qq notice group.add_robot`)
400
- 4. 群管理员是否在机器人资料页 **开启消息接收**(`qq notice group.msg_receive_open`);若出现 `group.msg_receive_close` 则群聊不会推送
401
- 5. 公域/群聊场景必须 **@ 机器人** 才会推送(`GROUP_AT_MESSAGE_CREATE`)
402
- 6. **QQ 频道** 与 **QQ 群** 是两套事件:频道 @ 需要 `GUILD_MESSAGES` 或 `PUBLIC_GUILD_MESSAGES`,不是 `GROUP_AND_C2C_EVENT`
403
-
404
- ### Q: Typing Indicator 群聊报 `40034105 主动消息失败, 无权限`?
405
-
406
- A: QQ **群聊**不允许无引用的主动消息。框架已对 QQ 群场景的「正在处理中…」自动附带 `reply` 引用触发消息;若仍失败,可在 Endpoint 配置中关闭群聊 typing:
407
-
408
- ```yaml
409
- typingIndicator:
410
- enabled: true
411
- groupConfig:
412
- type: none
413
- ```
414
-
415
- ### Q: 发送消息失败?
76
+ | 命令 | 说明 |
77
+ |------|------|
78
+ | `qq endpoint add [name]` | 手机 QQ 扫码绑定:下发二维码链接 → 确认后凭据写入 `.env`(`QQ_<NAME>_APPID/SECRET`),并追加 `plugins.qq.endpoints`(重启生效) |
79
+ | `qq endpoint cancel` | 取消进行中的扫码绑定(同时只允许一个流程) |
80
+ | `qq endpoint list` | 列出运行中与配置中的 endpoints |
81
+ | `qq endpoint remove <name>` | 从配置移除 endpoint(`.env` 键保留,可手动清理) |
416
82
 
417
- A: 可能的原因:
418
- 1. 超过主动消息频率限制
419
- 2. 没有对应频道/群的发送权限
420
- 3. 消息格式不符合规范
421
- 4. Token 已过期或失效
83
+ add/cancel/remove 受 `master` 限制:实例配置声明了 `master`(顶层或 `endpoints[i]`)时仅
84
+ master 可执行;未配置则放行(首个扫码绑定者即 owner)。二维码当前以链接文本下发
85
+ (出站富媒体待迁移),用手机 QQ 打开链接即可扫码。
422
86
 
423
- ### Q: 如何处理不同平台?
87
+ ## 环境变量
424
88
 
425
- A: 使用 `platform` 配置:
426
- - `qq`: QQ 频道和群聊
427
- - `qzone`: QQ 空间(如支持)
89
+ | 变量 | 说明 |
90
+ |------|------|
91
+ | `QQ_APPID` / `QQ_BOT_APPID` | 应用 AppID |
92
+ | `QQ_SECRET` / `QQ_BOT_SECRET` | 应用 Secret |
93
+ | `QQ_BOT_NAME` | 可选,默认 endpoint 名 |
428
94
 
429
- ## 相关链接
95
+ ## Webhook / middleware
430
96
 
431
- - [QQ 开放平台](https://q.qq.com/bot)
432
- - [QQ 机器人开发文档](https://bot.q.qq.com/wiki/)
433
- - [Zhin.js 官方文档](https://github.com/zhinjs/zhin)
97
+ `mode: webhook` 或 `mode: middleware` 经 `httpHostToken` 注册 POST 路由(默认 `/qq/webhook`),使用 qq-official-bot Middleware 接收器验签并入站;出站仍走 QQ HTTP API。Host 需注入 `httpHostToken`。
434
98
 
435
- ## AI 工具
99
+ ## AI 工具(Skill)
436
100
 
437
101
  | 类别 | 路径 |
438
102
  |------|------|
439
103
  | Permit 词汇 | `agent/PERMITS.md` |
440
- | 平台工具(8 个) | `agent/tools/`(`qq_*`:公会、频道、角色等) |
104
+ | 平台工具 | `agent/tools/`(频道、角色等) |
441
105
  | 技能说明 | `agent/skills/qq.md` |
442
- | 群管标准工具 | `createSceneManagementTools()` |
443
106
 
107
+ ## 平台权限(platform permit)
444
108
 
445
- ## 许可证
109
+ platform permit checker 由 `plugin.ts` 的 generation 生命周期注册;`@zhin.js/tool` descriptor 保留 `platforms` / `scopes` / `permissions`,CapabilityIngress 与 ToolSystem 统一经 Core `canAccessTool()` 执行门禁。
446
110
 
447
- MIT License
111
+ ## 迁移后出站能力变化
448
112
 
449
- ## 贡献
113
+ 迁移到 Plugin Runtime 后,出站统一经 `messageGatewayToken` 渲染为文本后发送(`sendPrivateMessage` / `sendGroupMessage` / `sendGuildMessage`)。旧 Adapter 的富媒体出站能力(图片 / 语音 / 视频、keyboard 按钮、markdown 模板等)暂未迁移,当前出站等价于纯文本。如需富媒体,可通过 endpoint 的 QQ API 封装或直接调用 QQ HTTP API 作为逃生舱。
450
114
 
451
- 欢迎提交 Issue 和 Pull Request!
115
+ ## 许可证
116
+
117
+ MIT License
package/adapters/qq.ts ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Convention entry: discover `adapters/qq.ts` → defineAdapter.
3
+ */
4
+ import { defineAdapter } from '@zhin.js/adapter';
5
+ import { messageGatewayToken } from '@zhin.js/core/runtime';
6
+ import { httpHostToken } from '@zhin.js/host-http';
7
+ import { QqHttpEndpoint, QqWebsocketEndpoint } from '../src/endpoint.js';
8
+ import {
9
+ resolveQqConfig,
10
+ type QqAdapterConfig,
11
+ } from '../src/protocol.js';
12
+ import { qqRuntimeStateToken } from '../src/qq-runtime-state.js';
13
+
14
+ export {
15
+ QqHttpEndpoint,
16
+ QqWebsocketEndpoint,
17
+ } from '../src/endpoint.js';
18
+ export type {
19
+ CreateQqBot,
20
+ CreateQqHttpBot,
21
+ QqBotTransport,
22
+ QqEndpointOptions,
23
+ QqHttpBotTransport,
24
+ QqHttpEndpointOptions,
25
+ } from '../src/endpoint.js';
26
+
27
+ export default defineAdapter<QqAdapterConfig>({
28
+ capabilities: ['inbound', 'outbound'],
29
+ create(context) {
30
+ const config = resolveQqConfig(context.config);
31
+ const gateway = context.use(messageGatewayToken);
32
+ // 注册到插件运行时状态(qq endpoint list 的"运行中"数据源)
33
+ context.use(qqRuntimeStateToken).endpoints.set(config.name, {
34
+ name: config.name,
35
+ mode: config.mode,
36
+ });
37
+ if (config.mode === 'webhook' || config.mode === 'middleware') {
38
+ return new QqHttpEndpoint({
39
+ id: context.id,
40
+ gateway,
41
+ http: context.use(httpHostToken),
42
+ config,
43
+ });
44
+ }
45
+ return new QqWebsocketEndpoint({
46
+ id: context.id,
47
+ gateway,
48
+ config,
49
+ });
50
+ },
51
+ });
@@ -1,9 +1,9 @@
1
- import { defineTool } from '@zhin.js/agent/tools';
1
+ import { defineAgentTool } from '@zhin.js/agent/tools';
2
2
  import { z } from 'zod';
3
3
  import { platformPermit } from '../../src/platform-permit.js';
4
4
  import { getQqAgentDeps } from '../../src/qq-agent-deps.js';
5
5
 
6
- export default defineTool<{ endpoint_id: string; guild_id: string; channel_id: string; user_id: string; role_id: string }>({
6
+ export default defineAgentTool<{ endpoint_id: string; guild_id: string; channel_id: string; user_id: string; role_id: string }>({
7
7
  description: '给成员添加 QQ 频道角色',
8
8
  inputSchema: z.object({
9
9
  endpoint_id: z.string().describe('Endpoint 名称'),