@alemonjs/qq-bot 2.1.32 → 2.1.33
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/README.md +1946 -35
- package/lib/hook.d.ts +2 -1
- package/lib/index.websoket.js +2 -2
- package/lib/message/group/GROUP_JOIN_REQUEST.d.ts +23 -0
- package/lib/message/group/GROUP_JOIN_REQUEST.js +1 -0
- package/lib/register.js +248 -44
- package/lib/sdk/api.d.ts +58 -3
- package/lib/sdk/api.js +138 -3
- package/lib/sdk/instance.js +2 -0
- package/lib/sdk/intents.d.ts +1 -1
- package/lib/sdk/intents.js +2 -0
- package/lib/sdk/message.group.d.ts +2 -0
- package/lib/sdk/typing.d.ts +123 -1
- package/lib/sends.d.ts +6 -2
- package/lib/sends.js +131 -73
- package/lib/upload.d.ts +21 -0
- package/lib/upload.js +60 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,63 +1,158 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @alemonjs/qq-bot
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
QQ 开放平台(QQ Bot)适配器,用于连接 [QQ 开放平台](https://q.qq.com/#/) 官方接口。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
- 平台:<https://q.qq.com/#/>
|
|
6
|
+
- 开发文档:<https://bot.q.qq.com/wiki/>
|
|
7
|
+
- 支持场景:群聊(Group)、单聊(C2C)、频道(Guild / 公域、私域)、频道私信(DMS)、互动按钮(Interaction)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
---
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## 简介
|
|
12
|
+
|
|
13
|
+
- **双连接模式**:默认 WebSocket 直连官方网关;配置 `route`/`port` 或 `ws` 可切换 Webhook 模式
|
|
14
|
+
- **多场景覆盖**:群聊 / 单聊走 OpenAPI v2(`/v2/users`、`/v2/groups`),频道走 Server-Inter 接口
|
|
15
|
+
- **完整消息类型**:文本、图片、Markdown、按钮(5×5)、Ark 卡片/列表/大卡、文件/音视频富媒体
|
|
16
|
+
- **群管理能力**:入群申请审批、入群自动审批策略、群成员禁言、群信息/状态/成员查询
|
|
17
|
+
- **大文件分片上传**:超 10MB 自动切换 `upload_prepare → COS 直传 → part_finish → 合并` 全流程
|
|
18
|
+
- **流式消息**:单聊持续更新同一条回复(`stream_messages`)+ 输入状态通知
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 目录
|
|
23
|
+
|
|
24
|
+
| 章节 | 说明 |
|
|
25
|
+
| ----------------------------------- | ----------------------------------------- |
|
|
26
|
+
| [简介](#简介) | 适配器能力概览 |
|
|
27
|
+
| [安装](#安装) | 安装命令 |
|
|
28
|
+
| [快速开始](#快速开始) | 最小可运行示例(Router + defineChildren) |
|
|
29
|
+
| [配置](#配置) | 完整配置项与连接模式 |
|
|
30
|
+
| [调用方式](#调用方式) | 四种 API 调用方式 |
|
|
31
|
+
| [API 总览](#api-overview) | 全部 API 索引(点击跳转详解) |
|
|
32
|
+
| [QQ-Bot 消息类型](#qq-bot-消息类型) | Format 与平台消息类型映射 |
|
|
33
|
+
| [API 详解](#api-详解) | 各 API 参数说明与示例 |
|
|
34
|
+
| [SDK 方法速查](#sdk-方法速查) | `QQBotAPI` 全部 SDK 方法 |
|
|
35
|
+
| [事件支持](#事件支持) | QQ 事件 → 标准事件映射表 |
|
|
36
|
+
| [事件数据结构](#事件数据结构) | 收到的消息数据结构与数据使用方法 |
|
|
37
|
+
| [常见问题](#常见问题) | FAQ |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 安装
|
|
10
42
|
|
|
11
43
|
```sh
|
|
12
44
|
yarn add @alemonjs/qq-bot
|
|
45
|
+
# 或
|
|
46
|
+
npm install @alemonjs/qq-bot
|
|
13
47
|
```
|
|
14
48
|
|
|
15
|
-
|
|
49
|
+
---
|
|
16
50
|
|
|
17
|
-
|
|
51
|
+
## 快速开始
|
|
52
|
+
|
|
53
|
+
1. 在 [QQ 开放平台](https://q.qq.com/) 创建机器人,获取 `AppID` 与 `AppSecret`。
|
|
54
|
+
|
|
55
|
+
2. 在项目 `alemon.config.yaml` 中配置:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
18
58
|
qq-bot:
|
|
19
|
-
#
|
|
20
|
-
app_id: ''
|
|
21
|
-
#
|
|
22
|
-
secret: ''
|
|
59
|
+
# 应用编号(必填)
|
|
60
|
+
app_id: 'YOUR_APP_ID'
|
|
61
|
+
# 应用密钥(必填)
|
|
62
|
+
secret: 'YOUR_APP_SECRET'
|
|
23
63
|
```
|
|
24
64
|
|
|
25
|
-
|
|
65
|
+
3. 编写第一个响应(标准工程结构:`src/index.ts` 注册路由,`src/response/*.ts` 一个能力一个文件):
|
|
26
66
|
|
|
27
|
-
|
|
67
|
+
```ts
|
|
68
|
+
// src/index.ts
|
|
69
|
+
import { Router, defineChildren } from 'alemonjs';
|
|
28
70
|
|
|
29
|
-
|
|
71
|
+
const router = Router.create({
|
|
72
|
+
events: ['message.create', 'private.message.create', 'interaction.create', 'private.interaction.create']
|
|
73
|
+
});
|
|
30
74
|
|
|
31
|
-
|
|
75
|
+
// 命令分组:支持 /、#、! 前缀,也允许裸命令
|
|
76
|
+
const appGroup = router.group({
|
|
77
|
+
routeText: {
|
|
78
|
+
prefixes: ['/', '#', '!'],
|
|
79
|
+
stripPrefix: true,
|
|
80
|
+
allowBare: true
|
|
81
|
+
}
|
|
82
|
+
});
|
|
32
83
|
|
|
33
|
-
|
|
84
|
+
// 注册命令:消息文本精确匹配“你好”
|
|
85
|
+
appGroup.use('你好', () => import('./response/hello'));
|
|
34
86
|
|
|
35
|
-
|
|
87
|
+
export default defineChildren({
|
|
88
|
+
register() {
|
|
89
|
+
return {
|
|
90
|
+
responseRouter: router.define
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
```
|
|
36
95
|
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
96
|
+
```ts
|
|
97
|
+
// src/response/hello.ts
|
|
98
|
+
import { useMessage, Format } from 'alemonjs';
|
|
99
|
+
|
|
100
|
+
export default async () => {
|
|
101
|
+
const [message] = useMessage();
|
|
102
|
+
await message.send({ format: Format.create().addText('你好呀 👋') });
|
|
103
|
+
};
|
|
45
104
|
```
|
|
46
105
|
|
|
47
|
-
|
|
106
|
+
> 更复杂的调用(调用群 API / 主动发送)见 [调用方式](#调用方式)。handler 内通过 `useEvent` / `useRoute` / `useMessage` 等 Hook 读取当前事件上下文,命令匹配交给 Router DSL,不在 handler 里重复判断命令名。
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 配置
|
|
111
|
+
|
|
112
|
+
### 完整配置示例
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
48
115
|
qq-bot:
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
|
|
116
|
+
# ==================== 必填 ====================
|
|
117
|
+
# 应用编号
|
|
118
|
+
app_id: ''
|
|
119
|
+
# 应用密钥(Webhook 模式下同时作为 ed25519 签名种子)
|
|
120
|
+
secret: ''
|
|
121
|
+
|
|
122
|
+
# ==================== 主人配置 ====================
|
|
123
|
+
# 通过 UserKey 识别主人(在 alemon 中生成)
|
|
124
|
+
master_key:
|
|
125
|
+
- 'xxx'
|
|
126
|
+
# 通过 UserId 识别主人(如 QQ 号)
|
|
127
|
+
master_id:
|
|
128
|
+
- 'yyy'
|
|
129
|
+
|
|
130
|
+
# ==================== 连接模式 ====================
|
|
131
|
+
# 1) 默认:WebSocket 直连官方网关(不配置 route/port/ws 即可)
|
|
132
|
+
# 2) Webhook 模式:配置 route + port(一旦生效官方将禁用 WebSocket 模式,需公网 ip/域名)
|
|
52
133
|
# port: 17157
|
|
53
134
|
# route: '/webhook'
|
|
54
|
-
# 3)
|
|
135
|
+
# 3) 连接已存在的 Webhook 服务(如自建转发服务)
|
|
136
|
+
# ws: 'ws://127.0.0.1:17157'
|
|
137
|
+
# 4) 自定义网关 / 域名代理
|
|
55
138
|
# gatewayURL: 'ws://[your ip]:8080'
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
#
|
|
60
|
-
|
|
139
|
+
# base_url_gateway: 'https://[your addr]'
|
|
140
|
+
# base_url_app_access_token: 'https://[your addr]'
|
|
141
|
+
|
|
142
|
+
# ==================== 环境 ====================
|
|
143
|
+
# 是否私域机器人(私域可订阅频道全量消息 MESSAGE_CREATE / 论坛事件)
|
|
144
|
+
is_private: false
|
|
145
|
+
# 沙盒环境(使用沙盒 API 域名)
|
|
146
|
+
sandbox: false
|
|
147
|
+
# WS 分片,默认 [0, 1]
|
|
148
|
+
shard: [0, 1]
|
|
149
|
+
# 自定义事件订阅(Intents),默认按 is_private 自动组装,一般无需配置
|
|
150
|
+
intents: []
|
|
151
|
+
|
|
152
|
+
# ==================== 消息格式 ====================
|
|
153
|
+
# 将 Markdown 强制转换为纯文本发送(没有 MD 权限但使用了 MD 数据格式时)
|
|
154
|
+
# 开启后 Markdown 与按钮将转为可读纯文本
|
|
155
|
+
markdownToText: false
|
|
61
156
|
# 隐藏不支持的消息类型(可选,默认: false)
|
|
62
157
|
# 1:一级隐藏,不可读占位符([视频]、[音频]、[图片]、[附件]等)被置空,可读内容保留
|
|
63
158
|
# 2:二级隐藏,按钮仅显示指令数据(如 /挑战),链接仅显示 URL
|
|
@@ -66,3 +161,1819 @@ qq-bot:
|
|
|
66
161
|
# 转换后内容为空时,将跳过发送并输出 info 日志
|
|
67
162
|
hideUnsupported: 1
|
|
68
163
|
```
|
|
164
|
+
|
|
165
|
+
### 多 Bot(WebSocket)
|
|
166
|
+
|
|
167
|
+
`bots` 的键是稳定 BotId/AppId。配置多个 Bot 时,主动操作需提供 `BotId`,或设置 `default_bot`;事件回复会自动使用事件所属 Bot。Webhook 不支持该配置。
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
qq-bot:
|
|
171
|
+
default_bot: 'app-a'
|
|
172
|
+
bots:
|
|
173
|
+
app-a:
|
|
174
|
+
secret: 'secret-a'
|
|
175
|
+
app-b:
|
|
176
|
+
secret: 'secret-b'
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 配置项说明
|
|
180
|
+
|
|
181
|
+
| 配置项 | 类型 | 默认值 | 必填 | 说明 |
|
|
182
|
+
| --------------------------- | ----------------- | ---------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
183
|
+
| `app_id` | string | - | ✅ | 应用编号(AppID),开放平台创建机器人后获得 |
|
|
184
|
+
| `secret` | string | - | ✅ | 应用密钥(AppSecret);Webhook 模式同时作为 [ed25519 签名](https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/sign.html) 的种子 |
|
|
185
|
+
| `master_key` | string[] | `[]` | - | 主人 UserKey 列表,使用 `isMaster` 判断 |
|
|
186
|
+
| `master_id` | string[] | `[]` | - | 主人 UserId 列表(如 QQ 号 / openid),使用 `isMaster` 判断 |
|
|
187
|
+
| `route` | string | `/webhook` | - | Webhook 回调路由,配置后启用 Webhook 模式 |
|
|
188
|
+
| `port` | string | `17157` | - | Webhook 本地监听端口 |
|
|
189
|
+
| `ws` | string | - | - | 已存在的 Webhook 服务地址,配置后作为 WS 客户端连接(`QQBotClient`) |
|
|
190
|
+
| `gatewayURL` | string | - | - | 直连 WebSocket 网关地址,配置后不再自动请求 `/gateway` |
|
|
191
|
+
| `base_url_gateway` | string | - | - | 网关域名代理(用于接入官方之外的服务端) |
|
|
192
|
+
| `base_url_app_access_token` | string | - | - | 获取 access_token 的域名代理 |
|
|
193
|
+
| `is_private` | boolean | `false` | - | 是否私域机器人;私域自动订阅 `GUILD_MESSAGES`、`FORUMS_EVENT`,公域订阅 `PUBLIC_GUILD_MESSAGES` |
|
|
194
|
+
| `sandbox` | boolean | `false` | - | 沙盒环境,使用 `https://sandbox.api.sgroup.qq.com` |
|
|
195
|
+
| `shard` | number[] | `[0, 1]` | - | WebSocket 分片 |
|
|
196
|
+
| `intents` | string[] | 自动组装 | - | 自定义事件订阅,覆盖默认组装逻辑(见 [intents.ts](src/sdk/intents.ts)) |
|
|
197
|
+
| `markdownToText` | boolean | `false` | - | Markdown 降级为纯文本发送 |
|
|
198
|
+
| `hideUnsupported` | boolean \| number | `false` | - | 隐藏不支持消息类型(1~4 级,见上) |
|
|
199
|
+
|
|
200
|
+
### 连接模式
|
|
201
|
+
|
|
202
|
+
| 模式 | 触发条件 | 说明 |
|
|
203
|
+
| ----------------- | -------------------------------------- | -------------------------------------------------------------------------- |
|
|
204
|
+
| WebSocket(默认) | 不配置 `route`/`port`/`ws` | 直连官方 `wss://api.sgroup.qq.com` 网关,自动鉴权、心跳、重连(最多 5 次) |
|
|
205
|
+
| Webhook | 配置 `route` 或 `port` | 本地 Koa 服务接收官方回调,ed25519 验签;需公网 ip/域名 |
|
|
206
|
+
| WS 客户端 | 配置 `ws` | 作为客户端连接已存在的 Webhook 服务(不启动本地服务) |
|
|
207
|
+
| 自定义网关 | 配置 `gatewayURL` / `base_url_gateway` | 用于连接自建网关或域名代理 |
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 调用方式
|
|
212
|
+
|
|
213
|
+
在 alemonjs 开发中调用 QQ Bot 能力有四种方式,按推荐程度排列:
|
|
214
|
+
|
|
215
|
+
### 一、事件内回复(最常用)
|
|
216
|
+
|
|
217
|
+
收到消息后直接回复,框架自动按当前事件场景(群/单聊/频道)选择发送通道。命令匹配交给 Router DSL,handler 只负责回复:
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
// src/index.ts —— 注册路由
|
|
221
|
+
appGroup.use('ping', () => import('./response/ping'));
|
|
222
|
+
|
|
223
|
+
// src/response/ping.ts
|
|
224
|
+
import { useMessage, Format } from 'alemonjs';
|
|
225
|
+
|
|
226
|
+
export default async () => {
|
|
227
|
+
const [message] = useMessage();
|
|
228
|
+
await message.send({ format: Format.create().addText('pong') });
|
|
229
|
+
};
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
结构化消息(文本 + 图片 + 按钮 + Markdown)用 `Format` 链式构建,文本、Markdown、按钮统一挂到同一个 `format`:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
// src/response/card.ts
|
|
236
|
+
import { useMessage, Format } from 'alemonjs';
|
|
237
|
+
|
|
238
|
+
export default async () => {
|
|
239
|
+
const [message] = useMessage();
|
|
240
|
+
|
|
241
|
+
// 按钮组:一行两个按钮(command 类型,点击自动在输入框插入指令)
|
|
242
|
+
const bt = Format.createButtonGroup().addRow().addButton('确认', '/confirm').addButton('取消', '/cancel');
|
|
243
|
+
|
|
244
|
+
await message.send({
|
|
245
|
+
format: Format.create().addText('请确认操作').addButtonGroup(bt)
|
|
246
|
+
});
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
> `useMessage` 不传事件时自动读取当前事件上下文。消息统一用 `Format` 构建,可组合 `addText` / `addImage` / `addMention` / `addButtonGroup` / `addMarkdown` / `addMarkdownOriginal` / `addAttachment` / `addAudio` / `addVideo`;QQ-Bot 平台消息类型详见 [QQ-Bot 消息类型](#qq-bot-消息类型)。
|
|
251
|
+
|
|
252
|
+
### 二、`useClient` 直接调用 SDK(推荐用于能力调用)
|
|
253
|
+
|
|
254
|
+
通过框架 `useClient(API)` 传入 `@alemonjs/qq-bot` 导出的 `API` 类,返回一个代理客户端,方法调用会透传到 SDK(`QQBotAPI`),返回原始接口数据。仅当通用 hooks 不够表达、需要平台特有能力时使用:
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
// src/response/group-info.ts
|
|
258
|
+
import { useClient, useEvent, useMessage, Format } from 'alemonjs';
|
|
259
|
+
import { API, platform } from '@alemonjs/qq-bot';
|
|
260
|
+
|
|
261
|
+
export default async () => {
|
|
262
|
+
const [event] = useEvent();
|
|
263
|
+
const [message] = useMessage();
|
|
264
|
+
|
|
265
|
+
// 仅 QQ-Bot 平台走平台特有 API
|
|
266
|
+
if (event.current.Platform !== platform) return;
|
|
267
|
+
|
|
268
|
+
const [client] = useClient(API);
|
|
269
|
+
// 获取群信息(群 openid 位于事件的 ChannelId)
|
|
270
|
+
const res = await client.groupsInfo('GROUP_OPENID');
|
|
271
|
+
|
|
272
|
+
await message.send({ format: Format.create().addText(JSON.stringify(res)) });
|
|
273
|
+
};
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
> 也支持 `@alemonjs/qq-bot` 自带的平台包装 `useClient(event)`(需传当前事件,返回 `[client, value]`),两者都会向适配器发送 `client.api` 透传请求。所有 SDK 方法见下方 [SDK 方法速查](#sdk-方法速查),如 `groupsInfo`、`groupsJoinRequestList`、`usersOpenMessages` 等均可直接调用。
|
|
277
|
+
|
|
278
|
+
### 三、`sendAction` 调用适配器 Action(能力动作)
|
|
279
|
+
|
|
280
|
+
`@alemonjs/qq-bot` 在 `cbp.onactions` 中注册了全部能力动作(`message.send`、`group.joinRequest.approve` 等)。在应用侧通过 `alemonjs` 的 `sendAction` 调用,返回标准 `Result[]`:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
// src/response/approve.ts
|
|
284
|
+
import { useEvent, useMessage, sendAction, ResultCode, Format } from 'alemonjs';
|
|
285
|
+
|
|
286
|
+
export default async () => {
|
|
287
|
+
const [event] = useEvent();
|
|
288
|
+
const [message] = useMessage();
|
|
289
|
+
|
|
290
|
+
const results = await sendAction({
|
|
291
|
+
action: 'group.joinRequest.approve',
|
|
292
|
+
payload: {
|
|
293
|
+
// 透传来源事件:群 openid / 成员 openid 由适配器自动推断,无需手动传
|
|
294
|
+
event: event.current,
|
|
295
|
+
params: {
|
|
296
|
+
op: 'approve',
|
|
297
|
+
joinRequestId: 'JOIN_REQUEST_ID'
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
const ok = results.some(item => item.code === ResultCode.Ok);
|
|
302
|
+
|
|
303
|
+
await message.send({ format: Format.create().addText(ok ? '已通过' : '操作失败') });
|
|
304
|
+
};
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
> **事件上下文自动推断**:与 `message.send` 一致,`payload` 中透传 `event`(来源事件对象)后,群 openid(群消息事件位于 `ChannelId`)、成员 openid(位于 `UserId`)、频道 id(`GuildId`)、子频道 id(`ChannelId`)等均可省略,由适配器自动推断;显式传入的 `payload.ChannelId` / `payload.UserId` / `params.groupOpenId` / `params.memberOpenId` 优先级更高。各 Action 的参数与响应见下方 [API 详解](#api-详解)。框架自带的 `useMember`、`useMessage`、`useMedia`、`useGuild`、`useRequest` 等 Hook 内部即通过 `sendAction` 发送动作,优先使用 Hook 而非直接调 Action。
|
|
308
|
+
|
|
309
|
+
### 四、主动发送消息(无事件上下文)
|
|
310
|
+
|
|
311
|
+
通过 `MessageDirect` 主动向目标发送消息(基于 `message.send.channel` / `message.send.user`):
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import { MessageDirect, Format } from 'alemonjs';
|
|
315
|
+
|
|
316
|
+
// 主动向群发送(SpaceId 传群 openid 或频道子频道 id)
|
|
317
|
+
await MessageDirect.create().sendToChannel({
|
|
318
|
+
SpaceId: 'GROUP_OPENID_OR_CHANNEL_ID',
|
|
319
|
+
format: Format.create().addText('群公告:今晚 8 点开黑')
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// 主动向用户发送(OpenID 传 C2C 用户 openid 或频道私信 guild_id)
|
|
323
|
+
await MessageDirect.create().sendToUser({
|
|
324
|
+
OpenID: 'USER_OPENID',
|
|
325
|
+
format: Format.create().addText('晚上好')
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
> 也支持底层 `sendAction({ action: 'message.send.channel', payload: { ChannelId, params: { format } } })` 等价调用。
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
<a id="api-overview"></a>
|
|
334
|
+
|
|
335
|
+
## API 总览(点击跳转)
|
|
336
|
+
|
|
337
|
+
### 消息
|
|
338
|
+
|
|
339
|
+
| API | 说明 |
|
|
340
|
+
| ------------------------------------------------- | ------------------------------------------- |
|
|
341
|
+
| [message.send](#api-message-send) | 回复 / 发送消息,自动按事件场景选择发送通道 |
|
|
342
|
+
| [message.send.channel](#api-message-send-channel) | 主动向频道(子频道/群)发送消息 |
|
|
343
|
+
| [message.send.user](#api-message-send-user) | 主动向用户(C2C/频道私信)发送消息 |
|
|
344
|
+
| [message.delete](#api-message-delete) | 撤回消息(群/频道/单聊/私信按场景分流) |
|
|
345
|
+
| [message.get](#api-message-get) | 获取频道指定消息 |
|
|
346
|
+
| [message.pin / message.unpin](#api-message-pin) | 添加 / 移除频道精华消息 |
|
|
347
|
+
| [mention.get](#api-mention-get) | 获取消息中 @ 提及的用户 |
|
|
348
|
+
| [message.input.notify](#api-message-input-notify) | 发送“正在输入”状态通知(仅单聊) |
|
|
349
|
+
|
|
350
|
+
### 群管理
|
|
351
|
+
|
|
352
|
+
| API | 说明 |
|
|
353
|
+
| ------------------------------------------------------------ | --------------------------- |
|
|
354
|
+
| [group.info](#api-group-info) | 获取群基本信息 |
|
|
355
|
+
| [group.botState](#api-group-bot-state) | 获取机器人群内状态 |
|
|
356
|
+
| [group.member.info](#api-group-member-info) | 获取群成员详情 |
|
|
357
|
+
| [group.joinRequest.list](#api-group-join-request-list) | 拉取入群申请列表 |
|
|
358
|
+
| [group.joinRequest.approve](#api-group-join-request-approve) | 审批入群申请(通过 / 拒绝) |
|
|
359
|
+
| [group.mute.setting](#api-group-mute-setting) | 查询群禁言状态 |
|
|
360
|
+
| [group.mute.set](#api-group-mute-set) | 设置群成员禁言 |
|
|
361
|
+
| [group.strategy.list](#api-group-strategy) | 入群自动审批策略列表 |
|
|
362
|
+
| [group.strategy.create](#api-group-strategy) | 创建入群自动审批策略 |
|
|
363
|
+
| [group.strategy.update](#api-group-strategy) | 修改入群自动审批策略 |
|
|
364
|
+
| [group.strategy.delete](#api-group-strategy) | 删除入群自动审批策略 |
|
|
365
|
+
| [group.strategy.execute](#api-group-strategy) | 执行策略全量扫描 |
|
|
366
|
+
| [group.strategy.whitelist](#api-group-strategy) | 修改策略白名单 |
|
|
367
|
+
|
|
368
|
+
### 频道(Guild)
|
|
369
|
+
|
|
370
|
+
| API | 说明 |
|
|
371
|
+
| ----------------------------------------- | ---------------------------- |
|
|
372
|
+
| [channel.info](#api-channel-info) | 获取子频道详情 |
|
|
373
|
+
| [channel.list](#api-channel-list) | 获取频道(服务器)子频道列表 |
|
|
374
|
+
| [channel.create](#api-channel-create) | 创建子频道 |
|
|
375
|
+
| [channel.update](#api-channel-update) | 修改子频道 |
|
|
376
|
+
| [channel.delete](#api-channel-delete) | 删除子频道 |
|
|
377
|
+
| [channel.announce](#api-channel-announce) | 创建 / 删除频道公告 |
|
|
378
|
+
|
|
379
|
+
### 成员
|
|
380
|
+
|
|
381
|
+
| API | 说明 |
|
|
382
|
+
| -------------------------------------------- | ---------------------------------------- |
|
|
383
|
+
| [member.info](#api-member-info) | 获取频道成员详情 |
|
|
384
|
+
| [member.list](#api-member-list) | 获取频道成员列表 |
|
|
385
|
+
| [member.kick](#api-member-kick) | 移出频道成员 |
|
|
386
|
+
| [member.ban / member.unban](#api-member-ban) | 禁言 / 解除禁言(QQ 频道以禁言代替封禁) |
|
|
387
|
+
| [member.mute](#api-member-mute) | 频道成员禁言(指定时长) |
|
|
388
|
+
|
|
389
|
+
### 服务器(Guild)
|
|
390
|
+
|
|
391
|
+
| API | 说明 |
|
|
392
|
+
| ----------------------------- | ------------------------ |
|
|
393
|
+
| [guild.info](#api-guild-info) | 获取频道(服务器)详情 |
|
|
394
|
+
| [guild.list](#api-guild-list) | 获取机器人加入的频道列表 |
|
|
395
|
+
| [guild.mute](#api-guild-mute) | 全员禁言 |
|
|
396
|
+
|
|
397
|
+
### 角色
|
|
398
|
+
|
|
399
|
+
| API | 说明 |
|
|
400
|
+
| --------------------------------------------- | --------------------- |
|
|
401
|
+
| [role.list](#api-role-list) | 获取频道身份组列表 |
|
|
402
|
+
| [role.create](#api-role-create) | 创建身份组 |
|
|
403
|
+
| [role.update](#api-role-update) | 修改身份组 |
|
|
404
|
+
| [role.delete](#api-role-delete) | 删除身份组 |
|
|
405
|
+
| [role.assign / role.remove](#api-role-assign) | 分配 / 移除成员身份组 |
|
|
406
|
+
|
|
407
|
+
### 媒体 / 文件 / 上传
|
|
408
|
+
|
|
409
|
+
| API | 说明 |
|
|
410
|
+
| --------------------------------------------------------- | ------------------------------------------ |
|
|
411
|
+
| [media.send.user](#api-media-send-user) | 向用户发送富媒体(图片/视频/音频/文件) |
|
|
412
|
+
| [media.upload.prepare](#api-media-upload-prepare) | 分片上传准备 |
|
|
413
|
+
| [media.upload.part.finish](#api-media-upload-part-finish) | 分片完成上报 |
|
|
414
|
+
| [media.upload.chunked](#api-media-upload-chunked) | 分片上传全流程(prepare→直传→finish→合并) |
|
|
415
|
+
| [file.send.channel / file.send.user](#api-file-send) | 向群 / 用户发送文件 |
|
|
416
|
+
| [stream.message.send](#api-stream-message-send) | 单聊流式消息 |
|
|
417
|
+
|
|
418
|
+
### 表情 / 权限 / 交互 / 其他
|
|
419
|
+
|
|
420
|
+
| API | 说明 |
|
|
421
|
+
| --------------------------------------------------- | -------------------------------- |
|
|
422
|
+
| [reaction.add / reaction.remove](#api-reaction-add) | 添加 / 删除表情表态 |
|
|
423
|
+
| [reaction.list](#api-reaction-list) | 表情表态用户列表 |
|
|
424
|
+
| [permission.get / permission.set](#api-permission) | 子频道用户权限查询 / 设置 |
|
|
425
|
+
| [interaction.response](#api-interaction-response) | 互动事件回应(解除按钮 loading) |
|
|
426
|
+
| [me.info](#api-me-info) | 获取机器人自身信息 |
|
|
427
|
+
| [me.guilds](#api-me-guilds) | 获取机器人频道列表 |
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## QQ-Bot 消息类型
|
|
432
|
+
|
|
433
|
+
QQ-Bot 平台支持的消息类型与 `Format` / `DataEnums` 的对应关系,以及平台特有的构造写法。
|
|
434
|
+
|
|
435
|
+
### Format → QQ-Bot 类型映射
|
|
436
|
+
|
|
437
|
+
| Format 构建器 | DataEnums 类型 | QQ-Bot 落地 | 说明 |
|
|
438
|
+
| ------------------------------------------- | --------------------------------------- | --------------------------------- | ---------------------------------------------------- |
|
|
439
|
+
| `Format.create().addText(text)` | `Text` | 纯文本 `msg_type: 0` | 最常用 |
|
|
440
|
+
| `addMention(userId)` | `Mention` | `<@user_id>` / `<@everyone>` | 群聊/单聊有效,频道无 @ 语法 |
|
|
441
|
+
| `Format.createMarkdown().addLink()` 等 | `Markdown` | Markdown 消息 `msg_type: 2` | 需要平台 MD 权限,否则可降级 |
|
|
442
|
+
| `addMarkdownOriginal('**raw**')` | `MarkdownOriginal` | Markdown 原始字符串 `msg_type: 2` | 平台侧直接渲染 raw 文本 |
|
|
443
|
+
| `addImage(url)` / `addImage(buffer)` | `Image` / `ImageFile` / `ImageURL` | 富媒体图片 `msg_type: 7` | 群/单聊先上传富媒体(file_type 1);频道走 multipart |
|
|
444
|
+
| `addAudio(url)` | `Audio` | 富媒体语音 `msg_type: 7` | 群/单聊支持(file_type 3);频道不支持降级 |
|
|
445
|
+
| `addVideo(url)` | `Video` | 富媒体视频 `msg_type: 7` | 群/单聊支持(file_type 2);频道降级 |
|
|
446
|
+
| `addAttachment(url, options?)` | `Attachment` | 富媒体文件 `msg_type: 7` | 群/单聊支持(file_type 4);上传失败时降级 |
|
|
447
|
+
| `Format.createButtonGroup().addButton(...)` | `BT.group` | 按钮(keyboard)`msg_type: 2` | 群/单聊最多 5 行 × 每行 5 个 |
|
|
448
|
+
| -(直接传 `DataEnums`) | `ButtonTemplate` | 平台按钮模板 `keyboard.id` | value 为平台侧模板 ID |
|
|
449
|
+
| -(直接传 `DataEnums`) | `Ark.list` / `Ark.Card` / `Ark.BigCard` | Ark 卡片 `msg_type: 3` | QQ-Bot 特有,`Format` 无内置构建器 |
|
|
450
|
+
|
|
451
|
+
### 文本与 @
|
|
452
|
+
|
|
453
|
+
```ts
|
|
454
|
+
import { useMessage, Format } from 'alemonjs';
|
|
455
|
+
|
|
456
|
+
export default async () => {
|
|
457
|
+
const [message] = useMessage();
|
|
458
|
+
|
|
459
|
+
await message.send({
|
|
460
|
+
format: Format.create().addText('欢迎 ').addMention('USER_OPENID').addText(' 加入!')
|
|
461
|
+
});
|
|
462
|
+
};
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Markdown(需平台权限)
|
|
466
|
+
|
|
467
|
+
使用 `Format.createMarkdown()` 链式构建:
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
import { useMessage, Format } from 'alemonjs';
|
|
471
|
+
|
|
472
|
+
export default async () => {
|
|
473
|
+
const [message] = useMessage();
|
|
474
|
+
|
|
475
|
+
const md = Format.createMarkdown()
|
|
476
|
+
.addTitle('今日推荐')
|
|
477
|
+
.addSubtitle('副标题')
|
|
478
|
+
.addText('正文内容')
|
|
479
|
+
.addBold('加粗')
|
|
480
|
+
.addItalic('斜体')
|
|
481
|
+
.addLink('显示文本', 'https://example.com')
|
|
482
|
+
.addImage('https://img.url', { width: 200, height: 100 })
|
|
483
|
+
.addCode('console.log(1)', { language: 'ts' })
|
|
484
|
+
.addList('选项一', '选项二')
|
|
485
|
+
.addBlockquote('引用')
|
|
486
|
+
.addDivider();
|
|
487
|
+
|
|
488
|
+
await message.send({ format: Format.create().addMarkdown(md) });
|
|
489
|
+
};
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
> 未开通 MD 权限时发送会失败,可在配置中开启 `markdownToText: true` 强制降级为纯文本。
|
|
493
|
+
|
|
494
|
+
### 按钮(BT.group)
|
|
495
|
+
|
|
496
|
+
使用 `Format.createButtonGroup()`,平台限制:最多 **5 行**,每行最多 **5 个**按钮,超出自动裁剪。
|
|
497
|
+
|
|
498
|
+
#### 数据结构
|
|
499
|
+
|
|
500
|
+
每个按钮最终发送到 QQ 平台的结构如下(适配器自动生成,`rawData` 可透传覆盖任意字段):
|
|
501
|
+
|
|
502
|
+
```jsonc
|
|
503
|
+
{
|
|
504
|
+
"keyboard": {
|
|
505
|
+
"content": {
|
|
506
|
+
// 小按钮样式(可选):整个键盘使用小号按钮
|
|
507
|
+
"style": { "font_size": "small" },
|
|
508
|
+
"rows": [
|
|
509
|
+
{
|
|
510
|
+
"buttons": [
|
|
511
|
+
{
|
|
512
|
+
"id": "1",
|
|
513
|
+
"render_data": {
|
|
514
|
+
"label": "按钮文字",
|
|
515
|
+
"visited_label": "点击后文字",
|
|
516
|
+
"style": 0 // 0 灰色线框 / 1 蓝色线框 / 3 红框 / 4 蓝底白字
|
|
517
|
+
},
|
|
518
|
+
"action": {
|
|
519
|
+
"type": 2, // 0 跳转链接 / 1 回调 / 2 指令
|
|
520
|
+
"permission": { "type": 2, "specify_user_ids": [], "specify_role_ids": [] },
|
|
521
|
+
"data": "指令或链接",
|
|
522
|
+
"enter": false, // 指令按钮点击后自动发送
|
|
523
|
+
"reply": false, // 指令按钮带引用回复
|
|
524
|
+
"anchor": 0, // 1 唤起选图器(仅单聊)
|
|
525
|
+
"click_limit": undefined, // 可操作点击次数限制
|
|
526
|
+
"at_bot_show_channel_list": false, // 弹出子频道选择器
|
|
527
|
+
"unsupport_tips": "当前客户端不支持此操作",
|
|
528
|
+
// 点击确认弹窗(可选)
|
|
529
|
+
"modal": { "content": "是否确认操作?", "confirm_text": "是", "cancel_text": "否" }
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
}
|
|
534
|
+
]
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
#### 基本用法
|
|
541
|
+
|
|
542
|
+
```ts
|
|
543
|
+
import { useMessage, Format } from 'alemonjs';
|
|
544
|
+
|
|
545
|
+
export default async () => {
|
|
546
|
+
const [message] = useMessage();
|
|
547
|
+
|
|
548
|
+
const bt = Format.createButtonGroup()
|
|
549
|
+
.addRow()
|
|
550
|
+
.addButton('指令按钮', '/command', { type: 'command' })
|
|
551
|
+
.addButton('跳转链接', 'https://example.com', { type: 'link' })
|
|
552
|
+
.addRow()
|
|
553
|
+
.addButton('自动回车', '/auto', { type: 'command', autoEnter: true });
|
|
554
|
+
|
|
555
|
+
await message.send({ format: Format.create().addText('请选择').addButtonGroup(bt) });
|
|
556
|
+
};
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
#### 按钮样式(4 种)
|
|
560
|
+
|
|
561
|
+
`render_data.style` 对应关系,通过 `options.style` 设置(样式名或数字均可):
|
|
562
|
+
|
|
563
|
+
| 样式名 | 样式编号 | 说明 |
|
|
564
|
+
| ------------- | -------- | ---------------- |
|
|
565
|
+
| `'gray'` | `0` | 灰色线框(默认) |
|
|
566
|
+
| `'blue'` | `1` | 蓝色线框 |
|
|
567
|
+
| `'red'` | `3` | 红框 |
|
|
568
|
+
| `'blue-fill'` | `4` | 蓝底白字 |
|
|
569
|
+
|
|
570
|
+
```ts
|
|
571
|
+
await message.send({
|
|
572
|
+
format: Format.create().addButtonGroup(
|
|
573
|
+
Format.createButtonGroup()
|
|
574
|
+
.addRow()
|
|
575
|
+
.addButton('灰框', '/a', { style: 'gray' })
|
|
576
|
+
.addButton('蓝框', '/b', { style: 'blue' })
|
|
577
|
+
.addButton('红框', '/c', { style: 'red' })
|
|
578
|
+
.addButton('蓝底白字', '/d', { style: 'blue-fill' })
|
|
579
|
+
)
|
|
580
|
+
});
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
#### 小按钮样式
|
|
584
|
+
|
|
585
|
+
小按钮是**整个按钮消息的全局配置**(`keyboard.content.style = { font_size: 'small' }`),在按钮组上调用一次 `.smallButton()` 即可让整组按钮全部变小,按钮本身仍按普通按钮书写,无需逐按钮设置:
|
|
586
|
+
|
|
587
|
+
```ts
|
|
588
|
+
// 只需在按钮组上设置一次 .smallButton()
|
|
589
|
+
const bt = Format.createButtonGroup()
|
|
590
|
+
.smallButton() // 全局小按钮样式
|
|
591
|
+
.addRow()
|
|
592
|
+
.addButton('选项一', '/opt1')
|
|
593
|
+
.addButton('选项二', '/opt2');
|
|
594
|
+
|
|
595
|
+
await message.send({ format: Format.create().addText('小按钮键盘').addButtonGroup(bt) });
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
> 等效的原始协议写法:`keyboard.content.style = { font_size: 'small' }`(放在 `keyboard.content` 层级,对整个键盘生效)。
|
|
599
|
+
|
|
600
|
+
#### 点击确认弹窗
|
|
601
|
+
|
|
602
|
+
按钮点击后弹出确认框(`action.modal`),确认后才继续执行:
|
|
603
|
+
|
|
604
|
+
```ts
|
|
605
|
+
await message.send({
|
|
606
|
+
format: Format.create().addButtonGroup(
|
|
607
|
+
Format.createButtonGroup()
|
|
608
|
+
.addRow()
|
|
609
|
+
.addButton('领取奖励', '/reward', {
|
|
610
|
+
type: 'command',
|
|
611
|
+
modal: { content: '确定要领取奖励吗?', confirmText: '领取', cancelText: '取消' }
|
|
612
|
+
})
|
|
613
|
+
)
|
|
614
|
+
});
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
#### options 说明
|
|
618
|
+
|
|
619
|
+
| 选项 | 类型 | 说明 |
|
|
620
|
+
| ---------------------- | ------------------------------------------------ | --------------------------------------------------------------------- |
|
|
621
|
+
| `type` | `'command'` \| `'link'` \| `'call'` | 指令按钮(默认,点击在输入框插入指令)/ 跳转链接 / 回调按钮 |
|
|
622
|
+
| `data` | string | 按钮携带的数据(`type=command` 为指令文本,`type=link` 为跳转 URL) |
|
|
623
|
+
| `autoEnter` | boolean | 指令按钮点击后自动发送 |
|
|
624
|
+
| `style` | `'gray'` \| `'blue'` \| `'red'` \| `'blue-fill'` | 按钮样式(见上表) |
|
|
625
|
+
| `modal` | `{ content?, confirmText?, cancelText? }` | 点击确认弹窗,确认后继续执行 |
|
|
626
|
+
| `permission` | `{ type?, userIds?, roleIds? }` | 操作权限:`type` 0 指定用户 / 1 仅管理 / 2 全部(默认)/ 3 指定身份组 |
|
|
627
|
+
| `toolTip` | string | 无权限点击时的提示 |
|
|
628
|
+
| `reply` | boolean | 指令按钮带引用回复本消息 |
|
|
629
|
+
| `anchor` | number | `1` 点击后唤起选图器(仅单聊场景客户端支持) |
|
|
630
|
+
| `clickLimit` | number | 可操作点击次数限制(默认不限) |
|
|
631
|
+
| `atBotShowChannelList` | boolean | 指令按钮点击后弹出子频道选择器 |
|
|
632
|
+
| `rawData` | object | 透传的原始按钮数据(可覆盖 `render_data` / `action` 任意字段) |
|
|
633
|
+
|
|
634
|
+
> 兼容旧写法:`data` 传对象 `{ click, confirm, cancel }` 也会转换为确认弹窗(`content=click`、`confirm_text=confirm`、`cancel_text=cancel`)。
|
|
635
|
+
|
|
636
|
+
点击按钮后触发 `INTERACTION_CREATE` 事件(`interaction.create`),`data.resolved.button_data` 即按钮的 `data`,适配器已自动回应(解除 loading)。
|
|
637
|
+
|
|
638
|
+
### Ark 卡片(QQ-Bot 特有)
|
|
639
|
+
|
|
640
|
+
`Format` 没有内置 Ark 构建器,直接以 `DataEnums` 传入:
|
|
641
|
+
|
|
642
|
+
```ts
|
|
643
|
+
import { useMessage } from 'alemonjs';
|
|
644
|
+
|
|
645
|
+
export default async () => {
|
|
646
|
+
const [message] = useMessage();
|
|
647
|
+
|
|
648
|
+
await message.send({
|
|
649
|
+
format: [
|
|
650
|
+
{
|
|
651
|
+
type: 'Ark.Card',
|
|
652
|
+
value: {
|
|
653
|
+
title: '标题',
|
|
654
|
+
cover: 'https://example.com/cover.png',
|
|
655
|
+
link: 'https://example.com',
|
|
656
|
+
subtitle: '副标题',
|
|
657
|
+
decs: '描述',
|
|
658
|
+
prompt: '提示语',
|
|
659
|
+
metadecs: '元描述'
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
]
|
|
663
|
+
});
|
|
664
|
+
};
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
| 类型 | 对应模板 | 说明 |
|
|
668
|
+
| ------------- | -------- | ------------------------------- |
|
|
669
|
+
| `Ark.list` | 模板 23 | 列表(`[tip, content]`) |
|
|
670
|
+
| `Ark.Card` | 模板 24 | 图文卡片(标题/封面/链接/描述) |
|
|
671
|
+
| `Ark.BigCard` | 模板 37 | 大卡片 |
|
|
672
|
+
|
|
673
|
+
> 模板详情参考官方文档 `server-inter/message/type/template/template_23.md`、`template_24.md`、`template_37.md`。
|
|
674
|
+
|
|
675
|
+
### 富媒体(图片 / 视频 / 音频 / 文件)
|
|
676
|
+
|
|
677
|
+
群聊与单聊支持图片、视频、语音、文件富媒体消息(`msg_type: 7`,先上传获取 `file_info`)。适配器按类型自动选择 `file_type` 并上传:
|
|
678
|
+
|
|
679
|
+
| 类型 | `file_type` | 格式 | 软限制 | 群聊 | 单聊 | 频道 |
|
|
680
|
+
| --------------------------------------- | ----------- | -------- | ------ | ---- | ---- | --------------- |
|
|
681
|
+
| 图片 `Image` / `ImageFile` / `ImageURL` | 1 | png、jpg | 20 MB | ✅ | ✅ | ✅(multipart) |
|
|
682
|
+
| 视频 `Video` | 2 | mp4 | 30 MB | ✅ | ✅ | ❌ 降级 |
|
|
683
|
+
| 语音 `Audio` | 3 | silk | 20 MB | ✅ | ✅ | ❌ 降级 |
|
|
684
|
+
| 文件 `Attachment` | 4 | 不限 | 200 MB | ✅ | ✅ | ❌ 降级 |
|
|
685
|
+
|
|
686
|
+
```ts
|
|
687
|
+
import { useMessage, Format } from 'alemonjs';
|
|
688
|
+
|
|
689
|
+
export default async () => {
|
|
690
|
+
const [message] = useMessage();
|
|
691
|
+
|
|
692
|
+
// 图片(URL / Buffer)
|
|
693
|
+
await message.send({ format: Format.create().addImage('https://example.com/a.png') });
|
|
694
|
+
// 视频(URL / file:// / base64:// / Buffer)
|
|
695
|
+
await message.send({ format: Format.create().addVideo('https://example.com/a.mp4') });
|
|
696
|
+
// 语音(silk 格式)
|
|
697
|
+
await message.send({ format: Format.create().addAudio('https://example.com/a.silk') });
|
|
698
|
+
// 文件
|
|
699
|
+
await message.send({ format: Format.create().addAttachment('https://example.com/a.zip', { filename: 'a.zip' }) });
|
|
700
|
+
};
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
> 说明:
|
|
704
|
+
>
|
|
705
|
+
> - 富媒体消息(`msg_type: 7`)无法携带原生 Markdown/按钮,适配器会将 MD/按钮降级为文本合入 `content`,并移除已作为富媒体发送的占位符
|
|
706
|
+
> - 值支持 `https://` / `http://`(自动拉取转 base64)、`file://` 本地路径、`base64://`、Buffer
|
|
707
|
+
> - 超过软限制时降级为文件类型,超过 200 MB 硬限制返回错误
|
|
708
|
+
> - 大于 10MB 的文件自动走分片上传流程
|
|
709
|
+
> - 频道(Guild)接口仅支持图片(`file_image`),视频/音频/文件在频道场景降级为 `[视频]` / `[音频]` / `[附件]` 占位文本
|
|
710
|
+
|
|
711
|
+
### 降级策略
|
|
712
|
+
|
|
713
|
+
`markdownToText` 与 `hideUnsupported` 两个配置项控制不支持类型的降级行为(见 [配置](#配置)):
|
|
714
|
+
|
|
715
|
+
- `markdownToText: true`:Markdown 与按钮全部转为纯文本发送,适合没有 MD 权限的机器人
|
|
716
|
+
- `hideUnsupported: 1~4`:按级别隐藏不支持的占位符(`[视频]`、`[音频]` 等),转换后内容为空则跳过发送
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
## API 详解
|
|
721
|
+
|
|
722
|
+
> 各 API 均对应官方开发文档中的 HTTP 接口。以下示例均为 `sendAction` 调用方式;除特殊说明外,均可使用 `useClient` 的 SDK 方法获得原始返回数据。需要场景上下文的 action 均支持在 `payload` 中透传 `event`(来源事件对象)自动推断群/频道/用户 id,显式传参优先级更高(见 [调用方式](#调用方式))。
|
|
723
|
+
|
|
724
|
+
### 消息 API
|
|
725
|
+
|
|
726
|
+
<a id="api-message-send"></a>
|
|
727
|
+
|
|
728
|
+
#### message.send — 回复 / 发送消息
|
|
729
|
+
|
|
730
|
+
自动按事件的 `_tag` 分发到对应发送通道(群 @、群消息、单聊、频道、频道私信),并支持图片 / Markdown / 按钮 / Ark。
|
|
731
|
+
|
|
732
|
+
**调用方式一:框架 `useMessage`(推荐)**
|
|
733
|
+
|
|
734
|
+
```ts
|
|
735
|
+
// src/response/hello.ts
|
|
736
|
+
import { useMessage, Format } from 'alemonjs';
|
|
737
|
+
|
|
738
|
+
export default async () => {
|
|
739
|
+
const [message] = useMessage();
|
|
740
|
+
|
|
741
|
+
// 纯文本
|
|
742
|
+
await message.send({ format: Format.create().addText('你好') });
|
|
743
|
+
// 图片(URL / Buffer)
|
|
744
|
+
await message.send({ format: Format.create().addImage('https://example.com/a.png') });
|
|
745
|
+
// Markdown(需要平台 MD 权限)
|
|
746
|
+
await message.send({
|
|
747
|
+
format: Format.create().addMarkdown(Format.createMarkdown().addTitle('标题').addText('内容'))
|
|
748
|
+
});
|
|
749
|
+
// 文本 + 按钮
|
|
750
|
+
await message.send({
|
|
751
|
+
format: Format.create().addText('请选择').addButtonGroup(Format.createButtonGroup().addRow().addButton('确认', '/confirm'))
|
|
752
|
+
});
|
|
753
|
+
};
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
**调用方式二:`sendAction`**
|
|
757
|
+
|
|
758
|
+
```ts
|
|
759
|
+
const results = await sendAction({
|
|
760
|
+
action: 'message.send',
|
|
761
|
+
payload: {
|
|
762
|
+
event, // 事件对象
|
|
763
|
+
params: {
|
|
764
|
+
format: [{ type: 'Text', value: '你好' }],
|
|
765
|
+
// 图片转存校验失败时拒绝发送(可选)
|
|
766
|
+
forceVerifyImageResource: true
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
**发送规则**(参考 `sends.ts`):
|
|
773
|
+
|
|
774
|
+
- 纯文本 → `msg_type: 0`
|
|
775
|
+
- 包含图片 → 富媒体上传后发送 `msg_type: 7`(Markdown/按钮自动降级为文本合入)
|
|
776
|
+
- 包含 Markdown/按钮 → `msg_type: 2`(`markdown.content` 前置合并文本)
|
|
777
|
+
- 包含 Ark 卡片 → `msg_type: 3`
|
|
778
|
+
- `markdownToText: true` 时全部降级为纯文本
|
|
779
|
+
- `hideUnsupported` 转换后内容为空时跳过发送
|
|
780
|
+
|
|
781
|
+
<a id="api-message-send-channel"></a>
|
|
782
|
+
|
|
783
|
+
#### message.send.channel — 主动发送到频道 / 群
|
|
784
|
+
|
|
785
|
+
| 参数 | 类型 | 说明 |
|
|
786
|
+
| --------------- | ----------- | ------------------------- |
|
|
787
|
+
| `ChannelId` | string | 群 openid 或频道子频道 id |
|
|
788
|
+
| `params.format` | DataEnums[] | 消息内容 |
|
|
789
|
+
|
|
790
|
+
```ts
|
|
791
|
+
// 推荐:MessageDirect
|
|
792
|
+
await MessageDirect.create().sendToChannel({
|
|
793
|
+
SpaceId: 'GROUP_OPENID',
|
|
794
|
+
format: Format.create().addText('通知')
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
// 或 sendAction
|
|
798
|
+
await sendAction({
|
|
799
|
+
action: 'message.send.channel',
|
|
800
|
+
payload: { ChannelId: 'GROUP_OPENID', params: { format: Format.create().addText('通知').value } }
|
|
801
|
+
});
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
<a id="api-message-send-user"></a>
|
|
805
|
+
|
|
806
|
+
#### message.send.user — 主动发送到用户
|
|
807
|
+
|
|
808
|
+
| 参数 | 类型 | 说明 |
|
|
809
|
+
| --------------- | ----------- | ----------------------------------- |
|
|
810
|
+
| `UserId` | string | C2C 用户 openid 或频道私信 guild_id |
|
|
811
|
+
| `params.format` | DataEnums[] | 消息内容 |
|
|
812
|
+
|
|
813
|
+
```ts
|
|
814
|
+
// 推荐:MessageDirect
|
|
815
|
+
await MessageDirect.create().sendToUser({
|
|
816
|
+
OpenID: 'USER_OPENID',
|
|
817
|
+
format: Format.create().addText('晚上好')
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// 或 sendAction
|
|
821
|
+
await sendAction({
|
|
822
|
+
action: 'message.send.user',
|
|
823
|
+
payload: { UserId: 'USER_OPENID', params: { format: Format.create().addText('晚上好').value } }
|
|
824
|
+
});
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
<a id="api-message-delete"></a>
|
|
828
|
+
|
|
829
|
+
#### message.delete — 撤回消息
|
|
830
|
+
|
|
831
|
+
按事件上下文(`SpaceId` / `OpenId`)自动分流到 群撤回 / 频道撤回 / 单聊撤回 / 私信撤回。
|
|
832
|
+
|
|
833
|
+
| 参数 | 类型 | 说明 |
|
|
834
|
+
| ----------- | ------ | ------------------------------------------------------------------------------------------------------------ |
|
|
835
|
+
| `MessageId` | string | 要撤回的消息 ID |
|
|
836
|
+
| `event` | object | 当前事件对象(`SpaceId`(`GROUP:`/`GUILD:`)、`OpenId`(`C2C:`/`DIRECT:`)、`ChannelId` 从中读取并自动分流) |
|
|
837
|
+
|
|
838
|
+
```ts
|
|
839
|
+
const [e] = useEvent();
|
|
840
|
+
|
|
841
|
+
await sendAction({
|
|
842
|
+
action: 'message.delete',
|
|
843
|
+
payload: { MessageId: e.current.MessageId, event: e.current }
|
|
844
|
+
});
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
> 对应 SDK:`groupMessageDelete`、`channelsMessagesDelete`、`userMessageDelete`、`dmsMessageDelete`。
|
|
848
|
+
|
|
849
|
+
<a id="api-message-get"></a>
|
|
850
|
+
|
|
851
|
+
#### message.get — 获取频道消息
|
|
852
|
+
|
|
853
|
+
| 参数 | 类型 | 说明 |
|
|
854
|
+
| ----------- | ------ | --------- |
|
|
855
|
+
| `ChannelId` | string | 子频道 id |
|
|
856
|
+
| `MessageId` | string | 消息 ID |
|
|
857
|
+
|
|
858
|
+
```ts
|
|
859
|
+
await sendAction({ action: 'message.get', payload: { ChannelId: 'CHANNEL_ID', MessageId: 'MSG_ID' } });
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
<a id="api-message-pin"></a>
|
|
863
|
+
|
|
864
|
+
#### message.pin / message.unpin — 频道精华消息
|
|
865
|
+
|
|
866
|
+
```ts
|
|
867
|
+
await sendAction({ action: 'message.pin', payload: { ChannelId: 'CHANNEL_ID', MessageId: 'MSG_ID' } });
|
|
868
|
+
await sendAction({ action: 'message.unpin', payload: { ChannelId: 'CHANNEL_ID', MessageId: 'MSG_ID' } });
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
> 对应 SDK:`channelsPinsPut` / `channelsPinsDelete`。
|
|
872
|
+
|
|
873
|
+
<a id="api-mention-get"></a>
|
|
874
|
+
|
|
875
|
+
#### mention.get — 获取 @ 提及用户
|
|
876
|
+
|
|
877
|
+
```ts
|
|
878
|
+
const [e] = useEvent();
|
|
879
|
+
const results = await sendAction({ action: 'mention.get', payload: { event: e.current } });
|
|
880
|
+
// results[0].data => [{ UserId, UserName, IsMaster, IsBot, UserKey }]
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
<a id="api-message-input-notify"></a>
|
|
884
|
+
|
|
885
|
+
#### message.input.notify — 输入状态通知(仅单聊)
|
|
886
|
+
|
|
887
|
+
展示“正在输入”状态,仅单聊支持;与流式消息是不同能力。
|
|
888
|
+
|
|
889
|
+
| 参数 | 类型 | 说明 |
|
|
890
|
+
| --------------------- | ------ | -------------------------------------- |
|
|
891
|
+
| `UserId` | string | 用户 openid |
|
|
892
|
+
| `params.input_type` | number | 输入状态类型,当前固定 `1`(正在输入) |
|
|
893
|
+
| `params.input_second` | number | 展示时长(秒),如 `60` |
|
|
894
|
+
|
|
895
|
+
```ts
|
|
896
|
+
await sendAction({
|
|
897
|
+
action: 'message.input.notify',
|
|
898
|
+
payload: { UserId: 'USER_OPENID', params: { input_type: 1, input_second: 60 } }
|
|
899
|
+
});
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
> 参考官方文档 `message/send-receive/streaming.md`:输入状态通过普通发消息接口发送 `msg_type=6` + `input_notify`。
|
|
903
|
+
|
|
904
|
+
---
|
|
905
|
+
|
|
906
|
+
### 群管理 API
|
|
907
|
+
|
|
908
|
+
> 群管理接口要求机器人拥有群管理员身份。通过 `sendAction` 调用时**推荐在 `payload` 中透传 `event`(来源事件对象)**,群 openid 与群成员 openid 由适配器自动推断(群消息事件中群 openid 位于 `ChannelId`、成员 openid 位于 `UserId`);也可用 `payload.ChannelId` / `payload.UserId` 或 `params.groupOpenId` / `params.memberOpenId` 显式覆盖。`useClient` 直接调 SDK 时按方法签名传参。
|
|
909
|
+
|
|
910
|
+
<a id="api-group-info"></a>
|
|
911
|
+
|
|
912
|
+
#### group.info — 获取群基本信息
|
|
913
|
+
|
|
914
|
+
```ts
|
|
915
|
+
// 推荐:透传来源事件,群 openid 自动推断
|
|
916
|
+
await sendAction({ action: 'group.info', payload: { event: event.current } });
|
|
917
|
+
// 或显式指定群
|
|
918
|
+
await sendAction({ action: 'group.info', payload: { params: { groupOpenId: 'GROUP_OPENID' } } });
|
|
919
|
+
```
|
|
920
|
+
|
|
921
|
+
> 对应 SDK:`groupsInfo`;官方文档 `server-inter/group/manage/get-group-info.md`。
|
|
922
|
+
|
|
923
|
+
<a id="api-group-bot-state"></a>
|
|
924
|
+
|
|
925
|
+
#### group.botState — 机器人群内状态
|
|
926
|
+
|
|
927
|
+
```ts
|
|
928
|
+
// 透传来源事件,群 openid 自动推断
|
|
929
|
+
await sendAction({ action: 'group.botState', payload: { event: event.current } });
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
> 对应 SDK:`groupsBotState`;官方文档 `server-inter/group/manage/get-bot-state.md`。
|
|
933
|
+
|
|
934
|
+
<a id="api-group-member-info"></a>
|
|
935
|
+
|
|
936
|
+
#### group.member.info — 群成员详情
|
|
937
|
+
|
|
938
|
+
```ts
|
|
939
|
+
// 推荐:透传事件,群 openid 与成员 openid 自动推断
|
|
940
|
+
await sendAction({ action: 'group.member.info', payload: { event: event.current } });
|
|
941
|
+
// 显式指定
|
|
942
|
+
await sendAction({ action: 'group.member.info', payload: { params: { groupOpenId: 'GROUP_OPENID', memberOpenId: 'MEMBER_OPENID' } } });
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
> 对应 SDK:`groupsMembersMessage`;官方文档 `server-inter/group/manage/get-member.md`。
|
|
946
|
+
|
|
947
|
+
<a id="api-group-join-request-list"></a>
|
|
948
|
+
|
|
949
|
+
#### group.joinRequest.list — 拉取入群申请列表
|
|
950
|
+
|
|
951
|
+
| 参数 | 类型 | 说明 |
|
|
952
|
+
| --------------- | ------ | --------------------------- |
|
|
953
|
+
| `params.cursor` | string | 分页游标,首次不传或传空 |
|
|
954
|
+
| `params.limit` | number | 单页数量,默认 20,最大 100 |
|
|
955
|
+
|
|
956
|
+
```ts
|
|
957
|
+
const results = await sendAction({
|
|
958
|
+
action: 'group.joinRequest.list',
|
|
959
|
+
payload: { event: event.current, params: { cursor: '', limit: 20 } }
|
|
960
|
+
});
|
|
961
|
+
// results[0].data => { list: JoinRequest[], next_cursor: string }
|
|
962
|
+
```
|
|
963
|
+
|
|
964
|
+
> 对应 SDK:`groupsJoinRequestList`;官方文档 `server-inter/group/manage/join-request.md`。
|
|
965
|
+
|
|
966
|
+
<a id="api-group-join-request-approve"></a>
|
|
967
|
+
|
|
968
|
+
#### group.joinRequest.approve — 审批入群申请
|
|
969
|
+
|
|
970
|
+
| 参数 | 类型 | 说明 |
|
|
971
|
+
| ----------------------------- | ------- | ---------------------------------------------------------------- |
|
|
972
|
+
| `event` | object | 来源事件(推荐):群 openid / 申请人 openid 自动推断,无需显式传 |
|
|
973
|
+
| `ChannelId` / `UserId` | string | 可选,显式指定群 openid / 申请人 openid(优先级高于 event 推断) |
|
|
974
|
+
| `params.op` | string | `approve` 通过 / `decline` 拒绝 |
|
|
975
|
+
| `params.joinRequestId` | string | 申请 ID(申请事件 `GROUP_JOIN_REQUEST` 中携带) |
|
|
976
|
+
| `params.rejectReason` | string | 拒绝理由(`op=decline` 时) |
|
|
977
|
+
| `params.addToMemberBlacklist` | boolean | 同时加入群黑名单(`op=decline` 时) |
|
|
978
|
+
|
|
979
|
+
```ts
|
|
980
|
+
// 通过(推荐:透传事件,自动推断群 openid / 申请人 openid)
|
|
981
|
+
await sendAction({
|
|
982
|
+
action: 'group.joinRequest.approve',
|
|
983
|
+
payload: {
|
|
984
|
+
event: event.current,
|
|
985
|
+
params: { op: 'approve', joinRequestId: 'JOIN_REQUEST_ID' }
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
// 拒绝并拉黑(显式指定目标)
|
|
990
|
+
await sendAction({
|
|
991
|
+
action: 'group.joinRequest.approve',
|
|
992
|
+
payload: {
|
|
993
|
+
ChannelId: 'GROUP_OPENID',
|
|
994
|
+
UserId: 'MEMBER_OPENID',
|
|
995
|
+
params: { op: 'decline', joinRequestId: 'JOIN_REQUEST_ID', rejectReason: '不符合入群要求', addToMemberBlacklist: true }
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
> 对应 SDK:`groupsApprovalJoinRequest`;官方文档 `server-inter/group/manage/join-request.md`。机器人收到 `GROUP_JOIN_REQUEST` 事件(`notice.create`)时即可调用审批。
|
|
1001
|
+
|
|
1002
|
+
<a id="api-group-mute-setting"></a>
|
|
1003
|
+
|
|
1004
|
+
#### group.mute.setting — 查询群禁言状态
|
|
1005
|
+
|
|
1006
|
+
```ts
|
|
1007
|
+
const results = await sendAction({ action: 'group.mute.setting', payload: { event: event.current } });
|
|
1008
|
+
// results[0].data => { global_rule: GlobalMuteRule, members: MemberMuteState[] }
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
> 对应 SDK:`groupsRestrictChatSetting`;官方文档 `server-inter/group/manage/mute.md`。
|
|
1012
|
+
|
|
1013
|
+
<a id="api-group-mute-set"></a>
|
|
1014
|
+
|
|
1015
|
+
#### group.mute.set — 设置群成员禁言
|
|
1016
|
+
|
|
1017
|
+
| 参数 | 类型 | 说明 |
|
|
1018
|
+
| ---------------- | -------------------- | ------------------------------ |
|
|
1019
|
+
| `params.members` | SetMemberMuteState[] | 禁言操作列表,单次不超过 10 个 |
|
|
1020
|
+
|
|
1021
|
+
`SetMemberMuteState`:`{ op: 'add' | 'update' | 'del', member_openid, mute_expire_at? }`(`mute_expire_at` 为 RFC3339 时间,`op=del` 可传空串立即解除)。
|
|
1022
|
+
|
|
1023
|
+
```ts
|
|
1024
|
+
await sendAction({
|
|
1025
|
+
action: 'group.mute.set',
|
|
1026
|
+
payload: {
|
|
1027
|
+
params: {
|
|
1028
|
+
members: [{ op: 'add', member_openid: 'MEMBER_OPENID', mute_expire_at: '2026-08-05T11:23:05+08:00' }]
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
> 对应 SDK:`groupsRestrictChatSettingPost`;官方文档 `server-inter/group/manage/mute.md`。注意新增/更新禁言只能操作普通成员,不能操作群主、管理员或机器人。
|
|
1035
|
+
|
|
1036
|
+
<a id="api-group-strategy"></a>
|
|
1037
|
+
|
|
1038
|
+
#### group.strategy.\* — 入群自动审批策略
|
|
1039
|
+
|
|
1040
|
+
一个机器人最多创建 20 个策略,策略仅在机器人拥有对应群管理员身份时生效。
|
|
1041
|
+
|
|
1042
|
+
**group.strategy.list — 策略列表**
|
|
1043
|
+
|
|
1044
|
+
```ts
|
|
1045
|
+
await sendAction({ action: 'group.strategy.list', payload: { params: { cursor: '', limit: 20 } } });
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
**group.strategy.create — 创建策略**
|
|
1049
|
+
|
|
1050
|
+
| 参数 | 类型 | 说明 |
|
|
1051
|
+
| --------------------- | -------- | ---------------------------------------------------------------------- |
|
|
1052
|
+
| `params.groupOpenIds` | string[] | 关联群 openid 列表,与 `groupIds` 二选一 |
|
|
1053
|
+
| `params.groupIds` | string[] | 关联 QQ 群号列表(字符串,避免 JS 精度问题),与 `groupOpenIds` 二选一 |
|
|
1054
|
+
| `params.isEnable` | string | `on` 启用 / `off` 关闭,默认 `on` |
|
|
1055
|
+
| `params.expireAt` | string | 过期时间(RFC3339),不传默认一年后 |
|
|
1056
|
+
| `params.remark` | string | 备注,最多 255 字 |
|
|
1057
|
+
|
|
1058
|
+
```ts
|
|
1059
|
+
await sendAction({
|
|
1060
|
+
action: 'group.strategy.create',
|
|
1061
|
+
payload: { params: { groupOpenIds: ['GROUP_OPENID_1'], isEnable: 'on', remark: '白名单自动入群' } }
|
|
1062
|
+
});
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
**group.strategy.update — 修改策略**
|
|
1066
|
+
|
|
1067
|
+
| 参数 | 类型 | 说明 |
|
|
1068
|
+
| ------------------------------------------------------- | ------ | ----------------------------------------------------------- |
|
|
1069
|
+
| `StrategyId` | string | 策略 ID |
|
|
1070
|
+
| `params.isEnable` / `params.expireAt` / `params.remark` | - | 同创建 |
|
|
1071
|
+
| `params.groupAction` | object | `{ op: 'add'\|'del', groupOpenIds?, groupIds? }` 关联群增删 |
|
|
1072
|
+
|
|
1073
|
+
**group.strategy.delete — 删除策略**
|
|
1074
|
+
|
|
1075
|
+
```ts
|
|
1076
|
+
await sendAction({ action: 'group.strategy.delete', payload: { StrategyId: 'STRATEGY_ID' } });
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
**group.strategy.execute — 执行策略**
|
|
1080
|
+
|
|
1081
|
+
对策略关联的全部群发起全量扫描,将命中白名单号码的入群申请自动审批通过。任务异步执行,约 10 分钟完成。
|
|
1082
|
+
|
|
1083
|
+
```ts
|
|
1084
|
+
await sendAction({ action: 'group.strategy.execute', payload: { StrategyId: 'STRATEGY_ID' } });
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
**group.strategy.whitelist — 修改白名单**
|
|
1088
|
+
|
|
1089
|
+
| 参数 | 类型 | 说明 |
|
|
1090
|
+
| ----------------------- | -------- | ------------------------------------------------------ |
|
|
1091
|
+
| `StrategyId` | string | 策略 ID |
|
|
1092
|
+
| `params.op` | string | `add` 新增 / `del` 删除 |
|
|
1093
|
+
| `params.whitelistUsers` | string[] | QQ 号码列表(字符串),单次最多 10000 个,总上限 10 万 |
|
|
1094
|
+
|
|
1095
|
+
```ts
|
|
1096
|
+
await sendAction({
|
|
1097
|
+
action: 'group.strategy.whitelist',
|
|
1098
|
+
payload: { StrategyId: 'STRATEGY_ID', params: { op: 'add', whitelistUsers: ['1234567'] } }
|
|
1099
|
+
});
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
> 对应 SDK:`groupsJoinApprovalStrategies` / `groupsJoinApprovalStrategyCreate` / `groupsJoinApprovalStrategyPatch` / `groupsJoinApprovalStrategyDelete` / `groupsJoinApprovalStrategyExecute` / `groupsJoinApprovalStrategyWhitelistUsers`;官方文档 `server-inter/group/manage/join-approval-strategy.md`。
|
|
1103
|
+
|
|
1104
|
+
---
|
|
1105
|
+
|
|
1106
|
+
### 频道(Guild)API
|
|
1107
|
+
|
|
1108
|
+
<a id="api-channel-info"></a>
|
|
1109
|
+
|
|
1110
|
+
#### channel.info — 子频道详情
|
|
1111
|
+
|
|
1112
|
+
```ts
|
|
1113
|
+
await sendAction({ action: 'channel.info', payload: { ChannelId: 'CHANNEL_ID' } });
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
> 对应 SDK:`channels`。
|
|
1117
|
+
|
|
1118
|
+
<a id="api-channel-list"></a>
|
|
1119
|
+
|
|
1120
|
+
#### channel.list — 子频道列表
|
|
1121
|
+
|
|
1122
|
+
```ts
|
|
1123
|
+
await sendAction({ action: 'channel.list', payload: { GuildId: 'GUILD_ID' } });
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
> 对应 SDK:`guildsChannels`。
|
|
1127
|
+
|
|
1128
|
+
<a id="api-channel-create"></a>
|
|
1129
|
+
|
|
1130
|
+
#### channel.create — 创建子频道
|
|
1131
|
+
|
|
1132
|
+
| 参数 | 类型 | 说明 |
|
|
1133
|
+
| ----------------- | ------ | ---------------- |
|
|
1134
|
+
| `GuildId` | string | 频道(服务器)id |
|
|
1135
|
+
| `params.name` | string | 子频道名称 |
|
|
1136
|
+
| `params.type` | number | 子频道类型 |
|
|
1137
|
+
| `params.parentId` | string | 父分组 id |
|
|
1138
|
+
|
|
1139
|
+
```ts
|
|
1140
|
+
await sendAction({ action: 'channel.create', payload: { GuildId: 'GUILD_ID', params: { name: '游戏区', type: 0 } } });
|
|
1141
|
+
```
|
|
1142
|
+
|
|
1143
|
+
<a id="api-channel-update"></a>
|
|
1144
|
+
|
|
1145
|
+
#### channel.update — 修改子频道
|
|
1146
|
+
|
|
1147
|
+
```ts
|
|
1148
|
+
await sendAction({ action: 'channel.update', payload: { ChannelId: 'CHANNEL_ID', params: { name: '新名称', position: 0 } } });
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
<a id="api-channel-delete"></a>
|
|
1152
|
+
|
|
1153
|
+
#### channel.delete — 删除子频道
|
|
1154
|
+
|
|
1155
|
+
```ts
|
|
1156
|
+
await sendAction({ action: 'channel.delete', payload: { ChannelId: 'CHANNEL_ID' } });
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
<a id="api-channel-announce"></a>
|
|
1160
|
+
|
|
1161
|
+
#### channel.announce — 频道公告
|
|
1162
|
+
|
|
1163
|
+
| 参数 | 类型 | 说明 |
|
|
1164
|
+
| ------------------ | ------- | ----------------------------------------- |
|
|
1165
|
+
| `GuildId` | string | 频道 id |
|
|
1166
|
+
| `params.messageId` | string | 公告消息 id |
|
|
1167
|
+
| `params.channelId` | string | 子频道 id(消息 id 存在时必传) |
|
|
1168
|
+
| `params.remove` | boolean | `true` 删除公告(`messageId='all'` 清空) |
|
|
1169
|
+
|
|
1170
|
+
```ts
|
|
1171
|
+
// 创建公告
|
|
1172
|
+
await sendAction({
|
|
1173
|
+
action: 'channel.announce',
|
|
1174
|
+
payload: { GuildId: 'GUILD_ID', params: { messageId: 'MSG_ID', channelId: 'CHANNEL_ID' } }
|
|
1175
|
+
});
|
|
1176
|
+
// 删除公告
|
|
1177
|
+
await sendAction({ action: 'channel.announce', payload: { GuildId: 'GUILD_ID', params: { remove: true, messageId: 'MSG_ID' } } });
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
> 对应 SDK:`guildsAnnounces` / `guildsAnnouncesDelete`。
|
|
1181
|
+
|
|
1182
|
+
---
|
|
1183
|
+
|
|
1184
|
+
### 成员 API
|
|
1185
|
+
|
|
1186
|
+
> 以下均为**频道(Guild)**成员接口,群成员接口见 [群管理 API](#群管理-api)。
|
|
1187
|
+
|
|
1188
|
+
<a id="api-member-info"></a>
|
|
1189
|
+
|
|
1190
|
+
#### member.info — 成员详情
|
|
1191
|
+
|
|
1192
|
+
```ts
|
|
1193
|
+
await sendAction({ action: 'member.info', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID' } });
|
|
1194
|
+
// 或使用框架 Hook:const [member] = useMember(); await member.info({ userId: 'USER_ID' });
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
> 对应 SDK:`guildsMembersMessage`。
|
|
1198
|
+
|
|
1199
|
+
<a id="api-member-list"></a>
|
|
1200
|
+
|
|
1201
|
+
#### member.list — 成员列表
|
|
1202
|
+
|
|
1203
|
+
```ts
|
|
1204
|
+
await sendAction({ action: 'member.list', payload: { GuildId: 'GUILD_ID', params: { After: '0', Limit: 100 } } });
|
|
1205
|
+
```
|
|
1206
|
+
|
|
1207
|
+
> 对应 SDK:`guildsMembers`。
|
|
1208
|
+
|
|
1209
|
+
<a id="api-member-kick"></a>
|
|
1210
|
+
|
|
1211
|
+
#### member.kick — 移出成员
|
|
1212
|
+
|
|
1213
|
+
```ts
|
|
1214
|
+
await sendAction({ action: 'member.kick', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID' } });
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
> 对应 SDK:`guildsMembersDelete`。
|
|
1218
|
+
|
|
1219
|
+
<a id="api-member-ban"></a>
|
|
1220
|
+
|
|
1221
|
+
#### member.ban / member.unban — 禁言 / 解除禁言
|
|
1222
|
+
|
|
1223
|
+
QQ 频道没有真正意义的封禁,适配器以**禁言**实现 ban:不传时长或时长小于等于 0 时默认禁言 7 天(604800 秒)。
|
|
1224
|
+
|
|
1225
|
+
```ts
|
|
1226
|
+
// 禁言(默认 7 天)
|
|
1227
|
+
await sendAction({ action: 'member.ban', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID' } });
|
|
1228
|
+
// 禁言指定时长(秒)
|
|
1229
|
+
await sendAction({ action: 'member.ban', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID', params: { duration: 3600 } } });
|
|
1230
|
+
// 解除
|
|
1231
|
+
await sendAction({ action: 'member.unban', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID' } });
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
> 对应 SDK:`guildsMemberMute`(`mute_seconds: '0'` 为解除)。
|
|
1235
|
+
|
|
1236
|
+
<a id="api-member-mute"></a>
|
|
1237
|
+
|
|
1238
|
+
#### member.mute — 成员禁言(指定时长)
|
|
1239
|
+
|
|
1240
|
+
```ts
|
|
1241
|
+
await sendAction({ action: 'member.mute', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID', params: { duration: 600 } } });
|
|
1242
|
+
```
|
|
1243
|
+
|
|
1244
|
+
---
|
|
1245
|
+
|
|
1246
|
+
### 服务器(Guild)API
|
|
1247
|
+
|
|
1248
|
+
<a id="api-guild-info"></a>
|
|
1249
|
+
|
|
1250
|
+
#### guild.info — 频道详情
|
|
1251
|
+
|
|
1252
|
+
```ts
|
|
1253
|
+
await sendAction({ action: 'guild.info', payload: { GuildId: 'GUILD_ID' } });
|
|
1254
|
+
```
|
|
1255
|
+
|
|
1256
|
+
> 对应 SDK:`guilds`。
|
|
1257
|
+
|
|
1258
|
+
<a id="api-guild-list"></a>
|
|
1259
|
+
|
|
1260
|
+
#### guild.list — 机器人频道列表
|
|
1261
|
+
|
|
1262
|
+
```ts
|
|
1263
|
+
const results = await sendAction({ action: 'guild.list' });
|
|
1264
|
+
```
|
|
1265
|
+
|
|
1266
|
+
> 对应 SDK:`usersMeGuilds`。
|
|
1267
|
+
|
|
1268
|
+
<a id="api-guild-mute"></a>
|
|
1269
|
+
|
|
1270
|
+
#### guild.mute — 全员禁言
|
|
1271
|
+
|
|
1272
|
+
```ts
|
|
1273
|
+
// 全员禁言(秒)
|
|
1274
|
+
await sendAction({ action: 'guild.mute', payload: { GuildId: 'GUILD_ID', params: { duration: 600 } } });
|
|
1275
|
+
// duration 为 0 时解除
|
|
1276
|
+
await sendAction({ action: 'guild.mute', payload: { GuildId: 'GUILD_ID', params: { duration: 0 } } });
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
> 对应 SDK:`guildsMuteAll`。
|
|
1280
|
+
|
|
1281
|
+
---
|
|
1282
|
+
|
|
1283
|
+
### 角色 API
|
|
1284
|
+
|
|
1285
|
+
<a id="api-role-list"></a>
|
|
1286
|
+
|
|
1287
|
+
#### role.list — 身份组列表
|
|
1288
|
+
|
|
1289
|
+
```ts
|
|
1290
|
+
await sendAction({ action: 'role.list', payload: { GuildId: 'GUILD_ID' } });
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
> 对应 SDK:`guildsRoles`。
|
|
1294
|
+
|
|
1295
|
+
<a id="api-role-create"></a>
|
|
1296
|
+
|
|
1297
|
+
#### role.create — 创建身份组
|
|
1298
|
+
|
|
1299
|
+
```ts
|
|
1300
|
+
await sendAction({ action: 'role.create', payload: { GuildId: 'GUILD_ID', params: { name: '管理员', color: 4278190080 } } });
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
> `color` 为 ARGB HEX 转换后的十进制数值。
|
|
1304
|
+
|
|
1305
|
+
<a id="api-role-update"></a>
|
|
1306
|
+
|
|
1307
|
+
#### role.update — 修改身份组
|
|
1308
|
+
|
|
1309
|
+
```ts
|
|
1310
|
+
await sendAction({ action: 'role.update', payload: { GuildId: 'GUILD_ID', RoleId: 'ROLE_ID', params: { name: '新名称' } } });
|
|
1311
|
+
```
|
|
1312
|
+
|
|
1313
|
+
<a id="api-role-delete"></a>
|
|
1314
|
+
|
|
1315
|
+
#### role.delete — 删除身份组
|
|
1316
|
+
|
|
1317
|
+
```ts
|
|
1318
|
+
await sendAction({ action: 'role.delete', payload: { GuildId: 'GUILD_ID', RoleId: 'ROLE_ID' } });
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
<a id="api-role-assign"></a>
|
|
1322
|
+
|
|
1323
|
+
#### role.assign / role.remove — 分配 / 移除成员身份组
|
|
1324
|
+
|
|
1325
|
+
```ts
|
|
1326
|
+
await sendAction({ action: 'role.assign', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID', RoleId: 'ROLE_ID' } });
|
|
1327
|
+
await sendAction({ action: 'role.remove', payload: { GuildId: 'GUILD_ID', UserId: 'USER_ID', RoleId: 'ROLE_ID' } });
|
|
1328
|
+
```
|
|
1329
|
+
|
|
1330
|
+
> 对应 SDK:`guildsRolesMembersPut` / `guildsRolesMembersDelete`(QQ Bot 的 channel_id 传空字符串使用默认)。
|
|
1331
|
+
|
|
1332
|
+
---
|
|
1333
|
+
|
|
1334
|
+
### 媒体 / 文件 / 上传 API
|
|
1335
|
+
|
|
1336
|
+
<a id="api-media-send-user"></a>
|
|
1337
|
+
|
|
1338
|
+
#### media.send.user — 发送富媒体到用户
|
|
1339
|
+
|
|
1340
|
+
| 参数 | 类型 | 说明 |
|
|
1341
|
+
| ------------- | ------ | ------------------------------------ |
|
|
1342
|
+
| `UserId` | string | 用户 openid |
|
|
1343
|
+
| `params.type` | string | `image` / `video` / `audio` / `file` |
|
|
1344
|
+
| `params.url` | string | 文件 URL |
|
|
1345
|
+
| `params.data` | string | base64 文件数据 |
|
|
1346
|
+
|
|
1347
|
+
```ts
|
|
1348
|
+
await sendAction({
|
|
1349
|
+
action: 'media.send.user',
|
|
1350
|
+
payload: { UserId: 'USER_OPENID', params: { type: 'image', url: 'https://example.com/a.png' } }
|
|
1351
|
+
});
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
> 对应 SDK:`postRichMediaByUser`。`media.send.channel` 当前不支持(QQ 频道无独立媒体发送接口,请改用 `message.send` 携带图片);`media.upload` 不支持纯上传。
|
|
1355
|
+
|
|
1356
|
+
<a id="api-media-upload-prepare"></a>
|
|
1357
|
+
|
|
1358
|
+
#### media.upload.prepare — 分片上传准备
|
|
1359
|
+
|
|
1360
|
+
| 参数 | 类型 | 说明 |
|
|
1361
|
+
| -------------------- | ----------------- | --------------------------------------------------------- |
|
|
1362
|
+
| `UserId` / `GroupId` | string | 用户 openid 或群 openid(二选一,优先 UserId) |
|
|
1363
|
+
| `params` | UploadPrepareData | `{ file_type, file_name, file_size, md5, sha1, md5_10m }` |
|
|
1364
|
+
|
|
1365
|
+
```ts
|
|
1366
|
+
await sendAction({
|
|
1367
|
+
action: 'media.upload.prepare',
|
|
1368
|
+
payload: { UserId: 'USER_OPENID', params: { file_type: 4, file_name: 'a.zip', file_size: '1024', md5: '...', sha1: '...', md5_10m: '...' } }
|
|
1369
|
+
});
|
|
1370
|
+
```
|
|
1371
|
+
|
|
1372
|
+
> `md5_10m` 为文件前 `10002432` 字节(约 10MB)的 MD5。文件软限制:图片 20MB / 视频 30MB / 语音 20MB / 文件 200MB,超软限制降级为文件类型,超 200MB 报错。
|
|
1373
|
+
|
|
1374
|
+
<a id="api-media-upload-part-finish"></a>
|
|
1375
|
+
|
|
1376
|
+
#### media.upload.part.finish — 分片完成上报
|
|
1377
|
+
|
|
1378
|
+
| 参数 | 类型 | 说明 |
|
|
1379
|
+
| -------------------- | -------------------- | -------------------------------------------- |
|
|
1380
|
+
| `UserId` / `GroupId` | string | 用户 / 群 openid |
|
|
1381
|
+
| `params` | UploadPartFinishData | `{ upload_id, part_index, block_size, md5 }` |
|
|
1382
|
+
|
|
1383
|
+
<a id="api-media-upload-chunked"></a>
|
|
1384
|
+
|
|
1385
|
+
#### media.upload.chunked — 分片上传全流程(推荐)
|
|
1386
|
+
|
|
1387
|
+
自动编排完整流程:`upload_prepare` 拿 `upload_id` 与每片 `presigned_url` → 分片字节 PUT 直传 COS → 每片成功上报 `upload_part_finish` → 全部完成后合并得到 `file_info`。
|
|
1388
|
+
|
|
1389
|
+
| 参数 | 类型 | 说明 |
|
|
1390
|
+
| ----------------------------------- | ---------------- | --------------------------------- |
|
|
1391
|
+
| `UserId` / `GroupId` | string | 用户 / 群 openid |
|
|
1392
|
+
| `params.file` 或 `params.file_path` | string \| Buffer | 本地文件路径或文件内容 |
|
|
1393
|
+
| `params.file_type` | number | 1 图片 / 2 视频 / 3 语音 / 4 文件 |
|
|
1394
|
+
| `params.file_name` | string | 文件名(可选) |
|
|
1395
|
+
| `params.srv_send_msg` | boolean | 是否合并后直接发送(可选) |
|
|
1396
|
+
|
|
1397
|
+
```ts
|
|
1398
|
+
await sendAction({
|
|
1399
|
+
action: 'media.upload.chunked',
|
|
1400
|
+
payload: { GroupId: 'GROUP_OPENID', params: { file: 'D:/a.zip', file_type: 4, file_name: 'a.zip' } }
|
|
1401
|
+
});
|
|
1402
|
+
```
|
|
1403
|
+
|
|
1404
|
+
> 发送消息时 `postRichMediaByUser` / `postRichMediaByGroup` 传入的 `file_data` 超过 10MB 也会自动走此流程。官方文档 `message/send-receive/chunked-upload.md`。
|
|
1405
|
+
|
|
1406
|
+
<a id="api-file-send"></a>
|
|
1407
|
+
|
|
1408
|
+
#### file.send.channel / file.send.user — 发送文件
|
|
1409
|
+
|
|
1410
|
+
```ts
|
|
1411
|
+
// 向群发送
|
|
1412
|
+
await sendAction({
|
|
1413
|
+
action: 'file.send.channel',
|
|
1414
|
+
payload: { ChannelId: 'GROUP_OPENID', params: { file_type: 4, url: 'https://example.com/a.zip', srv_send_msg: true } }
|
|
1415
|
+
});
|
|
1416
|
+
// 向用户发送
|
|
1417
|
+
await sendAction({
|
|
1418
|
+
action: 'file.send.user',
|
|
1419
|
+
payload: { UserId: 'USER_OPENID', params: { file_type: 4, url: 'https://example.com/a.zip' } }
|
|
1420
|
+
});
|
|
1421
|
+
```
|
|
1422
|
+
|
|
1423
|
+
<a id="api-stream-message-send"></a>
|
|
1424
|
+
|
|
1425
|
+
#### stream.message.send — 单聊流式消息
|
|
1426
|
+
|
|
1427
|
+
持续更新同一条回复:首次请求创建,后续请求带 `stream_msg_id` 更新,最后一包 `input_state: 10` 结束。
|
|
1428
|
+
|
|
1429
|
+
| 参数 | 类型 | 说明 |
|
|
1430
|
+
| ----------------------------------- | ------ | --------------------------------------------------- |
|
|
1431
|
+
| `UserId` | string | 用户 openid(仅单聊) |
|
|
1432
|
+
| `params.input_mode` | string | `append` 追加(默认)/ `replace` 替换(传全量正文) |
|
|
1433
|
+
| `params.input_state` | number | `1` 生成中 / `10` 生成结束 |
|
|
1434
|
+
| `params.content_type` | string | `text` / `markdown` |
|
|
1435
|
+
| `params.content_raw` | string | 当前展示内容 |
|
|
1436
|
+
| `params.msg_id` / `params.event_id` | string | 被动回复标识,二选一 |
|
|
1437
|
+
| `params.msg_seq` | number | 去重序号,同一条内保持一致 |
|
|
1438
|
+
| `params.index` | number | 分片序号,从 0 递增 |
|
|
1439
|
+
| `params.stream_msg_id` | string | 首次响应返回的 `id`,后续请求携带 |
|
|
1440
|
+
|
|
1441
|
+
```ts
|
|
1442
|
+
// 首次
|
|
1443
|
+
await sendAction({
|
|
1444
|
+
action: 'stream.message.send',
|
|
1445
|
+
payload: { UserId: 'USER_OPENID', params: { input_mode: 'replace', input_state: 1, content_type: 'text', content_raw: '正在分析...', msg_seq: 1, index: 0 } }
|
|
1446
|
+
});
|
|
1447
|
+
// 后续
|
|
1448
|
+
await sendAction({
|
|
1449
|
+
action: 'stream.message.send',
|
|
1450
|
+
payload: {
|
|
1451
|
+
UserId: 'USER_OPENID',
|
|
1452
|
+
params: {
|
|
1453
|
+
input_mode: 'replace',
|
|
1454
|
+
input_state: 1,
|
|
1455
|
+
content_type: 'text',
|
|
1456
|
+
content_raw: '正在分析,已找到资料...',
|
|
1457
|
+
msg_seq: 1,
|
|
1458
|
+
index: 1,
|
|
1459
|
+
stream_msg_id: 'STREAM_MSG_ID'
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
});
|
|
1463
|
+
// 结束
|
|
1464
|
+
await sendAction({
|
|
1465
|
+
action: 'stream.message.send',
|
|
1466
|
+
payload: {
|
|
1467
|
+
UserId: 'USER_OPENID',
|
|
1468
|
+
params: { input_mode: 'replace', input_state: 10, content_type: 'text', content_raw: '结论:...', msg_seq: 1, index: 2, stream_msg_id: 'STREAM_MSG_ID' }
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
```
|
|
1472
|
+
|
|
1473
|
+
> 官方文档 `message/send-receive/streaming.md`。`replace` 模式不允许修改已下发内容前缀(错误码 40007)。
|
|
1474
|
+
|
|
1475
|
+
---
|
|
1476
|
+
|
|
1477
|
+
### 表情 / 权限 / 交互 API
|
|
1478
|
+
|
|
1479
|
+
<a id="api-reaction-add"></a>
|
|
1480
|
+
|
|
1481
|
+
#### reaction.add / reaction.remove — 表情表态
|
|
1482
|
+
|
|
1483
|
+
| 参数 | 类型 | 说明 |
|
|
1484
|
+
| ----------- | ------ | --------- |
|
|
1485
|
+
| `ChannelId` | string | 子频道 id |
|
|
1486
|
+
| `MessageId` | string | 消息 ID |
|
|
1487
|
+
| `EmojiId` | string | 表情 id |
|
|
1488
|
+
|
|
1489
|
+
```ts
|
|
1490
|
+
await sendAction({ action: 'reaction.add', payload: { ChannelId: 'CHANNEL_ID', MessageId: 'MSG_ID', EmojiId: '106' } });
|
|
1491
|
+
await sendAction({ action: 'reaction.remove', payload: { ChannelId: 'CHANNEL_ID', MessageId: 'MSG_ID', EmojiId: '106' } });
|
|
1492
|
+
```
|
|
1493
|
+
|
|
1494
|
+
> 对应 SDK:`channelsMessagesReactionsPut` / `channelsMessagesReactionsDelete`(type 固定 `1` 系统表情)。
|
|
1495
|
+
|
|
1496
|
+
<a id="api-reaction-list"></a>
|
|
1497
|
+
|
|
1498
|
+
#### reaction.list — 表情表态用户列表
|
|
1499
|
+
|
|
1500
|
+
```ts
|
|
1501
|
+
const results = await sendAction({
|
|
1502
|
+
action: 'reaction.list',
|
|
1503
|
+
payload: { ChannelId: 'CHANNEL_ID', MessageId: 'MSG_ID', EmojiId: '106', params: { limit: 20 } }
|
|
1504
|
+
});
|
|
1505
|
+
```
|
|
1506
|
+
|
|
1507
|
+
> 对应 SDK:`channelsMessagesReactionsUsers`。
|
|
1508
|
+
|
|
1509
|
+
<a id="api-permission"></a>
|
|
1510
|
+
|
|
1511
|
+
#### permission.get / permission.set — 子频道用户权限
|
|
1512
|
+
|
|
1513
|
+
```ts
|
|
1514
|
+
await sendAction({ action: 'permission.get', payload: { ChannelId: 'CHANNEL_ID', UserId: 'USER_ID' } });
|
|
1515
|
+
// allow / deny 为权限位字符串,同一位同时为 1 时表现为删除权限
|
|
1516
|
+
await sendAction({ action: 'permission.set', payload: { ChannelId: 'CHANNEL_ID', UserId: 'USER_ID', params: { allow: '1', deny: '0' } } });
|
|
1517
|
+
```
|
|
1518
|
+
|
|
1519
|
+
> 对应 SDK:`channelsPermissions` / `channelsPermissionsPut`。
|
|
1520
|
+
|
|
1521
|
+
<a id="api-interaction-response"></a>
|
|
1522
|
+
|
|
1523
|
+
#### interaction.response — 互动事件回应
|
|
1524
|
+
|
|
1525
|
+
回应互动事件,解除客户端按钮 loading。收到 `INTERACTION_CREATE` 事件时适配器已自动调用(code 0),一般无需手动调用。
|
|
1526
|
+
|
|
1527
|
+
```ts
|
|
1528
|
+
await sendAction({ action: 'interaction.response', payload: { interaction_id: 'INTERACTION_ID', code: 0 } });
|
|
1529
|
+
```
|
|
1530
|
+
|
|
1531
|
+
> 对应 SDK:`interactionResponse`。互动按钮需 3 秒内响应。
|
|
1532
|
+
|
|
1533
|
+
<a id="api-me-info"></a>
|
|
1534
|
+
|
|
1535
|
+
#### me.info — 机器人自身信息
|
|
1536
|
+
|
|
1537
|
+
```ts
|
|
1538
|
+
const results = await sendAction({ action: 'me.info' });
|
|
1539
|
+
// results[0].data => { UserId, UserName, UserAvatar, IsBot: true, ... }
|
|
1540
|
+
```
|
|
1541
|
+
|
|
1542
|
+
> 对应 SDK:`usersMe`。
|
|
1543
|
+
|
|
1544
|
+
<a id="api-me-guilds"></a>
|
|
1545
|
+
|
|
1546
|
+
#### me.guilds — 机器人频道列表
|
|
1547
|
+
|
|
1548
|
+
```ts
|
|
1549
|
+
const results = await sendAction({ action: 'me.guilds' });
|
|
1550
|
+
```
|
|
1551
|
+
|
|
1552
|
+
> 对应 SDK:`usersMeGuilds`。
|
|
1553
|
+
|
|
1554
|
+
---
|
|
1555
|
+
|
|
1556
|
+
## SDK 方法速查
|
|
1557
|
+
|
|
1558
|
+
通过 `useClient(event)` 可直接调用 `QQBotAPI` 全部方法(跳过 Action 层,返回原始接口数据):
|
|
1559
|
+
|
|
1560
|
+
```ts
|
|
1561
|
+
// src/response/info.ts
|
|
1562
|
+
import { useClient, useEvent, useMessage, Format } from 'alemonjs';
|
|
1563
|
+
import { API, platform } from '@alemonjs/qq-bot';
|
|
1564
|
+
|
|
1565
|
+
export default async () => {
|
|
1566
|
+
const [event] = useEvent();
|
|
1567
|
+
const [message] = useMessage();
|
|
1568
|
+
|
|
1569
|
+
if (event.current.Platform !== platform) return;
|
|
1570
|
+
|
|
1571
|
+
const [client] = useClient(API);
|
|
1572
|
+
const res = await client.groupsInfo('GROUP_OPENID'); // 原始返回
|
|
1573
|
+
|
|
1574
|
+
await message.send({ format: Format.create().addText(JSON.stringify(res)) });
|
|
1575
|
+
};
|
|
1576
|
+
```
|
|
1577
|
+
|
|
1578
|
+
| 分类 | SDK 方法 | 说明 |
|
|
1579
|
+
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
|
1580
|
+
| 鉴权 | `getAuthentication` / `gateway` | 获取 access_token / 网关地址 |
|
|
1581
|
+
| 单聊 | `usersOpenMessages` | 发送单聊消息(msg_type 0/2/3/6/7) |
|
|
1582
|
+
| 单聊 | `userMessageDelete` | 撤回单聊消息 |
|
|
1583
|
+
| 单聊 | `postRichMediaByUser` | 发送单聊富媒体(超 10MB 自动分片) |
|
|
1584
|
+
| 单聊 | `streamMessages` | 流式消息 |
|
|
1585
|
+
| 群聊 | `groupOpenMessages` | 发送群聊消息 |
|
|
1586
|
+
| 群聊 | `groupMessageDelete` | 撤回群消息 |
|
|
1587
|
+
| 群聊 | `postRichMediaByGroup` | 发送群聊富媒体 |
|
|
1588
|
+
| 群管理 | `groupsInfo` / `groupsBotState` / `groupsMembersMessage` | 群信息 / 状态 / 成员 |
|
|
1589
|
+
| 群管理 | `groupsJoinRequestList` / `groupsApprovalJoinRequest` | 入群申请列表 / 审批 |
|
|
1590
|
+
| 群管理 | `groupsRestrictChatSetting` / `groupsRestrictChatSettingPost` | 禁言查询 / 设置 |
|
|
1591
|
+
| 群管理 | `groupsJoinApprovalStrategies` / `groupsJoinApprovalStrategyCreate` / `groupsJoinApprovalStrategyPatch` / `groupsJoinApprovalStrategyDelete` / `groupsJoinApprovalStrategyExecute` / `groupsJoinApprovalStrategyWhitelistUsers` | 入群自动审批策略 |
|
|
1592
|
+
| 分片上传 | `usersUploadPrepare` / `groupUploadPrepare` / `usersUploadPartFinish` / `groupUploadPartFinish` / `uploadPartDirect` | 分片上传各环节 |
|
|
1593
|
+
| 频道 | `guilds` / `guildsChannels` / `channels` / `guildsChannelsCreate` / `guildsChannelsUpdate` / `guildsChannelsdelete` | 频道与子频道管理 |
|
|
1594
|
+
| 频道消息 | `channelsMessages` / `channelsMessagesById` / `channelsMessagesDelete` / `dmsMessages` / `dmsMessageDelete` | 频道消息收发 |
|
|
1595
|
+
| 成员 | `guildsMembers` / `guildsMembersMessage` / `guildsMembersDelete` / `guildsRolesMembers` | 成员管理 |
|
|
1596
|
+
| 禁言 | `guildsMuteAll` / `guildsMute` / `guildsMemberMute` | 全员 / 批量 / 成员禁言 |
|
|
1597
|
+
| 角色 | `guildsRoles` / `guildsRolesPost` / `guildsRolesPatch` / `guildsRolesDelete` / `guildsRolesMembersPut` / `guildsRolesMembersDelete` | 身份组管理 |
|
|
1598
|
+
| 权限 | `channelsPermissions` / `channelsPermissionsPut` | 子频道权限 |
|
|
1599
|
+
| 表情 | `channelsMessagesReactionsPut` / `channelsMessagesReactionsDelete` / `channelsMessagesReactionsUsers` | 表情表态 |
|
|
1600
|
+
| 公告 | `guildsAnnounces` / `guildsAnnouncesDelete` | 频道公告 |
|
|
1601
|
+
| 精华 | `channelsPinsPut` / `channelsPinsDelete` / `channelsPins` | 精华消息 |
|
|
1602
|
+
| 日程 | `channelsSchedules` / `channelsSchedulesSchedule` / `channelsSchedulesPost` / `channelsSchedulesSchedulePatch` / `channelsSchedulesScheduleDelete` | 频道日程 |
|
|
1603
|
+
| 音频 | `channelsAudioPost` / `channelsMicPut` / `channelsMicDelete` | 音频控制 / 上下麦 |
|
|
1604
|
+
| 帖子 | `channelsThreads` / `channelsThreadsThread` / `channelsThreadsPut` / `channelsThreadsDelete` | 论坛帖子 |
|
|
1605
|
+
| 其他 | `usersMe` / `usersMeGuilds` / `usersMeDms` / `interactionResponse` | 机器人信息 / 私信会话 / 互动回应 |
|
|
1606
|
+
|
|
1607
|
+
---
|
|
1608
|
+
|
|
1609
|
+
## 事件支持
|
|
1610
|
+
|
|
1611
|
+
适配器将 QQ 事件转换为 alemonjs 标准事件,开发时通过 `Router` DSL / `useEvent` 订阅。
|
|
1612
|
+
|
|
1613
|
+
各事件对应的数据结构与读取方法见 [事件数据结构](#事件数据结构),快捷跳转:
|
|
1614
|
+
|
|
1615
|
+
| 分类 | 跳转 |
|
|
1616
|
+
| ----------------------------------- | ----------------------------------------- |
|
|
1617
|
+
| 消息事件(群 / 单聊 / 频道 / 私信) | [原始数据结构](#event-data-message) |
|
|
1618
|
+
| 互动按钮 | [互动按钮事件](#event-data-interaction) |
|
|
1619
|
+
| 入群申请 / 成员 / 好友 | [群管理·成员·好友事件](#event-data-group) |
|
|
1620
|
+
| 频道事件 | [频道事件](#event-data-guild) |
|
|
1621
|
+
| 字段读取 / @ 提及 / 按场景回复 | [数据使用方法](#event-data-usage) |
|
|
1622
|
+
|
|
1623
|
+
| QQ 事件 | 标准事件 | 场景 |
|
|
1624
|
+
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------ | -------------------------- |
|
|
1625
|
+
| `GROUP_MESSAGE_CREATE` / `GROUP_AT_MESSAGE_CREATE` | `message.create` | 群消息 / 群 @ |
|
|
1626
|
+
| `AT_MESSAGE_CREATE` / `MESSAGE_CREATE` | `message.create` | 频道 @ / 私域全量消息 |
|
|
1627
|
+
| `C2C_MESSAGE_CREATE` | `private.message.create` | 单聊 |
|
|
1628
|
+
| `DIRECT_MESSAGE_CREATE` | `private.message.create` | 频道私信 |
|
|
1629
|
+
| `INTERACTION_CREATE` | `interaction.create` / `private.interaction.create` | 互动按钮(群/单聊/频道) |
|
|
1630
|
+
| `MESSAGE_DELETE` / `PUBLIC_MESSAGE_DELETE` | `message.delete` | 频道消息撤回 |
|
|
1631
|
+
| `DIRECT_MESSAGE_DELETE` | `private.message.delete` | 私信撤回 |
|
|
1632
|
+
| `GROUP_MEMBER_ADD` / `GROUP_MEMBER_REMOVE` | `member.add` / `member.remove` | 群成员变动 |
|
|
1633
|
+
| `GUILD_MEMBER_ADD` / `GUILD_MEMBER_REMOVE` / `GUILD_MEMBER_UPDATE` | `member.add` / `member.remove` / `member.update` | 频道成员变动 |
|
|
1634
|
+
| `GROUP_ADD_ROBOT` / `GROUP_DEL_ROBOT` | `guild.join` / `guild.exit` | 机器人入群 / 退群 |
|
|
1635
|
+
| `GUILD_CREATE` / `GUILD_DELETE` / `GUILD_UPDATE` | `guild.join` / `guild.exit` / `guild.update` | 机器人加入/退出/频道更新 |
|
|
1636
|
+
| `CHANNEL_CREATE` / `CHANNEL_DELETE` / `CHANNEL_UPDATE` | `channel.create` / `channel.delete` / `channel.update` | 子频道变动 |
|
|
1637
|
+
| `MESSAGE_REACTION_ADD` / `MESSAGE_REACTION_REMOVE` | `message.reaction.add` / `message.reaction.remove` | 表情表态 |
|
|
1638
|
+
| `FRIEND_ADD` / `FRIEND_DEL` | `private.friend.add` / `private.friend.remove` | 好友添加 / 删除 |
|
|
1639
|
+
| `GROUP_JOIN_REQUEST` | `notice.create` | 用户申请加群(需群管理员) |
|
|
1640
|
+
| `GROUP_MSG_RECEIVE` / `GROUP_MSG_REJECT` / `MESSAGE_AUDIT_PASS` / `MESSAGE_AUDIT_REJECT` | `notice.create` | 群推送开关 / 消息审核 |
|
|
1641
|
+
| `C2C_MSG_RECEIVE` / `C2C_MSG_REJECT` | `private.notice.create` | 单聊推送开关 |
|
|
1642
|
+
| `ERROR` | - | 连接 / 事件处理错误 |
|
|
1643
|
+
|
|
1644
|
+
示例:监听入群申请事件
|
|
1645
|
+
|
|
1646
|
+
```ts
|
|
1647
|
+
// src/index.ts —— 注册纯事件订阅(非命令,使用 router.res)
|
|
1648
|
+
import { Router, defineChildren } from 'alemonjs';
|
|
1649
|
+
|
|
1650
|
+
const router = Router.create({ events: ['notice.create'] });
|
|
1651
|
+
|
|
1652
|
+
router.res({ events: ['notice.create'] }, () => import('./response/group-join-request'));
|
|
1653
|
+
|
|
1654
|
+
export default defineChildren({
|
|
1655
|
+
register() {
|
|
1656
|
+
return { responseRouter: router.define };
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
```
|
|
1660
|
+
|
|
1661
|
+
```ts
|
|
1662
|
+
// src/response/group-join-request.ts
|
|
1663
|
+
import { useEvent, sendAction } from 'alemonjs';
|
|
1664
|
+
|
|
1665
|
+
export default async () => {
|
|
1666
|
+
const [event] = useEvent();
|
|
1667
|
+
// value 为原始 QQ 事件:携带 group_openid / join_request_id / member_openid / username 等
|
|
1668
|
+
const value = event.value;
|
|
1669
|
+
|
|
1670
|
+
// notice.create 下通过 _tag 区分具体事件
|
|
1671
|
+
if (event.current._tag === 'GROUP_JOIN_REQUEST') {
|
|
1672
|
+
// 结合 group.joinRequest.approve 审批(透传事件,群 openid / 申请人 openid 自动推断)
|
|
1673
|
+
await sendAction({
|
|
1674
|
+
action: 'group.joinRequest.approve',
|
|
1675
|
+
payload: {
|
|
1676
|
+
event: event.current,
|
|
1677
|
+
params: { op: 'approve', joinRequestId: value.join_request_id }
|
|
1678
|
+
}
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
};
|
|
1682
|
+
```
|
|
1683
|
+
|
|
1684
|
+
---
|
|
1685
|
+
|
|
1686
|
+
## 事件数据结构
|
|
1687
|
+
|
|
1688
|
+
事件经过适配器转换为 alemonjs 标准事件后,统一包含两层数据:**标准字段**(框架约定,各平台一致)与 **`value` 原始数据**(QQ 平台推送的原样 payload)。以下结构均依据适配器 `src/register.ts` 与 `src/message/*` 类型定义整理。
|
|
1689
|
+
|
|
1690
|
+
### 统一标准字段
|
|
1691
|
+
|
|
1692
|
+
| 字段 | 类型 | 说明 |
|
|
1693
|
+
| ------------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
|
|
1694
|
+
| `name` | string | 标准事件名:`message.create`、`private.message.create`、`interaction.create`、`member.add`、`notice.create` 等 |
|
|
1695
|
+
| `value` | object | **原始 QQ 事件**(WebSocket 推送的 `d` 数据),各场景结构见下 |
|
|
1696
|
+
| `_tag` | string | 来源 QQ 事件名,如 `GROUP_AT_MESSAGE_CREATE`、`GROUP_JOIN_REQUEST`(`.add({ tag })` 存储为 `_tag`) |
|
|
1697
|
+
| `Platform` | string | `'qq-bot'` |
|
|
1698
|
+
| `BotId` | string | 机器人 app_id |
|
|
1699
|
+
| `GuildId` | string | 群 openid 或频道 id(群场景即群 openid) |
|
|
1700
|
+
| `SpaceId` | string | 空间标识:`GROUP:{group_openid}` / `GUILD:{channel_id}` |
|
|
1701
|
+
| `ChannelId` | string | 群 openid 或子频道 id |
|
|
1702
|
+
| `UserId` / `UserKey` | string | 发送者 ID 与框架生成的用户 Key |
|
|
1703
|
+
| `UserName` / `UserAvatar` | string | 昵称 / 头像 URL |
|
|
1704
|
+
| `IsMaster` / `IsBot` | boolean | 是否主人 / 是否机器人 |
|
|
1705
|
+
| `MessageId` | string | 消息 ID(入群申请为拼接 ID) |
|
|
1706
|
+
| `MessageText` | string | 消息文本(已去除 @ 占位符) |
|
|
1707
|
+
| `OpenId` | string | 回复标识:`C2C:{user_openid}` / `DIRECT:{guild_id}` |
|
|
1708
|
+
| `IsAtMe` / `IsPrivate` | boolean | 是否 @ 机器人 / 是否私聊 |
|
|
1709
|
+
| `Timestamp` | number | 框架自动注入的事件创建时间 |
|
|
1710
|
+
|
|
1711
|
+
### 场景标识规则
|
|
1712
|
+
|
|
1713
|
+
| 场景 | `SpaceId` | `OpenId` | `_tag` 示例 |
|
|
1714
|
+
| -------------- | ---------------------- | --------------------------- | -------------------------------------------------- |
|
|
1715
|
+
| 群聊 | `GROUP:{group_openid}` | `C2C:{member_openid}` | `GROUP_MESSAGE_CREATE` / `GROUP_AT_MESSAGE_CREATE` |
|
|
1716
|
+
| 单聊(C2C) | 无 | `C2C:{user_openid}` | `C2C_MESSAGE_CREATE` |
|
|
1717
|
+
| 频道 | `GUILD:{channel_id}` | `DIRECT:{guild_id}` | `AT_MESSAGE_CREATE` / `MESSAGE_CREATE` |
|
|
1718
|
+
| 频道私信 | 无 | `DIRECT:{guild_id}` | `DIRECT_MESSAGE_CREATE` |
|
|
1719
|
+
| 互动按钮(群) | `GROUP:{group_openid}` | `C2C:{group_member_openid}` | `INTERACTION_CREATE_GROUP` |
|
|
1720
|
+
|
|
1721
|
+
<a id="event-data-message"></a>
|
|
1722
|
+
|
|
1723
|
+
### 消息事件原始数据(`value`)
|
|
1724
|
+
|
|
1725
|
+
#### 群消息(`GROUP_MESSAGE_CREATE` / `GROUP_AT_MESSAGE_CREATE`)
|
|
1726
|
+
|
|
1727
|
+
```jsonc
|
|
1728
|
+
{
|
|
1729
|
+
"id": "消息ID",
|
|
1730
|
+
"content": "消息文本(含 @ 占位符)",
|
|
1731
|
+
"group_openid": "群 OpenID",
|
|
1732
|
+
"group_id": "QQ 群号",
|
|
1733
|
+
"timestamp": "2026-08-05T14:19:09+08:00",
|
|
1734
|
+
"message_scene": { "ext": [], "source": "" },
|
|
1735
|
+
"message_type": 0,
|
|
1736
|
+
"author": {
|
|
1737
|
+
"id": "发送者 ID",
|
|
1738
|
+
"member_openid": "群成员 OpenID",
|
|
1739
|
+
"union_openid": "统一标识",
|
|
1740
|
+
"username": "昵称",
|
|
1741
|
+
"bot": false
|
|
1742
|
+
},
|
|
1743
|
+
"mentions": [{ "id": "@目标 ID", "username": "昵称", "is_you": true, "member_openid": "..." }]
|
|
1744
|
+
}
|
|
1745
|
+
```
|
|
1746
|
+
|
|
1747
|
+
> 两个事件结构一致,区别仅在 `_tag` 与 `IsAtMe`:`GROUP_AT_MESSAGE_CREATE` 为群内 @ 机器人触发,`IsAtMe: true`;`GROUP_MESSAGE_CREATE` 为群内任意消息(需订阅全量推送)。
|
|
1748
|
+
|
|
1749
|
+
#### 单聊消息(`C2C_MESSAGE_CREATE` → `private.message.create`)
|
|
1750
|
+
|
|
1751
|
+
```jsonc
|
|
1752
|
+
{
|
|
1753
|
+
"id": "消息ID",
|
|
1754
|
+
"content": "消息文本",
|
|
1755
|
+
"timestamp": "2026-08-05T14:19:09+08:00",
|
|
1756
|
+
"author": { "id": "发送者 ID", "user_openid": "用户 OpenID", "username": "昵称" }
|
|
1757
|
+
}
|
|
1758
|
+
```
|
|
1759
|
+
|
|
1760
|
+
#### 频道消息(`AT_MESSAGE_CREATE` 公域 @ / `MESSAGE_CREATE` 私域全量 → `message.create`)
|
|
1761
|
+
|
|
1762
|
+
```jsonc
|
|
1763
|
+
{
|
|
1764
|
+
"id": "消息ID",
|
|
1765
|
+
"content": "消息文本(含 @ 占位符)",
|
|
1766
|
+
"guild_id": "频道 ID",
|
|
1767
|
+
"channel_id": "子频道 ID",
|
|
1768
|
+
"seq": 1,
|
|
1769
|
+
"seq_in_channel": "1",
|
|
1770
|
+
"timestamp": "2026-08-05T14:19:09+08:00",
|
|
1771
|
+
"author": { "id": "用户 ID", "username": "昵称", "avatar": "头像", "bot": false },
|
|
1772
|
+
"mentions": [{ "id": "@目标 ID", "username": "昵称", "avatar": "头像", "bot": false }],
|
|
1773
|
+
"member": { "joined_at": "", "nick": "", "roles": [] },
|
|
1774
|
+
"attachments": [{ "id": "", "url": "", "content_type": "", "filename": "", "size": 0, "width": 0, "height": 0 }]
|
|
1775
|
+
}
|
|
1776
|
+
```
|
|
1777
|
+
|
|
1778
|
+
#### 频道私信(`DIRECT_MESSAGE_CREATE` → `private.message.create`)
|
|
1779
|
+
|
|
1780
|
+
```jsonc
|
|
1781
|
+
{
|
|
1782
|
+
"id": "消息ID",
|
|
1783
|
+
"content": "消息文本",
|
|
1784
|
+
"guild_id": "私信会话 ID",
|
|
1785
|
+
"src_guild_id": "来源频道 ID",
|
|
1786
|
+
"channel_id": "子频道 ID",
|
|
1787
|
+
"direct_message": true,
|
|
1788
|
+
"timestamp": "2026-08-05T14:19:09+08:00",
|
|
1789
|
+
"author": { "id": "用户 ID", "username": "昵称", "avatar": "头像", "bot": false },
|
|
1790
|
+
"member": { "joined_at": "" },
|
|
1791
|
+
"attachments": []
|
|
1792
|
+
}
|
|
1793
|
+
```
|
|
1794
|
+
|
|
1795
|
+
<a id="event-data-interaction"></a>
|
|
1796
|
+
|
|
1797
|
+
### 互动按钮事件(`INTERACTION_CREATE`)
|
|
1798
|
+
|
|
1799
|
+
点击按钮触发,`data.resolved.button_data` 即按钮构造时传入的 `data`。分三种场景(`scene` 字段区分):
|
|
1800
|
+
|
|
1801
|
+
| `scene` | 标准事件 | 关键字段 |
|
|
1802
|
+
| --------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
1803
|
+
| `'group'` | `interaction.create`(`_tag: INTERACTION_CREATE_GROUP`) | `group_openid`、`group_member_openid`、`data.resolved.button_data` |
|
|
1804
|
+
| `'c2c'` | `private.interaction.create`(`_tag: INTERACTION_CREATE_C2C`) | `user_openid`、`data.resolved.button_data` |
|
|
1805
|
+
| `'guild'` | `interaction.create`(`_tag: INTERACTION_CREATE_GUILD`) | `guild_id`、`channel_id`、`data.resolved.user_id`、`data.resolved.message_id`、`data.resolved.button_data` |
|
|
1806
|
+
|
|
1807
|
+
```jsonc
|
|
1808
|
+
{
|
|
1809
|
+
"id": "互动 ID",
|
|
1810
|
+
"scene": "group", // group | c2c | guild
|
|
1811
|
+
"chat_type": 1, // 1 群 / 2 私聊 / 0 频道
|
|
1812
|
+
"type": 11, // 按钮交互
|
|
1813
|
+
"version": 1,
|
|
1814
|
+
"group_openid": "群 OpenID",
|
|
1815
|
+
"group_member_openid": "点击者 OpenID",
|
|
1816
|
+
"data": {
|
|
1817
|
+
"type": 11,
|
|
1818
|
+
"resolved": { "button_data": "按钮 data", "button_id": 1 }
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
```
|
|
1822
|
+
|
|
1823
|
+
> 适配器收到互动事件后会自动 `interactionResponse` 回应(解除按钮 loading),无需手动处理。
|
|
1824
|
+
|
|
1825
|
+
<a id="event-data-group"></a>
|
|
1826
|
+
|
|
1827
|
+
### 群管理 / 成员 / 好友事件
|
|
1828
|
+
|
|
1829
|
+
| 事件 | 标准事件 | 原始数据关键字段 |
|
|
1830
|
+
| ----------------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
1831
|
+
| `GROUP_JOIN_REQUEST`(用户申请加群) | `notice.create` | `group_openid`、`join_request_id`、`member_openid`、`username`、`apply_at`、`apply_source`、`verify_info`、`auto_approved` |
|
|
1832
|
+
| `GROUP_MEMBER_ADD` / `GROUP_MEMBER_REMOVE`(成员进/退群) | `member.add` / `member.remove` | `group_openid`、`member_openid`、`op_member_openid`、`username` |
|
|
1833
|
+
| `GROUP_ADD_ROBOT` / `GROUP_DEL_ROBOT`(机器人进/退群) | `guild.join` / `guild.exit` | `group_openid`、`op_member_openid`、`timestamp` |
|
|
1834
|
+
| `FRIEND_ADD` / `FRIEND_DEL`(好友添加/删除) | `private.friend.add` / `private.friend.remove` | `openid`、`timestamp` |
|
|
1835
|
+
| `GROUP_MSG_RECEIVE` / `GROUP_MSG_REJECT`(群推送开关) | `notice.create` | `group_openid`、`op_member_openid`、`timestamp` |
|
|
1836
|
+
| `MESSAGE_AUDIT_PASS` / `MESSAGE_AUDIT_REJECT`(群消息审核) | `notice.create` | `group_openid`、`message_id`、`audit_time` |
|
|
1837
|
+
|
|
1838
|
+
入群申请 `GROUP_JOIN_REQUEST` 完整字段:
|
|
1839
|
+
|
|
1840
|
+
```jsonc
|
|
1841
|
+
{
|
|
1842
|
+
"group_openid": "群 OpenID",
|
|
1843
|
+
"join_request_id": "申请 ID(审批回传)",
|
|
1844
|
+
"member_openid": "申请人 OpenID",
|
|
1845
|
+
"username": "申请人昵称",
|
|
1846
|
+
"apply_at": "2026-08-05T17:32:52+08:00",
|
|
1847
|
+
"apply_source": "self_apply", // self_apply 主动 / invited 被邀请
|
|
1848
|
+
"risk_tips": "",
|
|
1849
|
+
"union_openid": "",
|
|
1850
|
+
"invited_by": "",
|
|
1851
|
+
"bot": false,
|
|
1852
|
+
"verify_info": { "method": "verify_message", "verify_message": "验证消息", "review_qa_list": [] },
|
|
1853
|
+
"auto_approved": { "strategy_id": "st_xxx" } // 仅自动审批通过事件携带
|
|
1854
|
+
}
|
|
1855
|
+
```
|
|
1856
|
+
|
|
1857
|
+
<a id="event-data-guild"></a>
|
|
1858
|
+
|
|
1859
|
+
### 频道事件
|
|
1860
|
+
|
|
1861
|
+
| 事件 | 标准事件 | 原始数据关键字段 |
|
|
1862
|
+
| ------------------------------------------------------------------ | ------------------------------------------------------ | --------------------------------------------------------------------------- |
|
|
1863
|
+
| `GUILD_CREATE`(机器人加入频道) | `guild.join` | `id`、`name`、`op_user_id`、`owner_id`、`member_count`、`max_members` |
|
|
1864
|
+
| `GUILD_DELETE`(机器人退出频道) | `guild.exit` | `id`、`op_user_id` |
|
|
1865
|
+
| `GUILD_UPDATE`(频道信息更新) | `guild.update` | `id`、`name` |
|
|
1866
|
+
| `GUILD_MEMBER_ADD` / `GUILD_MEMBER_REMOVE` / `GUILD_MEMBER_UPDATE` | `member.add` / `member.remove` / `member.update` | `guild_id`、`user{id, username, avatar, bot}`、`nick`、`roles`、`joined_at` |
|
|
1867
|
+
| `CHANNEL_CREATE` / `CHANNEL_DELETE` / `CHANNEL_UPDATE` | `channel.create` / `channel.delete` / `channel.update` | `guild_id`、`id`、`name`、`type`、`parent_id` |
|
|
1868
|
+
| `MESSAGE_REACTION_ADD` / `MESSAGE_REACTION_REMOVE` | `message.reaction.add` / `message.reaction.remove` | `guild_id`、`channel_id`、`target{id}`、`emoji{id, type}`、`user_id` |
|
|
1869
|
+
|
|
1870
|
+
<a id="event-data-usage"></a>
|
|
1871
|
+
|
|
1872
|
+
### 数据使用方法
|
|
1873
|
+
|
|
1874
|
+
#### 读取标准字段与原始数据
|
|
1875
|
+
|
|
1876
|
+
```ts
|
|
1877
|
+
// src/response/read-data.ts —— 群消息事件处理
|
|
1878
|
+
import { useEvent, useMessage, Format } from 'alemonjs';
|
|
1879
|
+
|
|
1880
|
+
export default async () => {
|
|
1881
|
+
const [event] = useEvent();
|
|
1882
|
+
const [message] = useMessage();
|
|
1883
|
+
|
|
1884
|
+
const current = event.current; // 标准字段
|
|
1885
|
+
const value = event.value; // 原始 QQ 数据
|
|
1886
|
+
|
|
1887
|
+
const tag = current._tag; // 'GROUP_AT_MESSAGE_CREATE' | 'GROUP_MESSAGE_CREATE' | ...
|
|
1888
|
+
const groupId = current.GuildId; // 群 openid
|
|
1889
|
+
const userId = current.UserId; // 发送者 id
|
|
1890
|
+
const msg = current.MessageText; // 文本(已去 @)
|
|
1891
|
+
const isMaster = current.IsMaster; // 是否主人
|
|
1892
|
+
|
|
1893
|
+
// 原始数据补充信息(标准字段未覆盖的)
|
|
1894
|
+
const groupNo = value.group_id; // QQ 群号
|
|
1895
|
+
const memberOpenId = value.author.member_openid;
|
|
1896
|
+
const atMe = value.mentions?.some(m => m.is_you); // 是否 @ 机器人
|
|
1897
|
+
|
|
1898
|
+
if (isMaster) {
|
|
1899
|
+
await message.send({ format: Format.create().addText(`主人说:${msg}`) });
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
```
|
|
1903
|
+
|
|
1904
|
+
#### 区分子事件类型
|
|
1905
|
+
|
|
1906
|
+
`notice.create` / `message.create` 等标准事件可能由多个 QQ 事件转换而来,通过 `_tag` 区分:
|
|
1907
|
+
|
|
1908
|
+
```ts
|
|
1909
|
+
const tag = event.current._tag;
|
|
1910
|
+
|
|
1911
|
+
if (tag === 'GROUP_JOIN_REQUEST') {
|
|
1912
|
+
// 入群申请
|
|
1913
|
+
} else if (tag === 'MESSAGE_AUDIT_PASS') {
|
|
1914
|
+
// 群消息审核通过
|
|
1915
|
+
}
|
|
1916
|
+
```
|
|
1917
|
+
|
|
1918
|
+
#### 读取 @ 提及用户
|
|
1919
|
+
|
|
1920
|
+
```ts
|
|
1921
|
+
import { useMention } from 'alemonjs';
|
|
1922
|
+
|
|
1923
|
+
export default async () => {
|
|
1924
|
+
const [mention] = useMention();
|
|
1925
|
+
|
|
1926
|
+
const all = await mention.find(); // 全部提及
|
|
1927
|
+
const one = await mention.findOne({ IsBot: false }); // 第一个非机器人
|
|
1928
|
+
// each => { UserId, UserKey, UserName, IsMaster, IsBot }
|
|
1929
|
+
};
|
|
1930
|
+
```
|
|
1931
|
+
|
|
1932
|
+
#### 按场景回复
|
|
1933
|
+
|
|
1934
|
+
标准字段里的 `SpaceId` / `OpenId` 可直接用于主动发送(`MessageDirect`),无需手动解析:
|
|
1935
|
+
|
|
1936
|
+
```ts
|
|
1937
|
+
// 群消息 → 回同群
|
|
1938
|
+
await MessageDirect.create().sendToChannel({
|
|
1939
|
+
SpaceId: event.current.SpaceId, // 'GROUP:{group_openid}'
|
|
1940
|
+
format: Format.create().addText('收到')
|
|
1941
|
+
});
|
|
1942
|
+
// 单聊消息 → 回同用户
|
|
1943
|
+
await MessageDirect.create().sendToUser({
|
|
1944
|
+
OpenID: event.current.OpenId, // 'C2C:{user_openid}'
|
|
1945
|
+
format: Format.create().addText('收到')
|
|
1946
|
+
});
|
|
1947
|
+
```
|
|
1948
|
+
|
|
1949
|
+
---
|
|
1950
|
+
|
|
1951
|
+
## 常见问题
|
|
1952
|
+
|
|
1953
|
+
**1. Webhook 模式无法收到事件?**
|
|
1954
|
+
|
|
1955
|
+
- 需公网 ip/域名,且官方平台配置的回调地址与 `route`/`port` 一致;
|
|
1956
|
+
- Webhook 启用后官方会禁用 WebSocket 模式。
|
|
1957
|
+
|
|
1958
|
+
**2. 发送 Markdown 报错 / 不渲染?**
|
|
1959
|
+
|
|
1960
|
+
- 需要平台开通 Markdown 消息权限;未开通可在配置中开启 `markdownToText: true` 降级为纯文本。
|
|
1961
|
+
|
|
1962
|
+
**3. 图片 / 视频 / 语音 / 文件发送失败?**
|
|
1963
|
+
|
|
1964
|
+
- 富媒体有软限制(图片 20MB / 视频 30MB / 语音 20MB / 文件 200MB),超软限制自动降级为文件类型,超 200MB 硬限制报错;
|
|
1965
|
+
- 大于 10MB 的文件自动走分片上传流程(`media.upload.chunked`);
|
|
1966
|
+
- 文件格式需符合要求(视频 mp4、语音 silk);文件发送能力以平台开放为准,失败时会降级为 `[附件]` 文本。
|
|
1967
|
+
|
|
1968
|
+
**4. `member.ban` 与 `member.mute` 的区别?**
|
|
1969
|
+
|
|
1970
|
+
- QQ 频道无封禁能力,`member.ban` 使用禁言实现(默认 7 天);`member.mute` 按传入 `duration` 秒数禁言。
|
|
1971
|
+
|
|
1972
|
+
**5. 群管理接口报权限错误?**
|
|
1973
|
+
|
|
1974
|
+
- 机器人需拥有对应群的**管理员身份**;入群自动审批策略最多创建 20 个。
|
|
1975
|
+
|
|
1976
|
+
**6. `group.joinRequest.approve` 需要哪些参数?**
|
|
1977
|
+
|
|
1978
|
+
- 推荐在 `payload` 中透传 `event`(来源事件对象),群 openid / 申请人 openid 由适配器自动推断(群消息事件中群 openid 位于 `ChannelId`、成员 openid 位于 `UserId`);也可用 `payload.ChannelId` / `payload.UserId` 或 `params.groupOpenId` / `params.memberOpenId` 显式指定(优先级更高);
|
|
1979
|
+
- `joinRequestId` 从 `GROUP_JOIN_REQUEST` 事件(`notice.create`)携带的 `join_request_id` 获取。
|