@openclaw/qqbot 2026.6.11 → 2026.7.1-beta.1
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/dist/api.js +5 -5
- package/dist/{channel-DvWrqcZB.js → channel-Bh0MrYvU.js} +34 -100
- package/dist/{channel-entry-BGXFRmbg.js → channel-entry-LMtNY-J6.js} +3 -3
- package/dist/channel-entry-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/doctor-contract-api.js +168 -2
- package/dist/{gateway-CCkrdgaK.js → gateway-BgfQAYxB.js} +11 -162
- package/dist/{group-CvVXke_k.js → group-DQQsZ6Y0.js} +2 -2
- package/dist/{handler-runtime--OTMkFWa.js → handler-runtime-BQ72YPsH.js} +2 -2
- package/dist/{outbound-BQcPCo-z.js → outbound-Dfa2uOEp.js} +216 -4
- package/dist/{runtime-D1hn91-s.js → runtime-BL0xZzWe.js} +1 -1
- package/dist/runtime-api.js +1 -1
- package/dist/{sender-CRZ2af7P.js → sender-BHWpE_yH.js} +2 -2
- package/dist/{doctor-contract-BYTS8-ia.js → state-keys-CQKlAFyo.js} +7 -1
- package/dist/target-parser-DagYRQkP.js +71 -0
- package/dist/{tools-CeUI9pG-.js → tools-BvXe7bTq.js} +58 -13
- package/dist/tools-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
- package/skills/qqbot-channel/SKILL.md +54 -41
- package/skills/qqbot-channel/references/api_references.md +16 -8
- package/skills/qqbot-media/SKILL.md +6 -3
- package/skills/qqbot-remind/SKILL.md +15 -14
- package/dist/target-parser-BdCUmxK7.js +0 -285
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//#region extensions/qqbot/src/engine/messaging/target-parser.ts
|
|
2
|
+
/**
|
|
3
|
+
* Parse a qqbot target string into a structured delivery target.
|
|
4
|
+
*
|
|
5
|
+
* Supported formats:
|
|
6
|
+
* - `qqbot:c2c:openid` → C2C direct message
|
|
7
|
+
* - `qqbot:group:groupid` → Group message
|
|
8
|
+
* - `qqbot:channel:channelid` → Channel message
|
|
9
|
+
* - `c2c:openid` → C2C (without qqbot: prefix)
|
|
10
|
+
* - `group:groupid` → Group (without qqbot: prefix)
|
|
11
|
+
* - `channel:channelid` → Channel (without qqbot: prefix)
|
|
12
|
+
* - `openid` → C2C (bare openid, default)
|
|
13
|
+
*
|
|
14
|
+
* @param to - Raw target string.
|
|
15
|
+
* @returns Parsed target with type and id.
|
|
16
|
+
* @throws {Error} When the target format is invalid.
|
|
17
|
+
*/
|
|
18
|
+
function parseTarget(to) {
|
|
19
|
+
const id = to.replace(/^qqbot:/i, "");
|
|
20
|
+
if (id.startsWith("c2c:")) {
|
|
21
|
+
const userId = id.slice(4);
|
|
22
|
+
if (!userId) throw new Error(`Invalid c2c target format: ${to} - missing user ID`);
|
|
23
|
+
return {
|
|
24
|
+
type: "c2c",
|
|
25
|
+
id: userId
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (id.startsWith("group:")) {
|
|
29
|
+
const groupId = id.slice(6);
|
|
30
|
+
if (!groupId) throw new Error(`Invalid group target format: ${to} - missing group ID`);
|
|
31
|
+
return {
|
|
32
|
+
type: "group",
|
|
33
|
+
id: groupId
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (id.startsWith("channel:")) {
|
|
37
|
+
const channelId = id.slice(8);
|
|
38
|
+
if (!channelId) throw new Error(`Invalid channel target format: ${to} - missing channel ID`);
|
|
39
|
+
return {
|
|
40
|
+
type: "channel",
|
|
41
|
+
id: channelId
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (!id) throw new Error(`Invalid target format: ${to} - empty ID after removing qqbot: prefix`);
|
|
45
|
+
return {
|
|
46
|
+
type: "c2c",
|
|
47
|
+
id
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Normalize a QQ Bot target string into the canonical `qqbot:...` form.
|
|
52
|
+
*
|
|
53
|
+
* Returns `undefined` when the target does not look like a QQ Bot address.
|
|
54
|
+
*/
|
|
55
|
+
function normalizeTarget(target) {
|
|
56
|
+
const id = target.replace(/^qqbot:/i, "");
|
|
57
|
+
if (id.startsWith("c2c:") || id.startsWith("group:") || id.startsWith("channel:")) return `qqbot:${id}`;
|
|
58
|
+
if (/^[0-9a-fA-F]{32}$/.test(id)) return `qqbot:c2c:${id}`;
|
|
59
|
+
if (/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id)) return `qqbot:c2c:${id}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Return true when the string looks like a QQ Bot target ID.
|
|
63
|
+
*/
|
|
64
|
+
function looksLikeQQBotTarget(id) {
|
|
65
|
+
if (/^qqbot:(c2c|group|channel):/i.test(id)) return true;
|
|
66
|
+
if (/^(c2c|group|channel):/i.test(id)) return true;
|
|
67
|
+
if (/^[0-9a-fA-F]{32}$/.test(id)) return true;
|
|
68
|
+
return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(id);
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { normalizeTarget as n, parseTarget as r, looksLikeQQBotTarget as t };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { a as resolveQQBotAccount, r as listQQBotAccountIds } from "./config-C1qZbh0K.js";
|
|
2
2
|
import { a as formatErrorMessage, n as debugLog, t as debugError } from "./log-SDfMMBWe.js";
|
|
3
3
|
import { t as getRequestContext } from "./request-context-Bm7PTBD1.js";
|
|
4
|
-
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
4
|
+
import { readProviderTextResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
5
5
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
6
6
|
import { resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
7
7
|
import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
8
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
8
9
|
//#region extensions/qqbot/src/engine/tools/channel-api.ts
|
|
9
10
|
/**
|
|
10
11
|
* QQ Channel API proxy tool core logic.
|
|
@@ -33,7 +34,7 @@ const ChannelApiSchema = {
|
|
|
33
34
|
properties: {
|
|
34
35
|
method: {
|
|
35
36
|
type: "string",
|
|
36
|
-
description: "HTTP method. Allowed values: GET, POST, PUT, PATCH, DELETE.",
|
|
37
|
+
description: "HTTP method. Allowed values: GET, POST, PUT, PATCH, DELETE. Use DELETE and other mutating methods only after explicit user intent and target confirmation.",
|
|
37
38
|
enum: [
|
|
38
39
|
"GET",
|
|
39
40
|
"POST",
|
|
@@ -48,12 +49,20 @@ const ChannelApiSchema = {
|
|
|
48
49
|
},
|
|
49
50
|
body: {
|
|
50
51
|
type: "object",
|
|
51
|
-
description: "JSON request body for POST/PUT/PATCH requests. GET/DELETE usually do not need it."
|
|
52
|
+
description: "JSON request body for POST/PUT/PATCH requests. GET/DELETE usually do not need it. For write requests, include only fields the user explicitly asked to change."
|
|
52
53
|
},
|
|
53
54
|
query: {
|
|
54
55
|
type: "object",
|
|
55
56
|
description: "URL query parameters as key/value pairs appended to the path. For example, { \"limit\": \"100\", \"after\": \"0\" } becomes ?limit=100&after=0.",
|
|
56
57
|
additionalProperties: { type: "string" }
|
|
58
|
+
},
|
|
59
|
+
confirmed: {
|
|
60
|
+
type: "boolean",
|
|
61
|
+
description: "Required true for DELETE requests after the user confirms the exact QQ resource to delete."
|
|
62
|
+
},
|
|
63
|
+
bulkConfirmed: {
|
|
64
|
+
type: "boolean",
|
|
65
|
+
description: "Required true in addition to confirmed for bulk DELETE requests such as deleting all announcements."
|
|
57
66
|
}
|
|
58
67
|
},
|
|
59
68
|
required: ["method", "path"]
|
|
@@ -80,6 +89,33 @@ function validatePath(path) {
|
|
|
80
89
|
if (!path.startsWith("/")) return "path must start with /";
|
|
81
90
|
if (path.includes("..") || path.includes("//")) return "path must not contain .. or //";
|
|
82
91
|
if (!/^\/[a-zA-Z0-9\-._~:@!$&'()*+,;=/%]+$/.test(path) && path !== "/") return "path contains unsupported characters";
|
|
92
|
+
for (const segment of path.split("/").slice(1)) {
|
|
93
|
+
let decodedSegment;
|
|
94
|
+
try {
|
|
95
|
+
decodedSegment = decodeURIComponent(segment);
|
|
96
|
+
} catch {
|
|
97
|
+
return "path contains invalid percent encoding";
|
|
98
|
+
}
|
|
99
|
+
if (decodedSegment.includes("/") || decodedSegment.includes("\\")) return "path contains encoded path separators";
|
|
100
|
+
if (decodedSegment === "." || decodedSegment === "..") return "path must not contain . or .. segments";
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
function decodePathSegments(path) {
|
|
105
|
+
try {
|
|
106
|
+
return path.replace(/\/+$/, "").split("/").slice(1).map((segment) => decodeURIComponent(segment));
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function isBulkAnnouncementDeletePath(path) {
|
|
112
|
+
const segments = decodePathSegments(path);
|
|
113
|
+
return Boolean(segments && segments.length === 4 && segments[0]?.toLowerCase() === "guilds" && segments[2]?.toLowerCase() === "announces" && segments[3]?.toLowerCase() === "all");
|
|
114
|
+
}
|
|
115
|
+
function validateDeleteConfirmation(params) {
|
|
116
|
+
if (params.method.toUpperCase() !== "DELETE") return null;
|
|
117
|
+
if (!params.confirmed) return "DELETE requests require confirmed=true after the user confirms the exact QQ resource.";
|
|
118
|
+
if (isBulkAnnouncementDeletePath(params.path) && !params.bulkConfirmed) return "Deleting all announcements requires bulkConfirmed=true after a separate bulk-delete confirmation.";
|
|
83
119
|
return null;
|
|
84
120
|
}
|
|
85
121
|
function json$1(data) {
|
|
@@ -112,6 +148,14 @@ async function executeChannelApi(params, options) {
|
|
|
112
148
|
].includes(method)) return json$1({ error: `Unsupported HTTP method: ${method}. Allowed values: GET, POST, PUT, PATCH, DELETE` });
|
|
113
149
|
const pathError = validatePath(params.path);
|
|
114
150
|
if (pathError) return json$1({ error: pathError });
|
|
151
|
+
const confirmationError = validateDeleteConfirmation({
|
|
152
|
+
...params,
|
|
153
|
+
method
|
|
154
|
+
});
|
|
155
|
+
if (confirmationError) return json$1({
|
|
156
|
+
error: confirmationError,
|
|
157
|
+
path: params.path
|
|
158
|
+
});
|
|
115
159
|
if ((method === "GET" || method === "DELETE") && params.body && Object.keys(params.body).length > 0) debugLog(`[qqbot-channel-api] ${method} request with body, body will be ignored`);
|
|
116
160
|
try {
|
|
117
161
|
const url = buildUrl(params.path, params.query);
|
|
@@ -162,7 +206,7 @@ async function executeChannelApi(params, options) {
|
|
|
162
206
|
}
|
|
163
207
|
try {
|
|
164
208
|
debugLog(`[qqbot-channel-api] <<< Status: ${res.status} ${res.statusText}`);
|
|
165
|
-
const rawBody = res.ok ? await res
|
|
209
|
+
const rawBody = res.ok ? await readProviderTextResponse(res, "QQ channel API response") : await readResponseTextLimited(res, CHANNEL_API_ERROR_BODY_LIMIT_BYTES);
|
|
166
210
|
if (!rawBody || rawBody.trim() === "") {
|
|
167
211
|
if (res.ok) return json$1({
|
|
168
212
|
success: true,
|
|
@@ -227,16 +271,17 @@ function registerChannelTool(api) {
|
|
|
227
271
|
api.registerTool({
|
|
228
272
|
name: "qqbot_channel_api",
|
|
229
273
|
label: "QQBot Channel API",
|
|
230
|
-
description: "Authenticated HTTP proxy for QQ Open Platform channel APIs. Common endpoints: list guilds GET /users/@me/guilds | list channels GET /guilds/{guild_id}/channels | get channel GET /channels/{channel_id} | create channel POST /guilds/{guild_id}/channels | list members GET /guilds/{guild_id}/members?after=0&limit=100 | get member GET /guilds/{guild_id}/members/{user_id} | list threads GET /channels/{channel_id}/threads | create thread PUT /channels/{channel_id}/threads | create announce POST /guilds/{guild_id}/announces | create schedule POST /channels/{channel_id}/schedules. See the qqbot-channel skill for full endpoint details.",
|
|
274
|
+
description: "Authenticated HTTP proxy for QQ Open Platform channel APIs. Use write and delete endpoints only after explicit user intent; DELETE requires confirmed=true, and bulk deletes require bulkConfirmed=true after confirming the exact target. Common endpoints: list guilds GET /users/@me/guilds | list channels GET /guilds/{guild_id}/channels | get channel GET /channels/{channel_id} | create channel POST /guilds/{guild_id}/channels | list members GET /guilds/{guild_id}/members?after=0&limit=100 | get member GET /guilds/{guild_id}/members/{user_id} | list threads GET /channels/{channel_id}/threads | create thread PUT /channels/{channel_id}/threads | create announce POST /guilds/{guild_id}/announces | create schedule POST /channels/{channel_id}/schedules. See the qqbot-channel skill for full endpoint details.",
|
|
231
275
|
parameters: ChannelApiSchema,
|
|
232
276
|
async execute(_toolCallId, params) {
|
|
233
|
-
const { getAccessToken } = await import("./sender-
|
|
277
|
+
const { getAccessToken } = await import("./sender-BHWpE_yH.js").then((n) => n._);
|
|
234
278
|
return executeChannelApi(params, { accessToken: await getAccessToken(account.appId, account.clientSecret) });
|
|
235
279
|
}
|
|
236
280
|
}, { name: "qqbot_channel_api" });
|
|
237
281
|
}
|
|
238
282
|
//#endregion
|
|
239
283
|
//#region extensions/qqbot/src/engine/tools/remind-logic.ts
|
|
284
|
+
const QQBOT_DEFAULT_REMINDER_TIMEZONE = "Asia/Shanghai";
|
|
240
285
|
/**
|
|
241
286
|
* JSON Schema for AI tool parameters (used by framework registration).
|
|
242
287
|
* AI Tool 参数的 JSON Schema 定义(供框架注册使用)。
|
|
@@ -246,7 +291,7 @@ const RemindSchema = {
|
|
|
246
291
|
properties: {
|
|
247
292
|
action: {
|
|
248
293
|
type: "string",
|
|
249
|
-
description: "Action type. add=create a reminder, list=show reminders, remove=delete a reminder.",
|
|
294
|
+
description: "Action type. add=create a reminder only after explicit user request, list=show reminders, remove=delete a reminder by confirmed job ID.",
|
|
250
295
|
enum: [
|
|
251
296
|
"add",
|
|
252
297
|
"list",
|
|
@@ -263,11 +308,11 @@ const RemindSchema = {
|
|
|
263
308
|
},
|
|
264
309
|
time: {
|
|
265
310
|
type: "string",
|
|
266
|
-
description: "Time description. Supported formats:\n1. Relative time, for example \"5m\", \"1h\", \"1h30m\", or \"2d\"\n2. Cron expression, for example \"0 8 * * *\" or \"0 9 * * 1-5\"\nValues containing spaces are treated as cron expressions; everything else is treated as a one-shot relative delay.\nRequired when action=add."
|
|
311
|
+
description: "Time description. Supported formats:\n1. Relative time, for example \"5m\", \"1h\", \"1h30m\", or \"2d\"\n2. Cron expression, for example \"0 8 * * *\" or \"0 9 * * 1-5\"\nValues containing spaces are treated as cron expressions; everything else is treated as a one-shot relative delay.\nRequired when action=add. Ask for clarification before scheduling if the time is ambiguous."
|
|
267
312
|
},
|
|
268
313
|
timezone: {
|
|
269
314
|
type: "string",
|
|
270
|
-
description: "
|
|
315
|
+
description: "Optional IANA timezone used for cron reminders. Include it when the user provides or confirms a timezone; if omitted, QQBot preserves its existing default timezone."
|
|
271
316
|
},
|
|
272
317
|
name: {
|
|
273
318
|
type: "string",
|
|
@@ -333,7 +378,7 @@ function isCronExpression(timeStr) {
|
|
|
333
378
|
*/
|
|
334
379
|
function generateJobName(content) {
|
|
335
380
|
const trimmed = content.trim();
|
|
336
|
-
return `Reminder: ${trimmed.length > 20 ? `${trimmed
|
|
381
|
+
return `Reminder: ${trimmed.length > 20 ? `${truncateUtf16Safe(trimmed, 20)}…` : trimmed}`;
|
|
337
382
|
}
|
|
338
383
|
/** Build the reminder system prompt sent to the AI. */
|
|
339
384
|
function buildReminderPrompt(content) {
|
|
@@ -370,7 +415,7 @@ function buildOnceJob(params, atMs, to, accountId) {
|
|
|
370
415
|
function buildCronJob(params, to, accountId) {
|
|
371
416
|
const content = params.content;
|
|
372
417
|
const name = params.name || generateJobName(content);
|
|
373
|
-
const tz = params.timezone ||
|
|
418
|
+
const tz = params.timezone || QQBOT_DEFAULT_REMINDER_TIMEZONE;
|
|
374
419
|
return {
|
|
375
420
|
action: "add",
|
|
376
421
|
job: {
|
|
@@ -456,7 +501,7 @@ function prepareRemindCronAction(params, ctx = {}) {
|
|
|
456
501
|
ok: true,
|
|
457
502
|
action: "add",
|
|
458
503
|
cronAction: buildCronJob(params, resolvedTo, resolvedAccountId),
|
|
459
|
-
summary: `⏰ Recurring reminder: "${params.content}" (${params.time}, tz=${params.timezone ||
|
|
504
|
+
summary: `⏰ Recurring reminder: "${params.content}" (${params.time}, tz=${params.timezone || QQBOT_DEFAULT_REMINDER_TIMEZONE})`
|
|
460
505
|
};
|
|
461
506
|
const delayMs = parseRelativeTime(params.time);
|
|
462
507
|
if (delayMs == null) return {
|
|
@@ -515,7 +560,7 @@ function createRemindTool(toolContext = {}, deps = defaultDeps) {
|
|
|
515
560
|
return {
|
|
516
561
|
name: "qqbot_remind",
|
|
517
562
|
label: "QQBot Reminder",
|
|
518
|
-
description: "Create, list, and remove QQ reminders. This tool schedules Gateway cron jobs directly; do not call the cron tool after it succeeds.\nCreate: action=add, content=message, time=schedule (to is optional, resolved automatically from the current conversation)\nList: action=list\nRemove: action=remove, jobId=job id from list\nTime examples: \"5m\", \"1h\", \"0 8 * * *\"",
|
|
563
|
+
description: "Create, list, and remove QQ reminders. Use only for explicit user requests, and ask when reminder content, schedule, or timezone is ambiguous. This tool schedules Gateway cron jobs directly; do not call the cron tool after it succeeds.\nCreate: action=add, content=message, time=schedule (to is optional, resolved automatically from the current conversation)\nList: action=list\nRemove: action=remove, jobId=job id from list\nTime examples: \"5m\", \"1h\", \"0 8 * * *\"; include timezone for recurring cron reminders when known.",
|
|
519
564
|
parameters: RemindSchema,
|
|
520
565
|
async execute(_toolCallId, params) {
|
|
521
566
|
const ctx = getRequestContext();
|
package/dist/tools-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as registerQQBotTools } from "./tools-
|
|
1
|
+
import { t as registerQQBotTools } from "./tools-BvXe7bTq.js";
|
|
2
2
|
export { registerQQBotTools };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/qqbot",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.7.1-beta.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/qqbot",
|
|
9
|
-
"version": "2026.
|
|
9
|
+
"version": "2026.7.1-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@tencent-connect/qqbot-connector": "1.1.0",
|
|
12
12
|
"mpg123-decoder": "1.0.3",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"zod": "4.4.3"
|
|
16
16
|
},
|
|
17
17
|
"peerDependencies": {
|
|
18
|
-
"openclaw": ">=2026.
|
|
18
|
+
"openclaw": ">=2026.7.1-beta.1"
|
|
19
19
|
},
|
|
20
20
|
"peerDependenciesMeta": {
|
|
21
21
|
"openclaw": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/qqbot",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.7.1-beta.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "OpenClaw QQ Bot channel plugin for group and direct-message workflows.",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"zod": "4.4.3"
|
|
17
17
|
},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"openclaw": ">=2026.
|
|
19
|
+
"openclaw": ">=2026.7.1-beta.1"
|
|
20
20
|
},
|
|
21
21
|
"peerDependenciesMeta": {
|
|
22
22
|
"openclaw": {
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"minHostVersion": ">=2026.4.10"
|
|
46
46
|
},
|
|
47
47
|
"compat": {
|
|
48
|
-
"pluginApi": ">=2026.
|
|
48
|
+
"pluginApi": ">=2026.7.1-beta.1"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.
|
|
51
|
+
"openclawVersion": "2026.7.1-beta.1"
|
|
52
52
|
},
|
|
53
53
|
"release": {
|
|
54
54
|
"publishToClawHub": true,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qqbot-channel
|
|
3
|
-
description: QQ channel management skill. Use qqbot_channel_api
|
|
3
|
+
description: QQ channel management skill. Use qqbot_channel_api for explicit QQ channel-management requests; confirm write, delete, and bulk actions before calling authenticated QQ Open Platform endpoints.
|
|
4
4
|
metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qqbot"] } } }
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -18,15 +18,25 @@ metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qq
|
|
|
18
18
|
|
|
19
19
|
## 🔧 工具参数
|
|
20
20
|
|
|
21
|
-
| 参数
|
|
22
|
-
|
|
|
23
|
-
| `method`
|
|
24
|
-
| `path`
|
|
25
|
-
| `body`
|
|
26
|
-
| `query`
|
|
21
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
22
|
+
| --------------- | ------- | ---- | ---------------------------------------------------------------------------- |
|
|
23
|
+
| `method` | string | 是 | HTTP 方法:`GET`, `POST`, `PUT`, `PATCH`, `DELETE` |
|
|
24
|
+
| `path` | string | 是 | API 路径(不含域名),如 `/guilds/{guild_id}/channels`,需替换占位符为实际值 |
|
|
25
|
+
| `body` | object | 否 | 请求体 JSON(POST/PUT/PATCH 使用) |
|
|
26
|
+
| `query` | object | 否 | URL 查询参数键值对,值为字符串类型 |
|
|
27
|
+
| `confirmed` | boolean | 否 | `DELETE` 必须传 `true`,表示用户已确认精确删除目标 |
|
|
28
|
+
| `bulkConfirmed` | boolean | 否 | 批量 `DELETE`(如删除全部公告)必须额外传 `true` |
|
|
27
29
|
|
|
28
30
|
> 基础 URL:`https://api.sgroup.qq.com`,鉴权头 `Authorization: QQBot {token}` 由工具自动填充。
|
|
29
31
|
|
|
32
|
+
## 🛡️ 安全边界
|
|
33
|
+
|
|
34
|
+
- 只在用户明确要求管理 QQ 频道、子频道、公告、论坛帖子或日程时调用写入接口。
|
|
35
|
+
- `POST`、`PUT`、`PATCH` 和 `DELETE` 会修改真实 QQ 资源。调用前先复述目标频道/子频道/帖子/日程和预期改动;删除、批量删除、公告覆盖等不可逆或大范围操作必须等用户确认后再执行。
|
|
36
|
+
- 删除前优先用 `GET`/列表接口查出候选项,让用户选择具体 ID;不要根据模糊名称猜测删除目标。
|
|
37
|
+
- `DELETE` 请求必须传 `confirmed: true`,否则工具会拒绝执行。`announces/all` 这样的批量操作还必须传 `bulkConfirmed: true`,只有在用户明确说要删除全部公告并再次确认后才可使用。
|
|
38
|
+
- 成员资料、头像 URL、频道图标等属于用户/群组资料。默认只总结必要字段;只有用户要求查看头像/图标或视觉比对时才内联展示图片,不要无关转发头像 URL。
|
|
39
|
+
|
|
30
40
|
---
|
|
31
41
|
|
|
32
42
|
## ⭐ 接口速查
|
|
@@ -40,13 +50,13 @@ metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qq
|
|
|
40
50
|
|
|
41
51
|
### 子频道(Channel)
|
|
42
52
|
|
|
43
|
-
| 操作 | 方法
|
|
44
|
-
| -------------- |
|
|
45
|
-
| 获取子频道列表 | `GET`
|
|
46
|
-
| 获取子频道详情 | `GET`
|
|
47
|
-
| 创建子频道 | `POST`
|
|
48
|
-
| 修改子频道 | `PATCH`
|
|
49
|
-
| 删除子频道 |
|
|
53
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
54
|
+
| -------------- | ------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
55
|
+
| 获取子频道列表 | `GET` | `/guilds/{guild_id}/channels` | — |
|
|
56
|
+
| 获取子频道详情 | `GET` | `/channels/{channel_id}` | — |
|
|
57
|
+
| 创建子频道 | `POST` | `/guilds/{guild_id}/channels` | body: `name`\*, `type`\*, `position`\*, `sub_type`, `parent_id`, `private_type`, `private_user_ids`, `speak_permission`, `application_id` |
|
|
58
|
+
| 修改子频道 | `PATCH` | `/channels/{channel_id}` | body: `name`, `position`, `parent_id`, `private_type`, `speak_permission`(至少一个) |
|
|
59
|
+
| 删除子频道 | — | 见受确认保护的删除流程 | 破坏性操作;不要在未确认时调用 |
|
|
50
60
|
|
|
51
61
|
**子频道类型(type)**:`0`=文字, `2`=语音, `4`=分组(position≥2), `10005`=直播, `10006`=应用, `10007`=论坛
|
|
52
62
|
|
|
@@ -61,28 +71,28 @@ metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qq
|
|
|
61
71
|
|
|
62
72
|
### 公告(Announces)
|
|
63
73
|
|
|
64
|
-
| 操作 | 方法
|
|
65
|
-
| -------- |
|
|
66
|
-
| 创建公告 | `POST`
|
|
67
|
-
| 删除公告 |
|
|
74
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
75
|
+
| -------- | ------ | ------------------------------ | ------------------------------------------------------------------------------------------------ |
|
|
76
|
+
| 创建公告 | `POST` | `/guilds/{guild_id}/announces` | body: `message_id`, `channel_id`, `announces_type`(0=成员,1=欢迎), `recommend_channels`(最多3条) |
|
|
77
|
+
| 删除公告 | — | 见受确认保护的删除流程 | 破坏性操作;批量删除需二次确认 |
|
|
68
78
|
|
|
69
79
|
### 论坛(Forum)— 仅私域机器人
|
|
70
80
|
|
|
71
|
-
| 操作 | 方法
|
|
72
|
-
| ------------ |
|
|
73
|
-
| 获取帖子列表 | `GET`
|
|
74
|
-
| 获取帖子详情 | `GET`
|
|
75
|
-
| 发表帖子 | `PUT`
|
|
76
|
-
| 删除帖子 |
|
|
77
|
-
| 发表评论 | `POST`
|
|
81
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
82
|
+
| ------------ | ------ | ---------------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
83
|
+
| 获取帖子列表 | `GET` | `/channels/{channel_id}/threads` | — |
|
|
84
|
+
| 获取帖子详情 | `GET` | `/channels/{channel_id}/threads/{thread_id}` | — |
|
|
85
|
+
| 发表帖子 | `PUT` | `/channels/{channel_id}/threads` | body: `title`\*, `content`\*, `format`(1=文本,2=HTML,3=Markdown,4=JSON,默认3) |
|
|
86
|
+
| 删除帖子 | — | 见受确认保护的删除流程 | 破坏性操作;不要在未确认时调用 |
|
|
87
|
+
| 发表评论 | `POST` | `/channels/{channel_id}/threads/{thread_id}/comment` | body: `thread_author`\*, `content`\*, `thread_create_time`, `image` |
|
|
78
88
|
|
|
79
89
|
### 日程(Schedule)
|
|
80
90
|
|
|
81
|
-
| 操作 | 方法
|
|
82
|
-
| -------- |
|
|
83
|
-
| 创建日程 | `POST`
|
|
84
|
-
| 修改日程 | `PATCH`
|
|
85
|
-
| 删除日程 |
|
|
91
|
+
| 操作 | 方法 | 路径 | 参数说明 |
|
|
92
|
+
| -------- | ------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
|
|
93
|
+
| 创建日程 | `POST` | `/channels/{channel_id}/schedules` | body: `{ schedule: { name*, start_timestamp*, end_timestamp*, jump_channel_id, remind_type } }` |
|
|
94
|
+
| 修改日程 | `PATCH` | `/channels/{channel_id}/schedules/{schedule_id}` | body: `{ schedule: { name*, start_timestamp*, end_timestamp*, jump_channel_id, remind_type } }` |
|
|
95
|
+
| 删除日程 | — | 见受确认保护的删除流程 | 破坏性操作;不要在未确认时调用 |
|
|
86
96
|
|
|
87
97
|
**提醒类型(remind_type)**:`"0"`=不提醒, `"1"`=开始时, `"2"`=5分钟前, `"3"`=15分钟前, `"4"`=30分钟前, `"5"`=60分钟前
|
|
88
98
|
|
|
@@ -180,14 +190,17 @@ metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qq
|
|
|
180
190
|
}
|
|
181
191
|
```
|
|
182
192
|
|
|
183
|
-
###
|
|
193
|
+
### 受确认保护的删除流程
|
|
184
194
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
|
|
195
|
+
删除类 QQ API 不作为普通速查示例暴露。若用户明确要求删除资源,先读取并复述目标对象,确认后再调用 `qqbot_channel_api`:`method` 设为 `"DELETE"`,`confirmed` 设为 `true`,`path` 使用已确认对象对应的资源路径。
|
|
196
|
+
|
|
197
|
+
| 删除对象 | 已确认后使用的 `path` | 额外要求 |
|
|
198
|
+
| -------- | ------------------------------------------------ | ---------------------------------------- |
|
|
199
|
+
| 子频道 | `/channels/{channel_id}` | 确认子频道 ID 和名称 |
|
|
200
|
+
| 单条公告 | `/guilds/{guild_id}/announces/{message_id}` | 确认公告 ID |
|
|
201
|
+
| 全部公告 | `/guilds/{guild_id}/announces/all` | 用户再次确认后再传 `bulkConfirmed: true` |
|
|
202
|
+
| 帖子 | `/channels/{channel_id}/threads/{thread_id}` | 确认帖子 ID、标题/作者 |
|
|
203
|
+
| 日程 | `/channels/{channel_id}/schedules/{schedule_id}` | 确认日程 ID、名称/时间 |
|
|
191
204
|
|
|
192
205
|
---
|
|
193
206
|
|
|
@@ -222,7 +235,7 @@ metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qq
|
|
|
222
235
|
|
|
223
236
|
### 展示成员头像
|
|
224
237
|
|
|
225
|
-
成员详情返回的 `user.avatar` 是头像 URL
|
|
238
|
+
成员详情返回的 `user.avatar` 是头像 URL。默认只展示昵称、ID、加入时间等必要字段;当用户明确要求查看头像/图标或头像是当前任务的必要依据时,再用 Markdown 图片语法内联展示:
|
|
226
239
|
|
|
227
240
|
```
|
|
228
241
|
成员信息:
|
|
@@ -231,7 +244,7 @@ metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qq
|
|
|
231
244
|

|
|
232
245
|
```
|
|
233
246
|
|
|
234
|
-
|
|
247
|
+
不要无关输出原始头像 URL 或把头像作为普通链接转发。频道的 `icon` 字段同理:仅在用户明确需要查看时展示。
|
|
235
248
|
|
|
236
249
|
---
|
|
237
250
|
|
|
@@ -255,8 +268,8 @@ metadata: { "openclaw": { "emoji": "📡", "requires": { "config": ["channels.qq
|
|
|
255
268
|
3. **成员列表翻页**时可能返回重复成员,需按 `user.id` 去重
|
|
256
269
|
4. **公告**的两种类型(消息公告和推荐子频道公告)会互相顶替
|
|
257
270
|
5. **日程**的时间戳为毫秒级字符串
|
|
258
|
-
6.
|
|
271
|
+
6. **删除操作不可逆**,必须先确认精确目标并传 `confirmed: true`;批量删除需二次确认并传 `bulkConfirmed: true`
|
|
259
272
|
7. **论坛操作**仅私域机器人可用
|
|
260
273
|
8. **子频道分组**(type=4)的 `position` 必须 >= 2
|
|
261
274
|
9. **日程操作**有频率限制:单个管理员每天 10 次,单个频道每天 100 次
|
|
262
|
-
10. **头像/图标展示**:成员 `user.avatar` 和频道 `icon` 等图片 URL
|
|
275
|
+
10. **头像/图标展示**:成员 `user.avatar` 和频道 `icon` 等图片 URL 属于资料信息;默认总结必要字段,只在用户明确需要查看图片时用 Markdown 图片语法 `` 展示
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
通过 `qqbot_channel_api` 工具代理请求,工具自动处理鉴权。
|
|
6
6
|
|
|
7
|
+
## 调用安全规则
|
|
8
|
+
|
|
9
|
+
- `POST`、`PUT`、`PATCH` 和 `DELETE` 会修改真实 QQ 资源。调用前确认用户明确授权了该操作。
|
|
10
|
+
- 删除接口不可逆。删除前先用读取接口确认目标 ID、名称和范围,并把将要删除的对象复述给用户;`qqbot_channel_api` 要求 `confirmed: true` 才会执行 `DELETE`。
|
|
11
|
+
- 批量删除 sentinel 必须二次确认并额外传 `bulkConfirmed: true`;不要把模糊表达自动扩展成“删除全部”。
|
|
12
|
+
- 删除端点不作为普通 agent 速查路径列出。需要删除时,先用读取接口确认对象,再通过受确认保护的删除流程执行。
|
|
13
|
+
- 成员资料和头像 URL 只用于当前请求;除非用户明确要求查看头像/图标,不要内联展示或转发这些图片 URL。
|
|
14
|
+
|
|
7
15
|
---
|
|
8
16
|
|
|
9
17
|
## 📌 通用说明
|
|
@@ -346,9 +354,9 @@ interface Schedule {
|
|
|
346
354
|
|
|
347
355
|
---
|
|
348
356
|
|
|
349
|
-
###
|
|
357
|
+
### 删除子频道(破坏性操作)
|
|
350
358
|
|
|
351
|
-
> ⚠️
|
|
359
|
+
> ⚠️ 不可逆!仅私域机器人可用。调用前必须确认具体子频道 ID、子频道名称和用户删除意图,并传 `confirmed: true`;不要按模糊名称猜测删除目标。确认后使用子频道资源路径 `/channels/{channel_id}`。
|
|
352
360
|
|
|
353
361
|
---
|
|
354
362
|
|
|
@@ -415,9 +423,9 @@ interface Schedule {
|
|
|
415
423
|
|
|
416
424
|
---
|
|
417
425
|
|
|
418
|
-
###
|
|
426
|
+
### 删除公告(破坏性操作)
|
|
419
427
|
|
|
420
|
-
> `message_id
|
|
428
|
+
> 调用前必须确认具体公告 ID 并传 `confirmed: true`,确认后使用公告资源路径 `/guilds/{guild_id}/announces/{message_id}`。批量删除全部公告只能在用户明确要求并再次确认后使用 `/guilds/{guild_id}/announces/all`,并且必须额外传 `bulkConfirmed: true`。
|
|
421
429
|
|
|
422
430
|
---
|
|
423
431
|
|
|
@@ -453,9 +461,9 @@ interface Schedule {
|
|
|
453
461
|
|
|
454
462
|
---
|
|
455
463
|
|
|
456
|
-
###
|
|
464
|
+
### 删除帖子(破坏性操作)
|
|
457
465
|
|
|
458
|
-
> ⚠️
|
|
466
|
+
> ⚠️ 不可逆!仅私域机器人可用。调用前必须确认具体帖子 ID、帖子标题/作者和用户删除意图,并传 `confirmed: true`。确认后使用帖子资源路径 `/channels/{channel_id}/threads/{thread_id}`。
|
|
459
467
|
|
|
460
468
|
---
|
|
461
469
|
|
|
@@ -516,6 +524,6 @@ interface Schedule {
|
|
|
516
524
|
|
|
517
525
|
---
|
|
518
526
|
|
|
519
|
-
###
|
|
527
|
+
### 删除日程(破坏性操作)
|
|
520
528
|
|
|
521
|
-
> ⚠️
|
|
529
|
+
> ⚠️ 不可逆!需要管理频道权限。调用前必须确认具体日程 ID、日程名称/时间和用户删除意图,并传 `confirmed: true`。确认后使用日程资源路径 `/channels/{channel_id}/schedules/{schedule_id}`。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: qqbot-media
|
|
3
|
-
description: QQBot rich media send and receive support. Use <qqmedia> tags
|
|
3
|
+
description: QQBot rich media send and receive support. Use <qqmedia> tags only for explicit media send/view requests, treating inbound attachment paths as private current-conversation context.
|
|
4
4
|
metadata: { "openclaw": { "emoji": "📸", "requires": { "config": ["channels.qqbot"] } } }
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -22,8 +22,10 @@ metadata: { "openclaw": { "emoji": "📸", "requires": { "config": ["channels.qq
|
|
|
22
22
|
|
|
23
23
|
## 接收媒体
|
|
24
24
|
|
|
25
|
-
-
|
|
26
|
-
- 用户发来的**语音**路径在上下文中;若有 STT
|
|
25
|
+
- 用户发来的**图片**会由 QQBot 运行时下载到 OpenClaw 管理的 QQBot media 目录,路径只作为当前会话的附件上下文使用。
|
|
26
|
+
- 用户发来的**语音**路径在上下文中;若有 STT 能力则优先转写。
|
|
27
|
+
- 附件路径和远程 URL 可能包含用户私有内容。不要无关输出本地绝对路径,不要把附件转发到其他会话;只有用户明确要求回发、分析或转存该媒体时才使用。
|
|
28
|
+
- 不承诺长期保留附件。若用户需要长期保存,说明应由用户自行保存或重新发送。
|
|
27
29
|
|
|
28
30
|
## 规则
|
|
29
31
|
|
|
@@ -35,3 +37,4 @@ metadata: { "openclaw": { "emoji": "📸", "requires": { "config": ["channels.qq
|
|
|
35
37
|
6. 发送语音时不要重复语音中已朗读的文字
|
|
36
38
|
7. 多个媒体用多个标签
|
|
37
39
|
8. 以会话上下文中的能力说明为准(如未启用语音则不要发语音)
|
|
40
|
+
9. 不要扫描或发送上下文之外的本地文件;只使用用户提供、工具生成,或明确位于受信 media 目录中的路径
|