@zhin.js/adapter-icqq 1.0.70 → 1.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.
Files changed (111) hide show
  1. package/CHANGELOG.md +1134 -51
  2. package/README.md +116 -431
  3. package/adapters/icqq.js +31 -0
  4. package/adapters/icqq.ts +42 -0
  5. package/agent/PERMITS.md +24 -0
  6. package/agent/prompt-sections/platform.ts +17 -0
  7. package/commands/endpoint/add/[[id]].js +3 -0
  8. package/commands/endpoint/add/[[id]].ts +3 -0
  9. package/commands/endpoint/list.js +3 -0
  10. package/commands/endpoint/list.ts +3 -0
  11. package/commands/endpoint/remove/[id].js +3 -0
  12. package/commands/endpoint/remove/[id].ts +3 -0
  13. package/lib/client.d.ts +19 -0
  14. package/lib/client.js +3 -0
  15. package/lib/cq-message.d.ts +14 -0
  16. package/lib/cq-message.js +106 -0
  17. package/lib/endpoint.d.ts +31 -0
  18. package/lib/endpoint.js +1046 -0
  19. package/lib/forward-msg.d.ts +6 -0
  20. package/lib/forward-msg.js +107 -0
  21. package/lib/icqq-endpoint-commands.d.ts +1 -0
  22. package/lib/icqq-endpoint-commands.js +26 -0
  23. package/lib/icqq-guild.d.ts +57 -0
  24. package/lib/icqq-guild.js +109 -0
  25. package/lib/icqq-inbound.d.ts +165 -0
  26. package/lib/icqq-inbound.js +651 -0
  27. package/lib/icqq-inbox.d.ts +25 -0
  28. package/lib/icqq-inbox.js +129 -0
  29. package/lib/icqq-login-assist.d.ts +27 -0
  30. package/lib/icqq-login-assist.js +109 -0
  31. package/lib/icqq-runtime-state.d.ts +1 -0
  32. package/lib/icqq-runtime-state.js +6 -0
  33. package/lib/index.d.ts +5 -18
  34. package/lib/index.js +5 -124
  35. package/lib/outbound-media.d.ts +14 -0
  36. package/lib/outbound-media.js +76 -0
  37. package/lib/protocol.d.ts +276 -0
  38. package/lib/protocol.js +425 -0
  39. package/lib/types.d.ts +37 -11
  40. package/lib/types.js +0 -1
  41. package/package.json +80 -32
  42. package/plugin.js +19 -0
  43. package/schema.json +162 -0
  44. package/src/client.ts +29 -0
  45. package/src/cq-message.ts +113 -0
  46. package/src/endpoint.ts +1138 -0
  47. package/src/forward-msg.ts +112 -0
  48. package/src/icqq-endpoint-commands.ts +28 -0
  49. package/src/icqq-guild.ts +154 -0
  50. package/src/icqq-inbound.ts +814 -0
  51. package/src/icqq-inbox.ts +147 -0
  52. package/src/icqq-login-assist.ts +132 -0
  53. package/src/icqq-runtime-state.ts +7 -0
  54. package/src/index.ts +30 -149
  55. package/src/outbound-media.ts +103 -0
  56. package/src/protocol.ts +575 -0
  57. package/src/types.ts +40 -11
  58. package/tools/announce.js +16 -0
  59. package/tools/announce.ts +15 -0
  60. package/tools/essence.js +22 -0
  61. package/tools/essence.ts +20 -0
  62. package/tools/friend_list.js +18 -0
  63. package/tools/friend_list.ts +18 -0
  64. package/tools/group_files.js +22 -0
  65. package/tools/group_files.ts +20 -0
  66. package/tools/group_invite.js +23 -0
  67. package/tools/group_invite.ts +20 -0
  68. package/tools/group_kick.js +26 -0
  69. package/tools/group_kick.ts +22 -0
  70. package/tools/group_list.js +19 -0
  71. package/tools/group_list.ts +19 -0
  72. package/tools/group_member_info.js +24 -0
  73. package/tools/group_member_info.ts +26 -0
  74. package/tools/group_sign.js +19 -0
  75. package/tools/group_sign.ts +17 -0
  76. package/tools/list_muted.js +17 -0
  77. package/tools/list_muted.ts +16 -0
  78. package/tools/poke.js +16 -0
  79. package/tools/poke.ts +15 -0
  80. package/tools/send_user_like.js +25 -0
  81. package/tools/send_user_like.ts +22 -0
  82. package/tools/set_anonymous.js +21 -0
  83. package/tools/set_anonymous.ts +19 -0
  84. package/tools/set_mute.js +24 -0
  85. package/tools/set_mute.ts +21 -0
  86. package/tools/set_title.js +25 -0
  87. package/tools/set_title.ts +22 -0
  88. package/tools/user_info.js +14 -0
  89. package/tools/user_info.ts +13 -0
  90. package/LICENSE +0 -21
  91. package/client/ICQQManagement.tsx +0 -139
  92. package/client/LoginAssistPanel.tsx +0 -293
  93. package/client/index.tsx +0 -11
  94. package/client/tsconfig.json +0 -7
  95. package/client/utils/api.ts +0 -20
  96. package/dist/index.js +0 -1
  97. package/lib/adapter.d.ts +0 -34
  98. package/lib/adapter.d.ts.map +0 -1
  99. package/lib/adapter.js +0 -396
  100. package/lib/adapter.js.map +0 -1
  101. package/lib/bot.d.ts +0 -46
  102. package/lib/bot.d.ts.map +0 -1
  103. package/lib/bot.js +0 -457
  104. package/lib/bot.js.map +0 -1
  105. package/lib/index.d.ts.map +0 -1
  106. package/lib/index.js.map +0 -1
  107. package/lib/types.d.ts.map +0 -1
  108. package/lib/types.js.map +0 -1
  109. package/skills/icqq/SKILL.md +0 -22
  110. package/src/adapter.ts +0 -448
  111. package/src/bot.ts +0 -521
package/README.md CHANGED
@@ -1,483 +1,168 @@
1
1
  # @zhin.js/adapter-icqq
2
2
 
3
- Zhin.js ICQQ 适配器,基于 ICQQ 库实现的 QQ 机器人适配器,支持 QQ 群聊和私聊消息。
3
+ ICQQ Plugin Runtime 适配器 — 进程内直接使用 [@icqqjs/icqq](https://github.com/icqqjs/icqq) `Client`
4
4
 
5
5
  ## 功能特性
6
6
 
7
- - 🤖 支持 QQ 群聊和私聊消息处理
8
- - 🔐 完整的登录验证支持(短信验证、二维码、滑块验证)
9
- - 🖥️ **Web 控制台登录辅助**:与 `@zhin.js/console` 同时启用时,在 **`/icqq`** 页面提供「概览 + 登录辅助」;HTTP 接口为 **`GET /api/login-assist/pending`**、**`POST /api/login-assist/submit`**、**`POST /api/login-assist/cancel`**(由本适配器在路由上下文中注册,依赖核心 `loginAssist` 服务)。
10
- - 📨 消息发送和接收处理
11
- - 🔄 消息格式转换和适配
12
- - 📁 自动数据目录管理
13
- - 🖼️ 支持图片、语音、视频等多媒体消息
14
- - 🎯 支持 @ 提及和引用回复
15
- - 🔧 **群管理工具**:踢人、禁言、设置管理员、群名片、头衔等(AI 可调用)
16
- - 🔒 **权限控制**:基于群角色的工具权限过滤
7
+ - 群聊 / 私聊 / 群临时会话 / QQ 频道消息
8
+ - 入站:ICQQ Client 原始事件经 `Endpoint.emit(...)` 唯一入口上送;文本与图片 / 语音 / 视频 / 文件统一归一为 canonical `Segment` + `MediaRef`
9
+ - 出站:canonical Segment 直接投影为 ICQQ 原生 `Sendable`,再由 `sendGroupMsg` / `sendPrivateMsg` / …发送
10
+ - 群聊 reaction:`control.addReaction` / `removeReaction`(协议 ACK 失败不阻塞后续发送)
11
+ - Agent 工具:包根 `tools/`(`@zhin.js/tool` Feature;模型侧名为 `icqq__send_user_like` 等)
12
+ - Console Endpoint 管理:`src/endpoint.ts` 显式实现 `EndpointManagement`(好友/群/群成员列表、请求审批、删好友、踢人、禁言、设管理)
17
13
 
18
14
  ## 安装
19
15
 
20
16
  ```bash
21
17
  pnpm add @zhin.js/adapter-icqq @icqqjs/icqq
18
+ # 可选:未配置 signApiAddr 时走本地签名
19
+ pnpm add @icqqjs/qqsign
22
20
  ```
23
21
 
24
- ## 配置
25
-
26
- ### 密码登录
27
-
28
- ```typescript
29
- // zhin.config.ts
30
- import { defineConfig } from 'zhin.js'
31
-
32
- export default defineConfig({
33
- bots: [
34
- {
35
- context: 'icqq',
36
- name: process.env.ICQQ_LOGIN_UIN, // QQ 号(必需)
37
- password: process.env.ICQQ_PASSWORD, // QQ 密码
38
- platform: 2, // 登录设备平台
39
- log_level: 'info', // 日志级别
40
- data_dir: './data', // 数据目录
41
- sign_api_addr: process.env.SIGN_API // 签名 API 地址(可选)
42
- }
43
- ],
44
- plugins: [
45
- 'adapter-icqq'
46
- ]
47
- })
48
- ```
22
+ `@icqqjs/icqq` 是适配器的**可选对等依赖**(`peerDependencies` + `optional`)。应用侧需自行安装;monorepo 内另在 `devDependencies` 声明以便构建/测试。
49
23
 
50
- ### 扫码登录
51
-
52
- ```typescript
53
- export default defineConfig({
54
- bots: [
55
- {
56
- context: 'icqq',
57
- name: process.env.ICQQ_LOGIN_UIN, // QQ 号
58
- password: '', // 留空使用扫码登录
59
- platform: 2,
60
- log_level: 'info',
61
- data_dir: './data'
62
- }
63
- ],
64
- plugins: ['adapter-icqq']
65
- })
66
- ```
24
+ `@icqqjs/*` 发布在 GitHub Packages,需:
67
25
 
68
- ## 配置参数说明
69
-
70
- ### 必需参数
71
-
72
- - `context`: 固定为 `'icqq'`
73
- - `name`: QQ 账号(字符串格式)
74
-
75
- ### 可选参数
76
-
77
- - `password`: QQ 密码(留空则使用扫码登录)
78
- - `platform`: 登录平台类型
79
- - `1`: Android Phone(安卓手机)
80
- - `2`: Android Watch(安卓手表,推荐)
81
- - `3`: MacOS
82
- - `4`: 企点
83
- - `5`: iPad
84
- - `log_level`: 日志级别
85
- - `'off'`: 关闭
86
- - `'fatal'`: 致命错误
87
- - `'error'`: 错误
88
- - `'warn'`: 警告
89
- - `'info'`: 信息
90
- - `'debug'`: 调试
91
- - `'trace'`: 追踪
92
- - `data_dir`: 数据存储目录(默认:`./data`)
93
- - `sign_api_addr`: 签名服务器地址(可选,用于提高稳定性)
94
-
95
- ## 使用示例
96
-
97
- ### 基础消息处理
98
-
99
- ```typescript
100
- import { addCommand, MessageCommand } from 'zhin.js'
101
-
102
- addCommand(new MessageCommand('hello <name:text>')
103
- .action(async (message, result) => {
104
- return `你好,${result.params.name}!`
105
- })
106
- )
26
+ ```ini
27
+ @icqqjs:registry=https://npm.pkg.github.com
28
+ //npm.pkg.github.com/:_authToken=${NPM_TOKEN}
107
29
  ```
108
30
 
109
- ### 群聊消息
31
+ 并设置具有 `read:packages` 权限的 `NPM_TOKEN`(CI 使用仓库 secret `PERSONAL_TOKEN`)。
110
32
 
111
- ```typescript
112
- import { onGroupMessage } from 'zhin.js'
33
+ 签名:未配置 `signApiAddr` 时,若已安装 `@icqqjs/qqsign` 则自动走本地签名。
113
34
 
114
- onGroupMessage(async (message) => {
115
- console.log(`群 ${message.$channel.id} 收到消息:${message.$raw}`)
116
-
117
- // 回复群消息
118
- await message.$reply('收到消息了!')
119
- })
120
- ```
35
+ ## 前置条件
121
36
 
122
- ### 私聊消息
37
+ 1. 准备可登录的 QQ 账号,并选择远程 `signApiAddr` 或安装本地 `@icqqjs/qqsign`。
38
+ 2. 确保运行目录可持久化设备与登录状态;容器部署应挂载对应数据目录。
39
+ 3. 首次登录可能要求二维码、滑块或设备确认,可在 Console 登录待办或终端完成。
123
40
 
124
- ```typescript
125
- import { onPrivateMessage } from 'zhin.js'
41
+ ## 配置(Plugin Runtime)
126
42
 
127
- onPrivateMessage(async (message) => {
128
- console.log(`私聊消息来自:${message.$sender.name}`)
129
- await message.$reply('你好!')
130
- })
43
+ ```yaml
44
+ plugins:
45
+ icqq:
46
+ master: "1659488338" # 必填,顶层共享(/approve 与 master 角色)
47
+ autoReconnect: true
48
+ endpoints:
49
+ - id: "${ICQQ_ACCOUNT}" # QQ 号
50
+ # password: "${ICQQ_PASSWORD}" # 可选;不填则扫码登录
131
51
  ```
132
52
 
133
- ### 发送图片
53
+ 多账号:一个插件实例挂多个 endpoint(`endpoints` 数组逐项覆盖顶层字段,`id` 必填):
134
54
 
135
- ```typescript
136
- addCommand(new MessageCommand('pic <url:text>')
137
- .action(async (message, result) => {
138
- return [
139
- { type: 'image', data: { file: result.params.url } }
140
- ]
141
- })
142
- )
55
+ ```yaml
56
+ plugins:
57
+ icqq:
58
+ master: "1659488338"
59
+ endpoints:
60
+ - id: "${ICQQ_ACCOUNT}"
61
+ - id: "${ICQQ_ACCOUNT_2}"
62
+ - id: "${ICQQ_ACCOUNT_3}"
143
63
  ```
144
64
 
145
- ### 发送语音
146
-
147
- ```typescript
148
- addCommand(new MessageCommand('voice <file:text>')
149
- .action(async (message, result) => {
150
- return [
151
- { type: 'record', data: { file: result.params.file } }
152
- ]
153
- })
154
- )
155
- ```
156
-
157
- ### @ 提及
158
-
159
- ```typescript
160
- addCommand(new MessageCommand('at <user:at>')
161
- .action(async (message, result) => {
162
- const atUser = result.params.user
163
- return [
164
- { type: 'at', data: { qq: atUser.data.qq } },
165
- { type: 'text', data: { text: ' 你好!' } }
166
- ]
167
- })
168
- )
169
- ```
170
-
171
- ## Web 控制台登录辅助
172
-
173
- 1. 配置中同时启用 **`@zhin.js/console`** 与本适配器(且 console 建议 **`lazyLoad: false`**,以便扩展 `addEntry` 正常注册)。
174
- 2. 打开控制台中的 **ICQQ 管理**(`/icqq`),切换到 **登录辅助** Tab;仅展示当前 **icqq** 适配器相关的待处理登录步骤。
175
- 3. 若未启用 console,仅启动机器人进程时,仍可通过日志完成登录;**不提供** Console 专属的 `/api/login-assist` 降级路径。
176
-
177
- ## 登录流程
178
-
179
- ### 密码登录
180
-
181
- 1. 配置 QQ 号和密码
182
- 2. 启动机器人
183
- 3. 如需验证,根据提示输入验证码或完成滑块验证
184
- 4. 登录成功后会保存登录状态
185
-
186
- ### 扫码登录
187
-
188
- 1. 配置 QQ 号,密码留空
189
- 2. 启动机器人
190
- 3. 扫描控制台显示的二维码
191
- 4. 手机 QQ 确认登录
192
- 5. 登录成功后会保存登录状态
193
-
194
- ### 设备锁验证
195
-
196
- 如遇设备锁:
197
- 1. 选择短信验证或扫码验证
198
- 2. 短信验证:输入收到的验证码
199
- 3. 扫码验证:扫描二维码并在手机确认
200
-
201
- ## 消息类型支持
202
-
203
- ### 接收消息类型
204
-
205
- - ✅ 文本消息
206
- - ✅ 图片消息
207
- - ✅ 语音消息
208
- - ✅ 视频消息
209
- - ✅ @ 提及
210
- - ✅ 表情消息
211
- - ✅ 引用回复
212
- - ✅ 文件消息
213
- - ✅ JSON 卡片消息
214
-
215
- ### 发送消息类型
216
-
217
- - ✅ 文本消息
218
- - ✅ 图片消息(支持本地文件、URL、Base64)
219
- - ✅ 语音消息
220
- - ✅ 视频消息
221
- - ✅ @ 提及
222
- - ✅ 表情消息
223
- - ✅ 引用回复
224
- - ✅ 文件消息
225
- - ✅ JSON 卡片消息
226
- - ✅ 戳一戳
227
- - ✅ 音乐分享
228
-
229
- ## API 方法
230
-
231
- ```typescript
232
- const bot = app.adapters.get('icqq')?.bots.get('你的QQ号')
233
-
234
- // 发送群消息
235
- await bot.sendGroupMsg(groupId, '消息内容')
236
-
237
- // 发送私聊消息
238
- await bot.sendPrivateMsg(userId, '消息内容')
239
-
240
- // 撤回消息
241
- await bot.$recallMessage(messageId)
242
-
243
- // 获取群列表
244
- const groupList = bot.getGroupList()
245
-
246
- // 获取好友列表
247
- const friendList = bot.getFriendList()
248
-
249
- // 获取群成员信息
250
- const memberInfo = bot.getGroupMemberInfo(groupId, userId)
251
- ```
252
-
253
- ## 🔧 群管理工具(AI 可调用)
254
-
255
- 适配器通过覆写 `IGroupManagement` 标准方法自动注册群管理工具,同时保留平台特有工具,这些工具可以被 AI 调用,实现智能化的群管理。
256
-
257
- ### 标准群管工具(自动生成)
258
-
259
- 通过覆写 `IGroupManagement` 方法,`start()` 时自动检测并生成对应 Tool:
260
-
261
- | 工具 | 覆写方法 | 所需权限 | 说明 |
262
- |------|---------|----------|------|
263
- | `icqq_kick_member` | `kickMember` | 管理员 | 踢出群成员 |
264
- | `icqq_mute_member` | `muteMember` | 管理员 | 禁言群成员 |
265
- | `icqq_mute_all` | `muteAll` | 管理员 | 全员禁言 |
266
- | `icqq_set_admin` | `setAdmin` | 群主 | 设置/取消管理员 |
267
- | `icqq_set_nickname` | `setMemberNickname` | 管理员 | 设置群名片 |
268
- | `icqq_set_group_name` | `setGroupName` | 管理员 | 修改群名称 |
269
- | `icqq_list_members` | `listMembers` | 普通用户 | 获取群成员列表 |
270
-
271
- ### 平台特有工具(手动注册)
272
-
273
- | 工具 | 所需权限 | 说明 |
274
- |------|----------|------|
275
- | `icqq_set_title` | 群主 | 设置专属头衔 |
276
- | `icqq_announce` | 管理员 | 发送群公告 |
277
- | `icqq_poke` | 普通用户 | 戳一戳 |
278
- | `icqq_list_muted` | 普通用户 | 获取被禁言成员列表 |
279
- | `icqq_send_user_like` | 普通用户 | 发送用户点赞 |
280
- | `icqq_set_anonymous` | 管理员 | 开启/关闭匿名聊天 |
281
- | `icqq_group_files` | 普通用户 | 获取群文件列表 |
282
- | `icqq_friend_list` | 普通用户 | 获取好友列表 |
65
+ ## Send conversation
283
66
 
284
- ### 使用示例
67
+ | 类型 | conversation |
68
+ |------|--------------|
69
+ | 私聊 | `{ kind: 'private', id: uin }` |
70
+ | 群聊 | `{ kind: 'group', id: gid }` |
71
+ | 群临时会话 | `{ kind: 'private', id: uin, parent: { kind: 'group', id: gid } }` |
72
+ | 频道 | `{ kind: 'channel', id: channelId, parent: { kind: 'channel', id: guildId } }` |
285
73
 
286
- #### 通过 AI 对话管理群
74
+ ## 直接使用完整 ICQQ Client
287
75
 
288
- ```
289
- 用户(群主):把 @小明 踢出群并拉黑
290
- AI:已将 小明 踢出群并拉黑。
291
-
292
- 用户(管理员):禁言 @捣蛋鬼 1小时
293
- AI:已禁言 捣蛋鬼 3600 秒。
76
+ 适配器把 `icqq` 注册为 `@icqqjs/icqq.Client` 与 SDK `EventMap` 的类型判别项。
77
+ 所有 Feature 都使用同一个 `adapter` 字段完成类型缩窄与运行时过滤:
294
78
 
295
- 用户(管理员):发个群公告:明天下午3点开会
296
- AI:群公告已发送。
79
+ ```ts
80
+ import { defineHandler } from 'zhin.js/handler'
81
+ import '@zhin.js/adapter-icqq'
297
82
 
298
- 用户:戳一戳 @朋友
299
- AI:已戳了戳 朋友。
83
+ export default defineHandler({
84
+ adapter: 'icqq',
85
+ event: 'request.group.add',
86
+ async handle({ client, event }) {
87
+ await client.setGroupAddRequest(event.flag, true)
88
+ },
89
+ })
300
90
  ```
301
91
 
302
- #### 编程调用
303
-
304
- ```typescript
305
- // 获取 ICQQ Bot 实例
306
- const icqqAdapter = app.adapters.get('icqq')
307
- const bot = icqqAdapter?.bots.get('你的QQ号')
308
-
309
- // 踢出成员
310
- await bot.kickMember(groupId, userId, true) // 第三个参数为是否拉黑
311
-
312
- // 禁言成员(单位:秒)
313
- await bot.muteMember(groupId, userId, 600) // 禁言 10 分钟
314
- await bot.muteMember(groupId, userId, 0) // 解除禁言
315
-
316
- // 全员禁言
317
- await bot.muteAll(groupId, true) // 开启
318
- await bot.muteAll(groupId, false) // 关闭
319
-
320
- // 设置管理员
321
- await bot.setAdmin(groupId, userId, true) // 设为管理员
322
- await bot.setAdmin(groupId, userId, false) // 取消管理员
323
-
324
- // 设置群名片
325
- await bot.setCard(groupId, userId, '新名片')
326
-
327
- // 设置专属头衔
328
- await bot.setTitle(groupId, userId, '大佬', -1) // -1 表示永久
329
-
330
- // 修改群名
331
- await bot.setGroupName(groupId, '新群名')
92
+ `client` 就是当前账号的真实 ICQQ SDK 实例,不是 Endpoint facade。未知扩展事件可显式使用
93
+ `event: '*'`;此时 Client 类型保持完整,事件 payload 为 `unknown`。普通消息回复仍应走
94
+ `Message.$reply`;群管理、请求审批与 ICQQ 专属查询可直接调用当前事件的 `client`。
332
95
 
333
- // 发送群公告
334
- await bot.sendAnnounce(groupId, '公告内容')
96
+ Command context 的 `$client` 是按需读取的 getter:
335
97
 
336
- // 戳一戳
337
- await bot.pokeMember(groupId, userId)
338
-
339
- // 获取群成员列表
340
- const members = await bot.getMemberList(groupId)
341
-
342
- // 获取被禁言成员列表
343
- const mutedList = await bot.getMutedMembers(groupId)
344
-
345
- // 开启/关闭匿名
346
- await bot.setAnonymous(groupId, true)
98
+ ```ts
99
+ export default defineCommand({
100
+ adapter: 'icqq',
101
+ async execute(context) {
102
+ await context.$client.sendLike(Number(context.sender!.id), 10)
103
+ return 'ok'
104
+ },
105
+ })
347
106
  ```
348
107
 
349
- ### 发送者权限信息
350
-
351
- 消息中的 `$sender` 包含 ICQQ 特有的权限信息:
108
+ Middleware 使用同一个判别字段;不声明 `adapter` 时 `$client` 的类型是 `unknown`:
352
109
 
353
- ```typescript
354
- interface IcqqSenderInfo {
355
- id: string; // QQ 号
356
- name: string; // 昵称
357
- role?: GroupRole; // 'owner' | 'admin' | 'member'
358
- isOwner?: boolean; // 是否为群主
359
- isAdmin?: boolean; // 是否为管理员
360
- card?: string; // 群名片
361
- title?: string; // 专属头衔
362
- }
363
- ```
364
-
365
- #### 在插件中检查权限
366
-
367
- ```typescript
368
- onGroupMessage(async (message) => {
369
- const sender = message.$sender as IcqqSenderInfo;
370
-
371
- if (sender.isOwner) {
372
- console.log('这是群主的消息');
373
- }
374
-
375
- if (sender.isAdmin) {
376
- console.log('这是管理员的消息');
377
- }
378
-
379
- if (sender.role === 'member') {
380
- console.log('这是普通成员的消息');
381
- }
110
+ ```ts
111
+ export default defineMiddleware<Message>({
112
+ target: 'inbound',
113
+ adapter: 'icqq',
114
+ async handle(context, next) {
115
+ console.log(context.$client.uin, context.input.content)
116
+ await next()
117
+ },
382
118
  })
383
119
  ```
384
120
 
385
- ## 签名服务器
386
-
387
- 为了提高登录稳定性和避免风控,建议配置签名服务器:
121
+ Agent tool 的 IM turn 同样直接读取当前 Client:
388
122
 
389
- ```typescript
390
- {
391
- sign_api_addr: 'http://localhost:8080/sign' // 签名服务器地址
392
- }
123
+ ```ts
124
+ export default defineAgentTool({
125
+ adapter: 'icqq',
126
+ description: '获取群列表',
127
+ async execute(_input, context) {
128
+ return context.$client.getGroupList()
129
+ },
130
+ })
393
131
  ```
394
132
 
395
- 常用签名服务器:
396
- - [unidbg-fetch-qsign](https://github.com/fuqiuluo/unidbg-fetch-qsign)
397
- - [qsign](https://github.com/MliKiowa/NapCatQQ)
398
-
399
- ## 注意事项
133
+ `adapter` 缺失时 `$client` 静态类型为 `unknown`;声明后若实际 operation 不属于 ICQQ,
134
+ 运行时会在执行前拒绝。Client 只能在当前 operation 内使用。task、schedule 或 Host 等脱离
135
+ IM turn 的场景,才使用 `icqqClient.get(context, endpointId)` 显式选择账号。
400
136
 
401
- ### 账号安全
137
+ ## 架构
402
138
 
403
- - 建议使用小号测试
404
- - 避免短时间内发送大量消息
405
- - 建议配置签名服务器
406
- - 定期更新 ICQQ 版本
139
+ - `plugin.ts` + `adapters/icqq.ts`(`defineAdapter`)
140
+ - Client token:`src/client.ts`(直接引用 ICQQ `Client` / `EventMap`)
141
+ - Endpoint:`src/endpoint.ts`(组合 ICQQ `Client`,负责账号 transport 与 Zhin lifecycle)
142
+ - 协议常量 / 配置:`src/protocol.ts`
143
+ - Agent 工具:`tools/*.ts`;权限说明见 `agent/PERMITS.md`
407
144
 
408
- ### 风控问题
145
+ ## Plugin Runtime 迁移说明
409
146
 
410
- 如遇风控:
411
- 1. 降低消息发送频率
412
- 2. 配置签名服务器
413
- 3. 更换登录设备类型(platform 参数)
414
- 4. 使用扫码登录
415
- 5. 等待一段时间后重试
416
-
417
- ### 设备选择
418
-
419
- 推荐使用的 platform 值:
420
- - `2` (Android Watch): 最稳定,推荐
421
- - `5` (iPad): 功能较全
422
- - `3` (MacOS): 较稳定
423
-
424
- 避免使用:
425
- - `1` (Android Phone): 容易被风控
426
-
427
- ## 常见问题
428
-
429
- ### Q: 登录时提示"密码错误"?
430
-
431
- A:
432
- 1. 确认密码是否正确
433
- 2. 尝试使用扫码登录
434
- 3. 检查是否被风控,需要在手机 QQ 上验证
435
-
436
- ### Q: 登录后频繁掉线?
437
-
438
- A:
439
- 1. 配置签名服务器
440
- 2. 更换 platform 参数
441
- 3. 检查网络连接是否稳定
442
- 4. 降低消息发送频率
443
-
444
- ### Q: 无法发送图片?
445
-
446
- A:
447
- 1. 检查图片 URL 是否有效
448
- 2. 本地文件需使用绝对路径
449
- 3. 确认图片格式和大小符合要求
450
-
451
- ### Q: 如何处理滑块验证?
452
-
453
- A:
454
- 1. 控制台会显示滑块验证链接
455
- 2. 在浏览器中打开链接
456
- 3. 完成滑块验证
457
- 4. 将验证票据复制到控制台
458
-
459
- ## 相关链接
460
-
461
- - [ICQQ 项目](https://github.com/icqqjs/icqq)
462
- - [Zhin.js 官方文档](https://github.com/zhinjs/zhin)
463
- - [签名服务器](https://github.com/fuqiuluo/unidbg-fetch-qsign)
464
-
465
- ## 依赖项
466
-
467
- - `@icqqjs/icqq` - ICQQ 核心库
468
- - `zhin.js` - Zhin 核心框架
469
-
470
- ## 开发
471
-
472
- ```bash
473
- pnpm build # 构建
474
- pnpm clean # 清理构建文件
475
- ```
147
+ - 不再经过 `@icqqjs/cli` IPC 守护进程;登录态与协议栈都在本进程。
148
+ - `autoReconnect`:Client 断线后按配置自动重连(`stop()` 为主动断开,不触发重连)。
149
+ - `outboundMedia: file | base64`:`file` 在发送期间把 segment base64 物化为临时文件并于发送结束清理;`base64` 使用 ICQQ 原生支持的 `base64://` file 参数。
150
+ - ICQQ 的语音、视频、文件是独立消息元素;它们与其他段混发时适配器会明确拒绝,避免协议栈静默丢段。
151
+ - 入站语音 / 视频在 Endpoint 持有原生 Client 时解析可下载 URL;解析失败则保留真实平台引用,不伪造或丢弃媒体。
152
+ - **Console 社交/群管 RPC 已接线**:endpoint 把好友/群/群成员列表、请求审批和群管操作归一化为冻结的 `EndpointManagement`。Host 只消费该语义端口。
153
+ - 好友/入群请求与通知:经 the unified `Endpoint.emit(...)` ingress 分发到 `handlers`(`notice.receive` / `request.receive`);审批走 `Request.$approve` / `EndpointManagement.approveRequest`。Console `request.list` 优先读 `management.listRequests()`(`getSystemMsg`),不再写入 `unified_inbox_request/notice`。
154
+ - `system.*`(登录扫码等)分发到 `system.receive`。
155
+ - **登录辅助**:`system.login.qrcode|slider|device|auth` 经 `loginAssistToken`(`LoginAssist`)挂起待办;刷新后可用 Console `login.list` / `login.submit` 或终端 stdin 继续(对齐 icqq 官方 stdin 流程)。`system.online` / `login.error` 会清理该 endpoint 待办。
476
156
 
477
- ## 许可证
157
+ ## 故障排查
478
158
 
479
- MIT License
159
+ | 现象 | 排查 |
160
+ | --- | --- |
161
+ | 一直停在登录中 | 打开 Console 登录待办,完成二维码、滑块或设备确认 |
162
+ | 签名失败 | 检查 `signApiAddr`;本地模式确认 `@icqqjs/qqsign` 已安装且版本匹配 |
163
+ | 重启后重复登录 | 持久化设备与会话数据目录,避免每次生成新设备 |
164
+ | 请求或通知未显示 | 在 Endpoint 详情检查请求视图;ICQQ 优先读取平台请求列表 |
480
165
 
481
- ## 贡献
166
+ ## License
482
167
 
483
- 欢迎提交 Issue 和 Pull Request!
168
+ MIT
@@ -0,0 +1,31 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ /**
3
+ * Convention entry: discover `adapters/icqq.ts` → defineAdapter.
4
+ */
5
+ import { defineAdapter } from 'zhin.js/adapter';
6
+ import { loginAssistToken } from '@zhin.js/core/runtime';
7
+ import { IcqqEndpoint } from "../lib/endpoint.js";
8
+ import { icqqRuntimeStateToken } from "../lib/icqq-runtime-state.js";
9
+ import { resolveIcqqConfig, } from "../lib/protocol.js";
10
+ export { IcqqEndpoint } from "../lib/endpoint.js";
11
+ export default defineAdapter({
12
+ capabilities: ['inbound', 'outbound'],
13
+ operations: ['recall', 'reaction'],
14
+ segments: {
15
+ outboundMedia: ['base64', 'url', 'path'],
16
+ interactive: 'text',
17
+ },
18
+ create(context) {
19
+ const config = resolveIcqqConfig(context.config);
20
+ // 注册到插件运行时状态(icqq.endpoint list 的"运行中"数据源)
21
+ context.use(icqqRuntimeStateToken).endpoints.set(config.id, {
22
+ id: config.id,
23
+ mode: 'direct',
24
+ });
25
+ return new IcqqEndpoint({
26
+ id: context.id,
27
+ loginAssist: context.use(loginAssistToken),
28
+ config,
29
+ });
30
+ },
31
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Convention entry: discover `adapters/icqq.ts` → defineAdapter.
3
+ */
4
+ import { defineAdapter } from 'zhin.js/adapter';
5
+ import { loginAssistToken } from '@zhin.js/core/runtime';
6
+ import { IcqqEndpoint } from '../src/endpoint.js';
7
+ import { icqqRuntimeStateToken } from '../src/icqq-runtime-state.js';
8
+ import {
9
+ resolveIcqqConfig,
10
+ type IcqqAdapterConfig,
11
+ } from '../src/protocol.js';
12
+
13
+ export { IcqqEndpoint } from '../src/endpoint.js';
14
+ export type { IcqqEndpointOptions } from '../src/endpoint.js';
15
+
16
+ declare module '@zhin.js/core' {
17
+ interface AdapterEndpoints {
18
+ icqq: import('../src/endpoint.js').IcqqEndpoint;
19
+ }
20
+ }
21
+
22
+ export default defineAdapter<IcqqAdapterConfig>({
23
+ capabilities: ['inbound', 'outbound'],
24
+ operations: ['recall', 'reaction'],
25
+ segments: {
26
+ outboundMedia: ['base64', 'url', 'path'],
27
+ interactive: 'text',
28
+ },
29
+ create(context) {
30
+ const config = resolveIcqqConfig(context.config);
31
+ // 注册到插件运行时状态(icqq.endpoint list 的"运行中"数据源)
32
+ context.use(icqqRuntimeStateToken).endpoints.set(config.id, {
33
+ id: config.id,
34
+ mode: 'direct',
35
+ });
36
+ return new IcqqEndpoint({
37
+ id: context.id,
38
+ loginAssist: context.use(loginAssistToken),
39
+ config,
40
+ });
41
+ },
42
+ });