@tencent-connect/openclaw-qqbot 1.6.3 → 1.6.4-alpha.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/README.md +1 -1
- package/README.zh.md +1 -1
- package/clawdbot.plugin.json +1 -1
- package/dist/index.js +2 -0
- package/dist/src/api.d.ts +9 -0
- package/dist/src/api.js +11 -0
- package/dist/src/gateway.js +15 -13
- package/dist/src/slash-commands.js +207 -24
- package/dist/src/tools/channel.d.ts +16 -0
- package/dist/src/tools/channel.js +234 -0
- package/dist/src/types.d.ts +6 -0
- package/index.ts +2 -0
- package/moltbot.plugin.json +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/test-sendmedia.ts +116 -0
- package/scripts/upgrade-via-npm.sh +17 -6
- package/skills/qqbot-channel/SKILL.md +263 -0
- package/skills/qqbot-channel/references/api_references.md +521 -0
- package/src/api.ts +17 -0
- package/src/gateway.ts +14 -14
- package/src/openclaw-plugin-sdk.d.ts +39 -0
- package/src/slash-commands.ts +231 -23
- package/src/tools/channel.ts +281 -0
- package/src/types.ts +6 -0
package/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
|
3
3
|
|
|
4
4
|
import { qqbotPlugin } from "./src/channel.js";
|
|
5
5
|
import { setQQBotRuntime } from "./src/runtime.js";
|
|
6
|
+
import { registerChannelTool } from "./src/tools/channel.js";
|
|
6
7
|
|
|
7
8
|
const plugin = {
|
|
8
9
|
id: "openclaw-qqbot",
|
|
@@ -12,6 +13,7 @@ const plugin = {
|
|
|
12
13
|
register(api: OpenClawPluginApi) {
|
|
13
14
|
setQQBotRuntime(api.runtime);
|
|
14
15
|
api.registerChannel({ plugin: qqbotPlugin });
|
|
16
|
+
registerChannelTool(api);
|
|
15
17
|
},
|
|
16
18
|
};
|
|
17
19
|
|
package/moltbot.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "OpenClaw QQ Bot",
|
|
4
4
|
"description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
|
|
5
5
|
"channels": ["qqbot"],
|
|
6
|
-
"skills": ["skills/qqbot-cron", "skills/qqbot-media"],
|
|
6
|
+
"skills": ["skills/qqbot-channel", "skills/qqbot-cron", "skills/qqbot-media"],
|
|
7
7
|
"capabilities": {
|
|
8
8
|
"proactiveMessaging": true,
|
|
9
9
|
"cronJobs": true
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "OpenClaw QQ Bot",
|
|
4
4
|
"description": "QQ Bot channel plugin with message support, cron jobs, and proactive messaging",
|
|
5
5
|
"channels": ["qqbot"],
|
|
6
|
-
"skills": ["skills/qqbot-cron", "skills/qqbot-media"],
|
|
6
|
+
"skills": ["skills/qqbot-channel", "skills/qqbot-cron", "skills/qqbot-media"],
|
|
7
7
|
"capabilities": {
|
|
8
8
|
"proactiveMessaging": true,
|
|
9
9
|
"cronJobs": true
|
package/package.json
CHANGED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 测试 sendMedia 路径:语音、视频、文件
|
|
3
|
+
* 用法:npx tsx scripts/test-sendmedia.ts <openid>
|
|
4
|
+
*/
|
|
5
|
+
import { sendMedia } from "../src/outbound.js";
|
|
6
|
+
import type { ResolvedQQBotAccount } from "../src/types.js";
|
|
7
|
+
import * as fs from "node:fs";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
|
|
10
|
+
const LOG_FILE = "/tmp/test-sendmedia-output.log";
|
|
11
|
+
|
|
12
|
+
function log(msg: string) {
|
|
13
|
+
const line = msg + "\n";
|
|
14
|
+
process.stdout.write(line);
|
|
15
|
+
fs.appendFileSync(LOG_FILE, line);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeAppId(raw: unknown): string {
|
|
19
|
+
if (raw === null || raw === undefined) return "";
|
|
20
|
+
return String(raw).trim();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function detectConfigPath(): string | null {
|
|
24
|
+
const home = process.env.HOME || "/home/ubuntu";
|
|
25
|
+
for (const app of ["openclaw", "clawdbot", "moltbot"]) {
|
|
26
|
+
const p = path.join(home, `.${app}`, `${app}.json`);
|
|
27
|
+
if (fs.existsSync(p)) return p;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function loadAccount(): ResolvedQQBotAccount | null {
|
|
33
|
+
const configPath = detectConfigPath();
|
|
34
|
+
try {
|
|
35
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
36
|
+
const appId = process.env.QQBOT_APP_ID;
|
|
37
|
+
const clientSecret = process.env.QQBOT_CLIENT_SECRET;
|
|
38
|
+
if (appId && clientSecret) {
|
|
39
|
+
return { accountId: "default", appId: normalizeAppId(appId), clientSecret, enabled: true, secretSource: "env", markdownSupport: true, config: {} };
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
44
|
+
const qqbot = config.channels?.qqbot;
|
|
45
|
+
if (!qqbot) return null;
|
|
46
|
+
return {
|
|
47
|
+
accountId: "default",
|
|
48
|
+
appId: normalizeAppId(qqbot.appId ?? process.env.QQBOT_APP_ID),
|
|
49
|
+
clientSecret: qqbot.clientSecret || process.env.QQBOT_CLIENT_SECRET,
|
|
50
|
+
enabled: qqbot.enabled ?? true,
|
|
51
|
+
secretSource: qqbot.clientSecret ? "config" as const : "env" as const,
|
|
52
|
+
markdownSupport: qqbot.markdownSupport ?? true,
|
|
53
|
+
config: qqbot,
|
|
54
|
+
};
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function sleep(ms: number) { return new Promise(r => setTimeout(r, ms)); }
|
|
61
|
+
|
|
62
|
+
async function main() {
|
|
63
|
+
// 清空日志
|
|
64
|
+
fs.writeFileSync(LOG_FILE, "");
|
|
65
|
+
|
|
66
|
+
const openid = process.argv[2];
|
|
67
|
+
if (!openid) { log("用法: npx tsx scripts/test-sendmedia.ts <openid>"); process.exit(1); }
|
|
68
|
+
|
|
69
|
+
const account = loadAccount();
|
|
70
|
+
if (!account) { log("无法加载账户配置"); process.exit(1); }
|
|
71
|
+
|
|
72
|
+
const to = `c2c:${openid}`;
|
|
73
|
+
log(`目标: ${to}\n`);
|
|
74
|
+
|
|
75
|
+
// ===== 1. 语音 =====
|
|
76
|
+
log("==================================================");
|
|
77
|
+
log("TEST 1: 语音 (本地 WAV 文件)");
|
|
78
|
+
log("==================================================");
|
|
79
|
+
const wavPath = "/tmp/test-voice.wav";
|
|
80
|
+
if (fs.existsSync(wavPath)) {
|
|
81
|
+
const r1 = await sendMedia({ to, text: "测试语音 sendMedia", mediaUrl: wavPath, account });
|
|
82
|
+
log("结果: " + JSON.stringify(r1, null, 2));
|
|
83
|
+
} else {
|
|
84
|
+
log("跳过: /tmp/test-voice.wav 不存在");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await sleep(2000);
|
|
88
|
+
|
|
89
|
+
// ===== 2. 视频 =====
|
|
90
|
+
log("\n==================================================");
|
|
91
|
+
log("TEST 2: 视频 (公网 MP4 URL)");
|
|
92
|
+
log("==================================================");
|
|
93
|
+
const videoUrl = "https://www.w3schools.com/html/mov_bbb.mp4";
|
|
94
|
+
const r2 = await sendMedia({ to, text: "测试视频 sendMedia", mediaUrl: videoUrl, account });
|
|
95
|
+
log("结果: " + JSON.stringify(r2, null, 2));
|
|
96
|
+
|
|
97
|
+
await sleep(2000);
|
|
98
|
+
|
|
99
|
+
// ===== 3. 文件 =====
|
|
100
|
+
log("\n==================================================");
|
|
101
|
+
log("TEST 3: 文件 (本地 TXT 文件)");
|
|
102
|
+
log("==================================================");
|
|
103
|
+
const txtPath = "/tmp/test-doc.txt";
|
|
104
|
+
if (fs.existsSync(txtPath)) {
|
|
105
|
+
const r3 = await sendMedia({ to, text: "测试文件 sendMedia", mediaUrl: txtPath, account });
|
|
106
|
+
log("结果: " + JSON.stringify(r3, null, 2));
|
|
107
|
+
} else {
|
|
108
|
+
log("跳过: /tmp/test-doc.txt 不存在");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
log("\n==================================================");
|
|
112
|
+
log("全部测试完成");
|
|
113
|
+
log("==================================================");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
main().catch(err => { log("脚本异常: " + err); process.exit(1); });
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
# upgrade-via-npm.sh --version <version> # 升级到指定版本
|
|
12
12
|
# upgrade-via-npm.sh --self-version # 升级到当前仓库 package.json 版本
|
|
13
13
|
# upgrade-via-npm.sh --appid <appid> --secret <secret> # 首次安装时配置 appid/secret
|
|
14
|
+
# upgrade-via-npm.sh --no-restart # 只做文件替换,不重启 gateway(供热更指令使用)
|
|
14
15
|
|
|
15
16
|
set -eo pipefail
|
|
16
17
|
|
|
@@ -18,6 +19,7 @@ PKG_NAME="@tencent-connect/openclaw-qqbot"
|
|
|
18
19
|
INSTALL_SRC=""
|
|
19
20
|
APPID=""
|
|
20
21
|
SECRET=""
|
|
22
|
+
NO_RESTART=false
|
|
21
23
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
22
24
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
23
25
|
|
|
@@ -77,6 +79,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
77
79
|
SECRET="$2"
|
|
78
80
|
shift 2
|
|
79
81
|
;;
|
|
82
|
+
--no-restart)
|
|
83
|
+
NO_RESTART=true
|
|
84
|
+
shift 1
|
|
85
|
+
;;
|
|
80
86
|
-h|--help)
|
|
81
87
|
print_usage
|
|
82
88
|
exit 0
|
|
@@ -265,11 +271,16 @@ elif [ -n "$APPID" ] || [ -n "$SECRET" ]; then
|
|
|
265
271
|
echo "⚠️ --appid 和 --secret 必须同时提供"
|
|
266
272
|
fi
|
|
267
273
|
|
|
268
|
-
# [5/5] 重启 gateway
|
|
269
|
-
|
|
270
|
-
echo "
|
|
271
|
-
|
|
272
|
-
echo " ✅ gateway 已重启"
|
|
274
|
+
# [5/5] 重启 gateway 使新版本生效(除非 --no-restart)
|
|
275
|
+
if [ "$NO_RESTART" = "true" ]; then
|
|
276
|
+
echo ""
|
|
277
|
+
echo "[跳过重启] --no-restart 已指定,请手动执行: $CMD gateway restart"
|
|
273
278
|
else
|
|
274
|
-
echo "
|
|
279
|
+
echo ""
|
|
280
|
+
echo "[重启] 重启 gateway 使新版本生效..."
|
|
281
|
+
if $CMD gateway restart 2>&1; then
|
|
282
|
+
echo " ✅ gateway 已重启"
|
|
283
|
+
else
|
|
284
|
+
echo " ⚠️ gateway 重启失败,请手动执行: $CMD gateway restart"
|
|
285
|
+
fi
|
|
275
286
|
fi
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: qqbot-channel
|
|
3
|
+
description: QQ 频道管理技能。查询频道列表、子频道、成员、发帖、公告、日程等操作。使用 qqbot_channel_api 工具代理 QQ 开放平台 HTTP 接口,自动处理 Token 鉴权。当用户需要查看频道、管理子频道、查询成员、发布帖子/公告/日程时使用此技能。
|
|
4
|
+
metadata: {"openclaw":{"emoji":"📡","requires":{"config":["channels.qqbot"]}}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# QQ 频道 API 请求指导
|
|
8
|
+
|
|
9
|
+
`qqbot_channel_api` 是一个 QQ 开放平台 HTTP 代理工具,**自动填充鉴权 Token**。你只需要指定 HTTP 方法、API 路径、请求体和查询参数。
|
|
10
|
+
|
|
11
|
+
## 📚 详细参考文档
|
|
12
|
+
|
|
13
|
+
每个接口的完整参数说明、返回值结构和枚举值定义:
|
|
14
|
+
- `references/api_references.md`
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🔧 工具参数
|
|
19
|
+
|
|
20
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
21
|
+
|------|------|------|------|
|
|
22
|
+
| `method` | string | 是 | HTTP 方法:`GET`, `POST`, `PUT`, `PATCH`, `DELETE` |
|
|
23
|
+
| `path` | string | 是 | API 路径(不含域名),如 `/guilds/{guild_id}/channels`,需替换占位符为实际值 |
|
|
24
|
+
| `body` | object | 否 | 请求体 JSON(POST/PUT/PATCH 使用) |
|
|
25
|
+
| `query` | object | 否 | URL 查询参数键值对,值为字符串类型 |
|
|
26
|
+
|
|
27
|
+
> 基础 URL:`https://api.sgroup.qq.com`,鉴权头 `Authorization: QQBot {token}` 由工具自动填充。
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ⭐ 接口速查
|
|
32
|
+
|
|
33
|
+
### 频道(Guild)
|
|
34
|
+
|
|
35
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
36
|
+
|------|------|------|----------|
|
|
37
|
+
| 获取频道列表 | `GET` | `/users/@me/guilds` | query: `before`, `after`, `limit`(最大100) |
|
|
38
|
+
| 获取频道 API 权限 | `GET` | `/guilds/{guild_id}/api_permission` | — |
|
|
39
|
+
|
|
40
|
+
### 子频道(Channel)
|
|
41
|
+
|
|
42
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
43
|
+
|------|------|------|----------|
|
|
44
|
+
| 获取子频道列表 | `GET` | `/guilds/{guild_id}/channels` | — |
|
|
45
|
+
| 获取子频道详情 | `GET` | `/channels/{channel_id}` | — |
|
|
46
|
+
| 创建子频道 | `POST` | `/guilds/{guild_id}/channels` | body: `name`\*, `type`\*, `position`\*, `sub_type`, `parent_id`, `private_type`, `private_user_ids`, `speak_permission`, `application_id` |
|
|
47
|
+
| 修改子频道 | `PATCH` | `/channels/{channel_id}` | body: `name`, `position`, `parent_id`, `private_type`, `speak_permission`(至少一个) |
|
|
48
|
+
| 删除子频道 | `DELETE` | `/channels/{channel_id}` | ⚠️ 不可逆 |
|
|
49
|
+
|
|
50
|
+
**子频道类型(type)**:`0`=文字, `2`=语音, `4`=分组(position≥2), `10005`=直播, `10006`=应用, `10007`=论坛
|
|
51
|
+
|
|
52
|
+
### 成员(Member)
|
|
53
|
+
|
|
54
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
55
|
+
|------|------|------|----------|
|
|
56
|
+
| 获取成员列表 | `GET` | `/guilds/{guild_id}/members` | query: `after`(首次填0), `limit`(1-400) |
|
|
57
|
+
| 获取成员详情 | `GET` | `/guilds/{guild_id}/members/{user_id}` | — |
|
|
58
|
+
| 获取身份组成员列表 | `GET` | `/guilds/{guild_id}/roles/{role_id}/members` | query: `start_index`(首次填0), `limit`(1-400) |
|
|
59
|
+
| 获取在线成员数 | `GET` | `/channels/{channel_id}/online_nums` | — |
|
|
60
|
+
|
|
61
|
+
### 公告(Announces)
|
|
62
|
+
|
|
63
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
64
|
+
|------|------|------|----------|
|
|
65
|
+
| 创建公告 | `POST` | `/guilds/{guild_id}/announces` | body: `message_id`, `channel_id`, `announces_type`(0=成员,1=欢迎), `recommend_channels`(最多3条) |
|
|
66
|
+
| 删除公告 | `DELETE` | `/guilds/{guild_id}/announces/{message_id}` | message_id 设 `all` 删除所有 |
|
|
67
|
+
|
|
68
|
+
### 论坛(Forum)— 仅私域机器人
|
|
69
|
+
|
|
70
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
71
|
+
|------|------|------|----------|
|
|
72
|
+
| 获取帖子列表 | `GET` | `/channels/{channel_id}/threads` | — |
|
|
73
|
+
| 获取帖子详情 | `GET` | `/channels/{channel_id}/threads/{thread_id}` | — |
|
|
74
|
+
| 发表帖子 | `PUT` | `/channels/{channel_id}/threads` | body: `title`\*, `content`\*, `format`(1=文本,2=HTML,3=Markdown,4=JSON,默认3) |
|
|
75
|
+
| 删除帖子 | `DELETE` | `/channels/{channel_id}/threads/{thread_id}` | ⚠️ 不可逆 |
|
|
76
|
+
| 发表评论 | `POST` | `/channels/{channel_id}/threads/{thread_id}/comment` | body: `thread_author`\*, `content`\*, `thread_create_time`, `image` |
|
|
77
|
+
|
|
78
|
+
### 日程(Schedule)
|
|
79
|
+
|
|
80
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
81
|
+
|------|------|------|----------|
|
|
82
|
+
| 创建日程 | `POST` | `/channels/{channel_id}/schedules` | body: `{ schedule: { name*, start_timestamp*, end_timestamp*, jump_channel_id, remind_type } }` |
|
|
83
|
+
| 修改日程 | `PATCH` | `/channels/{channel_id}/schedules/{schedule_id}` | body: `{ schedule: { name*, start_timestamp*, end_timestamp*, jump_channel_id, remind_type } }` |
|
|
84
|
+
| 删除日程 | `DELETE` | `/channels/{channel_id}/schedules/{schedule_id}` | ⚠️ 不可逆 |
|
|
85
|
+
|
|
86
|
+
**提醒类型(remind_type)**:`"0"`=不提醒, `"1"`=开始时, `"2"`=5分钟前, `"3"`=15分钟前, `"4"`=30分钟前, `"5"`=60分钟前
|
|
87
|
+
|
|
88
|
+
> `*` 表示必填参数
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 💡 调用示例
|
|
93
|
+
|
|
94
|
+
### 获取频道列表
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"method": "GET",
|
|
99
|
+
"path": "/users/@me/guilds",
|
|
100
|
+
"query": { "limit": "100" }
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 获取子频道列表
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"method": "GET",
|
|
109
|
+
"path": "/guilds/123456/channels"
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 创建子频道
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"method": "POST",
|
|
118
|
+
"path": "/guilds/123456/channels",
|
|
119
|
+
"body": {
|
|
120
|
+
"name": "新频道",
|
|
121
|
+
"type": 0,
|
|
122
|
+
"position": 1,
|
|
123
|
+
"sub_type": 0
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 获取成员列表(分页)
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"method": "GET",
|
|
133
|
+
"path": "/guilds/123456/members",
|
|
134
|
+
"query": { "after": "0", "limit": "100" }
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 发表论坛帖子
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"method": "PUT",
|
|
143
|
+
"path": "/channels/789012/threads",
|
|
144
|
+
"body": {
|
|
145
|
+
"title": "公告标题",
|
|
146
|
+
"content": "# 标题\n\n公告内容",
|
|
147
|
+
"format": 3
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 创建日程
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"method": "POST",
|
|
157
|
+
"path": "/channels/456789/schedules",
|
|
158
|
+
"body": {
|
|
159
|
+
"schedule": {
|
|
160
|
+
"name": "周会",
|
|
161
|
+
"start_timestamp": "1770733800000",
|
|
162
|
+
"end_timestamp": "1770737400000",
|
|
163
|
+
"remind_type": "2"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 创建推荐子频道公告
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"method": "POST",
|
|
174
|
+
"path": "/guilds/123456/announces",
|
|
175
|
+
"body": {
|
|
176
|
+
"announces_type": 0,
|
|
177
|
+
"recommend_channels": [
|
|
178
|
+
{ "channel_id": "789012", "introduce": "欢迎来到攻略频道" }
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 删除所有公告
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"method": "DELETE",
|
|
189
|
+
"path": "/guilds/123456/announces/all"
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 🔄 常用操作流程
|
|
196
|
+
|
|
197
|
+
### 获取频道和子频道信息
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
1. GET /users/@me/guilds → 获取频道列表,拿到 guild_id
|
|
201
|
+
2. GET /guilds/{guild_id}/channels → 获取子频道列表,拿到 channel_id
|
|
202
|
+
3. GET /channels/{channel_id} → 获取子频道详情
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### 论坛发帖 + 评论
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
1. GET /guilds/{guild_id}/channels → 找到论坛子频道(type=10007)
|
|
209
|
+
2. PUT /channels/{channel_id}/threads → 发表帖子
|
|
210
|
+
3. GET /channels/{channel_id}/threads → 获取帖子列表
|
|
211
|
+
4. GET /channels/{channel_id}/threads/{thread_id} → 获取帖子详情(含 author_id)
|
|
212
|
+
5. POST /channels/{channel_id}/threads/{thread_id}/comment → 发表评论
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 成员管理
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
1. GET /users/@me/guilds → 获取 guild_id
|
|
219
|
+
2. GET /guilds/{guild_id}/members?after=0&limit=100 → 获取成员列表
|
|
220
|
+
翻页:用上次最后一个 user.id 作为 after,直到返回空数组
|
|
221
|
+
3. GET /guilds/{guild_id}/members/{user_id} → 获取指定成员详情
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### 展示成员头像
|
|
225
|
+
|
|
226
|
+
成员详情返回的 `user.avatar` 是头像 URL,**必须使用 Markdown 图片语法展示**,让用户直接看到头像图片,而非纯文本链接:
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
成员信息:
|
|
230
|
+
· 昵称:{nick}
|
|
231
|
+
· 头像:
|
|
232
|
+

|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
> **禁止**将头像 URL 作为纯文本或超链接展示(如 `查看头像`),必须用 `` 语法内联显示。频道的 `icon` 字段同理。
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 🚨 错误码处理
|
|
240
|
+
|
|
241
|
+
| 错误码 | 说明 | 解决方案 |
|
|
242
|
+
|--------|------|----------|
|
|
243
|
+
| **401** | Token 鉴权失败 | 检查 AppID 和 ClientSecret 配置 |
|
|
244
|
+
| **11241** | 频道 API 无权限 | 前往 QQ 开放平台申请权限,或调用 `GET /guilds/{guild_id}/api_permission` 查看可用权限 |
|
|
245
|
+
| **11242** | 仅私域机器人可用 | 需在 QQ 开放平台将机器人切换为私域模式 |
|
|
246
|
+
| **11243** | 需要管理频道权限 | 确保机器人拥有管理权限 |
|
|
247
|
+
| **11281** | 日程频率限制 | 单管理员/天限 10 次,单频道/天限 100 次 |
|
|
248
|
+
| **304023** | 推荐子频道超限 | 推荐子频道最多 3 条 |
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## ⚠️ 注意事项
|
|
253
|
+
|
|
254
|
+
1. **路径中的占位符**(如 `{guild_id}`、`{channel_id}`)必须替换为实际值
|
|
255
|
+
2. **query 参数的值必须为字符串类型**,如 `{ "limit": "100" }` 而非 `{ "limit": 100 }`
|
|
256
|
+
3. **成员列表翻页**时可能返回重复成员,需按 `user.id` 去重
|
|
257
|
+
4. **公告**的两种类型(消息公告和推荐子频道公告)会互相顶替
|
|
258
|
+
5. **日程**的时间戳为毫秒级字符串
|
|
259
|
+
6. **删除操作不可逆**,请谨慎使用
|
|
260
|
+
7. **论坛操作**仅私域机器人可用
|
|
261
|
+
8. **子频道分组**(type=4)的 `position` 必须 >= 2
|
|
262
|
+
9. **日程操作**有频率限制:单个管理员每天 10 次,单个频道每天 100 次
|
|
263
|
+
10. **头像/图标展示**:成员 `user.avatar` 和频道 `icon` 等图片 URL 必须使用 Markdown 图片语法 `` 展示,禁止作为纯文本或超链接展示
|