@tencent-connect/openclaw-qqbot 1.6.3-alpha.channel → 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.
@@ -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/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
2
2
  import { qqbotPlugin } from "./src/channel.js";
3
3
  import { setQQBotRuntime } from "./src/runtime.js";
4
+ import { registerChannelTool } from "./src/tools/channel.js";
4
5
  const plugin = {
5
6
  id: "openclaw-qqbot",
6
7
  name: "QQ Bot",
@@ -9,6 +10,7 @@ const plugin = {
9
10
  register(api) {
10
11
  setQQBotRuntime(api.runtime);
11
12
  api.registerChannel({ plugin: qqbotPlugin });
13
+ registerChannelTool(api);
12
14
  },
13
15
  };
14
16
  export default plugin;
package/dist/src/api.d.ts CHANGED
@@ -81,6 +81,15 @@ export declare function sendChannelMessage(accessToken: string, channelId: strin
81
81
  id: string;
82
82
  timestamp: string;
83
83
  }>;
84
+ /**
85
+ * 发送频道私信消息
86
+ * @param guildId - 私信会话的 guild_id(由 DIRECT_MESSAGE_CREATE 事件提供)
87
+ * @param msgId - 被动回复时必填
88
+ */
89
+ export declare function sendDmMessage(accessToken: string, guildId: string, content: string, msgId?: string): Promise<{
90
+ id: string;
91
+ timestamp: string;
92
+ }>;
84
93
  export declare function sendGroupMessage(accessToken: string, groupOpenid: string, content: string, msgId?: string): Promise<MessageResponse>;
85
94
  export declare function sendProactiveC2CMessage(accessToken: string, openid: string, content: string): Promise<MessageResponse>;
86
95
  export declare function sendProactiveGroupMessage(accessToken: string, groupOpenid: string, content: string): Promise<{
package/dist/src/api.js CHANGED
@@ -340,6 +340,17 @@ export async function sendChannelMessage(accessToken, channelId, content, msgId)
340
340
  ...(msgId ? { msg_id: msgId } : {}),
341
341
  });
342
342
  }
343
+ /**
344
+ * 发送频道私信消息
345
+ * @param guildId - 私信会话的 guild_id(由 DIRECT_MESSAGE_CREATE 事件提供)
346
+ * @param msgId - 被动回复时必填
347
+ */
348
+ export async function sendDmMessage(accessToken, guildId, content, msgId) {
349
+ return apiRequest(accessToken, "POST", `/dms/${guildId}/messages`, {
350
+ content,
351
+ ...(msgId ? { msg_id: msgId } : {}),
352
+ });
353
+ }
343
354
  export async function sendGroupMessage(accessToken, groupOpenid, content, msgId) {
344
355
  const msgSeq = msgId ? getNextMsgSeq(msgId) : 1;
345
356
  const body = buildMessageBody(content, msgId, msgSeq);
@@ -144,13 +144,25 @@ function findCli() {
144
144
  return null;
145
145
  }
146
146
  /**
147
- * 找到升级脚本路径
147
+ * 找到升级脚本路径(兼容源码运行、dist 运行、已安装扩展目录)
148
148
  */
149
149
  function getUpgradeScriptPath() {
150
150
  const currentFile = fileURLToPath(import.meta.url);
151
- const scriptDir = path.resolve(path.dirname(currentFile), "..", "..", "scripts");
152
- const scriptPath = path.join(scriptDir, "upgrade-via-npm.sh");
153
- return fs.existsSync(scriptPath) ? scriptPath : null;
151
+ const currentDir = path.dirname(currentFile);
152
+ const candidates = [
153
+ path.resolve(currentDir, "..", "..", "scripts", "upgrade-via-npm.sh"),
154
+ path.resolve(currentDir, "..", "scripts", "upgrade-via-npm.sh"),
155
+ path.resolve(process.cwd(), "scripts", "upgrade-via-npm.sh"),
156
+ ];
157
+ const homeDir = getHomeDir();
158
+ for (const cli of ["openclaw", "clawdbot", "moltbot"]) {
159
+ candidates.push(path.join(homeDir, `.${cli}`, "extensions", "openclaw-qqbot", "scripts", "upgrade-via-npm.sh"));
160
+ }
161
+ for (const p of candidates) {
162
+ if (fs.existsSync(p))
163
+ return p;
164
+ }
165
+ return null;
154
166
  }
155
167
  /**
156
168
  * 在 Windows 上查找可用的 bash(Git Bash / WSL 等)
@@ -184,25 +196,19 @@ function findBash() {
184
196
  * - 异步执行升级脚本(--no-restart,只做文件替换)
185
197
  * - 脚本完成后触发 gateway restart(当前进程会被杀掉)
186
198
  * - 新进程启动时 getStartupGreeting() 检测到版本变更,自动通知管理员
187
- *
188
- * @returns true 表示已启动升级流程,false 表示无法执行(如 Windows 无 bash)
189
199
  */
190
200
  function fireHotUpgrade(targetVersion) {
191
201
  const scriptPath = getUpgradeScriptPath();
192
202
  if (!scriptPath)
193
- return false;
203
+ return { ok: false, reason: "no-script" };
194
204
  const cli = findCli();
195
205
  if (!cli)
196
- return false;
206
+ return { ok: false, reason: "no-cli" };
197
207
  const bash = findBash();
198
208
  if (!bash)
199
- return false;
200
- const args = ["--no-restart"];
201
- if (targetVersion) {
202
- args.push("--version", targetVersion);
203
- }
209
+ return { ok: false, reason: "no-bash" };
204
210
  // 异步执行升级脚本
205
- execFile(bash, [scriptPath, ...args], {
211
+ execFile(bash, [scriptPath, "--no-restart", ...(targetVersion ? ["--version", targetVersion] : [])], {
206
212
  timeout: 120_000,
207
213
  env: { ...process.env },
208
214
  ...(isWindows() ? { windowsHide: true } : {}),
@@ -213,104 +219,51 @@ function fireHotUpgrade(targetVersion) {
213
219
  // 文件替换成功,触发 gateway restart
214
220
  execFile(cli, ["gateway", "restart"], { timeout: 30_000 }, () => { });
215
221
  });
216
- return true;
222
+ return { ok: true };
217
223
  }
218
224
  /**
219
- * /bot-upgrade — 查看版本更新状态 + 升级指引(根据 upgradeMode 决定行为)
220
- */
221
- registerCommand({
222
- name: "bot-upgrade",
223
- description: "查看版本更新与升级指引",
224
- handler: (ctx) => {
225
- // 升级相关指令仅在私聊中可用
226
- if (ctx.type !== "c2c") {
227
- return `💡 请在私聊中使用此指令`;
228
- }
229
- const upgradeMode = ctx.accountConfig?.upgradeMode || "doc";
230
- const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
231
- const info = getUpdateInfo();
232
- const lines = [];
233
- lines.push(`📌当前版本:v${PLUGIN_VERSION}`);
234
- if (info.checkedAt === 0) {
235
- lines.push(`⏳ 版本检查中,请稍后再试`);
236
- return lines.join("\n");
237
- }
238
- else if (info.error) {
239
- lines.push(`⚠️ 版本检查失败`);
240
- lines.push(`⬆️升级指引:[点击查看](${url})`);
241
- return lines.join("\n");
242
- }
243
- else if (info.hasUpdate && info.latest) {
244
- lines.push(`🆕最新可用版本:v${info.latest}`);
245
- }
246
- else {
247
- lines.push(`✅ 当前已是最新版本`);
248
- return lines.join("\n");
249
- }
250
- // 有新版本:根据 upgradeMode 决定行为
251
- if (upgradeMode === "hot-reload") {
252
- const started = fireHotUpgrade(info.latest);
253
- if (!started) {
254
- lines.push(`⚠️ 当前环境不支持热更新(需要 bash 环境)`);
255
- lines.push(`⬆️升级指引:[点击查看](${url})`);
256
- return lines.join("\n");
257
- }
258
- lines.push(``);
259
- lines.push(`🔄 正在执行热更新到 v${info.latest}...`);
260
- lines.push(`⏳ 升级过程约需 30~60 秒,完成后会自动通知您`);
261
- return lines.join("\n");
262
- }
263
- // doc 模式:展示升级文档
264
- lines.push(`⬆️升级指引:[点击查看](${url})`);
265
- lines.push(`🌟官方 GitHub 仓库:[点击前往](https://github.com/tencent-connect/openclaw-qqbot/)`);
266
- lines.push(``, `> 💡 提示:管理员可通过 <qqbot-cmd-input text="/bot-hot-upgrade" show="/bot-hot-upgrade"/> 直接执行热更新`);
267
- return lines.join("\n");
268
- },
269
- });
270
- /**
271
- * /bot-hot-upgrade — 直接执行热更新(无论 upgradeMode 配置如何)
225
+ * /bot-upgrade — 统一升级入口:能热更就热更,失败则返回升级指引
272
226
  *
273
227
  * 支持参数:
274
- * /bot-hot-upgrade — 升级到 latest
275
- * /bot-hot-upgrade 1.6.4 — 升级到指定版本
276
- * /bot-hot-upgrade --force — 强制升级(即使当前已是最新版)
228
+ * /bot-upgrade — 检查更新后自动热更
229
+ * /bot-upgrade 1.6.4 — 升级到指定版本
230
+ * /bot-upgrade --force — 强制升级(即使当前已是最新版)
277
231
  */
278
232
  registerCommand({
279
- name: "bot-hot-upgrade",
280
- description: "直接执行热更新升级",
233
+ name: "bot-upgrade",
234
+ description: "检查更新并自动热更(失败则返回升级指引)",
281
235
  handler: (ctx) => {
282
236
  // 升级相关指令仅在私聊中可用
283
237
  if (ctx.type !== "c2c") {
284
238
  return `💡 请在私聊中使用此指令`;
285
239
  }
286
- // 前置检查
287
- const scriptPath = getUpgradeScriptPath();
288
- if (!scriptPath) {
289
- return "❌ 升级脚本不存在,请检查安装是否完整";
290
- }
291
- const cli = findCli();
292
- if (!cli) {
293
- return "❌ 未找到 openclaw / clawdbot / moltbot CLI";
294
- }
240
+ const url = ctx.accountConfig?.upgradeUrl || DEFAULT_UPGRADE_URL;
295
241
  const args = ctx.args.trim();
296
242
  const info = getUpdateInfo();
297
- // 解析参数
298
243
  const isForce = args.includes("--force");
299
244
  const versionArg = args.replace("--force", "").trim() || undefined;
300
- // 如果没有指定版本,先检查是否有更新
301
245
  if (!versionArg && !isForce) {
302
246
  if (info.checkedAt === 0) {
303
247
  return `⏳ 版本检查中,请稍后再试`;
304
248
  }
305
- if (!info.hasUpdate && !info.error) {
306
- return `✅ 当前版本 v${PLUGIN_VERSION} 已是最新,无需升级\n\n> 💡 使用 /bot-hot-upgrade --force 可强制重新安装`;
249
+ if (info.error) {
250
+ return `⚠️ 版本检查失败\n⬆️升级指引:[点击查看](${url})`;
251
+ }
252
+ if (!info.hasUpdate) {
253
+ return `✅ 当前版本 v${PLUGIN_VERSION} 已是最新,无需升级\n\n> 💡 使用 /bot-upgrade --force 可强制重新安装`;
307
254
  }
308
255
  }
309
256
  const targetVersion = versionArg || info.latest || undefined;
310
257
  // 异步执行升级
311
- const started = fireHotUpgrade(targetVersion);
312
- if (!started) {
313
- return `❌ 当前环境不支持热更新(需要 bash 环境)\n\n> Windows 用户请安装 Git for Windows 后重试,或手动执行升级脚本`;
258
+ const startResult = fireHotUpgrade(targetVersion);
259
+ if (!startResult.ok) {
260
+ if (startResult.reason === "no-script") {
261
+ return `❌ 未找到本地升级脚本\n⬆️升级指引:[点击查看](${url})`;
262
+ }
263
+ if (startResult.reason === "no-cli") {
264
+ return `❌ 未找到 openclaw / clawdbot / moltbot CLI\n⬆️升级指引:[点击查看](${url})`;
265
+ }
266
+ return `❌ 当前环境不支持热更新(需要 bash 环境)\n⬆️升级指引:[点击查看](${url})\n\n> Windows 用户请安装 Git for Windows 后重试,或手动执行升级脚本`;
314
267
  }
315
268
  const lines = [
316
269
  `🔄 开始热更新...`,
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
 
@@ -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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tencent-connect/openclaw-qqbot",
3
- "version": "1.6.3-alpha.channel",
3
+ "version": "1.6.4-alpha.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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
+ ![头像]({user.avatar})
233
+ ```
234
+
235
+ > **禁止**将头像 URL 作为纯文本或超链接展示(如 `查看头像`),必须用 `![描述](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 图片语法 `![描述](URL)` 展示,禁止作为纯文本或超链接展示