@openclaw/qqbot 2026.6.10 → 2026.6.11

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 CHANGED
@@ -1,8 +1,8 @@
1
- import { t as qqbotPlugin } from "./channel-jbnSeYkd.js";
2
- import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-ZEfgeoL4.js";
3
- import { t as qqbotSetupPlugin } from "./channel.setup-CQ_DFfPx.js";
4
- import { t as getFrameworkCommands } from "./slash-commands-impl-BUH24Ycv.js";
5
- import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-CV5Y0isN.js";
6
- import { t as registerQQBotFull } from "./channel-entry-5YxjzTrA.js";
1
+ import { t as qqbotPlugin } from "./channel-DvWrqcZB.js";
2
+ import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-C1qZbh0K.js";
3
+ import { t as qqbotSetupPlugin } from "./channel.setup-CldhUlTO.js";
4
+ import { o as getFrameworkCommands } from "./group-CvVXke_k.js";
5
+ import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-CeUI9pG-.js";
6
+ import { t as registerQQBotFull } from "./channel-entry-BGXFRmbg.js";
7
7
  import { C as getMessageReplyConfig, D as OUTBOUND_ERROR_CODES, E as DEFAULT_MEDIA_SEND_ERROR, O as setOutboundAudioPort, S as checkMessageReplyLimit, T as recordMessageReply, _ as sendVideoMsg, a as sendText, b as resolveUserFacingMediaError, f as buildMediaTarget, g as sendPhoto, h as sendDocument, i as sendProactiveMessage, m as resolveOutboundMediaPath, n as sendCronMessage, p as parseTarget, r as sendMedia, v as sendVoice, w as getMessageReplyStats, x as MESSAGE_REPLY_LIMIT } from "./outbound-BQcPCo-z.js";
8
8
  export { DEFAULT_ACCOUNT_ID, DEFAULT_MEDIA_SEND_ERROR, MESSAGE_REPLY_LIMIT, OUTBOUND_ERROR_CODES, applyQQBotAccountConfig, buildMediaTarget, checkMessageReplyLimit, getFrameworkCommands, getMessageReplyConfig, getMessageReplyStats, listQQBotAccountIds, parseTarget, qqbotPlugin, qqbotSetupPlugin, recordMessageReply, registerChannelTool, registerQQBotFull, registerQQBotTools, registerRemindTool, resolveDefaultQQBotAccountId, resolveOutboundMediaPath, resolveQQBotAccount, resolveUserFacingMediaError, sendCronMessage, sendDocument, sendMedia, sendPhoto, sendProactiveMessage, sendText, sendVideoMsg, sendVoice, setOutboundAudioPort };
@@ -1,6 +1,6 @@
1
1
  import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
2
- import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, o as ensurePlatformAdapter, r as qqbotConfigAdapter, s as getBridgeLogger, t as qqbotChannelConfigSchema } from "./config-schema-BFLNZ8Nf.js";
3
- import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-ZEfgeoL4.js";
2
+ import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, o as ensurePlatformAdapter, r as qqbotConfigAdapter, s as getBridgeLogger, t as qqbotChannelConfigSchema } from "./config-schema-gMW9zKvQ.js";
3
+ import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-C1qZbh0K.js";
4
4
  import { t as getQQBotRuntime } from "./runtime-D1hn91-s.js";
5
5
  import { n as normalizeCompatibilityConfig, t as legacyConfigRules } from "./doctor-contract-BYTS8-ia.js";
6
6
  import { n as normalizeTarget, s as getQQBotDataPath, t as looksLikeQQBotTarget } from "./target-parser-BdCUmxK7.js";
@@ -370,7 +370,7 @@ function createQQBotApprovalCapability() {
370
370
  },
371
371
  load: async () => {
372
372
  ensurePlatformAdapter();
373
- return (await import("./handler-runtime-D0l75JR8.js")).qqbotApprovalNativeRuntime;
373
+ return (await import("./handler-runtime--OTMkFWa.js")).qqbotApprovalNativeRuntime;
374
374
  }
375
375
  })
376
376
  });
@@ -932,7 +932,7 @@ function isShortTableRowLine(line, table) {
932
932
  function isTableSeparatorLine(line) {
933
933
  if (!isTableRowLine(line)) return false;
934
934
  const cells = splitTableCells(line);
935
- return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell.trim()));
935
+ return cells.length > 0 && cells.every((cell) => /^:?-+:?$/.test(cell.trim()));
936
936
  }
937
937
  function splitTableCells(line) {
938
938
  return line.trim().slice(1, -1).split("|").map((cell) => cell.trim());
@@ -1066,7 +1066,7 @@ function resolveQQBotGroupToolPolicy(params) {
1066
1066
  //#region extensions/qqbot/src/channel.ts
1067
1067
  let gatewayModulePromise;
1068
1068
  function loadGatewayModule() {
1069
- gatewayModulePromise ??= import("./gateway-DilADGTY.js");
1069
+ gatewayModulePromise ??= import("./gateway-CCkrdgaK.js");
1070
1070
  return gatewayModulePromise;
1071
1071
  }
1072
1072
  let outboundMessagingModulePromise;
@@ -1,6 +1,6 @@
1
- import { a as resolveQQBotAccount } from "./config-ZEfgeoL4.js";
2
- import { t as getFrameworkCommands } from "./slash-commands-impl-BUH24Ycv.js";
3
- import { t as registerQQBotTools } from "./tools-CV5Y0isN.js";
1
+ import { a as resolveQQBotAccount } from "./config-C1qZbh0K.js";
2
+ import { d as PRIVATE_CHAT_ONLY_TEXT, n as resolveGroupCommandLevelFromAccountConfig, o as getFrameworkCommands } from "./group-CvVXke_k.js";
3
+ import { t as registerQQBotTools } from "./tools-CeUI9pG-.js";
4
4
  import { h as sendDocument } from "./outbound-BQcPCo-z.js";
5
5
  //#region extensions/qqbot/src/bridge/commands/framework-context-adapter.ts
6
6
  /**
@@ -15,7 +15,7 @@ const DEFAULT_QUEUE_SNAPSHOT = {
15
15
  maxConcurrentUsers: 10,
16
16
  senderPending: 0
17
17
  };
18
- function buildFrameworkSlashContext({ ctx, account, from, commandName }) {
18
+ function buildFrameworkSlashContext({ ctx, account, from, commandName, groupCommandLevel }) {
19
19
  const args = ctx.args ?? "";
20
20
  const rawContent = args ? `/${commandName} ${args}` : `/${commandName}`;
21
21
  return {
@@ -30,6 +30,7 @@ function buildFrameworkSlashContext({ ctx, account, from, commandName }) {
30
30
  appId: account.appId,
31
31
  accountConfig: account.config,
32
32
  commandAuthorized: ctx.isAuthorizedSender,
33
+ groupCommandLevel,
33
34
  queueSnapshot: { ...DEFAULT_QUEUE_SNAPSHOT }
34
35
  };
35
36
  }
@@ -97,7 +98,6 @@ async function dispatchFrameworkSlashResult({ result, account, from, logger }) {
97
98
  }
98
99
  //#endregion
99
100
  //#region extensions/qqbot/src/bridge/commands/framework-registration.ts
100
- const PRIVATE_CHAT_ONLY_TEXT = "💡 请在私聊中使用此指令";
101
101
  function isExplicitQQBotC2cFrom(from) {
102
102
  const raw = (from ?? "").trim();
103
103
  const stripped = raw.replace(/^qqbot:/iu, "");
@@ -115,14 +115,16 @@ function registerQQBotFrameworkCommands(api) {
115
115
  requireAuth: true,
116
116
  acceptsArgs: true,
117
117
  handler: async (ctx) => {
118
- if (cmd.c2cOnly && !isExplicitQQBotC2cFrom(ctx.from)) return { text: PRIVATE_CHAT_ONLY_TEXT };
119
118
  const from = parseQQBotFrom(ctx.from);
120
119
  const account = resolveQQBotAccount(ctx.config, ctx.accountId ?? void 0);
120
+ const groupCommandLevel = from.msgType === "group" || from.msgType === "guild" ? resolveGroupCommandLevelFromAccountConfig(account.config, from.targetId) : void 0;
121
+ if (cmd.c2cOnly && !isExplicitQQBotC2cFrom(ctx.from)) return { text: PRIVATE_CHAT_ONLY_TEXT };
121
122
  const slashCtx = buildFrameworkSlashContext({
122
123
  ctx,
123
124
  account,
124
125
  from,
125
- commandName: cmd.name
126
+ commandName: cmd.name,
127
+ groupCommandLevel
126
128
  });
127
129
  return await dispatchFrameworkSlashResult({
128
130
  result: await cmd.handler(slashCtx),
@@ -1,2 +1,2 @@
1
- import { t as registerQQBotFull } from "./channel-entry-5YxjzTrA.js";
1
+ import { t as registerQQBotFull } from "./channel-entry-BGXFRmbg.js";
2
2
  export { registerQQBotFull };
@@ -1,2 +1,2 @@
1
- import { t as qqbotPlugin } from "./channel-jbnSeYkd.js";
1
+ import { t as qqbotPlugin } from "./channel-DvWrqcZB.js";
2
2
  export { qqbotPlugin };
@@ -1,4 +1,4 @@
1
- import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-BFLNZ8Nf.js";
1
+ import { a as qqbotSetupAdapterShared, i as qqbotMeta, n as qqbotSetupWizard, r as qqbotConfigAdapter, t as qqbotChannelConfigSchema } from "./config-schema-gMW9zKvQ.js";
2
2
  //#region extensions/qqbot/src/channel.setup.ts
3
3
  /**
4
4
  * Setup-only QQBot plugin — lightweight subset used during `openclaw onboard`
@@ -74,8 +74,11 @@ function resolveAccountBase(cfg, accountId) {
74
74
  let accountConfig;
75
75
  let appId;
76
76
  if (resolvedAccountId === "default") {
77
- accountConfig = normalizeAccountConfig(asOptionalObjectRecord(qqbot));
78
- appId = normalizeAppId(qqbot?.appId);
77
+ accountConfig = normalizeAccountConfig({
78
+ ...asOptionalObjectRecord(qqbot),
79
+ ...asOptionalObjectRecord(qqbot?.accounts?.[DEFAULT_ACCOUNT_ID$1])
80
+ });
81
+ appId = normalizeAppId(accountConfig.appId);
79
82
  } else {
80
83
  const account = qqbot?.accounts?.[resolvedAccountId];
81
84
  accountConfig = normalizeAccountConfig(asOptionalObjectRecord(account));
@@ -1,5 +1,5 @@
1
1
  import { d as registerPlatformAdapterFactory, l as hasPlatformAdapter, n as normalizeLowercaseStringOrEmpty, u as registerPlatformAdapter } from "./string-normalize-R_0cKO7Q.js";
2
- import { a as resolveQQBotAccount, c as describeAccount, i as resolveDefaultQQBotAccountId, l as formatAllowFrom, n as applyQQBotAccountConfig, r as listQQBotAccountIds, s as applyAccountConfig, u as isAccountConfigured } from "./config-ZEfgeoL4.js";
2
+ import { a as resolveQQBotAccount, c as describeAccount, i as resolveDefaultQQBotAccountId, l as formatAllowFrom, n as applyQQBotAccountConfig, r as listQQBotAccountIds, s as applyAccountConfig, u as isAccountConfigured } from "./config-C1qZbh0K.js";
3
3
  import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString, normalizeSecretInputString } from "openclaw/plugin-sdk/secret-input";
4
4
  import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
5
5
  import { applyAccountNameToChannelSection, deleteAccountFromConfigSection, setAccountEnabledInConfigSection } from "openclaw/plugin-sdk/core";
@@ -401,8 +401,14 @@ const QQBotGroupPolicySchema = z.enum([
401
401
  "allowlist",
402
402
  "disabled"
403
403
  ]).optional();
404
+ const QQBotGroupCommandLevelSchema = z.enum([
405
+ "all",
406
+ "safety",
407
+ "strict"
408
+ ]).optional();
404
409
  const QQBotGroupSchema = z.object({
405
410
  requireMention: z.boolean().optional(),
411
+ commandLevel: QQBotGroupCommandLevelSchema,
406
412
  ignoreOtherMentions: z.boolean().optional(),
407
413
  historyLimit: z.number().optional(),
408
414
  name: z.string().optional(),
@@ -1,15 +1,15 @@
1
1
  import { C as getNextMsgSeq, D as downloadFile, F as StreamInputMode, I as StreamInputState, M as getMaxUploadSize, P as StreamContentType, T as openLocalFile, a as createRawInputNotifyFn, b as stopBackgroundTokenRefresh, c as getMessageApi, d as initSender, f as onMessageSent, g as sendText, h as sendMedia, i as clearTokenCache, j as getImageMimeType, k as formatFileSize, l as getPluginUserAgent, m as sendInputNotify, n as acknowledgeInteraction, o as getAccessToken, p as registerAccount, r as buildDeliveryTarget, s as getGatewayUrl, t as accountToCreds, u as initApiConfig, x as withTokenRetry, y as startBackgroundTokenRefresh } from "./sender-CRZ2af7P.js";
2
2
  import { c as getPlatformAdapter, i as normalizeOptionalString$1, n as normalizeLowercaseStringOrEmpty$1, o as readStringField, s as sanitizeFileName, t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
3
- import { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-BFLNZ8Nf.js";
4
- import { a as toGatewayAccount, i as openQQBotSyncKeyedStore, m as parseApprovalButtonData, n as createQQBotMarkdownChunker, o as authorizeQQBotApprovalAction, r as buildQQBotStateKey } from "./channel-jbnSeYkd.js";
5
- import { d as resolveAccountBase } from "./config-ZEfgeoL4.js";
3
+ import { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-gMW9zKvQ.js";
4
+ import { a as toGatewayAccount, i as openQQBotSyncKeyedStore, m as parseApprovalButtonData, n as createQQBotMarkdownChunker, o as authorizeQQBotApprovalAction, r as buildQQBotStateKey } from "./channel-DvWrqcZB.js";
5
+ import { d as resolveAccountBase } from "./config-C1qZbh0K.js";
6
6
  import { a as formatErrorMessage, i as formatDuration, n as debugLog, r as debugWarn, t as debugError } from "./log-SDfMMBWe.js";
7
7
  import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-D1hn91-s.js";
8
8
  import { a as getHomeDir, c as getQQBotMediaDir, d as isLocalPath, f as isWindows, i as checkSilkWasmAvailable, o as getQQBotDataDir, p as normalizePath, s as getQQBotDataPath, u as getTempDir } from "./target-parser-BdCUmxK7.js";
9
- import { a as matchSlashCommand, i as initCommands, n as getFrameworkVersion, r as getPluginVersion } from "./slash-commands-impl-BUH24Ycv.js";
9
+ import { a as resolveMentionPatterns, c as getPluginVersion, d as PRIVATE_CHAT_ONLY_TEXT, f as classifyCoreCommandForGroup, i as resolveGroupSettings, l as initCommands, n as resolveGroupCommandLevelFromAccountConfig, r as resolveGroupConfig, s as getFrameworkVersion, u as matchSlashCommand } from "./group-CvVXke_k.js";
10
10
  import { n as runWithRequestContext } from "./request-context-Bm7PTBD1.js";
11
11
  import { E as DEFAULT_MEDIA_SEND_ERROR, O as setOutboundAudioPort, _ as sendVideoMsg, b as resolveUserFacingMediaError, c as isCronReminderPayload, d as normalizeMediaTags, g as sendPhoto, h as sendDocument, l as isMediaPayload, o as decodeMediaPath, r as sendMedia$1, s as encodePayloadForCron, u as parseQQBotPayload, v as sendVoice, y as resolveTrustedOutboundMediaPath } from "./outbound-BQcPCo-z.js";
12
- import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
12
+ import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
13
13
  import { isImplicitSameChatApprovalAuthorization } from "openclaw/plugin-sdk/approval-auth-runtime";
14
14
  import * as fs$1 from "node:fs";
15
15
  import fs from "node:fs";
@@ -479,6 +479,12 @@ function resolveSlashCommandAuth(params) {
479
479
  }
480
480
  //#endregion
481
481
  //#region extensions/qqbot/src/engine/commands/slash-command-handler.ts
482
+ /**
483
+ * Slash command handler — intercept slash commands before message queue.
484
+ *
485
+ * Extracted from gateway.ts to keep the gateway connection logic thin.
486
+ * Handles urgent commands, normal slash commands, and file delivery.
487
+ */
482
488
  const URGENT_COMMANDS = ["/stop"];
483
489
  /**
484
490
  * Check if the message is a slash command and handle it.
@@ -490,14 +496,8 @@ async function trySlashCommand(msg, ctx) {
490
496
  const { account, log } = ctx;
491
497
  const content = (msg.content ?? "").trim();
492
498
  if (!content.startsWith("/")) return "enqueue";
493
- const contentLower = content.toLowerCase();
494
- if (URGENT_COMMANDS.some((cmd) => contentLower === cmd.toLowerCase() || contentLower.startsWith(cmd.toLowerCase() + " "))) {
495
- log?.info(`Urgent command detected: ${content.slice(0, 20)}`);
496
- return "urgent";
497
- }
498
- const receivedAt = Date.now();
499
- const peerId = ctx.getMessagePeerId(msg);
500
499
  const isGroup = msg.type === "group" || msg.type === "guild";
500
+ const groupCommandLevel = isGroup ? resolveGroupCommandLevelFromAccountConfig(account.config, msg.groupOpenid ?? msg.channelId ?? null) : void 0;
501
501
  const commandsAllowFrom = resolveQQBotCommandsAllowFrom(ctx.cfg);
502
502
  const commandAuthorized = ctx.resolveCommandAuthorized ? await ctx.resolveCommandAuthorized({
503
503
  isGroup,
@@ -513,6 +513,14 @@ async function trySlashCommand(msg, ctx) {
513
513
  groupAllowFrom: account.config?.groupAllowFrom,
514
514
  commandsAllowFrom
515
515
  });
516
+ const contentLower = content.toLowerCase();
517
+ if (URGENT_COMMANDS.some((cmd) => contentLower === cmd.toLowerCase() || contentLower.startsWith(cmd.toLowerCase() + " "))) {
518
+ if (isGroup && !commandAuthorized) return "enqueue";
519
+ log?.info(`Urgent command detected: ${content.slice(0, 20)}`);
520
+ return "urgent";
521
+ }
522
+ const receivedAt = Date.now();
523
+ const peerId = ctx.getMessagePeerId(msg);
516
524
  const cmdCtx = {
517
525
  type: msg.type,
518
526
  senderId: msg.senderId,
@@ -528,6 +536,7 @@ async function trySlashCommand(msg, ctx) {
528
536
  appId: account.appId,
529
537
  accountConfig: account.config,
530
538
  commandAuthorized,
539
+ groupCommandLevel,
531
540
  queueSnapshot: ctx.getQueueSnapshot(peerId)
532
541
  };
533
542
  try {
@@ -2569,72 +2578,6 @@ function classifyMedia(processed) {
2569
2578
  voiceTranscriptSources: processed.voiceTranscriptSources
2570
2579
  };
2571
2580
  }
2572
- const DEFAULT_GROUP_CONFIG = {
2573
- requireMention: true,
2574
- ignoreOtherMentions: false,
2575
- name: "",
2576
- historyLimit: 50
2577
- };
2578
- function readGroupsMap(cfg, accountId) {
2579
- const groups = asOptionalObjectRecord(resolveAccountBase(cfg, accountId).config.groups);
2580
- if (!groups) return {};
2581
- const normalized = {};
2582
- for (const [key, value] of Object.entries(groups)) {
2583
- const sub = asOptionalObjectRecord(value);
2584
- if (sub) normalized[key] = sub;
2585
- }
2586
- return normalized;
2587
- }
2588
- function readBoolean(obj, key) {
2589
- return asBoolean(obj[key]);
2590
- }
2591
- function readString(obj, key) {
2592
- const v = obj[key];
2593
- return typeof v === "string" && v.length > 0 ? v : void 0;
2594
- }
2595
- function readHistoryLimit(obj, key) {
2596
- const v = obj[key];
2597
- if (typeof v !== "number" || !Number.isFinite(v)) return;
2598
- return Math.max(0, Math.floor(v));
2599
- }
2600
- function resolveGroupConfig(cfg, groupOpenid, accountId) {
2601
- const account = resolveAccountBase(cfg, accountId);
2602
- const groups = readGroupsMap(cfg, accountId);
2603
- const wildcard = groups["*"] ?? {};
2604
- const specific = groupOpenid ? groups[groupOpenid] ?? {} : {};
2605
- const accountDefaultRequireMention = asBoolean(account.config.defaultRequireMention) ?? DEFAULT_GROUP_CONFIG.requireMention;
2606
- return {
2607
- requireMention: readBoolean(specific, "requireMention") ?? readBoolean(wildcard, "requireMention") ?? accountDefaultRequireMention,
2608
- ignoreOtherMentions: readBoolean(specific, "ignoreOtherMentions") ?? readBoolean(wildcard, "ignoreOtherMentions") ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
2609
- name: readString(specific, "name") ?? readString(wildcard, "name") ?? DEFAULT_GROUP_CONFIG.name,
2610
- prompt: readString(specific, "prompt") ?? readString(wildcard, "prompt"),
2611
- historyLimit: readHistoryLimit(specific, "historyLimit") ?? readHistoryLimit(wildcard, "historyLimit") ?? DEFAULT_GROUP_CONFIG.historyLimit
2612
- };
2613
- }
2614
- function resolveGroupSettings(params) {
2615
- const config = resolveGroupConfig(params.cfg, params.groupOpenid, params.accountId);
2616
- return {
2617
- config,
2618
- name: config.name || params.groupOpenid.slice(0, 8),
2619
- mentionPatterns: resolveMentionPatterns(params.cfg, params.agentId)
2620
- };
2621
- }
2622
- function resolveMentionPatterns(cfg, agentId) {
2623
- if (agentId) {
2624
- const agents = asOptionalObjectRecord(cfg.agents);
2625
- const agentGroupChat = (Array.isArray(agents?.list) ? agents?.list : []).find((a) => typeof a.id === "string" && a.id.trim().toLowerCase() === agentId.trim().toLowerCase())?.groupChat;
2626
- if (agentGroupChat && Object.hasOwn(agentGroupChat, "mentionPatterns")) {
2627
- const patterns = agentGroupChat.mentionPatterns;
2628
- return Array.isArray(patterns) ? patterns.filter((p) => typeof p === "string") : [];
2629
- }
2630
- }
2631
- const globalGroupChat = asOptionalObjectRecord(asOptionalObjectRecord(cfg.messages)?.groupChat);
2632
- if (globalGroupChat && Object.hasOwn(globalGroupChat, "mentionPatterns")) {
2633
- const patterns = globalGroupChat.mentionPatterns;
2634
- return Array.isArray(patterns) ? patterns.filter((p) => typeof p === "string") : [];
2635
- }
2636
- return [];
2637
- }
2638
2581
  //#endregion
2639
2582
  //#region extensions/qqbot/src/engine/gateway/stages/group-gate-stage.ts
2640
2583
  function runGroupGateStage(input) {
@@ -2694,6 +2637,7 @@ function runGroupGateStage(input) {
2694
2637
  const groupInfo = {
2695
2638
  gate,
2696
2639
  activation,
2640
+ commandLevel: settings.config.commandLevel,
2697
2641
  historyLimit,
2698
2642
  isMerged: isMergedTurn(event),
2699
2643
  mergedMessages: event.merge?.messages,
@@ -2704,6 +2648,12 @@ function runGroupGateStage(input) {
2704
2648
  behaviorPrompt
2705
2649
  }
2706
2650
  };
2651
+ const commandVisibility = classifyCoreCommandForGroup(userContent, settings.config.commandLevel);
2652
+ if (commandAuthorized && commandVisibility.visibility === "private" && gate.action !== "drop_other_mention") return {
2653
+ kind: "skip",
2654
+ groupInfo,
2655
+ skipReason: "private_command_only"
2656
+ };
2707
2657
  if (gate.action === "pass") return {
2708
2658
  kind: "pass",
2709
2659
  groupInfo
@@ -6412,6 +6362,18 @@ async function startGateway$1(ctx) {
6412
6362
  return;
6413
6363
  }
6414
6364
  if (inbound.skipped) {
6365
+ if (inbound.skipReason === "private_command_only") {
6366
+ log?.info("Rejected private-only command in qqbot group before mention gate", {
6367
+ accountId: account.accountId,
6368
+ messageId: event.messageId,
6369
+ senderId: event.senderId,
6370
+ type: event.type,
6371
+ groupOpenid: event.groupOpenid
6372
+ });
6373
+ await sendText(buildDeliveryTarget(event), PRIVATE_CHAT_ONLY_TEXT, accountToCreds(account), { msgId: event.messageId });
6374
+ inbound.typing.keepAlive?.stop();
6375
+ return;
6376
+ }
6415
6377
  log?.info(`Skipped group inbound: reason=${inbound.skipReason ?? "unknown"} group=${event.groupOpenid ?? ""}`, {
6416
6378
  accountId: account.accountId,
6417
6379
  messageId: event.messageId,
@@ -6421,6 +6383,20 @@ async function startGateway$1(ctx) {
6421
6383
  inbound.typing.keepAlive?.stop();
6422
6384
  return;
6423
6385
  }
6386
+ const groupCommandLevel = event.type === "group" || event.type === "guild" ? inbound.group?.commandLevel ?? resolveGroupCommandLevelFromAccountConfig(account.config, event.groupOpenid ?? event.channelId ?? null) : void 0;
6387
+ const groupCommandVisibility = event.type === "group" || event.type === "guild" ? classifyCoreCommandForGroup(inbound.agentBody, groupCommandLevel) : { visibility: "unknown" };
6388
+ if (groupCommandVisibility.visibility === "private") {
6389
+ log?.info(`Rejected private-only command in qqbot group: /${groupCommandVisibility.commandName}`, {
6390
+ accountId: account.accountId,
6391
+ messageId: event.messageId,
6392
+ senderId: event.senderId,
6393
+ type: event.type,
6394
+ groupOpenid: event.groupOpenid
6395
+ });
6396
+ await sendText(buildDeliveryTarget(event), PRIVATE_CHAT_ONLY_TEXT, accountToCreds(account), { msgId: event.messageId });
6397
+ inbound.typing.keepAlive?.stop();
6398
+ return;
6399
+ }
6424
6400
  try {
6425
6401
  await runWithRequestContext({
6426
6402
  accountId: account.accountId,
@@ -1,6 +1,8 @@
1
+ import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
2
+ import { d as resolveAccountBase } from "./config-C1qZbh0K.js";
1
3
  import { n as debugLog } from "./log-SDfMMBWe.js";
2
4
  import { a as getHomeDir, f as isWindows, l as getQQBotMediaPath, o as getQQBotDataDir } from "./target-parser-BdCUmxK7.js";
3
- import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
5
+ import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
4
6
  import fs from "node:fs";
5
7
  import path from "node:path";
6
8
  import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
@@ -876,6 +878,118 @@ function registerBuiltinSlashCommands(registry) {
876
878
  registerGroupAllwaysCommand(registry);
877
879
  }
878
880
  //#endregion
881
+ //#region extensions/qqbot/src/engine/commands/command-visibility.ts
882
+ const PRIVATE_CHAT_ONLY_TEXT = "该命令仅限私聊使用,请在私聊中发送。";
883
+ const GROUP_VISIBLE_CORE_COMMANDS = new Set([
884
+ "help",
885
+ "btw",
886
+ "side",
887
+ "stop"
888
+ ]);
889
+ const STRICT_CORE_COMMANDS = new Set(["new", "reset"]);
890
+ const GROUP_HIDDEN_CORE_COMMANDS = new Set([
891
+ "goal",
892
+ "usage",
893
+ "activation",
894
+ "send",
895
+ "reset",
896
+ "new",
897
+ "name",
898
+ "compact",
899
+ "think",
900
+ "thinking",
901
+ "t",
902
+ "fast",
903
+ "reasoning",
904
+ "reason",
905
+ "queue"
906
+ ]);
907
+ const PRIVATE_ONLY_CORE_COMMANDS = new Set([
908
+ "commands",
909
+ "tools",
910
+ "skill",
911
+ "diagnostics",
912
+ "crestodian",
913
+ "tasks",
914
+ "allowlist",
915
+ "approve",
916
+ "context",
917
+ "export-session",
918
+ "export",
919
+ "export-trajectory",
920
+ "trajectory",
921
+ "tts",
922
+ "whoami",
923
+ "id",
924
+ "session",
925
+ "subagents",
926
+ "acp",
927
+ "focus",
928
+ "unfocus",
929
+ "agents",
930
+ "steer",
931
+ "tell",
932
+ "config",
933
+ "mcp",
934
+ "plugins",
935
+ "plugin",
936
+ "debug",
937
+ "status",
938
+ "restart",
939
+ "trace",
940
+ "verbose",
941
+ "v",
942
+ "elevated",
943
+ "elev",
944
+ "exec",
945
+ "model",
946
+ "models",
947
+ "bash"
948
+ ]);
949
+ function parseSlashCommandName(content) {
950
+ const trimmed = (content ?? "").trim();
951
+ if (!trimmed.startsWith("/")) return;
952
+ return ((trimmed.slice(1).split(/\s+/, 1)[0]?.trim().toLowerCase() ?? "").split(/[@::]/u, 1)[0] ?? "") || void 0;
953
+ }
954
+ function classifyCoreCommandForGroup(content, commandLevel = "all") {
955
+ const commandName = parseSlashCommandName(content);
956
+ if (!commandName) return { visibility: "unknown" };
957
+ if (commandLevel === "all") return {
958
+ commandName,
959
+ visibility: GROUP_VISIBLE_CORE_COMMANDS.has(commandName) ? "group" : "hidden"
960
+ };
961
+ if (commandLevel === "strict") {
962
+ if (commandName === "stop") return {
963
+ commandName,
964
+ visibility: "group"
965
+ };
966
+ if (STRICT_CORE_COMMANDS.has(commandName)) return {
967
+ commandName,
968
+ visibility: "hidden"
969
+ };
970
+ return {
971
+ commandName,
972
+ visibility: "private"
973
+ };
974
+ }
975
+ if (GROUP_VISIBLE_CORE_COMMANDS.has(commandName)) return {
976
+ commandName,
977
+ visibility: "group"
978
+ };
979
+ if (GROUP_HIDDEN_CORE_COMMANDS.has(commandName)) return {
980
+ commandName,
981
+ visibility: "hidden"
982
+ };
983
+ if (PRIVATE_ONLY_CORE_COMMANDS.has(commandName)) return {
984
+ commandName,
985
+ visibility: "private"
986
+ };
987
+ return {
988
+ commandName,
989
+ visibility: "unknown"
990
+ };
991
+ }
992
+ //#endregion
879
993
  //#region extensions/qqbot/src/engine/commands/slash-commands.ts
880
994
  /** Lowercase and trim a string. */
881
995
  function lc(s) {
@@ -930,10 +1044,13 @@ var SlashCommandRegistry = class {
930
1044
  const args = spaceIdx === -1 ? "" : content.slice(spaceIdx + 1).trim();
931
1045
  const cmd = this.commands.get(cmdName);
932
1046
  if (!cmd) return null;
933
- if (cmd.c2cOnly && ctx.type !== "c2c") return `💡 请在私聊中使用此指令`;
1047
+ const isGroup = ctx.type === "group" || ctx.type === "guild";
1048
+ const groupCommandLevel = ctx.groupCommandLevel ?? "all";
1049
+ if (isGroup && groupCommandLevel === "strict") return PRIVATE_CHAT_ONLY_TEXT;
1050
+ if (cmd.c2cOnly && ctx.type !== "c2c") return PRIVATE_CHAT_ONLY_TEXT;
934
1051
  if (cmd.requireAuth && !ctx.commandAuthorized) {
935
1052
  log?.info?.(`[qqbot] Slash command /${cmd.name} rejected: sender ${ctx.senderId} is not authorized`);
936
- return `⛔ 权限不足:请先在 channels.qqbot.${ctx.type === "group" || ctx.type === "guild" ? "groupAllowFrom" : "allowFrom"} 中配置明确的发送者列表后再使用 /${cmd.name}。`;
1053
+ return `⛔ 权限不足:请先在 channels.qqbot.${isGroup ? "groupAllowFrom" : "allowFrom"} 中配置明确的发送者列表后再使用 /${cmd.name}。`;
937
1054
  }
938
1055
  if (args === "?") {
939
1056
  if (cmd.usage) return `📖 /${cmd.name} 用法:\n\n${cmd.usage}`;
@@ -977,5 +1094,83 @@ function getPluginVersion() {
977
1094
  function getFrameworkVersion() {
978
1095
  return getFrameworkVersionString();
979
1096
  }
1097
+ const DEFAULT_GROUP_PROMPT = "If the sender is a bot, respond only when they explicitly @mention you to ask a question or request assistance with a specific task; keep your replies concise and clear, avoiding the urge to race other bots to answer or engage in lengthy, unproductive exchanges. In group chats, prioritize responding to messages from human users; bots should maintain a collaborative rather than competitive dynamic to ensure the conversation remains orderly and does not result in message flooding.";
1098
+ const DEFAULT_GROUP_CONFIG = {
1099
+ requireMention: true,
1100
+ ignoreOtherMentions: false,
1101
+ commandLevel: "all",
1102
+ name: "",
1103
+ historyLimit: 50
1104
+ };
1105
+ function readGroupsMap(cfg, accountId) {
1106
+ const groups = asOptionalObjectRecord(resolveAccountBase(cfg, accountId).config.groups);
1107
+ if (!groups) return {};
1108
+ const normalized = {};
1109
+ for (const [key, value] of Object.entries(groups)) {
1110
+ const sub = asOptionalObjectRecord(value);
1111
+ if (sub) normalized[key] = sub;
1112
+ }
1113
+ return normalized;
1114
+ }
1115
+ function readBoolean(obj, key) {
1116
+ return asBoolean(obj[key]);
1117
+ }
1118
+ function readString(obj, key) {
1119
+ const v = obj[key];
1120
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1121
+ }
1122
+ function readCommandLevel(obj, key) {
1123
+ const v = readString(obj, key);
1124
+ return v === "all" || v === "safety" || v === "strict" ? v : void 0;
1125
+ }
1126
+ function readHistoryLimit(obj, key) {
1127
+ const v = obj[key];
1128
+ if (typeof v !== "number" || !Number.isFinite(v)) return;
1129
+ return Math.max(0, Math.floor(v));
1130
+ }
1131
+ function resolveGroupConfig(cfg, groupOpenid, accountId) {
1132
+ const account = resolveAccountBase(cfg, accountId);
1133
+ const groups = readGroupsMap(cfg, accountId);
1134
+ const wildcard = groups["*"] ?? {};
1135
+ const specific = groupOpenid ? groups[groupOpenid] ?? {} : {};
1136
+ const accountDefaultRequireMention = asBoolean(account.config.defaultRequireMention) ?? DEFAULT_GROUP_CONFIG.requireMention;
1137
+ return {
1138
+ requireMention: readBoolean(specific, "requireMention") ?? readBoolean(wildcard, "requireMention") ?? accountDefaultRequireMention,
1139
+ ignoreOtherMentions: readBoolean(specific, "ignoreOtherMentions") ?? readBoolean(wildcard, "ignoreOtherMentions") ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
1140
+ commandLevel: readCommandLevel(specific, "commandLevel") ?? readCommandLevel(wildcard, "commandLevel") ?? DEFAULT_GROUP_CONFIG.commandLevel,
1141
+ name: readString(specific, "name") ?? readString(wildcard, "name") ?? DEFAULT_GROUP_CONFIG.name,
1142
+ prompt: readString(specific, "prompt") ?? readString(wildcard, "prompt"),
1143
+ historyLimit: readHistoryLimit(specific, "historyLimit") ?? readHistoryLimit(wildcard, "historyLimit") ?? DEFAULT_GROUP_CONFIG.historyLimit
1144
+ };
1145
+ }
1146
+ function resolveGroupCommandLevelFromAccountConfig(accountConfig, groupOpenid) {
1147
+ const groups = asOptionalObjectRecord(accountConfig?.groups);
1148
+ const wildcard = asOptionalObjectRecord(groups?.["*"]) ?? {};
1149
+ return readCommandLevel(groupOpenid ? asOptionalObjectRecord(groups?.[groupOpenid]) ?? {} : {}, "commandLevel") ?? readCommandLevel(wildcard, "commandLevel") ?? DEFAULT_GROUP_CONFIG.commandLevel;
1150
+ }
1151
+ function resolveGroupSettings(params) {
1152
+ const config = resolveGroupConfig(params.cfg, params.groupOpenid, params.accountId);
1153
+ return {
1154
+ config,
1155
+ name: config.name || params.groupOpenid.slice(0, 8),
1156
+ mentionPatterns: resolveMentionPatterns(params.cfg, params.agentId)
1157
+ };
1158
+ }
1159
+ function resolveMentionPatterns(cfg, agentId) {
1160
+ if (agentId) {
1161
+ const agents = asOptionalObjectRecord(cfg.agents);
1162
+ const agentGroupChat = (Array.isArray(agents?.list) ? agents?.list : []).find((a) => typeof a.id === "string" && a.id.trim().toLowerCase() === agentId.trim().toLowerCase())?.groupChat;
1163
+ if (agentGroupChat && Object.hasOwn(agentGroupChat, "mentionPatterns")) {
1164
+ const patterns = agentGroupChat.mentionPatterns;
1165
+ return Array.isArray(patterns) ? patterns.filter((p) => typeof p === "string") : [];
1166
+ }
1167
+ }
1168
+ const globalGroupChat = asOptionalObjectRecord(asOptionalObjectRecord(cfg.messages)?.groupChat);
1169
+ if (globalGroupChat && Object.hasOwn(globalGroupChat, "mentionPatterns")) {
1170
+ const patterns = globalGroupChat.mentionPatterns;
1171
+ return Array.isArray(patterns) ? patterns.filter((p) => typeof p === "string") : [];
1172
+ }
1173
+ return [];
1174
+ }
980
1175
  //#endregion
981
- export { matchSlashCommand as a, initCommands as i, getFrameworkVersion as n, getPluginVersion as r, getFrameworkCommands as t };
1176
+ export { resolveMentionPatterns as a, getPluginVersion as c, PRIVATE_CHAT_ONLY_TEXT as d, classifyCoreCommandForGroup as f, resolveGroupSettings as i, initCommands as l, resolveGroupCommandLevelFromAccountConfig as n, getFrameworkCommands as o, resolveGroupConfig as r, getFrameworkVersion as s, DEFAULT_GROUP_PROMPT as t, matchSlashCommand as u };
@@ -1,7 +1,7 @@
1
1
  import { c as getMessageApi, t as accountToCreds } from "./sender-CRZ2af7P.js";
2
- import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-BFLNZ8Nf.js";
3
- import { c as matchesQQBotApprovalAccount, d as buildApprovalKeyboard, f as buildExecApprovalText, h as resolveApprovalTarget, l as resolveQQBotExecApprovalConfig, p as buildPluginApprovalText, s as isQQBotExecApprovalClientEnabled, u as shouldHandleQQBotExecApprovalRequest } from "./channel-jbnSeYkd.js";
4
- import { a as resolveQQBotAccount } from "./config-ZEfgeoL4.js";
2
+ import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-gMW9zKvQ.js";
3
+ import { c as matchesQQBotApprovalAccount, d as buildApprovalKeyboard, f as buildExecApprovalText, h as resolveApprovalTarget, l as resolveQQBotExecApprovalConfig, p as buildPluginApprovalText, s as isQQBotExecApprovalClientEnabled, u as shouldHandleQQBotExecApprovalRequest } from "./channel-DvWrqcZB.js";
4
+ import { a as resolveQQBotAccount } from "./config-C1qZbh0K.js";
5
5
  import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
6
6
  import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
7
7
  //#region extensions/qqbot/src/bridge/approval/handler-runtime.ts
@@ -1,2 +1,2 @@
1
- import { t as qqbotSetupPlugin } from "./channel.setup-CQ_DFfPx.js";
1
+ import { t as qqbotSetupPlugin } from "./channel.setup-CldhUlTO.js";
2
2
  export { qqbotSetupPlugin };
@@ -1,4 +1,4 @@
1
- import { a as resolveQQBotAccount, r as listQQBotAccountIds } from "./config-ZEfgeoL4.js";
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
4
  import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
package/dist/tools-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as registerQQBotTools } from "./tools-CV5Y0isN.js";
1
+ import { t as registerQQBotTools } from "./tools-CeUI9pG-.js";
2
2
  export { registerQQBotTools };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.6.10",
3
+ "version": "2026.6.11",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/qqbot",
9
- "version": "2026.6.10",
9
+ "version": "2026.6.11",
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.6.10"
18
+ "openclaw": ">=2026.6.11"
19
19
  },
20
20
  "peerDependenciesMeta": {
21
21
  "openclaw": {
@@ -2,6 +2,7 @@
2
2
  "id": "qqbot",
3
3
  "name": "QQ Bot",
4
4
  "description": "OpenClaw QQ Bot channel plugin for group and direct-message workflows.",
5
+ "icon": "https://cdn.simpleicons.org/qq",
5
6
  "activation": {
6
7
  "onStartup": false
7
8
  },
@@ -106,6 +107,41 @@
106
107
  }
107
108
  ]
108
109
  },
110
+ "group": {
111
+ "type": "object",
112
+ "additionalProperties": true,
113
+ "properties": {
114
+ "requireMention": {
115
+ "type": "boolean"
116
+ },
117
+ "commandLevel": {
118
+ "type": "string",
119
+ "enum": [
120
+ "all",
121
+ "safety",
122
+ "strict"
123
+ ]
124
+ },
125
+ "ignoreOtherMentions": {
126
+ "type": "boolean"
127
+ },
128
+ "historyLimit": {
129
+ "type": "number"
130
+ },
131
+ "name": {
132
+ "type": "string"
133
+ },
134
+ "prompt": {
135
+ "type": "string"
136
+ }
137
+ }
138
+ },
139
+ "groups": {
140
+ "type": "object",
141
+ "additionalProperties": {
142
+ "$ref": "#/$defs/group"
143
+ }
144
+ },
109
145
  "account": {
110
146
  "type": "object",
111
147
  "additionalProperties": true,
@@ -183,6 +219,9 @@
183
219
  }
184
220
  }
185
221
  ]
222
+ },
223
+ "groups": {
224
+ "$ref": "#/$defs/groups"
186
225
  }
187
226
  }
188
227
  }
@@ -273,6 +312,9 @@
273
312
  },
274
313
  "defaultAccount": {
275
314
  "type": "string"
315
+ },
316
+ "groups": {
317
+ "$ref": "#/$defs/groups"
276
318
  }
277
319
  }
278
320
  },
@@ -541,6 +583,14 @@
541
583
  "requireMention": {
542
584
  "type": "boolean"
543
585
  },
586
+ "commandLevel": {
587
+ "type": "string",
588
+ "enum": [
589
+ "all",
590
+ "safety",
591
+ "strict"
592
+ ]
593
+ },
544
594
  "ignoreOtherMentions": {
545
595
  "type": "boolean"
546
596
  },
@@ -897,6 +947,14 @@
897
947
  "requireMention": {
898
948
  "type": "boolean"
899
949
  },
950
+ "commandLevel": {
951
+ "type": "string",
952
+ "enum": [
953
+ "all",
954
+ "safety",
955
+ "strict"
956
+ ]
957
+ },
900
958
  "ignoreOtherMentions": {
901
959
  "type": "boolean"
902
960
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.6.10",
3
+ "version": "2026.6.11",
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.6.10"
19
+ "openclaw": ">=2026.6.11"
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.6.10"
48
+ "pluginApi": ">=2026.6.11"
49
49
  },
50
50
  "build": {
51
- "openclawVersion": "2026.6.10"
51
+ "openclawVersion": "2026.6.11"
52
52
  },
53
53
  "release": {
54
54
  "publishToClawHub": true,