@zhin.js/adapter-icqq 1.0.71 → 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.
- package/CHANGELOG.md +1125 -52
- package/README.md +116 -431
- package/adapters/icqq.js +31 -0
- package/adapters/icqq.ts +42 -0
- package/agent/PERMITS.md +24 -0
- package/agent/prompt-sections/platform.ts +17 -0
- package/commands/endpoint/add/[[id]].js +3 -0
- package/commands/endpoint/add/[[id]].ts +3 -0
- package/commands/endpoint/list.js +3 -0
- package/commands/endpoint/list.ts +3 -0
- package/commands/endpoint/remove/[id].js +3 -0
- package/commands/endpoint/remove/[id].ts +3 -0
- package/lib/client.d.ts +19 -0
- package/lib/client.js +3 -0
- package/lib/cq-message.d.ts +14 -0
- package/lib/cq-message.js +106 -0
- package/lib/endpoint.d.ts +31 -0
- package/lib/endpoint.js +1046 -0
- package/lib/forward-msg.d.ts +6 -0
- package/lib/forward-msg.js +107 -0
- package/lib/icqq-endpoint-commands.d.ts +1 -0
- package/lib/icqq-endpoint-commands.js +26 -0
- package/lib/icqq-guild.d.ts +57 -0
- package/lib/icqq-guild.js +109 -0
- package/lib/icqq-inbound.d.ts +165 -0
- package/lib/icqq-inbound.js +651 -0
- package/lib/icqq-inbox.d.ts +25 -0
- package/lib/icqq-inbox.js +129 -0
- package/lib/icqq-login-assist.d.ts +27 -0
- package/lib/icqq-login-assist.js +109 -0
- package/lib/icqq-runtime-state.d.ts +1 -0
- package/lib/icqq-runtime-state.js +6 -0
- package/lib/index.d.ts +5 -18
- package/lib/index.js +5 -124
- package/lib/outbound-media.d.ts +14 -0
- package/lib/outbound-media.js +76 -0
- package/lib/protocol.d.ts +276 -0
- package/lib/protocol.js +425 -0
- package/lib/types.d.ts +37 -11
- package/lib/types.js +0 -1
- package/package.json +80 -32
- package/plugin.js +19 -0
- package/schema.json +162 -0
- package/src/client.ts +29 -0
- package/src/cq-message.ts +113 -0
- package/src/endpoint.ts +1138 -0
- package/src/forward-msg.ts +112 -0
- package/src/icqq-endpoint-commands.ts +28 -0
- package/src/icqq-guild.ts +154 -0
- package/src/icqq-inbound.ts +814 -0
- package/src/icqq-inbox.ts +147 -0
- package/src/icqq-login-assist.ts +132 -0
- package/src/icqq-runtime-state.ts +7 -0
- package/src/index.ts +30 -149
- package/src/outbound-media.ts +103 -0
- package/src/protocol.ts +575 -0
- package/src/types.ts +40 -11
- package/tools/announce.js +16 -0
- package/tools/announce.ts +15 -0
- package/tools/essence.js +22 -0
- package/tools/essence.ts +20 -0
- package/tools/friend_list.js +18 -0
- package/tools/friend_list.ts +18 -0
- package/tools/group_files.js +22 -0
- package/tools/group_files.ts +20 -0
- package/tools/group_invite.js +23 -0
- package/tools/group_invite.ts +20 -0
- package/tools/group_kick.js +26 -0
- package/tools/group_kick.ts +22 -0
- package/tools/group_list.js +19 -0
- package/tools/group_list.ts +19 -0
- package/tools/group_member_info.js +24 -0
- package/tools/group_member_info.ts +26 -0
- package/tools/group_sign.js +19 -0
- package/tools/group_sign.ts +17 -0
- package/tools/list_muted.js +17 -0
- package/tools/list_muted.ts +16 -0
- package/tools/poke.js +16 -0
- package/tools/poke.ts +15 -0
- package/tools/send_user_like.js +25 -0
- package/tools/send_user_like.ts +22 -0
- package/tools/set_anonymous.js +21 -0
- package/tools/set_anonymous.ts +19 -0
- package/tools/set_mute.js +24 -0
- package/tools/set_mute.ts +21 -0
- package/tools/set_title.js +25 -0
- package/tools/set_title.ts +22 -0
- package/tools/user_info.js +14 -0
- package/tools/user_info.ts +13 -0
- package/LICENSE +0 -21
- package/client/ICQQManagement.tsx +0 -139
- package/client/LoginAssistPanel.tsx +0 -293
- package/client/index.tsx +0 -11
- package/client/tsconfig.json +0 -7
- package/client/utils/api.ts +0 -20
- package/dist/index.js +0 -1
- package/lib/adapter.d.ts +0 -34
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js +0 -396
- package/lib/adapter.js.map +0 -1
- package/lib/bot.d.ts +0 -46
- package/lib/bot.d.ts.map +0 -1
- package/lib/bot.js +0 -457
- package/lib/bot.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- package/skills/icqq/SKILL.md +0 -22
- package/src/adapter.ts +0 -448
- package/src/bot.ts +0 -521
package/README.md
CHANGED
|
@@ -1,483 +1,168 @@
|
|
|
1
1
|
# @zhin.js/adapter-icqq
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
ICQQ Plugin Runtime 适配器 — 进程内直接使用 [@icqqjs/icqq](https://github.com/icqqjs/icqq) `Client`
|
|
4
4
|
|
|
5
5
|
## 功能特性
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
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
|
-
|
|
112
|
-
import { onGroupMessage } from 'zhin.js'
|
|
33
|
+
签名:未配置 `signApiAddr` 时,若已安装 `@icqqjs/qqsign` 则自动走本地签名。
|
|
113
34
|
|
|
114
|
-
|
|
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
|
-
|
|
125
|
-
import { onPrivateMessage } from 'zhin.js'
|
|
41
|
+
## 配置(Plugin Runtime)
|
|
126
42
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
```
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
296
|
-
|
|
79
|
+
```ts
|
|
80
|
+
import { defineHandler } from 'zhin.js/handler'
|
|
81
|
+
import '@zhin.js/adapter-icqq'
|
|
297
82
|
|
|
298
|
-
|
|
299
|
-
|
|
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
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
```
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
-
```
|
|
390
|
-
{
|
|
391
|
-
|
|
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
|
-
|
|
397
|
-
|
|
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
|
-
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
|
|
159
|
+
| 现象 | 排查 |
|
|
160
|
+
| --- | --- |
|
|
161
|
+
| 一直停在登录中 | 打开 Console 登录待办,完成二维码、滑块或设备确认 |
|
|
162
|
+
| 签名失败 | 检查 `signApiAddr`;本地模式确认 `@icqqjs/qqsign` 已安装且版本匹配 |
|
|
163
|
+
| 重启后重复登录 | 持久化设备与会话数据目录,避免每次生成新设备 |
|
|
164
|
+
| 请求或通知未显示 | 在 Endpoint 详情检查请求视图;ICQQ 优先读取平台请求列表 |
|
|
480
165
|
|
|
481
|
-
##
|
|
166
|
+
## License
|
|
482
167
|
|
|
483
|
-
|
|
168
|
+
MIT
|
package/adapters/icqq.js
ADDED
|
@@ -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
|
+
});
|
package/adapters/icqq.ts
ADDED
|
@@ -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
|
+
});
|