@openclaw/qqbot 2026.6.5 → 2026.6.6-beta.2

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-D13iTVCr.js";
1
+ import { t as qqbotPlugin } from "./channel-CIb8DUm3.js";
2
2
  import { a as resolveQQBotAccount, i as resolveDefaultQQBotAccountId, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID } from "./config-ZEfgeoL4.js";
3
3
  import { t as qqbotSetupPlugin } from "./channel.setup-CQ_DFfPx.js";
4
- import { t as getFrameworkCommands } from "./slash-commands-impl-FRw-Dl44.js";
4
+ import { t as getFrameworkCommands } from "./slash-commands-impl-DVApdSop.js";
5
5
  import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-2d9t-N1b.js";
6
- import { t as registerQQBotFull } from "./channel-entry-fUBLXKPr.js";
6
+ import { t as registerQQBotFull } from "./channel-entry-CfzBEypJ.js";
7
7
  import { C as getMessageReplyStats, D as setOutboundAudioPort, E as OUTBOUND_ERROR_CODES, S as getMessageReplyConfig, T as DEFAULT_MEDIA_SEND_ERROR, _ as sendVideoMsg, a as sendText, b as MESSAGE_REPLY_LIMIT, 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 recordMessageReply, x as checkMessageReplyLimit, y as resolveUserFacingMediaError } from "./outbound-C9wV892v.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 };
@@ -377,7 +377,7 @@ function createQQBotApprovalCapability() {
377
377
  },
378
378
  load: async () => {
379
379
  ensurePlatformAdapter();
380
- return (await import("./handler-runtime-DZXDF0GH.js")).qqbotApprovalNativeRuntime;
380
+ return (await import("./handler-runtime-ChANl8Bb.js")).qqbotApprovalNativeRuntime;
381
381
  }
382
382
  })
383
383
  });
@@ -667,7 +667,7 @@ function resolveQQBotGroupToolPolicy(params) {
667
667
  //#region extensions/qqbot/src/channel.ts
668
668
  let gatewayModulePromise;
669
669
  function loadGatewayModule() {
670
- gatewayModulePromise ??= import("./gateway-DcL6jnps.js");
670
+ gatewayModulePromise ??= import("./gateway-fUg0QRoq.js");
671
671
  return gatewayModulePromise;
672
672
  }
673
673
  let outboundMessagingModulePromise;
@@ -1,5 +1,5 @@
1
1
  import { a as resolveQQBotAccount } from "./config-ZEfgeoL4.js";
2
- import { t as getFrameworkCommands } from "./slash-commands-impl-FRw-Dl44.js";
2
+ import { t as getFrameworkCommands } from "./slash-commands-impl-DVApdSop.js";
3
3
  import { t as registerQQBotTools } from "./tools-2d9t-N1b.js";
4
4
  import { h as sendDocument } from "./outbound-C9wV892v.js";
5
5
  //#region extensions/qqbot/src/bridge/commands/framework-context-adapter.ts
@@ -1,2 +1,2 @@
1
- import { t as registerQQBotFull } from "./channel-entry-fUBLXKPr.js";
1
+ import { t as registerQQBotFull } from "./channel-entry-CfzBEypJ.js";
2
2
  export { registerQQBotFull };
@@ -1,2 +1,2 @@
1
- import { t as qqbotPlugin } from "./channel-D13iTVCr.js";
1
+ import { t as qqbotPlugin } from "./channel-CIb8DUm3.js";
2
2
  export { qqbotPlugin };
@@ -1,12 +1,12 @@
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-DIMG7jHz.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
3
  import { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-BFLNZ8Nf.js";
4
- import { a as authorizeQQBotApprovalAction, i as toGatewayAccount, n as buildQQBotStateKey, p as parseApprovalButtonData, r as openQQBotSyncKeyedStore } from "./channel-D13iTVCr.js";
4
+ import { a as authorizeQQBotApprovalAction, i as toGatewayAccount, n as buildQQBotStateKey, p as parseApprovalButtonData, r as openQQBotSyncKeyedStore } from "./channel-CIb8DUm3.js";
5
5
  import { d as resolveAccountBase } from "./config-ZEfgeoL4.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-B9UoQ5NI.js";
8
8
  import { a as getHomeDir, c as getQQBotMediaDir, d as isLocalPath, f as isWindows, i as checkSilkWasmAvailable, m as resolveQQBotPayloadLocalFilePath, 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-FRw-Dl44.js";
9
+ import { a as matchSlashCommand, i as initCommands, n as getFrameworkVersion, r as getPluginVersion } from "./slash-commands-impl-DVApdSop.js";
10
10
  import { n as runWithRequestContext } from "./request-context-Bm7PTBD1.js";
11
11
  import { D as setOutboundAudioPort, T as DEFAULT_MEDIA_SEND_ERROR, _ as sendVideoMsg, 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 resolveUserFacingMediaError } from "./outbound-C9wV892v.js";
12
12
  import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -21,11 +21,12 @@ import * as path$1 from "node:path";
21
21
  import path from "node:path";
22
22
  import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
23
23
  import { MAX_TIMER_TIMEOUT_MS, finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
24
- import { resolveRuntimeServiceVersion } from "openclaw/plugin-sdk/cli-runtime";
25
24
  import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
25
+ import { resolveRuntimeServiceVersion } from "openclaw/plugin-sdk/cli-runtime";
26
26
  import WebSocket from "ws";
27
27
  import { resolveAmbientNodeProxyAgent } from "openclaw/plugin-sdk/extension-shared";
28
28
  import { buildChannelInboundEventContext } from "openclaw/plugin-sdk/channel-inbound";
29
+ import { SILENT_REPLY_TOKEN, isSilentReplyPayloadText } from "openclaw/plugin-sdk/reply-chunking";
29
30
  import { Buffer as Buffer$1 } from "node:buffer";
30
31
  import { fileURLToPath } from "node:url";
31
32
  import { parseAccessGroupAllowFromEntry } from "openclaw/plugin-sdk/access-groups";
@@ -2595,11 +2596,13 @@ function readHistoryLimit(obj, key) {
2595
2596
  return Math.max(0, Math.floor(v));
2596
2597
  }
2597
2598
  function resolveGroupConfig(cfg, groupOpenid, accountId) {
2599
+ const account = resolveAccountBase(cfg, accountId);
2598
2600
  const groups = readGroupsMap(cfg, accountId);
2599
2601
  const wildcard = groups["*"] ?? {};
2600
2602
  const specific = groupOpenid ? groups[groupOpenid] ?? {} : {};
2603
+ const accountDefaultRequireMention = asBoolean(account.config.defaultRequireMention) ?? DEFAULT_GROUP_CONFIG.requireMention;
2601
2604
  return {
2602
- requireMention: readBoolean(specific, "requireMention") ?? readBoolean(wildcard, "requireMention") ?? DEFAULT_GROUP_CONFIG.requireMention,
2605
+ requireMention: readBoolean(specific, "requireMention") ?? readBoolean(wildcard, "requireMention") ?? accountDefaultRequireMention,
2603
2606
  ignoreOtherMentions: readBoolean(specific, "ignoreOtherMentions") ?? readBoolean(wildcard, "ignoreOtherMentions") ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
2604
2607
  name: readString(specific, "name") ?? readString(wildcard, "name") ?? DEFAULT_GROUP_CONFIG.name,
2605
2608
  prompt: readString(specific, "prompt") ?? readString(wildcard, "prompt"),
@@ -5734,6 +5737,19 @@ function immediateToolProgressText(payload) {
5734
5737
  if (payload.mediaUrl || payload.mediaUrls?.length) return;
5735
5738
  return text;
5736
5739
  }
5740
+ function hasReplyMedia(payload) {
5741
+ return Boolean(payload.mediaUrl || payload.mediaUrls?.length);
5742
+ }
5743
+ function isSilentBlockReplyText(text) {
5744
+ return !text || text === "[SKIP]" || isSilentReplyPayloadText(text, SILENT_REPLY_TOKEN);
5745
+ }
5746
+ function blockReplyTextForDelivery(payload) {
5747
+ const text = payload.text ?? "";
5748
+ return isSilentBlockReplyText(text.trim()) ? "" : text;
5749
+ }
5750
+ function isSilentBlockReply(payload) {
5751
+ return !hasReplyMedia(payload) && isSilentBlockReplyText((payload.text ?? "").trim());
5752
+ }
5737
5753
  /**
5738
5754
  * Dispatch the AI reply for the given inbound context.
5739
5755
  *
@@ -5761,50 +5777,78 @@ async function dispatchOutbound(inbound, deps) {
5761
5777
  const ctxPayload = await buildCtxPayload(inbound, runtime, cfg);
5762
5778
  let hasResponse = false;
5763
5779
  let hasBlockResponse = false;
5780
+ let hasVisibleBlockResponse = false;
5764
5781
  let toolDeliverCount = 0;
5765
5782
  const toolTexts = [];
5766
5783
  const toolMediaUrls = [];
5767
5784
  let toolFallbackSent = false;
5768
5785
  let toolRenewalCount = 0;
5786
+ let skippedSilentBlockResponse = false;
5769
5787
  let timeoutId = null;
5770
5788
  let toolOnlyTimeoutId = null;
5789
+ const markBlockResponse = () => {
5790
+ hasBlockResponse = true;
5791
+ inbound.typing.keepAlive?.stop();
5792
+ if (timeoutId) {
5793
+ clearTimeout(timeoutId);
5794
+ timeoutId = null;
5795
+ }
5796
+ if (toolOnlyTimeoutId) {
5797
+ clearTimeout(toolOnlyTimeoutId);
5798
+ toolOnlyTimeoutId = null;
5799
+ }
5800
+ };
5801
+ const sendToolMediaWithTimeout = async (mediaUrl, labels) => {
5802
+ const ac = new AbortController();
5803
+ let mediaTimeoutId = null;
5804
+ try {
5805
+ const result = await Promise.race([sendMedia$1({
5806
+ to: qualifiedTarget,
5807
+ text: "",
5808
+ mediaUrl,
5809
+ accountId: account.accountId,
5810
+ replyToId: event.messageId,
5811
+ account
5812
+ }).then((r) => {
5813
+ if (ac.signal.aborted) return {
5814
+ channel: "qqbot",
5815
+ error: "suppressed"
5816
+ };
5817
+ return r;
5818
+ }), new Promise((resolve) => {
5819
+ mediaTimeoutId = setTimeout(() => {
5820
+ ac.abort();
5821
+ resolve({
5822
+ channel: "qqbot",
5823
+ error: "timeout"
5824
+ });
5825
+ }, TOOL_MEDIA_SEND_TIMEOUT);
5826
+ })]);
5827
+ if (result.error) log?.error(`${labels.resultError}: ${result.error}`);
5828
+ } catch (err) {
5829
+ log?.error(`${labels.thrownError}: ${String(err)}`);
5830
+ } finally {
5831
+ if (mediaTimeoutId) clearTimeout(mediaTimeoutId);
5832
+ }
5833
+ };
5771
5834
  const sendToolFallback = async () => {
5772
5835
  if (toolMediaUrls.length > 0) {
5773
- for (const mediaUrl of toolMediaUrls) {
5774
- const ac = new AbortController();
5775
- try {
5776
- const result = await Promise.race([sendMedia$1({
5777
- to: qualifiedTarget,
5778
- text: "",
5779
- mediaUrl,
5780
- accountId: account.accountId,
5781
- replyToId: event.messageId,
5782
- account
5783
- }).then((r) => {
5784
- if (ac.signal.aborted) return {
5785
- channel: "qqbot",
5786
- error: "suppressed"
5787
- };
5788
- return r;
5789
- }), new Promise((resolve) => {
5790
- setTimeout(() => {
5791
- ac.abort();
5792
- resolve({
5793
- channel: "qqbot",
5794
- error: "timeout"
5795
- });
5796
- }, TOOL_MEDIA_SEND_TIMEOUT);
5797
- })]);
5798
- if (result.error) log?.error(`Tool fallback error: ${result.error}`);
5799
- } catch (err) {
5800
- log?.error(`Tool fallback failed: ${String(err)}`);
5801
- }
5802
- }
5836
+ for (const mediaUrl of toolMediaUrls) await sendToolMediaWithTimeout(mediaUrl, {
5837
+ resultError: "Tool fallback error",
5838
+ thrownError: "Tool fallback failed"
5839
+ });
5803
5840
  return;
5804
5841
  }
5805
5842
  if (toolTexts.length > 0) await sendErrorMessage(toolTexts.slice(-3).join("\n---\n").slice(0, 2e3));
5806
5843
  };
5807
5844
  const hasPendingToolFallbackPayload = () => toolTexts.length > 0 || toolMediaUrls.length > 0;
5845
+ const flushPendingToolDeliveriesOnce = async () => {
5846
+ if (toolFallbackSent || !hasPendingToolFallbackPayload()) return false;
5847
+ await flushPendingToolDeliveries();
5848
+ toolFallbackSent = true;
5849
+ recordOutbound();
5850
+ return true;
5851
+ };
5808
5852
  const renewToolOnlyFallback = () => {
5809
5853
  if (toolFallbackSent) return false;
5810
5854
  if (toolOnlyTimeoutId) {
@@ -5813,7 +5857,7 @@ async function dispatchOutbound(inbound, deps) {
5813
5857
  toolRenewalCount++;
5814
5858
  }
5815
5859
  toolOnlyTimeoutId = setTimeout(() => {
5816
- if (!hasBlockResponse && !toolFallbackSent) {
5860
+ if (!hasBlockResponse && !toolFallbackSent && !skippedSilentBlockResponse) {
5817
5861
  toolFallbackSent = true;
5818
5862
  sendToolFallback().catch(() => {});
5819
5863
  }
@@ -5840,6 +5884,32 @@ async function dispatchOutbound(inbound, deps) {
5840
5884
  textToSpeech: (params) => runtime.tts.textToSpeech(params),
5841
5885
  audioFileToSilkBase64: async (p) => await audioFileToSilkBase64(p) ?? void 0
5842
5886
  } };
5887
+ const flushPendingToolDeliveries = async () => {
5888
+ if (toolMediaUrls.length > 0) {
5889
+ const urlsToSend = [...toolMediaUrls];
5890
+ toolMediaUrls.length = 0;
5891
+ for (const mediaUrl of urlsToSend) await sendToolMediaWithTimeout(mediaUrl, {
5892
+ resultError: "Tool media forward error",
5893
+ thrownError: "Tool media forward failed"
5894
+ });
5895
+ }
5896
+ if (toolTexts.length > 0) {
5897
+ const textsToSend = [...toolTexts];
5898
+ toolTexts.length = 0;
5899
+ for (const text of textsToSend) await sendTextOnlyReply(text, {
5900
+ type: event.type,
5901
+ senderId: event.senderId,
5902
+ messageId: event.messageId,
5903
+ channelId: event.channelId,
5904
+ groupOpenid: event.groupOpenid,
5905
+ msgIdx: event.msgIdx
5906
+ }, {
5907
+ account,
5908
+ qualifiedTarget,
5909
+ log
5910
+ }, sendWithRetry, () => void 0, deliverDeps);
5911
+ }
5912
+ };
5843
5913
  const recordOutbound = () => runtime.channel.activity.record({
5844
5914
  channel: "qqbot",
5845
5915
  accountId: account.accountId,
@@ -5943,16 +6013,12 @@ async function dispatchOutbound(inbound, deps) {
5943
6013
  renewToolOnlyFallback();
5944
6014
  return;
5945
6015
  }
5946
- hasBlockResponse = true;
5947
- inbound.typing.keepAlive?.stop();
5948
- if (timeoutId) {
5949
- clearTimeout(timeoutId);
5950
- timeoutId = null;
5951
- }
5952
- if (toolOnlyTimeoutId) {
5953
- clearTimeout(toolOnlyTimeoutId);
5954
- toolOnlyTimeoutId = null;
6016
+ markBlockResponse();
6017
+ if (!streamingController && isSilentBlockReply(payload)) {
6018
+ if (!await flushPendingToolDeliveriesOnce() && event.type === "group") log?.info(`Model decided to skip group message (${(payload.text ?? "").trim() || "empty reply"}) from ${event.senderId}`);
6019
+ return;
5955
6020
  }
6021
+ hasVisibleBlockResponse = true;
5956
6022
  if (streamingController && !streamingController.isTerminalPhase) {
5957
6023
  try {
5958
6024
  await streamingController.onDeliver(payload);
@@ -5978,7 +6044,7 @@ async function dispatchOutbound(inbound, deps) {
5978
6044
  return quoteRef;
5979
6045
  }
5980
6046
  };
5981
- let replyText = payload.text ?? "";
6047
+ let replyText = blockReplyTextForDelivery(payload);
5982
6048
  const deliverEvent = {
5983
6049
  type: event.type,
5984
6050
  senderId: event.senderId,
@@ -6025,6 +6091,15 @@ async function dispatchOutbound(inbound, deps) {
6025
6091
  clearTimeout(timeoutId);
6026
6092
  timeoutId = null;
6027
6093
  }
6094
+ },
6095
+ onSkip: (_payload, info) => {
6096
+ if (!streamingController && (info.kind === "block" || info.kind === "final") && (info.reason === "silent" || info.reason === "empty")) skippedSilentBlockResponse = true;
6097
+ },
6098
+ onFreshSettledDelivery: async () => {
6099
+ if (skippedSilentBlockResponse && !hasVisibleBlockResponse) {
6100
+ markBlockResponse();
6101
+ if (await flushPendingToolDeliveriesOnce()) return { visibleReplySent: true };
6102
+ }
6028
6103
  }
6029
6104
  },
6030
6105
  replyOptions: {
@@ -6048,13 +6123,20 @@ async function dispatchOutbound(inbound, deps) {
6048
6123
  try {
6049
6124
  await Promise.race([dispatchPromise, timeoutPromise]);
6050
6125
  } catch {
6051
- if (timeoutId) clearTimeout(timeoutId);
6126
+ if (timeoutId) {
6127
+ clearTimeout(timeoutId);
6128
+ timeoutId = null;
6129
+ }
6052
6130
  } finally {
6131
+ if (timeoutId) {
6132
+ clearTimeout(timeoutId);
6133
+ timeoutId = null;
6134
+ }
6053
6135
  if (toolOnlyTimeoutId) {
6054
6136
  clearTimeout(toolOnlyTimeoutId);
6055
6137
  toolOnlyTimeoutId = null;
6056
6138
  }
6057
- if (toolDeliverCount > 0 && !hasBlockResponse && !toolFallbackSent) {
6139
+ if (toolDeliverCount > 0 && !hasBlockResponse && !toolFallbackSent && !skippedSilentBlockResponse) {
6058
6140
  toolFallbackSent = true;
6059
6141
  await sendToolFallback();
6060
6142
  }
@@ -1,6 +1,6 @@
1
1
  import { c as getMessageApi, t as accountToCreds } from "./sender-DIMG7jHz.js";
2
2
  import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-BFLNZ8Nf.js";
3
- import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-D13iTVCr.js";
3
+ import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-CIb8DUm3.js";
4
4
  import { a as resolveQQBotAccount } from "./config-ZEfgeoL4.js";
5
5
  import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
6
6
  import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
@@ -4,6 +4,7 @@ import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
7
+ import { clearRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
7
8
  //#region extensions/qqbot/src/engine/commands/builtin/state.ts
8
9
  let resolveVersionGetter = () => "unknown";
9
10
  let approveRuntimeGetter = null;
@@ -418,6 +419,88 @@ function registerClearStorageCommands(registry) {
418
419
  });
419
420
  }
420
421
  //#endregion
422
+ //#region extensions/qqbot/src/engine/commands/builtin/register-group-allways.ts
423
+ function registerGroupAllwaysCommand(registry) {
424
+ registry.register({
425
+ name: "bot-group-allways",
426
+ description: "修改群消息默认响应模式",
427
+ requireAuth: true,
428
+ c2cOnly: true,
429
+ usage: [
430
+ `/bot-group-allways on AI 自主判断何时发言(无需 @)`,
431
+ `/bot-group-allways off 仅在被 @ 时回复`,
432
+ `/bot-group-allways 查看当前设置`,
433
+ ``,
434
+ `设为 on 后,AI 会自主判断每条消息是否需要回复(无需 @)。`,
435
+ `仍可通过 groups.{groupId}.requireMention 对单个群覆盖。`,
436
+ ``,
437
+ `优先级:具体群配置 > 通配符 "*" > defaultRequireMention(本指令)> 默认 true`
438
+ ].join("\n"),
439
+ handler: async (ctx) => {
440
+ const arg = ctx.args.trim().toLowerCase();
441
+ const currentRequireMention = ctx.accountConfig?.defaultRequireMention ?? true;
442
+ if (!arg) return [
443
+ `🤖 群自主发言状态:${currentRequireMention ? "❌ 仅被 @ 时回复" : "✅ 自主判断何时发言"}`,
444
+ `使用 <qqbot-cmd-input text="/bot-group-allways on" show="/bot-group-allways on"/> 设为自主发言`,
445
+ `使用 <qqbot-cmd-input text="/bot-group-allways off" show="/bot-group-allways off"/> 设为仅被 @ 时回复`
446
+ ].join("\n");
447
+ if (arg !== "on" && arg !== "off") return `❌ 参数错误,请使用 on 或 off\n\n示例:/bot-group-allways on`;
448
+ const newRequireMention = arg === "off";
449
+ if (newRequireMention === currentRequireMention) return `🤖 群自主发言已经是"${arg}"状态,无需操作`;
450
+ let runtime;
451
+ try {
452
+ const getter = getApproveRuntimeGetter();
453
+ if (!getter) throw new Error("runtime not available");
454
+ runtime = getter();
455
+ } catch {
456
+ const fwVer = resolveRuntimeServiceVersion();
457
+ const ver = getPluginVersionString();
458
+ return [
459
+ `❌ 当前版本不支持该指令`,
460
+ ``,
461
+ `🦞框架版本:${fwVer}`,
462
+ `🤖QQBot 插件版本:v${ver}`,
463
+ ``,
464
+ `可通过以下命令手动设置:`,
465
+ ``,
466
+ `\`\`\`shell`,
467
+ `# 设为 AI 自主判断何时发言(defaultRequireMention=false)`,
468
+ `openclaw config set channels.qqbot.defaultRequireMention false`,
469
+ `# 或设为仅被 @ 时回复(defaultRequireMention=true)`,
470
+ `openclaw config set channels.qqbot.defaultRequireMention true`,
471
+ `\`\`\``
472
+ ].join("\n");
473
+ }
474
+ try {
475
+ const configApi = runtime.config;
476
+ const currentCfg = structuredClone(configApi.current());
477
+ const qqbot = (currentCfg.channels ?? {}).qqbot;
478
+ if (!qqbot) return `❌ 配置文件中未找到 qqbot 通道配置`;
479
+ const accountId = ctx.accountId;
480
+ if (accountId !== "default" && Boolean(qqbot.accounts?.[accountId])) {
481
+ const nextAccounts = { ...qqbot.accounts ?? {} };
482
+ const acct = { ...nextAccounts[accountId] };
483
+ acct.defaultRequireMention = newRequireMention;
484
+ nextAccounts[accountId] = acct;
485
+ qqbot.accounts = nextAccounts;
486
+ } else qqbot.defaultRequireMention = newRequireMention;
487
+ await configApi.replaceConfigFile({
488
+ nextConfig: currentCfg,
489
+ afterWrite: { mode: "auto" }
490
+ });
491
+ clearRuntimeConfigSnapshot();
492
+ return [
493
+ `✅ 群自主发言已设置为 ${newRequireMention ? "**off**(仅被 @ 时回复)" : "**on**(AI 自主判断何时发言)"}`,
494
+ ``,
495
+ newRequireMention ? `仅在被 @ 机器人才会回复。` : `AI 将自主判断群消息是否需要回复,无需被 @ 即可发言。`
496
+ ].join("\n");
497
+ } catch (err) {
498
+ return `❌ 配置写入失败: ${err instanceof Error ? err.message : String(err)}`;
499
+ }
500
+ }
501
+ });
502
+ }
503
+ //#endregion
421
504
  //#region extensions/qqbot/src/engine/commands/builtin/log-helpers.ts
422
505
  /** Read user-configured log file paths from local config files. */
423
506
  function getConfiguredLogFiles() {
@@ -790,6 +873,7 @@ function registerBuiltinSlashCommands(registry) {
790
873
  registerClearStorageCommands(registry);
791
874
  registerStreamingCommands(registry);
792
875
  registerApproveCommands(registry);
876
+ registerGroupAllwaysCommand(registry);
793
877
  }
794
878
  //#endregion
795
879
  //#region extensions/qqbot/src/engine/commands/slash-commands.ts
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.6.5",
3
+ "version": "2026.6.6-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/qqbot",
9
- "version": "2026.6.5",
9
+ "version": "2026.6.6-beta.2",
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.5"
18
+ "openclaw": ">=2026.6.6-beta.2"
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.6.5",
3
+ "version": "2026.6.6-beta.2",
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.5"
19
+ "openclaw": ">=2026.6.6-beta.2"
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.5"
48
+ "pluginApi": ">=2026.6.6-beta.2"
49
49
  },
50
50
  "build": {
51
- "openclawVersion": "2026.6.5"
51
+ "openclawVersion": "2026.6.6-beta.2"
52
52
  },
53
53
  "release": {
54
54
  "publishToClawHub": true,