@zhin.js/adapter-icqq 6.0.2 → 7.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 (125) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +48 -435
  3. package/adapters/icqq.ts +92 -0
  4. package/agent/tools/announce.ts +2 -2
  5. package/agent/tools/essence.ts +2 -2
  6. package/agent/tools/friend_list.ts +2 -2
  7. package/agent/tools/group_files.ts +2 -2
  8. package/agent/tools/group_invite.ts +2 -2
  9. package/agent/tools/group_list.ts +2 -2
  10. package/agent/tools/group_sign.ts +2 -2
  11. package/agent/tools/list_muted.ts +2 -2
  12. package/agent/tools/poke.ts +2 -2
  13. package/agent/tools/send_user_like.ts +2 -2
  14. package/agent/tools/set_anonymous.ts +2 -2
  15. package/agent/tools/set_title.ts +2 -2
  16. package/lib/agent-prompt.d.ts +2 -0
  17. package/lib/{src/agent-prompt.js → agent-prompt.js} +0 -1
  18. package/lib/cq-message.d.ts +9 -0
  19. package/lib/{src/cq-message.js → cq-message.js} +0 -1
  20. package/lib/endpoint.d.ts +101 -0
  21. package/lib/endpoint.js +715 -0
  22. package/lib/forward-msg.d.ts +26 -0
  23. package/lib/{src/forward-msg.js → forward-msg.js} +0 -1
  24. package/lib/icqq-agent-deps.d.ts +25 -0
  25. package/lib/icqq-agent-deps.js +32 -0
  26. package/lib/icqq-guild.d.ts +53 -0
  27. package/lib/{src/icqq-guild.js → icqq-guild.js} +4 -2
  28. package/lib/icqq-inbound.d.ts +139 -0
  29. package/lib/{src/icqq-inbound.js → icqq-inbound.js} +27 -3
  30. package/lib/icqq-inbox.d.ts +33 -0
  31. package/lib/icqq-inbox.js +129 -0
  32. package/lib/index.d.ts +6 -0
  33. package/lib/index.js +6 -0
  34. package/lib/ipc-client.d.ts +73 -0
  35. package/lib/{src/ipc-client.js → ipc-client.js} +0 -1
  36. package/lib/outbound-media.d.ts +13 -0
  37. package/lib/{src/outbound-media.js → outbound-media.js} +0 -1
  38. package/lib/protocol.d.ts +269 -0
  39. package/lib/{src/protocol.js → protocol.js} +124 -2
  40. package/lib/types.d.ts +87 -0
  41. package/lib/{src/types.js → types.js} +0 -1
  42. package/package.json +49 -45
  43. package/plugin.ts +32 -0
  44. package/schema.json +104 -0
  45. package/src/endpoint.ts +704 -795
  46. package/src/icqq-agent-deps.ts +48 -9
  47. package/src/icqq-guild.ts +10 -4
  48. package/src/icqq-inbound.ts +31 -2
  49. package/src/icqq-inbox.ts +155 -0
  50. package/src/index.ts +36 -69
  51. package/src/protocol.ts +191 -1
  52. package/src/types.ts +16 -0
  53. package/client/ICQQManagement.tsx +0 -147
  54. package/client/LoginAssistPanel.tsx +0 -300
  55. package/client/index.tsx +0 -11
  56. package/client/tsconfig.json +0 -7
  57. package/client/utils/api.ts +0 -30
  58. package/dist/index.js +0 -34
  59. package/lib/agent/tools/announce.js +0 -21
  60. package/lib/agent/tools/announce.js.map +0 -1
  61. package/lib/agent/tools/essence.js +0 -22
  62. package/lib/agent/tools/essence.js.map +0 -1
  63. package/lib/agent/tools/friend_list.js +0 -19
  64. package/lib/agent/tools/friend_list.js.map +0 -1
  65. package/lib/agent/tools/group_files.js +0 -29
  66. package/lib/agent/tools/group_files.js.map +0 -1
  67. package/lib/agent/tools/group_invite.js +0 -20
  68. package/lib/agent/tools/group_invite.js.map +0 -1
  69. package/lib/agent/tools/group_list.js +0 -20
  70. package/lib/agent/tools/group_list.js.map +0 -1
  71. package/lib/agent/tools/group_sign.js +0 -19
  72. package/lib/agent/tools/group_sign.js.map +0 -1
  73. package/lib/agent/tools/list_muted.js +0 -23
  74. package/lib/agent/tools/list_muted.js.map +0 -1
  75. package/lib/agent/tools/poke.js +0 -20
  76. package/lib/agent/tools/poke.js.map +0 -1
  77. package/lib/agent/tools/send_user_like.js +0 -22
  78. package/lib/agent/tools/send_user_like.js.map +0 -1
  79. package/lib/agent/tools/set_anonymous.js +0 -22
  80. package/lib/agent/tools/set_anonymous.js.map +0 -1
  81. package/lib/agent/tools/set_title.js +0 -25
  82. package/lib/agent/tools/set_title.js.map +0 -1
  83. package/lib/src/adapter.js +0 -128
  84. package/lib/src/adapter.js.map +0 -1
  85. package/lib/src/agent-prompt.js.map +0 -1
  86. package/lib/src/cq-message.js.map +0 -1
  87. package/lib/src/endpoint.js +0 -755
  88. package/lib/src/endpoint.js.map +0 -1
  89. package/lib/src/forward-msg.js.map +0 -1
  90. package/lib/src/get-msg.js +0 -46
  91. package/lib/src/get-msg.js.map +0 -1
  92. package/lib/src/icqq-agent-deps.js +0 -10
  93. package/lib/src/icqq-agent-deps.js.map +0 -1
  94. package/lib/src/icqq-guild.js.map +0 -1
  95. package/lib/src/icqq-inbound.js.map +0 -1
  96. package/lib/src/icqq-login-assist-bridge.js +0 -148
  97. package/lib/src/icqq-login-assist-bridge.js.map +0 -1
  98. package/lib/src/icqq-side-events.js +0 -130
  99. package/lib/src/icqq-side-events.js.map +0 -1
  100. package/lib/src/index.js +0 -50
  101. package/lib/src/index.js.map +0 -1
  102. package/lib/src/ipc-client.js.map +0 -1
  103. package/lib/src/login-assist-routes.js +0 -29
  104. package/lib/src/login-assist-routes.js.map +0 -1
  105. package/lib/src/login-ipc-contract.js +0 -35
  106. package/lib/src/login-ipc-contract.js.map +0 -1
  107. package/lib/src/outbound-media.js.map +0 -1
  108. package/lib/src/protocol.js.map +0 -1
  109. package/lib/src/routes.js +0 -67
  110. package/lib/src/routes.js.map +0 -1
  111. package/lib/src/segment-mapper.js +0 -2
  112. package/lib/src/segment-mapper.js.map +0 -1
  113. package/lib/src/tools/index.js +0 -23
  114. package/lib/src/tools/index.js.map +0 -1
  115. package/lib/src/types.js.map +0 -1
  116. package/plugin.yml +0 -3
  117. package/src/adapter.ts +0 -151
  118. package/src/get-msg.ts +0 -56
  119. package/src/icqq-login-assist-bridge.ts +0 -196
  120. package/src/icqq-side-events.ts +0 -143
  121. package/src/login-assist-routes.ts +0 -39
  122. package/src/login-ipc-contract.ts +0 -52
  123. package/src/routes.ts +0 -83
  124. package/src/segment-mapper.ts +0 -1
  125. package/src/tools/index.ts +0 -31
package/CHANGELOG.md CHANGED
@@ -1,5 +1,70 @@
1
1
  # @zhin.js/adapter-icqq
2
2
 
3
+ ## 7.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 7db69c1: 命令前缀改为适配器配置项:`MessageDispatcher` 不再硬编码 `/`,默认按消息所属适配器实例 config 的 `commandPrefix` 解析(默认 `''` 无前缀,任意文本按命令匹配),`endpoints[i].commandPrefix` 逐项覆盖;`ImRuntime({ commandPrefix })` 仍可设全局静态前缀。全部 20 个平台适配器 schema 新增 `commandPrefix` 属性。
8
+
9
+ BREAKING(行为变化):未配置时命令不再需要 `/` 前缀——原 `/zt` 写法不再命中,直接发 `zt` 即可;需要斜杠风格的适配器请在配置里显式设 `commandPrefix: '/'`。
10
+
11
+ - e5c84ed: Adapter 多账号:插件实例 config 支持 `endpoints: [{name, ...覆盖}]` 数组,`expandEndpointConfigs` 将一个实例展开为多个 endpoint record(id 为 `<slotId>~<name>`,顶层字段共享、逐项覆盖),替代多 `instanceKey` 方案;Console `/api/plugins` 收敛为一个插件卡片 + 多 endpoint。icqq / qq schema 与 README 补 `endpoints` 配置。
12
+
13
+ Plugin Runtime Console Host:补 `/esm/*` React/router ESM 代理路由(legacy `consoleApiRouter` 对齐),TypeScriptClientBuilder 裸导入改写为 `/esm/<enc>.mjs`。
14
+
15
+ - 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 同步迁移。
16
+ - f32c424: 适配器配置统一为 endpoints 数组格式:`plugins.<adapter>` 为该 adapter 所有 endpoint 的通用配置,`plugins.<adapter>.endpoints[i]` 为单个 endpoint 的特殊配置(逐项覆盖,`name` 必填)。slack / github schema 新增 `endpoints` 属性;icqq / qq / slack 顶层必填改为 `anyOf`(单 endpoint 字段或 `endpoints` 数组二选一,兼容 Ajv strictRequired)。
17
+ - Updated dependencies [7db69c1]
18
+ - Updated dependencies [e5c84ed]
19
+ - Updated dependencies [3ea84a0]
20
+ - Updated dependencies [1ddcd70]
21
+ - Updated dependencies [ac9da66]
22
+ - @zhin.js/core@1.4.0
23
+ - @zhin.js/adapter@1.1.0
24
+ - @zhin.js/plugin-runtime@1.1.0
25
+ - @zhin.js/agent@1.0.5
26
+ - zhin.js@5.0.0
27
+
28
+ ## 6.0.3
29
+
30
+ ### Patch Changes
31
+
32
+ - cc5c94d: 约定式插件运行时迁移(breaking):插件与适配器由 `usePlugin()` / `extends Adapter` 迁移为 `definePlugin` / `defineAdapter` + `plugin.ts` + 约定目录(`adapters/`、`commands/`、`components/`、`tools/` 等)。
33
+
34
+ - 新增约定式运行时包:`@zhin.js/plugin-runtime`、`@zhin.js/adapter`、`@zhin.js/runtime`、`@zhin.js/host-http`(首版 1.0.0 走 init-publish,不在本 changeset 内 bump)。
35
+ - 全部 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 推迟的连接模式已补齐。
36
+ - 游戏 / 工具 / 服务插件同步迁移到约定目录结构。
37
+ - CLI 增加 plugin-runtime host installer(http/database/outbound/schedule/console 等)。
38
+
39
+ 后续加固(同批):
40
+
41
+ - CLI:`zhin runtime start --daemon`(pidfile/崩溃拉起/风暴保护),orphan watchdog 防僵尸进程;legacy `zhin dev` / `zhin start` 已移除(含 `zhin restart`),`zhin stop` 兼容新 daemon。
42
+ - 安全:builtin 工具统一走 `security/policy-facade.ts` 的 `runToolPolicies`(声明式策略表,deny 优先);审计日志 close flush + 背压队列;`splitCompoundCommand` 引号感知、`extractCommandName` 去引号堵绕过。
43
+ - 日志:Logger 双堆栈修复、本地时区、`getLogger` 挂树(`setLevel` 递归生效)、第三方库(log4js/discord)桥接、启动人读总结。
44
+ - 结构:`plugins/games/shared` 迁为 `packages/game-kit`(`@zhin.js/game-kit`);死目录 `plugins/adapters/common` 删除。
45
+ - 脚手架:`create-zhin-app` / `zhin new` / scaffold-wizard 生成物改为 Plugin Runtime 形态(minimal-bot 同构,新配置格式)。
46
+ - Console:endpoint.list 真实名称与 phase、schema:get-all 按 instanceKey 映射、db:\* 接 DatabaseHost。
47
+
48
+ 注:按仓库发布惯例(见 1bb345dd2),本次 breaking 迁移统一使用 patch,避免 zhin.js 5.0 级联。
49
+
50
+ - 447f3e2: 迁移缺口修复(legacy 功能对齐):
51
+
52
+ - html 段出站规范化:经 `@zhin.js/html-renderer` 渲染为 image 段(sandbox 豁免、无渲染器时降级文本),修复真实平台 `[object Object]`。
53
+ - 群聊 @ 触发 AI:适配器入站标注 `metadata.mentioned`(icqq/qq/slack/onebot11/onebot12/napcat/milky/discord/telegram/kook/dingtalk/satori),`matchAiTrigger` 补齐 ignorePrefixes/respondToAt/respondToPrivate/keywords(默认值与 legacy 对齐)。
54
+ - im_transcripts 全量流水恢复写入(chat_history 工具可用);群聊旁听上下文回迁。
55
+ - `ai.trigger.timeout/thinkingMessage/errorTemplate` 生效;masters/trusted 角色解析对齐 legacy。
56
+ - `Message.sender` 统一为用户 ID(onebot11/12、napcat、milky 原误传显示名);quote_id 经 metadata 接入 AI 引用上下文。
57
+
58
+ - Updated dependencies [16ec4e8]
59
+ - Updated dependencies [cc5c94d]
60
+ - Updated dependencies [447f3e2]
61
+ - @zhin.js/core@1.3.5
62
+ - @zhin.js/agent@1.0.4
63
+ - zhin.js@4.1.3
64
+ - @zhin.js/logger@1.0.75
65
+ - @zhin.js/plugin-runtime@1.0.1
66
+ - @zhin.js/adapter@1.0.1
67
+
3
68
  ## 6.0.2
4
69
 
5
70
  ### Patch Changes
package/README.md CHANGED
@@ -1,464 +1,77 @@
1
1
  # @zhin.js/adapter-icqq
2
2
 
3
- > **QQ 群助手 + 可选 @AI** — 先在 [demo.zhin.dev](https://demo.zhin.dev) 或 Sandbox 调试逻辑,再接实机 QQ。启用 AI:`npx zhin setup --ai`。接入本适配器:`npx zhin setup --adapters`。
4
-
5
- Zhin.js ICQQ 适配器,通过 **[@icqqjs/cli](https://github.com/icqqjs/cli) 守护进程 IPC** 连接已登录的 QQ 账号,支持群聊和私聊消息。协议与登录由 CLI 负责,Zhin 侧只配置 QQ 号并连接本地/远程守护进程。
3
+ ICQQ Plugin Runtime 适配器 — 通过 [@icqqjs/cli](https://github.com/icqqjs/cli) 守护进程 IPC 连接已登录 QQ 账号(无 `httpHostToken`)。
6
4
 
7
5
  ## 功能特性
8
6
 
9
- - 🤖 支持 QQ 群聊、私聊与**群临时会话**消息处理
10
- - 🔐 登录由 `icqq login` 完成(短信/二维码/滑块等由 CLI 处理)
11
- - 🖥️ **Web 控制台登录辅助**:与 `@zhin.js/host-api` 同时启用时,在 **`/icqq`** 页面提供「概览 + 登录辅助」;HTTP 接口为 **`GET /api/login-assist/pending`**、**`POST /api/login-assist/submit`**、**`POST /api/login-assist/cancel`**(由本适配器在路由上下文中注册,依赖核心 `loginAssist` 服务)。
12
- - 📨 消息发送和接收处理
13
- - 🔄 消息格式转换和适配
14
- - 📁 自动数据目录管理
15
- - 🖼️ 支持图片、语音、视频等多媒体消息
16
- - 🎯 支持 @ 提及和引用回复
17
- - 🔧 **群管理工具**:踢人、禁言、设置管理员、群名片、头衔等(AI 可调用)
18
- - 🔒 **权限控制**:基于群角色的工具权限过滤
7
+ - 群聊 / 私聊 / 群临时会话 / QQ 频道消息
8
+ - 入站:`messageGatewayToken`(IPC 事件订阅)
9
+ - 出站:`send({ target, payload })` → `send_group_msg` / `send_private_msg` / …
10
+ - Agent 工具:`agent/tools/`(戳一戳、群管、好友列表等)保留
11
+ - Console Endpoint 管理:`src/endpoint.ts` 显式实现 `EndpointManagement`(好友/群/群成员列表、请求审批、删好友、踢人、禁言、设管理),Console 使用 `endpoint.friends` / `endpoint.groups` / `endpoint.group_members` 等规范 RPC
19
12
 
20
13
  ## 安装
21
14
 
22
15
  ```bash
23
16
  pnpm add @zhin.js/adapter-icqq
17
+ pnpm add -g @icqqjs/cli # 或 npx icqq login
24
18
  ```
25
19
 
26
- 登录与守护进程由 **@icqqjs/cli** 提供(与适配器包解耦,可全局安装):
27
-
28
- ```bash
29
- pnpm add -g @icqqjs/cli
30
- # 或:npx icqq login
31
- ```
32
-
33
- ## 配置
34
-
35
- 在 `zhin.config.yml` 中声明 bot(**不在此填写 QQ 密码**):
20
+ ## 配置(Plugin Runtime)
36
21
 
37
22
  ```yaml
38
- endpoints:
39
- - context: icqq
40
- name: "${ICQQ_ACCOUNT}" # QQ 号,须与 icqq login 的账号一致
41
- autoReconnect: true # IPC 断开后自动重连,默认 true
42
- # 远程 RPC(可选;默认连接 ~/.icqq/<uin>/daemon.sock)
43
- # rpc:
44
- # host: 10.0.0.2
45
- # port: 9527
46
- # token: ${ICQQ_RPC_TOKEN}
47
- typingIndicator: # 可选:AI 处理中的群/私聊提示
48
- enabled: true
49
- defaultEmoji: "⏳"
50
-
51
23
  plugins:
52
- - "@zhin.js/adapter-icqq"
53
- - "@zhin.js/host-router" # 可选
54
- - "@zhin.js/host-api" # 可选,Remote Console /icqq 页
55
- ```
56
-
57
- TypeScript 配置等价写法:
58
-
59
- ```typescript
60
- import { defineConfig } from 'zhin.js'
61
-
62
- export default defineConfig({
63
- endpoints: [
64
- {
65
- context: 'icqq',
66
- name: process.env.ICQQ_ACCOUNT!,
67
- },
68
- ],
69
- plugins: ['@zhin.js/adapter-icqq'],
70
- })
71
- ```
72
-
73
- ## 配置参数说明
74
-
75
- ### 必需参数
76
-
77
- - `context`: 固定为 `'icqq'`
78
- - `name`: QQ 号(字符串,与守护进程账号一致)
79
-
80
- ### 可选参数
81
-
82
- - `autoReconnect`: IPC/RPC 意外断开时是否指数退避重连(默认 `true`)
83
- - `rpc`: 远程守护进程连接(`host` / `port` / `token`);不配置则使用本地 Unix socket `~/.icqq/<uin>/daemon.sock`
84
- - `typingIndicator`: AI 处理中的 reaction 或临时消息提示(见 `IcqqEndpointConfig` 类型)
85
-
86
- ## 使用示例
87
-
88
- ### 基础消息处理
89
-
90
- ```typescript
91
- import { addCommand, MessageCommand } from 'zhin.js'
92
-
93
- addCommand(new MessageCommand('hello <name:text>')
94
- .action(async (message, result) => {
95
- return `你好,${result.params.name}!`
96
- })
97
- )
98
- ```
99
-
100
- ### 群聊消息
101
-
102
- ```typescript
103
- import { onGroupMessage } from 'zhin.js'
104
-
105
- onGroupMessage(async (message) => {
106
- console.log(`群 ${message.$channel.id} 收到消息:${message.$raw}`)
107
-
108
- // 回复群消息
109
- await message.$reply('收到消息了!')
110
- })
111
- ```
112
-
113
- ### 私聊消息
114
-
115
- ```typescript
116
- import { onPrivateMessage } from 'zhin.js'
117
-
118
- onPrivateMessage(async (message) => {
119
- console.log(`私聊消息来自:${message.$sender.name}`)
120
- await message.$reply('你好!')
121
- })
122
- ```
123
-
124
- ### 发送图片
125
-
126
- ```typescript
127
- addCommand(new MessageCommand('pic <url:text>')
128
- .action(async (message, result) => {
129
- return [
130
- { type: 'image', data: { file: result.params.url } }
131
- ]
132
- })
133
- )
24
+ icqq:
25
+ master: "1659488338" # 必填,顶层共享(/approve 与 master 角色)
26
+ autoReconnect: true
27
+ endpoints:
28
+ - name: "${ICQQ_ACCOUNT}" # QQ 号,须与 icqq login 一致
29
+ # rpc 为 endpoint 级字段:
30
+ # - name: "${ICQQ_ACCOUNT_2}"
31
+ # rpc:
32
+ # host: 10.0.0.2
33
+ # port: 9527
34
+ # token: ${ICQQ_RPC_TOKEN}
134
35
  ```
135
36
 
136
- **Zhin 与 icqq 守护进程不在同一台机器时**,请在 `zhin.config.yml` 为该 bot 设置 `outboundMedia: base64`(或配置 `rpc` 时会自动默认 `base64`)。此时 segment 保留 `base64`,经 CQ `[image:base64://...]` 交给守护进程解码,不要使用仅在本机有效的临时文件路径。
137
-
138
- Agent **`generate_image` 文生图**出站的大图同样依赖上述配置;同机默认可用本机临时文件路径(`outboundMedia: file`)。详见 [文生图 (generate_image)](https://github.com/zhinjs/zhin/blob/main/docs/advanced/ai.md#文生图-generate_image)。
37
+ 多账号:一个插件实例挂多个 endpoint(`endpoints` 数组逐项覆盖顶层字段,`name` 必填):
139
38
 
140
39
  ```yaml
141
- endpoints:
142
- - context: icqq
143
- name: "123456"
144
- outboundMedia: base64 # 异机 / 远程 RPC
145
- # rpc: { host: ..., port: ..., token: ... } # 配置 rpc 时默认已是 base64
146
- ```
147
-
148
- ### 发送语音
149
-
150
- ```typescript
151
- addCommand(new MessageCommand('voice <file:text>')
152
- .action(async (message, result) => {
153
- return [
154
- { type: 'record', data: { file: result.params.file } }
155
- ]
156
- })
157
- )
158
- ```
159
-
160
- ### @ 提及
161
-
162
- ```typescript
163
- addCommand(new MessageCommand('at <user:at>')
164
- .action(async (message, result) => {
165
- const atUser = result.params.user
166
- return [
167
- { type: 'at', data: { qq: atUser.data.qq } },
168
- { type: 'text', data: { text: ' 你好!' } }
169
- ]
170
- })
171
- )
172
- ```
173
-
174
- ## 登录流程
175
-
176
- 1. **先登录 QQ**:在终端执行 `icqq login`,按 CLI 提示完成密码/扫码/滑块/设备锁等验证;会话由 `@icqqjs/cli` 守护进程维护。
177
- 2. **再启动 Zhin**:`zhin.config.yml` 中 `endpoints[].name` 与上一步 QQ 号一致,启动 `zhin dev` / `zhin start`;适配器连接守护进程并开始收发消息。
178
- 3. 若守护进程未运行或账号未登录,适配器会保持未连接状态,日志中会提示检查 `icqq` 守护进程。
179
-
180
- ### Web 控制台登录辅助(可选)
181
-
182
- 与 **`@zhin.js/host-api`** 同时启用时,Remote Console 的 **ICQQ 管理**(`/icqq`)可提供登录辅助 Tab,对应 API:`GET/POST /api/login-assist/*`(依赖核心 `loginAssist` 服务)。
183
-
184
- 守护进程在登录/重连时按 **`@icqqjs/cli` IPC 契约**推送 `IpcEvent`(`id: "*"` + `event: "system.login.*"` + `data`)。其中 **`system.login.auth`** 携带 `url` + `device` 设备指纹(形状见 `@icqqjs/icqq` `EventMap`)。适配器将其桥接到 `LoginAssist` 并在控制台展示;用户完成验证后,续传 invoke 与 CLI `LoginFlow` 一致(`login` / `submit_slider` / `submit_sms_code` / `send_sms_code`,见 `login-ipc-contract.ts`)。
185
-
186
- ## 消息类型支持
187
-
188
- ### 接收消息类型
189
-
190
- - ✅ 文本消息
191
- - ✅ 图片消息
192
- - ✅ 语音消息
193
- - ✅ 视频消息
194
- - ✅ @ 提及
195
- - ✅ 表情消息
196
- - ✅ 引用回复
197
- - ✅ 文件消息
198
- - ✅ JSON 卡片消息
199
- - ✅ **群临时会话**(`message_type=private` + `sub_type=group`;`$channel` 为 `private` + `parent.group`)
200
-
201
- ### 发送消息类型
202
-
203
- - ✅ 文本消息
204
- - ✅ 图片消息(支持本地文件、URL、Base64)
205
- - ✅ 语音消息
206
- - ✅ 视频消息
207
- - ✅ @ 提及
208
- - ✅ 表情消息
209
- - ✅ 引用回复
210
- - ✅ 文件消息
211
- - ✅ JSON 卡片消息
212
- - ✅ 戳一戳
213
- - ✅ 音乐分享
214
- - ✅ **群临时会话**(`type: private` + `parent: { type: 'group', id }` → IPC `send_temp_msg`)
215
-
216
- ## 群临时会话
217
-
218
- 当群成员通过「临时会话」私聊机器人时,新版 `@icqqjs/cli` 会以私聊事件推送(`sub_type: group`,`sender.group_id` 为来源群)。适配器使用 `private` + `parent.group` 表达场景,而非发明新的顶层 `MessageType`。
219
-
220
- | 字段 | 值 |
221
- |------|-----|
222
- | `$channel.type` | `private`(对端 QQ) |
223
- | `$channel.parent` | `{ type: 'group', id: 群号 }` |
224
-
225
- `message.$reply` 会原样带上 `parent`,出站自动走 `send_temp_msg`。
226
-
227
- ```typescript
228
- await adapter.sendMessage({
229
- context: 'icqq',
230
- endpoint: '你的QQ号',
231
- type: 'private',
232
- id: '987654321',
233
- parent: { type: 'group', id: '123456789' },
234
- content: [{ type: 'text', data: { text: '你好' } }],
235
- })
236
- ```
237
-
238
- ## API 方法
239
-
240
- ```typescript
241
- const endpoint = app.adapters.get('icqq')?.endpoints.get('你的QQ号')
242
-
243
- // 发送群消息
244
- await endpoint.sendGroupMsg(groupId, '消息内容')
245
-
246
- // 发送私聊消息
247
- await endpoint.sendPrivateMsg(userId, '消息内容')
248
-
249
- // 撤回消息
250
- await endpoint.$recallMessage(messageId)
251
-
252
- // 获取群列表
253
- const groupList = endpoint.getGroupList()
254
-
255
- // 获取好友列表
256
- const friendList = endpoint.getFriendList()
257
-
258
- // 获取群成员信息
259
- const memberInfo = endpoint.getGroupMemberInfo(groupId, userId)
260
- ```
261
-
262
- ## 🔧 群管理工具(AI 可调用)
263
-
264
-
265
- 适配器通过覆写 `ISceneManagement` 标准方法自动注册群管理工具,同时保留平台特有工具,这些工具可以被 AI 调用,实现智能化的群管理。
266
-
267
- ### 标准群管工具(自动生成)
268
-
269
- 通过覆写 `ISceneManagement` 方法,`start()` 时自动检测并生成对应 Tool:
270
-
271
- | 工具 | 覆写方法 | 所需权限 | 说明 |
272
- |------|---------|----------|------|
273
- | `icqq_kick_member` | `kickMember` | 管理员 | 踢出群成员 |
274
- | `icqq_mute_member` | `muteMember` | 管理员 | 禁言群成员 |
275
- | `icqq_mute_all` | `muteAll` | 管理员 | 全员禁言 |
276
- | `icqq_set_admin` | `setAdmin` | 群主 | 设置/取消管理员 |
277
- | `icqq_set_nickname` | `setMemberNickname` | 管理员 | 设置群名片 |
278
- | `icqq_set_group_name` | `setGroupName` | 管理员 | 修改群名称 |
279
- | `icqq_list_members` | `listMembers` | 普通用户 | 获取群成员列表 |
280
-
281
- ### 平台特有工具(手动注册)
282
-
283
- | 工具 | 所需权限 | 说明 |
284
- |------|----------|------|
285
- | `icqq_set_title` | 群主 | 设置专属头衔 |
286
- | `icqq_announce` | 管理员 | 发送群公告 |
287
- | `icqq_poke` | 普通用户 | 戳一戳 |
288
- | `icqq_list_muted` | 普通用户 | 获取被禁言成员列表 |
289
- | `icqq_send_user_like` | 普通用户 | 发送用户点赞 |
290
- | `icqq_set_anonymous` | 管理员 | 开启/关闭匿名聊天 |
291
- | `icqq_group_files` | 普通用户 | 获取群文件列表 |
292
- | `icqq_friend_list` | 普通用户 | 获取好友列表 |
293
-
294
- ### 使用示例
295
-
296
- #### 通过 AI 对话管理群
297
-
298
- ```
299
- 用户(群主):把 @小明 踢出群并拉黑
300
- AI:已将 小明 踢出群并拉黑。
301
-
302
- 用户(管理员):禁言 @捣蛋鬼 1小时
303
- AI:已禁言 捣蛋鬼 3600 秒。
304
-
305
- 用户(管理员):发个群公告:明天下午3点开会
306
- AI:群公告已发送。
307
-
308
- 用户:戳一戳 @朋友
309
- AI:已戳了戳 朋友。
310
- ```
311
-
312
- #### 编程调用
313
-
314
- ```typescript
315
- // 获取 ICQQ Endpoint 实例
316
- const icqqAdapter = app.adapters.get('icqq')
317
- const endpoint = icqqAdapter?.endpoints.get('你的QQ号')
318
-
319
- // 踢出成员
320
- await endpoint.kickMember(groupId, userId, true) // 第三个参数为是否拉黑
321
-
322
- // 禁言成员(单位:秒)
323
- await endpoint.muteMember(groupId, userId, 600) // 禁言 10 分钟
324
- await endpoint.muteMember(groupId, userId, 0) // 解除禁言
325
-
326
- // 全员禁言
327
- await endpoint.muteAll(groupId, true) // 开启
328
- await endpoint.muteAll(groupId, false) // 关闭
329
-
330
- // 设置管理员
331
- await endpoint.setAdmin(groupId, userId, true) // 设为管理员
332
- await endpoint.setAdmin(groupId, userId, false) // 取消管理员
333
-
334
- // 设置群名片
335
- await endpoint.setCard(groupId, userId, '新名片')
336
-
337
- // 设置专属头衔
338
- await endpoint.setTitle(groupId, userId, '大佬', -1) // -1 表示永久
339
-
340
- // 修改群名
341
- await endpoint.setGroupName(groupId, '新群名')
342
-
343
- // 发送群公告
344
- await endpoint.sendAnnounce(groupId, '公告内容')
345
-
346
- // 戳一戳
347
- await endpoint.pokeMember(groupId, userId)
348
-
349
- // 获取群成员列表
350
- const members = await endpoint.getMemberList(groupId)
351
-
352
- // 获取被禁言成员列表
353
- const mutedList = await endpoint.getMutedMembers(groupId)
354
-
355
- // 开启/关闭匿名
356
- await endpoint.setAnonymous(groupId, true)
357
- ```
358
-
359
- ### 发送者权限信息
360
-
361
- 消息中的 `$sender` 包含 ICQQ 特有的权限信息:
362
-
363
- ```typescript
364
- interface IcqqSenderInfo {
365
- id: string; // QQ 号
366
- name: string; // 昵称
367
- role?: GroupRole; // 'owner' | 'admin' | 'member'
368
- isOwner?: boolean; // 是否为群主
369
- isAdmin?: boolean; // 是否为管理员
370
- card?: string; // 群名片
371
- title?: string; // 专属头衔
372
- }
373
- ```
374
-
375
- #### 在插件中检查权限
376
-
377
- ```typescript
378
- onGroupMessage(async (message) => {
379
- const sender = message.$sender as IcqqSenderInfo;
380
-
381
- if (sender.isOwner) {
382
- console.log('这是群主的消息');
383
- }
384
-
385
- if (sender.isAdmin) {
386
- console.log('这是管理员的消息');
387
- }
388
-
389
- if (sender.role === 'member') {
390
- console.log('这是普通成员的消息');
391
- }
392
- })
40
+ plugins:
41
+ icqq:
42
+ master: "1659488338" # 顶层字段所有 endpoint 共享
43
+ endpoints:
44
+ - name: "${ICQQ_ACCOUNT}"
45
+ - name: "${ICQQ_ACCOUNT_2}"
46
+ - name: "${ICQQ_ACCOUNT_3}" # 各账号须分别 icqq login
393
47
  ```
394
48
 
395
- ## icqq CLI 与 AI bash 安全
396
-
397
-
398
- 完整说明与指令表见仓库文档:[docs/advanced/ai.md](../../../docs/advanced/ai.md)(锚点:`#icqq-bash-exec`、`#owner-approve-commands`)。
399
-
400
- ## 注意事项
401
-
402
- ### 账号安全
403
-
404
- - 建议使用小号测试
405
- - 避免短时间内发送大量消息
406
- - 保持 `@icqqjs/cli` 与适配器版本更新
49
+ 先执行 `icqq login`,再启动 Zhin。
407
50
 
408
- ### 风控与掉线
51
+ ## Send target
409
52
 
410
- - 登录设备类型、签名服务等由 **`icqq login` / CLI 配置** 管理,不在 `zhin.config` 的 bot 段配置
411
- - 掉线时先检查守护进程:`icqq` 是否仍在运行、账号是否被踢下线
412
- - 可降低发消息频率,并在手机 QQ 完成安全验证后重新 `icqq login`
53
+ | 类型 | target |
54
+ |------|--------|
55
+ | 私聊 | `private:uin` |
56
+ | 群聊 | `group:gid` |
57
+ | 群临时会话 | `temp:gid:uin` |
58
+ | 频道 | `channel:guildId:channelId` |
413
59
 
414
- ## 常见问题
60
+ ## 架构
415
61
 
416
- ### Q: 启动后 bot 一直未连接?
417
-
418
- A:
419
- 1. 确认已对该 QQ 号执行过 `icqq login` 且守护进程在运行
420
- 2. 确认 `endpoints[].name` 与登录 QQ 号一致
421
- 3. 远程部署时检查 `rpc.host` / `rpc.port` / `rpc.token`
422
-
423
- ### Q: 还需要在 zhin.config 里写 password / platform 吗?
424
-
425
- A: **不需要**。这些字段属于旧版直连协议,当前适配器仅通过 IPC 连接 CLI 守护进程。
426
-
427
- ### Q: 无法发送图片?
428
-
429
- A:
430
- 1. 检查图片 URL 是否有效
431
- 2. 本地文件需使用绝对路径
432
- 3. 确认图片格式和大小符合要求
433
-
434
- ### Q: 如何处理滑块/设备锁验证?
435
-
436
- A: 在运行 `icqq login` 的终端按提示操作;若启用了 Host API,也可在 Remote Console `/icqq` 登录辅助 Tab 提交验证码。
437
-
438
- ## 相关链接
439
-
440
- - [@icqqjs/cli](https://github.com/icqqjs/cli)
441
- - [ICQQ 协议库](https://github.com/icqqjs/icqq)
442
- - [Zhin.js 官方文档](https://github.com/zhinjs/zhin)
443
- - [签名服务器](https://github.com/fuqiuluo/unidbg-fetch-qsign)
444
-
445
- ## 依赖项
446
-
447
- - `@icqqjs/cli` - 登录与守护进程(运行时需已安装并在 PATH 中)
448
- - `zhin.js` / `@zhin.js/core` - Zhin 框架(peer)
449
- - `@zhin.js/host-router` / `@zhin.js/host-api` - 可选,Console 与 HTTP 路由
450
-
451
- ## 开发
452
-
453
- ```bash
454
- pnpm build # 构建
455
- pnpm clean # 清理构建文件
456
- ```
62
+ - `plugin.ts` + `adapters/icqq.ts`(`defineAdapter`)
63
+ - 协议常量 / 配置:`src/protocol.ts`
64
+ - IPC 客户端:`src/ipc-client.ts`(无 host-http)
65
+ - Console loginAssist / host-router 延期
66
+ - 旧 `usePlugin` / `extends Adapter` / Endpoint 生产入口已删除
457
67
 
458
- ## 许可证
68
+ ## Plugin Runtime 迁移说明
459
69
 
460
- MIT License
70
+ - `autoReconnect` 已恢复实现:IPC/RPC 意外断开后按指数退避自动重连(`stop()` 为主动断开,不触发重连)。
71
+ - `outboundMedia: file | base64` 已恢复实现:`file` 模式把 segment base64 落盘为临时文件后发 `[image:path]`;`base64` 模式(配置 `rpc` 时默认)发 `[image:base64://...]` 由守护进程解码。
72
+ - **Console 社交/群管 RPC 已接线**:endpoint 在 Adapter 内把 ICQQ 的 `get_friend_list` / `get_group_list` / `get_group_member_list`、请求审批和群管操作归一化为冻结的 `EndpointManagement` 对象。Host 只消费该语义端口,不再探测方法别名或读取 `friends` / `groups` SDK 缓存。
73
+ - **notice / request 入站事件已移除**:Plugin Runtime 的 `MessageGateway` 仅有 message 通道,没有 `notice.receive` / `request.receive` 事件机制,旧 `icqq-side-events.ts` / `get-msg.ts` / `login-ipc-contract.ts` / `agent-prompt.ts` 随之删除。需要好友/入群请求处理能力时请等待 runtime 提供事件通道后再恢复。
461
74
 
462
- ## 贡献
75
+ ## License
463
76
 
464
- 欢迎提交 Issue 和 Pull Request!
77
+ MIT
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Convention entry: discover `adapters/icqq.ts` → defineAdapter.
3
+ */
4
+ import { defineAdapter, type AdapterContext } from '@zhin.js/adapter';
5
+ import { messageGatewayToken } from '@zhin.js/core/runtime';
6
+ import { formatCompact, getLogger } from '@zhin.js/logger';
7
+ import {
8
+ databaseHostToken,
9
+ INBOX_TABLE_NOTICE,
10
+ INBOX_TABLE_REQUEST,
11
+ runtimeEventPublisherToken,
12
+ type Token,
13
+ } from '@zhin.js/plugin-runtime';
14
+ import { IcqqIpcEndpoint, type IcqqInboxHooks } from '../src/endpoint.js';
15
+ import {
16
+ resolveIcqqConfig,
17
+ type IcqqAdapterConfig,
18
+ } from '../src/protocol.js';
19
+
20
+ export { IcqqIpcEndpoint } from '../src/endpoint.js';
21
+ export type {
22
+ CreateIcqqIpc,
23
+ IcqqEndpointOptions,
24
+ IcqqIpcTransport,
25
+ } from '../src/endpoint.js';
26
+
27
+ const logger = getLogger('icqq');
28
+
29
+ export default defineAdapter<IcqqAdapterConfig>({
30
+ capabilities: ['inbound', 'outbound'],
31
+ create(context) {
32
+ // Client-library / IPC daemon path — no httpHostToken.
33
+ // Console loginAssist + host-router routes deferred.
34
+ const inbox = resolveInboxHooks(context);
35
+ return new IcqqIpcEndpoint({
36
+ id: context.id,
37
+ gateway: context.use(messageGatewayToken),
38
+ config: resolveIcqqConfig(context.config),
39
+ ...(inbox ? { inbox } : {}),
40
+ });
41
+ },
42
+ });
43
+
44
+ /** 资源可选(轻量 Root 无 DatabaseHost / console hub):缺失时降级为不写收件箱。 */
45
+ function tryUse<T>(context: AdapterContext<IcqqAdapterConfig>, token: Token<T>): T | undefined {
46
+ try {
47
+ return context.use(token);
48
+ } catch {
49
+ return undefined;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * 收件箱钩子:写 unified_inbox_request/notice(按 platform_*_id 去重,
55
+ * 推送事件与 GET_SYSTEM_MSG 首拉可能重复同一请求),并经 console hub 广播。
56
+ */
57
+ function resolveInboxHooks(context: AdapterContext<IcqqAdapterConfig>): IcqqInboxHooks | undefined {
58
+ const host = tryUse(context, databaseHostToken);
59
+ const publisher = tryUse(context, runtimeEventPublisherToken);
60
+ if (!host && !publisher) return undefined;
61
+ const insertDeduped = async (
62
+ table: string,
63
+ idField: 'platform_request_id' | 'platform_notice_id',
64
+ row: Record<string, unknown>,
65
+ ): Promise<void> => {
66
+ const model = host?.models.get(table);
67
+ if (!model) return;
68
+ try {
69
+ const existing = await model.select().where({
70
+ adapter: row.adapter,
71
+ endpoint_id: row.endpoint_id,
72
+ [idField]: row[idField],
73
+ });
74
+ if (Array.isArray(existing) && existing.length > 0) return;
75
+ await model.insert(row);
76
+ } catch (error) {
77
+ logger.warn(formatCompact({
78
+ op: 'inbox_insert',
79
+ table,
80
+ error: error instanceof Error ? error.message : String(error),
81
+ }));
82
+ }
83
+ };
84
+ const publish = publisher;
85
+ return {
86
+ recordRequest: (row) => insertDeduped(INBOX_TABLE_REQUEST, 'platform_request_id', row),
87
+ recordNotice: (row) => insertDeduped(INBOX_TABLE_NOTICE, 'platform_notice_id', row),
88
+ ...(publish
89
+ ? { publish: (type: string, data: unknown): void => publish.publish(type, data) }
90
+ : {}),
91
+ };
92
+ }