@openclaw/qqbot 2026.5.26-beta.2 → 2026.5.27-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 CHANGED
@@ -1,9 +1,9 @@
1
- import { C as recordMessageReply, E as setOutboundAudioPort, S as getMessageReplyStats, T as OUTBOUND_ERROR_CODES, _ as sendVoice, a as sendText, b as checkMessageReplyLimit, d as buildMediaTarget, f as parseTarget, g as sendVideoMsg, h as sendPhoto, i as sendProactiveMessage, m as sendDocument, n as sendCronMessage, p as resolveOutboundMediaPath, r as sendMedia, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR, x as getMessageReplyConfig, y as MESSAGE_REPLY_LIMIT } from "./outbound-Ce2aX078.js";
1
+ import { C as recordMessageReply, E as setOutboundAudioPort, S as getMessageReplyStats, T as OUTBOUND_ERROR_CODES, _ as sendVoice, a as sendText, b as checkMessageReplyLimit, d as buildMediaTarget, f as parseTarget, g as sendVideoMsg, h as sendPhoto, i as sendProactiveMessage, m as sendDocument, n as sendCronMessage, p as resolveOutboundMediaPath, r as sendMedia, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR, x as getMessageReplyConfig, y as MESSAGE_REPLY_LIMIT } from "./outbound-DHc9KJCp.js";
2
2
  import { c as listQQBotAccountIds, l as resolveDefaultQQBotAccountId, o as DEFAULT_ACCOUNT_ID, s as applyQQBotAccountConfig, u as resolveQQBotAccount } from "./config-schema-iX2iJzKm.js";
3
- import { t as qqbotPlugin } from "./channel-DtN9gbLm.js";
3
+ import { t as qqbotPlugin } from "./channel-C_7BS_5A.js";
4
4
  import { C as debugError, l as getAccessToken, w as debugLog, z as formatErrorMessage } from "./runtime-TzkQ0YbR.js";
5
5
  import { t as qqbotSetupPlugin } from "./channel.setup-BxGpVkdx.js";
6
- import { r as getFrameworkCommands, t as getRequestContext } from "./request-context-D-rKRREP.js";
6
+ import { r as getFrameworkCommands, t as getRequestContext } from "./request-context-Dge7ttkg.js";
7
7
  import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
8
8
  //#region extensions/qqbot/src/engine/tools/channel-api.ts
9
9
  /**
@@ -2,12 +2,12 @@ import { c as getPlatformAdapter, t as asOptionalObjectRecord } from "./string-n
2
2
  import { a as qqbotSetupAdapterShared, c as listQQBotAccountIds, i as qqbotMeta, m as getBridgeLogger, n as qqbotSetupWizard, o as DEFAULT_ACCOUNT_ID$1, p as ensurePlatformAdapter, r as qqbotConfigAdapter, s as applyQQBotAccountConfig, t as qqbotChannelConfigSchema, u as resolveQQBotAccount } from "./config-schema-iX2iJzKm.js";
3
3
  import { T as debugWarn, t as getQQBotRuntime, w as debugLog, z as formatErrorMessage } from "./runtime-TzkQ0YbR.js";
4
4
  import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
5
- import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-message";
5
+ import { createMessageReceiptFromOutboundResults, defineChannelMessageAdapter } from "openclaw/plugin-sdk/channel-outbound";
6
6
  import { createChannelApprovalCapability } from "openclaw/plugin-sdk/approval-delivery-runtime";
7
7
  import { createLazyChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-adapter-runtime";
8
8
  import { resolveApprovalRequestChannelAccountId, resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
9
9
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
10
- import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
10
+ import { markImplicitSameChatApprovalAuthorization, resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
11
11
  import { createChannelExecApprovalProfile, isChannelExecApprovalClientEnabledFromConfig, matchesApprovalRequestFilters } from "openclaw/plugin-sdk/approval-client-runtime";
12
12
  import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
13
13
  import * as fs$1 from "node:fs";
@@ -28,7 +28,13 @@ function buildExecApprovalText(request) {
28
28
  lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${expiresIn} \u79d2`);
29
29
  return lines.join("\n");
30
30
  }
31
- function buildPluginApprovalText(request) {
31
+ function appendCommandActionLines(lines, actions) {
32
+ const commandActions = actions.filter((action) => (action.kind === "command" || action.decision == null) && typeof action.command === "string" && action.command.trim().length > 0);
33
+ if (commandActions.length === 0) return;
34
+ lines.push("", "🗨️ 命令:");
35
+ for (const action of commandActions) lines.push(`- ${action.label}: ${action.command.trim()}`);
36
+ }
37
+ function buildPluginApprovalText(request, actions = []) {
32
38
  const timeoutSec = Math.round((request.request.timeoutMs ?? 12e4) / 1e3);
33
39
  const lines = [`${request.request.severity === "critical" ? "🔴" : request.request.severity === "info" ? "🔵" : "🟡"} \u5ba1\u6279\u8bf7\u6c42`, ""];
34
40
  lines.push(`\u{1f4cb} ${request.request.title}`);
@@ -36,6 +42,7 @@ function buildPluginApprovalText(request) {
36
42
  if (request.request.toolName) lines.push(`\u{1f527} \u5de5\u5177: ${request.request.toolName}`);
37
43
  if (request.request.pluginId) lines.push(`\u{1f50c} \u63d2\u4ef6: ${request.request.pluginId}`);
38
44
  if (request.request.agentId) lines.push(`\u{1f916} Agent: ${request.request.agentId}`);
45
+ appendCommandActionLines(lines, actions);
39
46
  lines.push("", `\u23f1\ufe0f \u8d85\u65f6: ${timeoutSec} \u79d2`);
40
47
  return lines.join("\n");
41
48
  }
@@ -241,7 +248,7 @@ const isQQBotExecApprovalApprover = qqbotExecApprovalProfile.isApprover;
241
248
  const isQQBotExecApprovalAuthorizedSender = qqbotExecApprovalProfile.isAuthorizedSender;
242
249
  const shouldHandleQQBotExecApprovalRequest = qqbotExecApprovalProfile.shouldHandleRequest;
243
250
  function authorizeQQBotApprovalAction(params) {
244
- if (resolveQQBotExecApprovalConfig(params) === void 0) return { authorized: true };
251
+ if (resolveQQBotExecApprovalConfig(params) === void 0) return markImplicitSameChatApprovalAuthorization({ authorized: true });
245
252
  return (params.approvalKind === "plugin" ? isQQBotExecApprovalApprover(params) : isQQBotExecApprovalAuthorizedSender(params)) ? { authorized: true } : {
246
253
  authorized: false,
247
254
  reason: "You are not authorized to approve this request."
@@ -374,7 +381,7 @@ function createQQBotApprovalCapability() {
374
381
  },
375
382
  load: async () => {
376
383
  ensurePlatformAdapter();
377
- return (await import("./handler-runtime-I6vCvj2m.js")).qqbotApprovalNativeRuntime;
384
+ return (await import("./handler-runtime-CSVOet7I.js")).qqbotApprovalNativeRuntime;
378
385
  }
379
386
  })
380
387
  });
@@ -868,7 +875,7 @@ function looksLikeQQBotTarget(id) {
868
875
  //#region extensions/qqbot/src/channel.ts
869
876
  let gatewayModulePromise;
870
877
  function loadGatewayModule() {
871
- gatewayModulePromise ??= import("./gateway-DuIz6XAh.js");
878
+ gatewayModulePromise ??= import("./gateway-BdWwKDtj.js");
872
879
  return gatewayModulePromise;
873
880
  }
874
881
  function createQQBotSendReceipt(params) {
@@ -886,7 +893,7 @@ function createQQBotSendReceipt(params) {
886
893
  async function sendQQBotText(params) {
887
894
  await loadGatewayModule();
888
895
  const account = resolveQQBotAccount(params.cfg, params.accountId);
889
- const { sendText } = await import("./outbound-Ce2aX078.js").then((n) => n.t);
896
+ const { sendText } = await import("./outbound-DHc9KJCp.js").then((n) => n.t);
890
897
  const result = await sendText({
891
898
  to: params.to,
892
899
  text: params.text,
@@ -908,7 +915,7 @@ async function sendQQBotText(params) {
908
915
  async function sendQQBotMedia(params) {
909
916
  await loadGatewayModule();
910
917
  const account = resolveQQBotAccount(params.cfg, params.accountId);
911
- const { sendMedia } = await import("./outbound-Ce2aX078.js").then((n) => n.t);
918
+ const { sendMedia } = await import("./outbound-DHc9KJCp.js").then((n) => n.t);
912
919
  const result = await sendMedia({
913
920
  to: params.to,
914
921
  text: params.text ?? "",
@@ -1,2 +1,2 @@
1
- import { t as qqbotPlugin } from "./channel-DtN9gbLm.js";
1
+ import { t as qqbotPlugin } from "./channel-C_7BS_5A.js";
2
2
  export { qqbotPlugin };
@@ -1,10 +1,11 @@
1
- import { E as setOutboundAudioPort, _ as sendVoice, c as isMediaPayload, g as sendVideoMsg, h as sendPhoto, l as parseQQBotPayload, m as sendDocument, o as encodePayloadForCron, r as sendMedia, s as isCronReminderPayload, u as normalizeMediaTags, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-Ce2aX078.js";
1
+ import { E as setOutboundAudioPort, _ as sendVoice, c as isMediaPayload, g as sendVideoMsg, h as sendPhoto, l as parseQQBotPayload, m as sendDocument, o as encodePayloadForCron, r as sendMedia, s as isCronReminderPayload, u as normalizeMediaTags, v as resolveUserFacingMediaError, w as DEFAULT_MEDIA_SEND_ERROR } from "./outbound-DHc9KJCp.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 { f as resolveAccountBase, h as setBridgeLogger, p as ensurePlatformAdapter } from "./config-schema-iX2iJzKm.js";
4
- import { C as parseApprovalButtonData, a as getQQBotDataDir, d as isWindows, f as normalizePath$1, h as authorizeQQBotApprovalAction, i as getHomeDir, l as getTempDir, m as toGatewayAccount, o as getQQBotDataPath, p as resolveQQBotPayloadLocalFilePath, r as checkSilkWasmAvailable, s as getQQBotMediaDir, u as isLocalPath } from "./channel-DtN9gbLm.js";
4
+ import { C as parseApprovalButtonData, a as getQQBotDataDir, d as isWindows, f as normalizePath$1, h as authorizeQQBotApprovalAction, i as getHomeDir, l as getTempDir, m as toGatewayAccount, o as getQQBotDataPath, p as resolveQQBotPayloadLocalFilePath, r as checkSilkWasmAvailable, s as getQQBotMediaDir, u as isLocalPath } from "./channel-C_7BS_5A.js";
5
5
  import { B as StreamContentType, C as debugError, D as getNextMsgSeq, F as getImageMimeType, H as StreamInputState, I as getMaxUploadSize, N as formatFileSize, R as formatDuration, S as withTokenRetry, T as debugWarn, V as StreamInputMode, _ as sendInputNotify, a as acknowledgeInteraction, b as startBackgroundTokenRefresh, c as createRawInputNotifyFn, d as getMessageApi, f as getPluginUserAgent, g as registerAccount, h as onMessageSent, i as accountToCreds, j as downloadFile, k as openLocalFile, l as getAccessToken, m as initSender, n as getQQBotRuntimeForEngine, o as buildDeliveryTarget, p as initApiConfig, s as clearTokenCache, t as getQQBotRuntime, u as getGatewayUrl, v as sendMedia$1, w as debugLog, x as stopBackgroundTokenRefresh, y as sendText, z as formatErrorMessage } from "./runtime-TzkQ0YbR.js";
6
- import { a as getPluginVersion, i as getFrameworkVersion, n as runWithRequestContext, o as initCommands, s as matchSlashCommand } from "./request-context-D-rKRREP.js";
6
+ import { a as getPluginVersion, i as getFrameworkVersion, n as runWithRequestContext, o as initCommands, s as matchSlashCommand } from "./request-context-Dge7ttkg.js";
7
7
  import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
8
+ import { isImplicitSameChatApprovalAuthorization } from "openclaw/plugin-sdk/approval-auth-runtime";
8
9
  import * as fs$1 from "node:fs";
9
10
  import fs from "node:fs";
10
11
  import * as os$1 from "node:os";
@@ -18,8 +19,10 @@ import { resolveRuntimeServiceVersion } from "openclaw/plugin-sdk/cli-runtime";
18
19
  import { getRuntimeConfig } from "openclaw/plugin-sdk/runtime-config-snapshot";
19
20
  import WebSocket from "ws";
20
21
  import { resolveAmbientNodeProxyAgent } from "openclaw/plugin-sdk/extension-shared";
22
+ import { buildChannelInboundEventContext } from "openclaw/plugin-sdk/channel-inbound";
21
23
  import { Buffer as Buffer$1 } from "node:buffer";
22
24
  import { fileURLToPath } from "node:url";
25
+ import { parseAccessGroupAllowFromEntry } from "openclaw/plugin-sdk/access-groups";
23
26
  import { createChannelIngressResolver, defineStableChannelIngressIdentity } from "openclaw/plugin-sdk/channel-ingress-runtime";
24
27
  import { resolveInboundMentionDecision } from "openclaw/plugin-sdk/channel-mention-gating";
25
28
  import { createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
@@ -3031,6 +3034,18 @@ function resolveQQBotEffectivePolicies(input) {
3031
3034
  //#endregion
3032
3035
  //#region extensions/qqbot/src/engine/gateway/interaction-handler.ts
3033
3036
  /**
3037
+ * INTERACTION_CREATE event handler.
3038
+ *
3039
+ * Handles three interaction branches:
3040
+ *
3041
+ * 1. **Config query** (type=2001) — reads config, ACKs with `claw_cfg`.
3042
+ * 2. **Config update** (type=2002) — writes config, ACKs with updated snapshot.
3043
+ * 3. **Approval button** (other) — ACKs, resolves authorized approval actions.
3044
+ *
3045
+ * Config query/update require `runtime.config`. When unavailable, those
3046
+ * branches fall through to a bare ACK (backward-compatible).
3047
+ */
3048
+ /**
3034
3049
  * Build the canonical `claw_cfg` snapshot returned in interaction ACKs.
3035
3050
  *
3036
3051
  * Pure function — all resolution helpers live in engine/config/.
@@ -3138,12 +3153,13 @@ function createInteractionHandler(account, runtime, log, options) {
3138
3153
  return;
3139
3154
  }
3140
3155
  handleApprovalButtonInteraction({
3141
- accountId: account.accountId,
3156
+ account,
3142
3157
  creds,
3143
3158
  event,
3144
3159
  getActiveCfg: options?.getActiveCfg ?? runtime.config?.current,
3145
3160
  log,
3146
- parsed
3161
+ parsed,
3162
+ resolveCommandAuthorized: options?.resolveCommandAuthorized
3147
3163
  });
3148
3164
  };
3149
3165
  }
@@ -3161,11 +3177,12 @@ async function handleApprovalButtonInteraction(params) {
3161
3177
  params.log?.error(`Approval button rejected: active config failed to load: ${err instanceof Error ? err.message : String(err)}`);
3162
3178
  return;
3163
3179
  }
3164
- const authorization = authorizeApprovalButtonActor({
3180
+ const authorization = await authorizeApprovalButtonActor({
3165
3181
  cfg,
3166
- accountId: params.accountId,
3182
+ account: params.account,
3167
3183
  event: params.event,
3168
- approvalKind: resolveApprovalKind(params.parsed.approvalId)
3184
+ approvalKind: resolveApprovalKind(params.parsed.approvalId),
3185
+ resolveCommandAuthorized: params.resolveCommandAuthorized
3169
3186
  });
3170
3187
  if (!authorization.authorized) {
3171
3188
  await acknowledgeApprovalInteraction(params.creds, params.event, params.log, { content: authorization.reason ?? "You are not authorized to approve this request." });
@@ -3192,23 +3209,42 @@ async function acknowledgeApprovalInteraction(creds, event, log, data) {
3192
3209
  log?.error(`Interaction ACK failed: ${err instanceof Error ? err.message : String(err)}`);
3193
3210
  }
3194
3211
  }
3195
- function authorizeApprovalButtonActor(params) {
3212
+ async function authorizeApprovalButtonActor(params) {
3196
3213
  const senderIds = resolveApprovalActorSenderIds(params.event);
3197
- if (senderIds.length === 0) return authorizeQQBotApprovalAction({
3198
- cfg: params.cfg,
3199
- accountId: params.accountId,
3200
- senderId: null,
3201
- approvalKind: params.approvalKind
3202
- });
3214
+ if (senderIds.length === 0) {
3215
+ const result = authorizeQQBotApprovalAction({
3216
+ cfg: params.cfg,
3217
+ accountId: params.account.accountId,
3218
+ senderId: null,
3219
+ approvalKind: params.approvalKind
3220
+ });
3221
+ return result.authorized && isImplicitSameChatApprovalAuthorization(result) ? {
3222
+ authorized: false,
3223
+ reason: "You are not authorized to approve this request."
3224
+ } : result;
3225
+ }
3203
3226
  let denial;
3204
3227
  for (const senderId of senderIds) {
3205
3228
  const result = authorizeQQBotApprovalAction({
3206
3229
  cfg: params.cfg,
3207
- accountId: params.accountId,
3230
+ accountId: params.account.accountId,
3208
3231
  senderId,
3209
3232
  approvalKind: params.approvalKind
3210
3233
  });
3211
- if (result.authorized) return result;
3234
+ if (result.authorized) {
3235
+ if (!isImplicitSameChatApprovalAuthorization(result) || await isImplicitApprovalButtonActorAuthorized({
3236
+ cfg: params.cfg,
3237
+ account: params.account,
3238
+ event: params.event,
3239
+ senderId,
3240
+ resolveCommandAuthorized: params.resolveCommandAuthorized
3241
+ })) return result;
3242
+ denial ??= {
3243
+ authorized: false,
3244
+ reason: "You are not authorized to approve this request."
3245
+ };
3246
+ continue;
3247
+ }
3212
3248
  denial ??= result;
3213
3249
  }
3214
3250
  return denial ?? {
@@ -3216,6 +3252,32 @@ function authorizeApprovalButtonActor(params) {
3216
3252
  reason: "You are not authorized to approve this request."
3217
3253
  };
3218
3254
  }
3255
+ async function isImplicitApprovalButtonActorAuthorized(params) {
3256
+ const accountConfig = resolveApprovalButtonAccountConfig(params.cfg, params.account.accountId);
3257
+ const authInput = {
3258
+ cfg: params.cfg,
3259
+ accountId: params.account.accountId,
3260
+ senderId: params.senderId,
3261
+ isGroup: Boolean(params.event.group_openid),
3262
+ conversationId: params.event.group_openid ?? params.event.user_openid ?? params.senderId,
3263
+ allowFrom: accountConfig.allowFrom,
3264
+ groupAllowFrom: accountConfig.groupAllowFrom,
3265
+ commandsAllowFrom: resolveQQBotCommandsAllowFrom(params.cfg)
3266
+ };
3267
+ return params.resolveCommandAuthorized ? await params.resolveCommandAuthorized(authInput) : resolveSlashCommandAuth(authInput);
3268
+ }
3269
+ function resolveApprovalButtonAccountConfig(cfg, accountId) {
3270
+ const qqbot = readRecord(readRecord(cfg.channels)?.qqbot);
3271
+ const accounts = readRecord(qqbot?.accounts);
3272
+ if (accountId === "default") return {
3273
+ ...qqbot,
3274
+ ...readRecord(accounts?.default)
3275
+ };
3276
+ return readRecord(accounts?.[accountId]) ?? {};
3277
+ }
3278
+ function readRecord(value) {
3279
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
3280
+ }
3219
3281
  function resolveApprovalActorSenderIds(event) {
3220
3282
  return uniqueStrings([event.group_member_openid, event.user_openid].flatMap((value) => {
3221
3283
  const normalized = typeof value === "string" ? value.trim() : "";
@@ -5662,6 +5724,17 @@ function resolveResponseTimeoutMs(cfg) {
5662
5724
  }
5663
5725
  //#endregion
5664
5726
  //#region extensions/qqbot/src/engine/gateway/outbound-dispatch.ts
5727
+ /**
5728
+ * Outbound dispatcher — manage AI reply delivery, tool fallback, and timeouts.
5729
+ *
5730
+ * Responsibilities:
5731
+ * 1. Build ctxPayload and call runtime.dispatchReply
5732
+ * 2. Tool deliver collection + fallback timeout
5733
+ * 3. Block deliver pipeline (consumeQuoteRef → media tags → structured payload → plain text)
5734
+ * 4. Timeout / error handling
5735
+ *
5736
+ * Separated from gateway.ts for testability and to keep handleMessage thin.
5737
+ */
5665
5738
  const TOOL_ONLY_TIMEOUT = 6e4;
5666
5739
  const MAX_TOOL_RENEWALS = 3;
5667
5740
  const TOOL_MEDIA_SEND_TIMEOUT = 45e3;
@@ -5689,7 +5762,7 @@ async function dispatchOutbound(inbound, deps) {
5689
5762
  };
5690
5763
  const sendWithRetry = (sendFn) => sendWithTokenRetry(account.appId, account.clientSecret, sendFn, log, account.accountId);
5691
5764
  const sendErrorMessage = (errorText) => sendErrorToTarget(replyCtx, errorText);
5692
- const ctxPayload = buildCtxPayload(inbound, runtime, cfg);
5765
+ const ctxPayload = await buildCtxPayload(inbound, runtime, cfg);
5693
5766
  let hasResponse = false;
5694
5767
  let hasBlockResponse = false;
5695
5768
  let toolDeliverCount = 0;
@@ -5783,7 +5856,7 @@ async function dispatchOutbound(inbound, deps) {
5783
5856
  const cfgWithSession = cfg;
5784
5857
  const agentId = inbound.route.agentId ?? "default";
5785
5858
  const storePath = runtime.channel.session.resolveStorePath(cfgWithSession.session?.store, { agentId });
5786
- const dispatchPromise = runtime.channel.turn.run({
5859
+ const dispatchPromise = runtime.channel.inbound.run({
5787
5860
  channel: "qqbot",
5788
5861
  accountId: inbound.route.accountId,
5789
5862
  raw: inbound,
@@ -5979,59 +6052,75 @@ function resolveCommandSource(inbound, runtime, cfg) {
5979
6052
  if (!runtime.channel.commands?.isControlCommandMessage?.(commandBody, cfg)) return;
5980
6053
  return "text";
5981
6054
  }
5982
- function buildCtxPayload(inbound, runtime, cfg) {
6055
+ async function buildCtxPayload(inbound, runtime, cfg) {
5983
6056
  const { event } = inbound;
5984
6057
  const commandSource = resolveCommandSource(inbound, runtime, cfg);
5985
- return runtime.channel.reply.finalizeInboundContext({
5986
- Body: inbound.body,
5987
- BodyForAgent: inbound.agentBody,
5988
- RawBody: event.content,
5989
- CommandBody: event.content,
5990
- From: inbound.fromAddress,
5991
- To: inbound.fromAddress,
5992
- SessionKey: inbound.route.sessionKey,
5993
- AccountId: inbound.route.accountId,
5994
- ChatType: inbound.isGroupChat ? "group" : "direct",
5995
- GroupSystemPrompt: inbound.groupSystemPrompt,
5996
- SenderId: event.senderId,
5997
- SenderName: event.senderName,
5998
- Provider: "qqbot",
5999
- Surface: "qqbot",
6000
- MessageSid: event.messageId,
6001
- Timestamp: new Date(event.timestamp).getTime(),
6002
- OriginatingChannel: "qqbot",
6003
- OriginatingTo: inbound.fromAddress,
6004
- QQChannelId: event.channelId,
6005
- QQGuildId: event.guildId,
6006
- QQGroupOpenid: event.groupOpenid,
6007
- QQVoiceAsrReferAvailable: inbound.hasAsrReferFallback,
6008
- QQVoiceTranscriptSources: inbound.voiceTranscriptSources,
6009
- QQVoiceAttachmentPaths: inbound.uniqueVoicePaths,
6010
- QQVoiceAttachmentUrls: inbound.uniqueVoiceUrls,
6011
- QQVoiceAsrReferTexts: inbound.uniqueVoiceAsrReferTexts,
6012
- QQVoiceInputStrategy: "prefer_audio_stt_then_asr_fallback",
6013
- CommandAuthorized: inbound.commandAuthorized,
6014
- ...commandSource ? { CommandSource: commandSource } : {},
6015
- ...inbound.voiceMediaTypes.length > 0 ? {
6016
- MediaTypes: inbound.voiceMediaTypes,
6017
- MediaType: inbound.voiceMediaTypes[0]
6018
- } : {},
6019
- ...inbound.localMediaPaths.length > 0 ? {
6020
- MediaPaths: inbound.localMediaPaths,
6021
- MediaPath: inbound.localMediaPaths[0],
6022
- MediaTypes: inbound.localMediaTypes,
6023
- MediaType: inbound.localMediaTypes[0]
6024
- } : {},
6025
- ...inbound.remoteMediaUrls.length > 0 ? {
6026
- MediaUrls: inbound.remoteMediaUrls,
6027
- MediaUrl: inbound.remoteMediaUrls[0]
6028
- } : {},
6029
- ...inbound.replyTo ? {
6030
- ReplyToId: inbound.replyTo.id,
6031
- ReplyToBody: inbound.replyTo.body,
6032
- ReplyToSender: inbound.replyTo.sender,
6033
- ReplyToIsQuote: inbound.replyTo.isQuote
6034
- } : {}
6058
+ const hasImageMedia = inbound.localMediaPaths.length > 0 || inbound.remoteMediaUrls.length > 0;
6059
+ return buildChannelInboundEventContext({
6060
+ finalize: runtime.channel.reply.finalizeInboundContext,
6061
+ channel: "qqbot",
6062
+ accountId: inbound.route.accountId,
6063
+ messageId: event.messageId,
6064
+ timestamp: new Date(event.timestamp).getTime(),
6065
+ from: inbound.fromAddress,
6066
+ sender: {
6067
+ id: event.senderId,
6068
+ name: event.senderName
6069
+ },
6070
+ conversation: {
6071
+ kind: inbound.isGroupChat ? "group" : "direct",
6072
+ id: inbound.peerId
6073
+ },
6074
+ route: {
6075
+ agentId: inbound.route.agentId ?? "main",
6076
+ routeSessionKey: inbound.route.sessionKey,
6077
+ accountId: inbound.route.accountId
6078
+ },
6079
+ reply: { to: inbound.fromAddress },
6080
+ message: {
6081
+ body: inbound.body,
6082
+ bodyForAgent: inbound.agentBody,
6083
+ rawBody: event.content,
6084
+ commandBody: event.content
6085
+ },
6086
+ access: { commands: { authorized: inbound.commandAuthorized } },
6087
+ command: commandSource ? {
6088
+ kind: "text-slash",
6089
+ body: event.content,
6090
+ authorized: inbound.commandAuthorized
6091
+ } : void 0,
6092
+ media: hasImageMedia ? void 0 : inbound.voiceMediaTypes.map((contentType) => ({ contentType })),
6093
+ supplemental: {
6094
+ quote: inbound.replyTo ? {
6095
+ id: inbound.replyTo.id,
6096
+ body: inbound.replyTo.body,
6097
+ sender: inbound.replyTo.sender,
6098
+ isQuote: inbound.replyTo.isQuote
6099
+ } : void 0,
6100
+ groupSystemPrompt: inbound.groupSystemPrompt
6101
+ },
6102
+ extra: {
6103
+ QQChannelId: event.channelId,
6104
+ QQGuildId: event.guildId,
6105
+ QQGroupOpenid: event.groupOpenid,
6106
+ QQVoiceAsrReferAvailable: inbound.hasAsrReferFallback,
6107
+ QQVoiceTranscriptSources: inbound.voiceTranscriptSources,
6108
+ QQVoiceAttachmentPaths: inbound.uniqueVoicePaths,
6109
+ QQVoiceAttachmentUrls: inbound.uniqueVoiceUrls,
6110
+ QQVoiceAsrReferTexts: inbound.uniqueVoiceAsrReferTexts,
6111
+ QQVoiceInputStrategy: "prefer_audio_stt_then_asr_fallback",
6112
+ ...commandSource ? { CommandSource: commandSource } : {},
6113
+ ...inbound.localMediaPaths.length > 0 ? {
6114
+ MediaPaths: inbound.localMediaPaths,
6115
+ MediaPath: inbound.localMediaPaths[0],
6116
+ MediaTypes: inbound.localMediaTypes,
6117
+ MediaType: inbound.localMediaTypes[0]
6118
+ } : {},
6119
+ ...inbound.remoteMediaUrls.length > 0 ? {
6120
+ MediaUrls: inbound.remoteMediaUrls,
6121
+ MediaUrl: inbound.remoteMediaUrls[0]
6122
+ } : {}
6123
+ }
6035
6124
  });
6036
6125
  }
6037
6126
  //#endregion
@@ -6225,7 +6314,10 @@ async function startGateway$1(ctx) {
6225
6314
  });
6226
6315
  }
6227
6316
  };
6228
- const handleInteraction = createInteractionHandler(account, ctx.runtime, log, { getActiveCfg: () => activeCfgProvider.getActiveCfg() });
6317
+ const handleInteraction = createInteractionHandler(account, ctx.runtime, log, {
6318
+ getActiveCfg: () => activeCfgProvider.getActiveCfg(),
6319
+ resolveCommandAuthorized: (params) => adapters.access.resolveSlashCommandAuthorization(params)
6320
+ });
6229
6321
  await new GatewayConnection({
6230
6322
  account,
6231
6323
  abortSignal: ctx.abortSignal,
@@ -6443,7 +6535,7 @@ function createSdkAccessAdapter() {
6443
6535
  };
6444
6536
  }
6445
6537
  async function resolveQQBotSlashCommandAuthorized(params) {
6446
- const explicitAllowFrom = normalizeQQBotAllowFrom(params.commandsAllowFrom ?? (params.isGroup && params.groupAllowFrom && params.groupAllowFrom.length > 0 ? params.groupAllowFrom : params.allowFrom)).filter((entry) => entry !== "*");
6538
+ const explicitAllowFrom = normalizeQQBotCommandAllowFrom(params.commandsAllowFrom ?? (params.isGroup && params.groupAllowFrom && params.groupAllowFrom.length > 0 ? params.groupAllowFrom : params.allowFrom));
6447
6539
  if (explicitAllowFrom.length === 0) return false;
6448
6540
  return (await createChannelIngressResolver({
6449
6541
  channelId: "qqbot",
@@ -6467,6 +6559,20 @@ async function resolveQQBotSlashCommandAuthorized(params) {
6467
6559
  command: { modeWhenAccessGroupsOff: "configured" }
6468
6560
  })).commandAccess.authorized;
6469
6561
  }
6562
+ function normalizeQQBotCommandAllowFrom(rawAllowFrom) {
6563
+ const entries = [];
6564
+ for (const rawEntry of rawAllowFrom ?? []) {
6565
+ const entry = String(rawEntry).trim();
6566
+ if (!entry) continue;
6567
+ if (parseAccessGroupAllowFromEntry(entry)) {
6568
+ entries.push(entry);
6569
+ continue;
6570
+ }
6571
+ const normalized = normalizeQQBotSenderId(entry);
6572
+ if (normalized && normalized !== "*") entries.push(normalized);
6573
+ }
6574
+ return entries;
6575
+ }
6470
6576
  //#endregion
6471
6577
  //#region extensions/qqbot/src/bridge/gateway.ts
6472
6578
  /**
@@ -1,11 +1,14 @@
1
1
  import { m as getBridgeLogger, p as ensurePlatformAdapter, u as resolveQQBotAccount } from "./config-schema-iX2iJzKm.js";
2
- import { S as buildPluginApprovalText, _ as matchesQQBotApprovalAccount, b as buildApprovalKeyboard, g as isQQBotExecApprovalClientEnabled, v as resolveQQBotExecApprovalConfig, w as resolveApprovalTarget, x as buildExecApprovalText, y as shouldHandleQQBotExecApprovalRequest } from "./channel-DtN9gbLm.js";
2
+ import { S as buildPluginApprovalText, _ as matchesQQBotApprovalAccount, b as buildApprovalKeyboard, g as isQQBotExecApprovalClientEnabled, v as resolveQQBotExecApprovalConfig, w as resolveApprovalTarget, x as buildExecApprovalText, y as shouldHandleQQBotExecApprovalRequest } from "./channel-C_7BS_5A.js";
3
3
  import { d as getMessageApi, i as accountToCreds } from "./runtime-TzkQ0YbR.js";
4
4
  import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
5
5
  import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
6
6
  //#region extensions/qqbot/src/bridge/approval/handler-runtime.ts
7
7
  function isExecRequest(request) {
8
- return "expiresAtMs" in request;
8
+ return "command" in request.request;
9
+ }
10
+ function listDecisionActions(actions) {
11
+ return actions.flatMap((action) => action.kind === "decision" && action.decision ? [action.decision] : []);
9
12
  }
10
13
  function resolveQQTarget(request) {
11
14
  const sessionConversation = resolveApprovalRequestSessionConversation({
@@ -71,8 +74,9 @@ const qqbotApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
71
74
  presentation: {
72
75
  buildPendingPayload: ({ request, view }) => {
73
76
  const req = request;
74
- const text = isExecRequest(req) ? buildExecApprovalText(req) : buildPluginApprovalText(req);
75
- const keyboard = buildApprovalKeyboard(req.id, view.actions.map((action) => action.decision));
77
+ const text = isExecRequest(req) ? buildExecApprovalText(req) : buildPluginApprovalText(req, view.actions);
78
+ const decisionActions = listDecisionActions(view.actions);
79
+ const keyboard = decisionActions.length > 0 ? buildApprovalKeyboard(req.id, decisionActions) : void 0;
76
80
  getBridgeLogger().debug?.(`[qqbot:approval-runtime] buildPendingPayload requestId=${req.id} kind=${isExecRequest(req) ? "exec" : "plugin"}`);
77
81
  return {
78
82
  text,
@@ -1,5 +1,5 @@
1
1
  import { i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
2
- import { a as getQQBotDataDir, f as normalizePath, n as parseTarget$1, p as resolveQQBotPayloadLocalFilePath, s as getQQBotMediaDir, u as isLocalPath } from "./channel-DtN9gbLm.js";
2
+ import { a as getQQBotDataDir, f as normalizePath, n as parseTarget$1, p as resolveQQBotPayloadLocalFilePath, s as getQQBotMediaDir, u as isLocalPath } from "./channel-C_7BS_5A.js";
3
3
  import { A as checkFileSize, C as debugError, E as UploadDailyLimitExceededError, F as getImageMimeType, I as getMaxUploadSize, L as readFileAsync, M as fileExistsAsync, N as formatFileSize, O as UPLOAD_PREPARE_FALLBACK_CODE, P as getFileTypeName, T as debugWarn, i as accountToCreds, j as downloadFile, p as initApiConfig, v as sendMedia$1, w as debugLog, y as sendText$1, z as formatErrorMessage } from "./runtime-TzkQ0YbR.js";
4
4
  import { pathExistsSync, resolveLocalPathFromRootsSync } from "openclaw/plugin-sdk/security-runtime";
5
5
  import path from "node:path";
@@ -1,4 +1,4 @@
1
- import { a as getQQBotDataDir, c as getQQBotMediaPath, d as isWindows, i as getHomeDir } from "./channel-DtN9gbLm.js";
1
+ import { a as getQQBotDataDir, c as getQQBotMediaPath, d as isWindows, i as getHomeDir } from "./channel-C_7BS_5A.js";
2
2
  import { w as debugLog } from "./runtime-TzkQ0YbR.js";
3
3
  import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import fs from "node:fs";
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.5.26-beta.2",
3
+ "version": "2026.5.27-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/qqbot",
9
- "version": "2026.5.26-beta.2",
9
+ "version": "2026.5.27-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.5.26-beta.2"
18
+ "openclaw": ">=2026.5.27-beta.1"
19
19
  },
20
20
  "peerDependenciesMeta": {
21
21
  "openclaw": {
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "id": "qqbot",
3
+ "name": "QQ Bot",
4
+ "description": "OpenClaw QQ Bot channel plugin for group and direct-message workflows.",
3
5
  "activation": {
4
6
  "onStartup": false
5
7
  },
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@openclaw/qqbot",
3
- "version": "2026.5.26-beta.2",
3
+ "version": "2026.5.27-beta.1",
4
4
  "private": false,
5
- "description": "OpenClaw QQ Bot channel plugin",
5
+ "description": "OpenClaw QQ Bot channel plugin for group and direct-message workflows.",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/openclaw/openclaw"
@@ -16,7 +16,7 @@
16
16
  "zod": "4.4.3"
17
17
  },
18
18
  "peerDependencies": {
19
- "openclaw": ">=2026.5.26-beta.2"
19
+ "openclaw": ">=2026.5.27-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.5.26-beta.2"
48
+ "pluginApi": ">=2026.5.27-beta.1"
49
49
  },
50
50
  "build": {
51
- "openclawVersion": "2026.5.26-beta.2"
51
+ "openclawVersion": "2026.5.27-beta.1"
52
52
  },
53
53
  "release": {
54
54
  "publishToClawHub": true,