@openclaw/qqbot 2026.6.8 → 2026.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +5 -5
- package/dist/{channel-entry-CfzBEypJ.js → channel-entry-5YxjzTrA.js} +3 -3
- package/dist/channel-entry-api.js +1 -1
- package/dist/{channel-M0rnT4bN.js → channel-jbnSeYkd.js} +417 -18
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{gateway-CElyH-CJ.js → gateway-DilADGTY.js} +37 -10
- package/dist/{handler-runtime-BZCDG_mN.js → handler-runtime-D0l75JR8.js} +2 -2
- package/dist/{outbound-C9wV892v.js → outbound-BQcPCo-z.js} +46 -4
- package/dist/{runtime-B9UoQ5NI.js → runtime-D1hn91-s.js} +1 -1
- package/dist/runtime-api.js +1 -1
- package/dist/{sender-DIMG7jHz.js → sender-CRZ2af7P.js} +51 -32
- package/dist/{slash-commands-impl-DVApdSop.js → slash-commands-impl-BUH24Ycv.js} +0 -4
- package/dist/{tools-2d9t-N1b.js → tools-CV5Y0isN.js} +53 -32
- package/dist/tools-api.js +1 -1
- package/npm-shrinkwrap.json +3 -3
- package/package.json +4 -4
package/dist/api.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-jbnSeYkd.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-
|
|
5
|
-
import { n as registerRemindTool, r as registerChannelTool, t as registerQQBotTools } from "./tools-
|
|
6
|
-
import { t as registerQQBotFull } from "./channel-entry-
|
|
7
|
-
import { C as
|
|
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";
|
|
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,7 +1,7 @@
|
|
|
1
1
|
import { a as resolveQQBotAccount } from "./config-ZEfgeoL4.js";
|
|
2
|
-
import { t as getFrameworkCommands } from "./slash-commands-impl-
|
|
3
|
-
import { t as registerQQBotTools } from "./tools-
|
|
4
|
-
import { h as sendDocument } from "./outbound-
|
|
2
|
+
import { t as getFrameworkCommands } from "./slash-commands-impl-BUH24Ycv.js";
|
|
3
|
+
import { t as registerQQBotTools } from "./tools-CV5Y0isN.js";
|
|
4
|
+
import { h as sendDocument } from "./outbound-BQcPCo-z.js";
|
|
5
5
|
//#region extensions/qqbot/src/bridge/commands/framework-context-adapter.ts
|
|
6
6
|
/**
|
|
7
7
|
* Default queue snapshot used for framework-registered commands.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as registerQQBotFull } from "./channel-entry-
|
|
1
|
+
import { t as registerQQBotFull } from "./channel-entry-5YxjzTrA.js";
|
|
2
2
|
export { registerQQBotFull };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
2
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
3
|
import { a as resolveQQBotAccount, n as applyQQBotAccountConfig, r as listQQBotAccountIds, t as DEFAULT_ACCOUNT_ID$1 } from "./config-ZEfgeoL4.js";
|
|
4
|
-
import { t as getQQBotRuntime } from "./runtime-
|
|
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";
|
|
7
7
|
import { getExecApprovalReplyMetadata } from "openclaw/plugin-sdk/approval-runtime";
|
|
@@ -296,6 +296,9 @@ function canResolveTarget(request) {
|
|
|
296
296
|
bundledFallback: true
|
|
297
297
|
})?.id != null;
|
|
298
298
|
}
|
|
299
|
+
function resolveNativeDeliveryState(params) {
|
|
300
|
+
return isNativeDeliveryEnabled(params) ? { kind: "enabled" } : { kind: "disabled" };
|
|
301
|
+
}
|
|
299
302
|
function createQQBotApprovalCapability() {
|
|
300
303
|
return createChannelApprovalCapability({
|
|
301
304
|
authorizeActorAction: ({ cfg, accountId, senderId, approvalKind }) => authorizeQQBotApprovalAction({
|
|
@@ -304,18 +307,8 @@ function createQQBotApprovalCapability() {
|
|
|
304
307
|
senderId,
|
|
305
308
|
approvalKind
|
|
306
309
|
}),
|
|
307
|
-
getActionAvailabilityState:
|
|
308
|
-
|
|
309
|
-
cfg,
|
|
310
|
-
accountId
|
|
311
|
-
}) ? { kind: "enabled" } : { kind: "disabled" };
|
|
312
|
-
},
|
|
313
|
-
getExecInitiatingSurfaceState: ({ cfg, accountId }) => {
|
|
314
|
-
return isNativeDeliveryEnabled({
|
|
315
|
-
cfg,
|
|
316
|
-
accountId
|
|
317
|
-
}) ? { kind: "enabled" } : { kind: "disabled" };
|
|
318
|
-
},
|
|
310
|
+
getActionAvailabilityState: resolveNativeDeliveryState,
|
|
311
|
+
getExecInitiatingSurfaceState: resolveNativeDeliveryState,
|
|
319
312
|
describeExecApprovalSetup: ({ accountId }) => {
|
|
320
313
|
return `QQBot native exec approvals are enabled by default. To restrict who can approve, configure \`${accountId && accountId !== "default" ? `channels.qqbot.accounts.${accountId}` : "channels.qqbot"}.execApprovals.approvers\` with QQ user OpenIDs.`;
|
|
321
314
|
},
|
|
@@ -377,7 +370,7 @@ function createQQBotApprovalCapability() {
|
|
|
377
370
|
},
|
|
378
371
|
load: async () => {
|
|
379
372
|
ensurePlatformAdapter();
|
|
380
|
-
return (await import("./handler-runtime-
|
|
373
|
+
return (await import("./handler-runtime-D0l75JR8.js")).qqbotApprovalNativeRuntime;
|
|
381
374
|
}
|
|
382
375
|
})
|
|
383
376
|
});
|
|
@@ -649,6 +642,412 @@ function clearAccountCredentials(cfg, accountId) {
|
|
|
649
642
|
};
|
|
650
643
|
}
|
|
651
644
|
//#endregion
|
|
645
|
+
//#region extensions/qqbot/src/engine/messaging/markdown-table-chunking.ts
|
|
646
|
+
const QQBOT_MARKDOWN_SAFE_CHUNK_BYTE_LIMIT = 3600;
|
|
647
|
+
function chunkQQBotMarkdownText(text, limit, baseChunker) {
|
|
648
|
+
const chunker = createQQBotMarkdownChunker(baseChunker);
|
|
649
|
+
return [...chunker.chunkText(text, limit), ...chunker.flushPendingText(limit)];
|
|
650
|
+
}
|
|
651
|
+
function createQQBotMarkdownChunker(baseChunker) {
|
|
652
|
+
const state = new QQBotMarkdownChunkingState(baseChunker);
|
|
653
|
+
return {
|
|
654
|
+
chunkText: (text, limit) => state.chunkText(text, limit),
|
|
655
|
+
flushPendingText: (limit) => state.flushPendingText(limit)
|
|
656
|
+
};
|
|
657
|
+
}
|
|
658
|
+
var QQBotMarkdownChunkingState = class {
|
|
659
|
+
constructor(baseChunker) {
|
|
660
|
+
this.baseChunker = baseChunker;
|
|
661
|
+
this.activeTable = null;
|
|
662
|
+
this.pendingHeaderLine = null;
|
|
663
|
+
this.pendingHeaderCells = [];
|
|
664
|
+
this.tableLines = [];
|
|
665
|
+
this.textLines = [];
|
|
666
|
+
this.pendingRowFragment = null;
|
|
667
|
+
this.activeFence = null;
|
|
668
|
+
this.pendingTextFenceOpenLine = null;
|
|
669
|
+
this.pendingFenceLineFragment = null;
|
|
670
|
+
}
|
|
671
|
+
chunkText(text, limit) {
|
|
672
|
+
if (!text) return [];
|
|
673
|
+
if (limit <= 0) return this.baseChunker(text, limit);
|
|
674
|
+
const chunkLimit = resolveQQBotMarkdownChunkLimit(limit);
|
|
675
|
+
const chunks = [];
|
|
676
|
+
const textWithPendingRow = this.consumePendingRowPrefix(text);
|
|
677
|
+
const textWithPendingFenceLine = this.consumePendingFenceLinePrefix(textWithPendingRow);
|
|
678
|
+
const hasTrailingNewline = textWithPendingFenceLine.endsWith("\n");
|
|
679
|
+
const lines = textWithPendingFenceLine.split("\n");
|
|
680
|
+
for (const [index, line] of lines.entries()) {
|
|
681
|
+
const isTrailingSplitLine = index === lines.length - 1 && line === "";
|
|
682
|
+
this.consumeLine(line, {
|
|
683
|
+
limit: chunkLimit,
|
|
684
|
+
chunks,
|
|
685
|
+
hasTrailingNewline,
|
|
686
|
+
isTrailingSplitLine,
|
|
687
|
+
isLastLine: index === lines.length - 1
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
this.flushText(chunks, chunkLimit);
|
|
691
|
+
this.flushTable(chunks);
|
|
692
|
+
return chunks;
|
|
693
|
+
}
|
|
694
|
+
flushPendingText(limit) {
|
|
695
|
+
const chunkLimit = resolveQQBotMarkdownChunkLimit(limit);
|
|
696
|
+
const chunks = [];
|
|
697
|
+
this.flushPendingRowFragment(chunks, chunkLimit);
|
|
698
|
+
this.flushPendingFenceLineFragment();
|
|
699
|
+
this.flushPendingHeaderAsText();
|
|
700
|
+
this.flushText(chunks, chunkLimit);
|
|
701
|
+
this.flushTable(chunks);
|
|
702
|
+
return chunks;
|
|
703
|
+
}
|
|
704
|
+
consumeLine(line, params) {
|
|
705
|
+
const fence = parseFenceLine(line);
|
|
706
|
+
if (fence) {
|
|
707
|
+
this.endTable(params.chunks);
|
|
708
|
+
if (!this.activeFence) {
|
|
709
|
+
this.pushTextLine(line);
|
|
710
|
+
this.activeFence = fence;
|
|
711
|
+
this.clearPendingTableHeader();
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (isClosingFenceLine(line, this.activeFence)) {
|
|
715
|
+
this.pushFenceTextLine(line);
|
|
716
|
+
this.activeFence = null;
|
|
717
|
+
this.clearPendingTableHeader();
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
this.pushFenceTextLine(line);
|
|
721
|
+
this.clearPendingTableHeader();
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if (this.activeFence) {
|
|
725
|
+
if (params.isLastLine && !params.hasTrailingNewline) {
|
|
726
|
+
this.pendingFenceLineFragment = mergeFenceLineFragments(this.pendingFenceLineFragment, line);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
this.pushFenceTextLine(line);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
if (isIncompleteTableRowFragment(line) || this.activeTable && isShortTableRowLine(line, this.activeTable)) {
|
|
733
|
+
if (params.isLastLine) {
|
|
734
|
+
this.flushText(params.chunks, params.limit);
|
|
735
|
+
this.pendingRowFragment = mergeRowFragments(this.pendingRowFragment, line);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
this.pushTextLine(renderMalformedPipeLineAsText(line));
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (this.pendingHeaderLine && isTableSeparatorLine(line)) {
|
|
742
|
+
this.flushText(params.chunks, params.limit);
|
|
743
|
+
this.activeTable = {
|
|
744
|
+
header: this.pendingHeaderLine,
|
|
745
|
+
separator: line,
|
|
746
|
+
cells: this.pendingHeaderCells
|
|
747
|
+
};
|
|
748
|
+
this.pendingHeaderLine = null;
|
|
749
|
+
this.pendingHeaderCells = [];
|
|
750
|
+
this.ensureTableHeader();
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
if (isTableRowLine(line) && this.activeTable && !isTableSeparatorLine(line)) {
|
|
754
|
+
this.flushText(params.chunks, params.limit);
|
|
755
|
+
this.appendTableRow(line, params.limit, params.chunks);
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
if (this.activeTable) {
|
|
759
|
+
if (!line.trim() && params.isTrailingSplitLine) return;
|
|
760
|
+
this.endTable(params.chunks);
|
|
761
|
+
}
|
|
762
|
+
if (isTableRowLine(line) && !isTableSeparatorLine(line)) {
|
|
763
|
+
this.flushText(params.chunks, params.limit);
|
|
764
|
+
this.pendingHeaderLine = line;
|
|
765
|
+
this.pendingHeaderCells = splitTableCells(line);
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
this.flushPendingHeaderAsText();
|
|
769
|
+
this.pushTextLine(line);
|
|
770
|
+
}
|
|
771
|
+
pushTextLine(line) {
|
|
772
|
+
this.textLines.push(line);
|
|
773
|
+
}
|
|
774
|
+
pushFenceTextLine(line) {
|
|
775
|
+
if (this.textLines.length === 0 && this.activeFence) this.pendingTextFenceOpenLine = this.activeFence.openLine;
|
|
776
|
+
this.textLines.push(line);
|
|
777
|
+
}
|
|
778
|
+
appendTableRow(line, limit, chunks) {
|
|
779
|
+
if (utf8ByteLength([
|
|
780
|
+
this.activeTable.header,
|
|
781
|
+
this.activeTable.separator,
|
|
782
|
+
line
|
|
783
|
+
].join("\n")) > limit) {
|
|
784
|
+
this.dropHeaderOnlyTableChunk();
|
|
785
|
+
this.flushTable(chunks);
|
|
786
|
+
this.pushOversizedTableRow(line, limit, chunks);
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
this.ensureTableHeader();
|
|
790
|
+
if (utf8ByteLength([...this.tableLines, line].join("\n")) <= limit) {
|
|
791
|
+
this.tableLines.push(line);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
this.flushTable(chunks);
|
|
795
|
+
this.ensureTableHeader();
|
|
796
|
+
this.tableLines.push(line);
|
|
797
|
+
}
|
|
798
|
+
pushOversizedTableRow(line, limit, chunks) {
|
|
799
|
+
pushBaseChunks(chunks, renderTableRowAsFields(this.activeTable.cells, splitTableCells(line)), limit, this.baseChunker);
|
|
800
|
+
}
|
|
801
|
+
ensureTableHeader() {
|
|
802
|
+
if (this.tableLines.length > 0 || !this.activeTable) return;
|
|
803
|
+
this.tableLines.push(this.activeTable.header, this.activeTable.separator);
|
|
804
|
+
}
|
|
805
|
+
flushText(chunks, limit) {
|
|
806
|
+
if (this.textLines.length === 0) return;
|
|
807
|
+
if (this.flushFenceText(chunks, limit)) return;
|
|
808
|
+
let text = this.textLines.join("\n");
|
|
809
|
+
this.textLines = [];
|
|
810
|
+
if (this.pendingTextFenceOpenLine) {
|
|
811
|
+
text = `${this.pendingTextFenceOpenLine}\n${text}`;
|
|
812
|
+
this.pendingTextFenceOpenLine = null;
|
|
813
|
+
}
|
|
814
|
+
if (this.activeFence) text = `${text}\n${this.activeFence.closeLine}`;
|
|
815
|
+
if (!text) return;
|
|
816
|
+
pushBaseChunks(chunks, text, limit, this.baseChunker);
|
|
817
|
+
}
|
|
818
|
+
flushFenceText(chunks, limit) {
|
|
819
|
+
const pendingFenceOpenLine = this.pendingTextFenceOpenLine;
|
|
820
|
+
const firstLineFence = pendingFenceOpenLine ? null : parseFenceLine(this.textLines[0] ?? "");
|
|
821
|
+
const fence = pendingFenceOpenLine ? parseFenceLine(pendingFenceOpenLine) : firstLineFence;
|
|
822
|
+
if (!fence) return false;
|
|
823
|
+
const bodyLines = pendingFenceOpenLine ? [...this.textLines] : this.textLines.slice(1);
|
|
824
|
+
this.textLines = [];
|
|
825
|
+
this.pendingTextFenceOpenLine = null;
|
|
826
|
+
if (bodyLines.length > 0 && isClosingFenceLine(bodyLines[bodyLines.length - 1], fence)) bodyLines.pop();
|
|
827
|
+
if (this.activeFence && bodyLines.length === 0) return true;
|
|
828
|
+
pushFenceLineChunks({
|
|
829
|
+
chunks,
|
|
830
|
+
openLine: fence.openLine,
|
|
831
|
+
closeLine: fence.closeLine,
|
|
832
|
+
bodyLines,
|
|
833
|
+
limit,
|
|
834
|
+
baseChunker: this.baseChunker
|
|
835
|
+
});
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
consumePendingFenceLinePrefix(text) {
|
|
839
|
+
if (!this.pendingFenceLineFragment) return text;
|
|
840
|
+
const separator = shouldJoinFenceLineFragments(this.pendingFenceLineFragment, text) ? "" : "\n";
|
|
841
|
+
const merged = `${this.pendingFenceLineFragment}${separator}${text}`;
|
|
842
|
+
this.pendingFenceLineFragment = null;
|
|
843
|
+
return merged;
|
|
844
|
+
}
|
|
845
|
+
flushPendingFenceLineFragment() {
|
|
846
|
+
if (!this.pendingFenceLineFragment) return;
|
|
847
|
+
this.pushFenceTextLine(this.pendingFenceLineFragment);
|
|
848
|
+
this.pendingFenceLineFragment = null;
|
|
849
|
+
}
|
|
850
|
+
flushPendingHeaderAsText() {
|
|
851
|
+
if (!this.pendingHeaderLine) return;
|
|
852
|
+
this.pushTextLine(this.pendingHeaderLine);
|
|
853
|
+
this.pendingHeaderLine = null;
|
|
854
|
+
this.pendingHeaderCells = [];
|
|
855
|
+
}
|
|
856
|
+
clearPendingTableHeader() {
|
|
857
|
+
this.pendingHeaderLine = null;
|
|
858
|
+
this.pendingHeaderCells = [];
|
|
859
|
+
}
|
|
860
|
+
consumePendingRowPrefix(text) {
|
|
861
|
+
if (!this.pendingRowFragment) return text;
|
|
862
|
+
const separator = this.pendingRowFragment.trimEnd().endsWith("|") && text && !/^[\s|]/.test(text) ? " " : "";
|
|
863
|
+
const merged = `${this.pendingRowFragment}${separator}${text}`;
|
|
864
|
+
this.pendingRowFragment = null;
|
|
865
|
+
return merged;
|
|
866
|
+
}
|
|
867
|
+
flushPendingRowFragment(chunks, limit) {
|
|
868
|
+
if (!this.pendingRowFragment) return;
|
|
869
|
+
const fragment = this.pendingRowFragment;
|
|
870
|
+
this.pendingRowFragment = null;
|
|
871
|
+
pushBaseChunks(chunks, this.activeTable ? renderTableRowAsFields(this.activeTable.cells, splitPartialTableCells(fragment)) : renderMalformedPipeLineAsText(fragment), limit, this.baseChunker);
|
|
872
|
+
}
|
|
873
|
+
flushTable(chunks) {
|
|
874
|
+
if (this.tableLines.length === 0) return;
|
|
875
|
+
chunks.push(this.tableLines.join("\n"));
|
|
876
|
+
this.tableLines = [];
|
|
877
|
+
}
|
|
878
|
+
dropHeaderOnlyTableChunk() {
|
|
879
|
+
if (this.activeTable && this.tableLines.length === 2 && this.tableLines[0] === this.activeTable.header && this.tableLines[1] === this.activeTable.separator) this.tableLines = [];
|
|
880
|
+
}
|
|
881
|
+
endTable(chunks) {
|
|
882
|
+
this.flushTable(chunks);
|
|
883
|
+
this.activeTable = null;
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
function isTableRowLine(line) {
|
|
887
|
+
const trimmed = line.trim();
|
|
888
|
+
return trimmed.startsWith("|") && trimmed.endsWith("|") && splitTableCells(trimmed).length >= 2;
|
|
889
|
+
}
|
|
890
|
+
function resolveQQBotMarkdownChunkLimit(limit) {
|
|
891
|
+
return Math.min(limit, QQBOT_MARKDOWN_SAFE_CHUNK_BYTE_LIMIT);
|
|
892
|
+
}
|
|
893
|
+
function pushBaseChunks(chunks, text, byteLimit, baseChunker) {
|
|
894
|
+
for (const chunk of baseChunker(text, byteLimit)) {
|
|
895
|
+
if (!chunk) continue;
|
|
896
|
+
if (utf8ByteLength(chunk) <= byteLimit) {
|
|
897
|
+
chunks.push(chunk);
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
chunks.push(...splitByUtf8ByteLimit(chunk, byteLimit));
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
function splitByUtf8ByteLimit(text, byteLimit) {
|
|
904
|
+
if (!text) return [];
|
|
905
|
+
const chunks = [];
|
|
906
|
+
let current = "";
|
|
907
|
+
let currentBytes = 0;
|
|
908
|
+
for (const char of text) {
|
|
909
|
+
const charBytes = utf8ByteLength(char);
|
|
910
|
+
if (current && currentBytes + charBytes > byteLimit) {
|
|
911
|
+
chunks.push(current);
|
|
912
|
+
current = "";
|
|
913
|
+
currentBytes = 0;
|
|
914
|
+
}
|
|
915
|
+
current += char;
|
|
916
|
+
currentBytes += charBytes;
|
|
917
|
+
}
|
|
918
|
+
if (current) chunks.push(current);
|
|
919
|
+
return chunks;
|
|
920
|
+
}
|
|
921
|
+
function utf8ByteLength(text) {
|
|
922
|
+
return Buffer.byteLength(text, "utf8");
|
|
923
|
+
}
|
|
924
|
+
function isIncompleteTableRowFragment(line) {
|
|
925
|
+
const trimmed = line.trim();
|
|
926
|
+
return trimmed.startsWith("|") && !trimmed.endsWith("|") && splitPartialTableCells(trimmed).length >= 2;
|
|
927
|
+
}
|
|
928
|
+
function isShortTableRowLine(line, table) {
|
|
929
|
+
if (!isTableRowLine(line) || isTableSeparatorLine(line)) return false;
|
|
930
|
+
return splitTableCells(line).length < table.cells.length;
|
|
931
|
+
}
|
|
932
|
+
function isTableSeparatorLine(line) {
|
|
933
|
+
if (!isTableRowLine(line)) return false;
|
|
934
|
+
const cells = splitTableCells(line);
|
|
935
|
+
return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell.trim()));
|
|
936
|
+
}
|
|
937
|
+
function splitTableCells(line) {
|
|
938
|
+
return line.trim().slice(1, -1).split("|").map((cell) => cell.trim());
|
|
939
|
+
}
|
|
940
|
+
function splitPartialTableCells(line) {
|
|
941
|
+
return line.trim().replace(/^\|/, "").split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
|
|
942
|
+
}
|
|
943
|
+
function mergeRowFragments(pending, next) {
|
|
944
|
+
return pending ? `${pending}${next}` : next;
|
|
945
|
+
}
|
|
946
|
+
function mergeFenceLineFragments(pending, next) {
|
|
947
|
+
return pending ? `${pending}${next}` : next;
|
|
948
|
+
}
|
|
949
|
+
function shouldJoinFenceLineFragments(pending, next) {
|
|
950
|
+
if (!next || next.startsWith("\n")) return true;
|
|
951
|
+
const trimmedPending = pending.trimEnd();
|
|
952
|
+
const trimmedNext = next.trimStart();
|
|
953
|
+
if (!trimmedPending || !trimmedNext) return true;
|
|
954
|
+
if (/\d\.$/.test(trimmedPending) && /^\d/.test(trimmedNext)) return true;
|
|
955
|
+
if (/[.([{:,+\-*/%=&|^<>\\]$/.test(trimmedPending)) return true;
|
|
956
|
+
return hasUnclosedQuote(trimmedPending) || hasUnclosedDelimiter(trimmedPending);
|
|
957
|
+
}
|
|
958
|
+
function hasUnclosedQuote(line) {
|
|
959
|
+
let single = false;
|
|
960
|
+
let double = false;
|
|
961
|
+
let escaped = false;
|
|
962
|
+
for (const char of line) {
|
|
963
|
+
if (escaped) {
|
|
964
|
+
escaped = false;
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
if (char === "\\") {
|
|
968
|
+
escaped = true;
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
if (char === "'" && !double) {
|
|
972
|
+
single = !single;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
if (char === "\"" && !single) double = !double;
|
|
976
|
+
}
|
|
977
|
+
return single || double;
|
|
978
|
+
}
|
|
979
|
+
function hasUnclosedDelimiter(line) {
|
|
980
|
+
const stack = [];
|
|
981
|
+
const pairs = {
|
|
982
|
+
"(": ")",
|
|
983
|
+
"[": "]",
|
|
984
|
+
"{": "}"
|
|
985
|
+
};
|
|
986
|
+
const closers = new Set(Object.values(pairs));
|
|
987
|
+
for (const char of line) {
|
|
988
|
+
if (pairs[char]) {
|
|
989
|
+
stack.push(pairs[char]);
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
if (closers.has(char)) {
|
|
993
|
+
if (stack.at(-1) === char) stack.pop();
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return stack.length > 0;
|
|
997
|
+
}
|
|
998
|
+
function renderMalformedPipeLineAsText(line) {
|
|
999
|
+
return splitPartialTableCells(line).join(" ");
|
|
1000
|
+
}
|
|
1001
|
+
function renderTableRowAsFields(headers, cells) {
|
|
1002
|
+
return cells.map((cell, index) => {
|
|
1003
|
+
const header = headers[index]?.trim();
|
|
1004
|
+
return header ? `${header}: ${cell}` : cell;
|
|
1005
|
+
}).join("\n");
|
|
1006
|
+
}
|
|
1007
|
+
function pushFenceLineChunks(params) {
|
|
1008
|
+
const { chunks, openLine, closeLine, bodyLines, limit, baseChunker } = params;
|
|
1009
|
+
let currentLines = [];
|
|
1010
|
+
const render = (lines) => [
|
|
1011
|
+
openLine,
|
|
1012
|
+
...lines,
|
|
1013
|
+
closeLine
|
|
1014
|
+
].join("\n");
|
|
1015
|
+
const flushCurrent = () => {
|
|
1016
|
+
if (currentLines.length === 0) return;
|
|
1017
|
+
chunks.push(render(currentLines));
|
|
1018
|
+
currentLines = [];
|
|
1019
|
+
};
|
|
1020
|
+
for (const line of bodyLines) {
|
|
1021
|
+
const candidate = [...currentLines, line];
|
|
1022
|
+
if (utf8ByteLength(render(candidate)) <= limit) {
|
|
1023
|
+
currentLines = candidate;
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
flushCurrent();
|
|
1027
|
+
const singleLineChunk = render([line]);
|
|
1028
|
+
if (utf8ByteLength(singleLineChunk) <= limit) {
|
|
1029
|
+
currentLines = [line];
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
pushBaseChunks(chunks, singleLineChunk, limit, baseChunker);
|
|
1033
|
+
}
|
|
1034
|
+
if (currentLines.length > 0 || bodyLines.length === 0) chunks.push(render(currentLines));
|
|
1035
|
+
}
|
|
1036
|
+
function parseFenceLine(line) {
|
|
1037
|
+
const match = line.match(/^(\s*)(`{3,}|~{3,})/);
|
|
1038
|
+
if (!match?.[2]) return null;
|
|
1039
|
+
return {
|
|
1040
|
+
openLine: line,
|
|
1041
|
+
closeLine: `${match[1] ?? ""}${match[2]}`,
|
|
1042
|
+
marker: match[2]
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function isClosingFenceLine(line, fence) {
|
|
1046
|
+
const markerChar = fence.marker[0] === "`" ? "`" : "~";
|
|
1047
|
+
const match = line.match(/^(\s*)(`{3,}|~{3,})\s*$/);
|
|
1048
|
+
return Boolean(match?.[2] && match[2][0] === markerChar && match[2].length >= fence.marker.length);
|
|
1049
|
+
}
|
|
1050
|
+
//#endregion
|
|
652
1051
|
//#region extensions/qqbot/src/group-policy.ts
|
|
653
1052
|
function resolveQQBotGroupToolPolicy(params) {
|
|
654
1053
|
return resolveChannelGroupToolsPolicy({
|
|
@@ -667,12 +1066,12 @@ function resolveQQBotGroupToolPolicy(params) {
|
|
|
667
1066
|
//#region extensions/qqbot/src/channel.ts
|
|
668
1067
|
let gatewayModulePromise;
|
|
669
1068
|
function loadGatewayModule() {
|
|
670
|
-
gatewayModulePromise ??= import("./gateway-
|
|
1069
|
+
gatewayModulePromise ??= import("./gateway-DilADGTY.js");
|
|
671
1070
|
return gatewayModulePromise;
|
|
672
1071
|
}
|
|
673
1072
|
let outboundMessagingModulePromise;
|
|
674
1073
|
function loadOutboundMessagingModule() {
|
|
675
|
-
outboundMessagingModulePromise ??= import("./outbound-
|
|
1074
|
+
outboundMessagingModulePromise ??= import("./outbound-BQcPCo-z.js").then((n) => n.t);
|
|
676
1075
|
return outboundMessagingModulePromise;
|
|
677
1076
|
}
|
|
678
1077
|
function createQQBotSendReceipt(params) {
|
|
@@ -822,7 +1221,7 @@ const qqbotPlugin = {
|
|
|
822
1221
|
},
|
|
823
1222
|
outbound: {
|
|
824
1223
|
deliveryMode: "direct",
|
|
825
|
-
chunker: (text, limit) => getQQBotRuntime().channel.text.chunkMarkdownText
|
|
1224
|
+
chunker: (text, limit) => chunkQQBotMarkdownText(text, limit, getQQBotRuntime().channel.text.chunkMarkdownText),
|
|
826
1225
|
chunkerMode: "markdown",
|
|
827
1226
|
textChunkLimit: 5e3,
|
|
828
1227
|
sanitizeText: ({ text }) => sanitizeAssistantVisibleText(text),
|
|
@@ -950,4 +1349,4 @@ const qqbotPlugin = {
|
|
|
950
1349
|
}
|
|
951
1350
|
};
|
|
952
1351
|
//#endregion
|
|
953
|
-
export {
|
|
1352
|
+
export { toGatewayAccount as a, matchesQQBotApprovalAccount as c, buildApprovalKeyboard as d, buildExecApprovalText as f, resolveApprovalTarget as h, openQQBotSyncKeyedStore as i, resolveQQBotExecApprovalConfig as l, parseApprovalButtonData as m, createQQBotMarkdownChunker as n, authorizeQQBotApprovalAction as o, buildPluginApprovalText as p, buildQQBotStateKey as r, isQQBotExecApprovalClientEnabled as s, qqbotPlugin as t, shouldHandleQQBotExecApprovalRequest as u };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as qqbotPlugin } from "./channel-
|
|
1
|
+
import { t as qqbotPlugin } from "./channel-jbnSeYkd.js";
|
|
2
2
|
export { qqbotPlugin };
|
|
@@ -1,21 +1,22 @@
|
|
|
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-
|
|
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
3
|
import { c as setBridgeLogger, o as ensurePlatformAdapter } from "./config-schema-BFLNZ8Nf.js";
|
|
4
|
-
import { a as
|
|
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
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
|
-
import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-
|
|
8
|
-
import { a as getHomeDir, c as getQQBotMediaDir, d as isLocalPath, f as isWindows, i as checkSilkWasmAvailable,
|
|
9
|
-
import { a as matchSlashCommand, i as initCommands, n as getFrameworkVersion, r as getPluginVersion } from "./slash-commands-impl-
|
|
7
|
+
import { n as getQQBotRuntimeForEngine, t as getQQBotRuntime } from "./runtime-D1hn91-s.js";
|
|
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";
|
|
10
10
|
import { n as runWithRequestContext } from "./request-context-Bm7PTBD1.js";
|
|
11
|
-
import {
|
|
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
12
|
import { asBoolean, 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";
|
|
16
16
|
import * as os$1 from "node:os";
|
|
17
|
-
import
|
|
17
|
+
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
18
18
|
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
19
|
+
import crypto from "node:crypto";
|
|
19
20
|
import { privateFileStoreSync, readRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
|
|
20
21
|
import * as path$1 from "node:path";
|
|
21
22
|
import path from "node:path";
|
|
@@ -1802,6 +1803,7 @@ var GatewayConnection = class {
|
|
|
1802
1803
|
* Migrated from `src/stt.ts` — uses core/utils/string-normalize instead
|
|
1803
1804
|
* of broad SDK text barrels.
|
|
1804
1805
|
*/
|
|
1806
|
+
const STT_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
|
1805
1807
|
/** Resolve the STT configuration from the nested config object. */
|
|
1806
1808
|
function resolveSTTConfig(cfg) {
|
|
1807
1809
|
const channelStt = asOptionalObjectRecord(asOptionalObjectRecord(asOptionalObjectRecord(cfg.channels)?.qqbot)?.stt);
|
|
@@ -1855,7 +1857,7 @@ async function transcribeAudio(audioPath, cfg) {
|
|
|
1855
1857
|
});
|
|
1856
1858
|
try {
|
|
1857
1859
|
if (!resp.ok) {
|
|
1858
|
-
const detail = await resp
|
|
1860
|
+
const detail = await readResponseTextLimited(resp, STT_ERROR_BODY_LIMIT_BYTES).catch(() => "");
|
|
1859
1861
|
throw new Error(`STT failed (HTTP ${resp.status}): ${detail.slice(0, 300)}`);
|
|
1860
1862
|
}
|
|
1861
1863
|
return normalizeOptionalString$1((await resp.json()).text) ?? null;
|
|
@@ -3997,7 +3999,7 @@ function formatMediaTypeLabel(mediaType) {
|
|
|
3997
3999
|
return mediaType[0].toUpperCase() + mediaType.slice(1);
|
|
3998
4000
|
}
|
|
3999
4001
|
function validateStructuredPayloadLocalPath(ctx, payloadPath, mediaType) {
|
|
4000
|
-
const allowedPath =
|
|
4002
|
+
const allowedPath = resolveTrustedOutboundMediaPath(payloadPath);
|
|
4001
4003
|
if (allowedPath) return allowedPath;
|
|
4002
4004
|
ctx.log?.error(`Blocked ${mediaType} payload local path outside QQ Bot media storage`);
|
|
4003
4005
|
return null;
|
|
@@ -5870,6 +5872,7 @@ async function dispatchOutbound(inbound, deps) {
|
|
|
5870
5872
|
if (!hasResponse) reject(/* @__PURE__ */ new Error("Response timeout"));
|
|
5871
5873
|
}, responseTimeoutMs);
|
|
5872
5874
|
});
|
|
5875
|
+
const markdownChunker = createQQBotMarkdownChunker((text, limit) => runtime.channel.text.chunkMarkdownText(text, limit));
|
|
5873
5876
|
const deliverDeps = {
|
|
5874
5877
|
mediaSender: {
|
|
5875
5878
|
sendPhoto: (target, imageUrl) => sendPhoto(target, imageUrl),
|
|
@@ -5878,7 +5881,30 @@ async function dispatchOutbound(inbound, deps) {
|
|
|
5878
5881
|
sendDocument: (target, filePath) => sendDocument(target, filePath),
|
|
5879
5882
|
sendMedia: (opts) => sendMedia$1(opts)
|
|
5880
5883
|
},
|
|
5881
|
-
chunkText: (text, limit) =>
|
|
5884
|
+
chunkText: (text, limit) => markdownChunker.chunkText(text, limit)
|
|
5885
|
+
};
|
|
5886
|
+
const flushPendingMarkdownText = async () => {
|
|
5887
|
+
const pendingChunks = markdownChunker.flushPendingText(TEXT_CHUNK_LIMIT);
|
|
5888
|
+
if (pendingChunks.length === 0) return;
|
|
5889
|
+
const passthroughDeps = {
|
|
5890
|
+
...deliverDeps,
|
|
5891
|
+
chunkText: (text) => [text]
|
|
5892
|
+
};
|
|
5893
|
+
for (const chunk of pendingChunks) {
|
|
5894
|
+
await sendTextOnlyReply(chunk, {
|
|
5895
|
+
type: event.type,
|
|
5896
|
+
senderId: event.senderId,
|
|
5897
|
+
messageId: event.messageId,
|
|
5898
|
+
channelId: event.channelId,
|
|
5899
|
+
groupOpenid: event.groupOpenid,
|
|
5900
|
+
msgIdx: event.msgIdx
|
|
5901
|
+
}, {
|
|
5902
|
+
account,
|
|
5903
|
+
qualifiedTarget,
|
|
5904
|
+
log
|
|
5905
|
+
}, sendWithRetry, () => void 0, passthroughDeps);
|
|
5906
|
+
recordOutbound();
|
|
5907
|
+
}
|
|
5882
5908
|
};
|
|
5883
5909
|
const replyDeps = { tts: {
|
|
5884
5910
|
textToSpeech: (params) => runtime.tts.textToSpeech(params),
|
|
@@ -6140,6 +6166,7 @@ async function dispatchOutbound(inbound, deps) {
|
|
|
6140
6166
|
toolFallbackSent = true;
|
|
6141
6167
|
await sendToolFallback();
|
|
6142
6168
|
}
|
|
6169
|
+
await flushPendingMarkdownText();
|
|
6143
6170
|
if (streamingController && !streamingController.isTerminalPhase) try {
|
|
6144
6171
|
streamingController.markFullyComplete();
|
|
6145
6172
|
await streamingController.onIdle();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as getMessageApi, t as accountToCreds } from "./sender-
|
|
1
|
+
import { c as getMessageApi, t as accountToCreds } from "./sender-CRZ2af7P.js";
|
|
2
2
|
import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-BFLNZ8Nf.js";
|
|
3
|
-
import { c as
|
|
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
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";
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { A as getFileTypeName, D as downloadFile, E as checkFileSize, L as __exportAll, M as getMaxUploadSize, N as readFileAsync, O as fileExistsAsync, S as UploadDailyLimitExceededError, g as sendText$1, h as sendMedia$1, j as getImageMimeType, k as formatFileSize, t as accountToCreds, u as initApiConfig, w as UPLOAD_PREPARE_FALLBACK_CODE } from "./sender-
|
|
1
|
+
import { A as getFileTypeName, D as downloadFile, E as checkFileSize, L as __exportAll, M as getMaxUploadSize, N as readFileAsync, O as fileExistsAsync, S as UploadDailyLimitExceededError, g as sendText$1, h as sendMedia$1, j as getImageMimeType, k as formatFileSize, t as accountToCreds, u as initApiConfig, w as UPLOAD_PREPARE_FALLBACK_CODE } from "./sender-CRZ2af7P.js";
|
|
2
2
|
import { i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
|
|
3
3
|
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-SDfMMBWe.js";
|
|
4
4
|
import { c as getQQBotMediaDir, d as isLocalPath, m as resolveQQBotPayloadLocalFilePath, o as getQQBotDataDir, p as normalizePath$1, r as parseTarget$1 } from "./target-parser-BdCUmxK7.js";
|
|
5
5
|
import { pathExistsSync, resolveLocalPathFromRootsSync } from "openclaw/plugin-sdk/security-runtime";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { getFileExtension } from "openclaw/plugin-sdk/media-mime";
|
|
8
|
+
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/sandbox";
|
|
8
9
|
//#region extensions/qqbot/src/engine/messaging/outbound-audio-port.ts
|
|
9
10
|
let outboundAudioPort = null;
|
|
10
11
|
/**
|
|
@@ -193,6 +194,47 @@ function buildFileTooLargeResult(fileType, fileSize) {
|
|
|
193
194
|
};
|
|
194
195
|
}
|
|
195
196
|
//#endregion
|
|
197
|
+
//#region extensions/qqbot/src/engine/messaging/trusted-media-path.ts
|
|
198
|
+
let cachedTrustedTmpRoot;
|
|
199
|
+
function trustedOpenClawTmpRoot() {
|
|
200
|
+
if (cachedTrustedTmpRoot === void 0) try {
|
|
201
|
+
cachedTrustedTmpRoot = resolvePreferredOpenClawTmpDir();
|
|
202
|
+
} catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
return cachedTrustedTmpRoot;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Resolve a local outbound media path against every trusted root, returning the
|
|
209
|
+
* canonical path or null when it sits outside all of them.
|
|
210
|
+
*
|
|
211
|
+
* QQBot is the only channel that root-sandboxes outbound local files, and the
|
|
212
|
+
* same check runs at three sites (`resolveOutboundMediaPath`, the voice send
|
|
213
|
+
* re-check, and structured-payload validation), so they must all agree or a file
|
|
214
|
+
* accepted at one gate is rejected at the next. Beyond the QQ Bot media storage
|
|
215
|
+
* roots, this also trusts OpenClaw's permission-hardened temp root, where
|
|
216
|
+
* framework scratch media is written (e.g. cron auto-TTS voice files). Core
|
|
217
|
+
* already treats that temp root as a sanctioned media root (`buildMediaLocalRoots`);
|
|
218
|
+
* without it here, auto-routed sends are dropped and cron delivery silently loses
|
|
219
|
+
* the message.
|
|
220
|
+
*
|
|
221
|
+
* `allowMissing` lets callers accept a not-yet-flushed temp file (e.g. TTS still
|
|
222
|
+
* writing) under the temp root; existence is then enforced later by the voice
|
|
223
|
+
* send re-check before upload.
|
|
224
|
+
*/
|
|
225
|
+
function resolveTrustedOutboundMediaPath(p, options = {}) {
|
|
226
|
+
const storageRootPath = resolveQQBotPayloadLocalFilePath(p);
|
|
227
|
+
if (storageRootPath) return storageRootPath;
|
|
228
|
+
const tmpRoot = trustedOpenClawTmpRoot();
|
|
229
|
+
if (!tmpRoot) return null;
|
|
230
|
+
return resolveLocalPathFromRootsSync({
|
|
231
|
+
filePath: p,
|
|
232
|
+
roots: [tmpRoot],
|
|
233
|
+
label: "OpenClaw temp media root",
|
|
234
|
+
allowMissing: options.allowMissing === true
|
|
235
|
+
})?.path ?? null;
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
196
238
|
//#region extensions/qqbot/src/engine/messaging/outbound-media-send.ts
|
|
197
239
|
/**
|
|
198
240
|
* Low-level outbound media sends (photo, voice, video, document) and path resolution.
|
|
@@ -252,7 +294,7 @@ function resolveOutboundMediaPath(rawPath, mediaKind, options = {}) {
|
|
|
252
294
|
ok: true,
|
|
253
295
|
mediaPath: normalizedPath
|
|
254
296
|
};
|
|
255
|
-
const allowedPath =
|
|
297
|
+
const allowedPath = resolveTrustedOutboundMediaPath(normalizedPath, { allowMissing: options.allowMissingLocalPath });
|
|
256
298
|
if (allowedPath) return {
|
|
257
299
|
ok: true,
|
|
258
300
|
mediaPath: allowedPath
|
|
@@ -462,7 +504,7 @@ async function sendVoiceFromLocal(ctx, mediaPath, directUploadFormats, transcode
|
|
|
462
504
|
error: "Voice generate failed"
|
|
463
505
|
};
|
|
464
506
|
if (fileSize > getMaxUploadSize(3)) return buildFileTooLargeResult(3, fileSize);
|
|
465
|
-
const safeMediaPath =
|
|
507
|
+
const safeMediaPath = resolveTrustedOutboundMediaPath(mediaPath);
|
|
466
508
|
if (!safeMediaPath) {
|
|
467
509
|
debugWarn(`sendVoice: blocked local voice path outside QQ Bot media storage`);
|
|
468
510
|
return {
|
|
@@ -1368,4 +1410,4 @@ async function sendCronMessage(account, to, message) {
|
|
|
1368
1410
|
});
|
|
1369
1411
|
}
|
|
1370
1412
|
//#endregion
|
|
1371
|
-
export {
|
|
1413
|
+
export { getMessageReplyConfig as C, OUTBOUND_ERROR_CODES as D, DEFAULT_MEDIA_SEND_ERROR as E, setOutboundAudioPort as O, checkMessageReplyLimit as S, recordMessageReply as T, sendVideoMsg as _, sendText as a, resolveUserFacingMediaError as b, isCronReminderPayload as c, normalizeMediaTags as d, buildMediaTarget as f, sendPhoto as g, sendDocument as h, sendProactiveMessage as i, isMediaPayload as l, resolveOutboundMediaPath as m, sendCronMessage as n, decodeMediaPath as o, parseTarget as p, sendMedia as r, encodePayloadForCron as s, outbound_exports as t, parseQQBotPayload as u, sendVoice as v, getMessageReplyStats as w, MESSAGE_REPLY_LIMIT as x, resolveTrustedOutboundMediaPath as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { v as setOpenClawVersion } from "./sender-
|
|
1
|
+
import { v as setOpenClawVersion } from "./sender-CRZ2af7P.js";
|
|
2
2
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
3
3
|
//#region extensions/qqbot/src/bridge/runtime.ts
|
|
4
4
|
const { setRuntime: _setRuntime, clearRuntime: resetQQBotRuntimeForTest, getRuntime: getQQBotRuntime } = createPluginRuntimeStore({
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-
|
|
1
|
+
import { r as setQQBotRuntime, t as getQQBotRuntime } from "./runtime-D1hn91-s.js";
|
|
2
2
|
export { getQQBotRuntime, setQQBotRuntime };
|
|
@@ -2,9 +2,10 @@ import { c as getPlatformAdapter, i as normalizeOptionalString, s as sanitizeFil
|
|
|
2
2
|
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-SDfMMBWe.js";
|
|
3
3
|
import * as fs$1 from "node:fs";
|
|
4
4
|
import os from "node:os";
|
|
5
|
+
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
6
|
+
import { fetchWithSsrFGuard, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
5
7
|
import * as crypto$1 from "node:crypto";
|
|
6
8
|
import crypto from "node:crypto";
|
|
7
|
-
import { fetchWithSsrFGuard, isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
8
9
|
import { FsSafeError, openLocalFileSafely, readRegularFile, statRegularFileSync } from "openclaw/plugin-sdk/security-runtime";
|
|
9
10
|
import * as path$1 from "node:path";
|
|
10
11
|
import { mimeTypeFromFilePath } from "openclaw/plugin-sdk/media-mime";
|
|
@@ -72,6 +73,13 @@ const StreamContentType = { MARKDOWN: "markdown" };
|
|
|
72
73
|
const DEFAULT_BASE_URL = "https://api.sgroup.qq.com";
|
|
73
74
|
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
74
75
|
const FILE_UPLOAD_TIMEOUT_MS = 12e4;
|
|
76
|
+
const QQBOT_API_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
|
77
|
+
function resolveQqbotApiSsrfPolicy(url) {
|
|
78
|
+
return {
|
|
79
|
+
hostnameAllowlist: [new URL(url).hostname],
|
|
80
|
+
allowRfc2544BenchmarkRange: true
|
|
81
|
+
};
|
|
82
|
+
}
|
|
75
83
|
/**
|
|
76
84
|
* Stateful HTTP client for the QQ Open Platform.
|
|
77
85
|
*
|
|
@@ -125,8 +133,16 @@ var ApiClient = class {
|
|
|
125
133
|
this.logger.debug(`[qqbot:api] >>> Body: ${JSON.stringify(logBody)}`);
|
|
126
134
|
}
|
|
127
135
|
let res;
|
|
136
|
+
let release;
|
|
128
137
|
try {
|
|
129
|
-
|
|
138
|
+
const guarded = await fetchWithSsrFGuard({
|
|
139
|
+
url,
|
|
140
|
+
init: fetchInit,
|
|
141
|
+
auditContext: "qqbot-api",
|
|
142
|
+
policy: resolveQqbotApiSsrfPolicy(url)
|
|
143
|
+
});
|
|
144
|
+
res = guarded.response;
|
|
145
|
+
release = guarded.release;
|
|
130
146
|
} catch (err) {
|
|
131
147
|
clearTimeout(timeoutId);
|
|
132
148
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -138,32 +154,38 @@ var ApiClient = class {
|
|
|
138
154
|
} finally {
|
|
139
155
|
clearTimeout(timeoutId);
|
|
140
156
|
}
|
|
141
|
-
const traceId = res.headers.get("x-tps-trace-id") ?? "";
|
|
142
|
-
this.logger?.info?.(`[qqbot:api] <<< Status: ${res.status} ${res.statusText}${traceId ? ` | TraceId: ${traceId}` : ""}`);
|
|
143
|
-
let rawBody;
|
|
144
157
|
try {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
const traceId = res.headers.get("x-tps-trace-id") ?? "";
|
|
159
|
+
this.logger?.info?.(`[qqbot:api] <<< Status: ${res.status} ${res.statusText}${traceId ? ` | TraceId: ${traceId}` : ""}`);
|
|
160
|
+
const readBody = async (limitBytes) => {
|
|
161
|
+
try {
|
|
162
|
+
return limitBytes === void 0 ? await res.text() : await readResponseTextLimited(res, limitBytes);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
throw new ApiError(`Failed to read response [${path}]: ${formatErrorMessage(err)}`, res.status, path);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const rawBody = res.ok ? await readBody() : await readBody(QQBOT_API_ERROR_BODY_LIMIT_BYTES);
|
|
168
|
+
this.logger?.debug?.(`[qqbot:api] <<< Body: ${rawBody}`);
|
|
169
|
+
const isHtmlResponse = (res.headers.get("content-type") ?? "").includes("text/html") || rawBody.trimStart().startsWith("<");
|
|
170
|
+
if (!res.ok) {
|
|
171
|
+
if (isHtmlResponse) throw new ApiError(`${res.status === 502 || res.status === 503 || res.status === 504 ? "调用发生异常,请稍候重试" : res.status === 429 ? "请求过于频繁,已被限流" : `开放平台返回 HTTP ${res.status}`}(${path}),请稍后重试`, res.status, path);
|
|
172
|
+
try {
|
|
173
|
+
const error = JSON.parse(rawBody);
|
|
174
|
+
const bizCode = error.code ?? error.err_code;
|
|
175
|
+
throw new ApiError(`API Error [${path}]: ${error.message ?? rawBody}`, res.status, path, bizCode, error.message);
|
|
176
|
+
} catch (parseErr) {
|
|
177
|
+
if (parseErr instanceof ApiError) throw parseErr;
|
|
178
|
+
throw new ApiError(`API Error [${path}] HTTP ${res.status}: ${rawBody.slice(0, 200)}`, res.status, path);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (isHtmlResponse) throw new ApiError(`QQ 服务端返回了非 JSON 响应(${path}),可能是临时故障,请稍后重试`, res.status, path);
|
|
153
182
|
try {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
throw new ApiError(
|
|
157
|
-
} catch (parseErr) {
|
|
158
|
-
if (parseErr instanceof ApiError) throw parseErr;
|
|
159
|
-
throw new ApiError(`API Error [${path}] HTTP ${res.status}: ${rawBody.slice(0, 200)}`, res.status, path);
|
|
183
|
+
return JSON.parse(rawBody);
|
|
184
|
+
} catch {
|
|
185
|
+
throw new ApiError(`开放平台响应格式异常(${path}),请稍后重试`, res.status, path);
|
|
160
186
|
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
return JSON.parse(rawBody);
|
|
165
|
-
} catch {
|
|
166
|
-
throw new ApiError(`开放平台响应格式异常(${path}),请稍后重试`, res.status, path);
|
|
187
|
+
} finally {
|
|
188
|
+
await release?.();
|
|
167
189
|
}
|
|
168
190
|
}
|
|
169
191
|
};
|
|
@@ -680,6 +702,7 @@ const MAX_CONCURRENT_PARTS = 10;
|
|
|
680
702
|
const MAX_PART_FINISH_RETRY_TIMEOUT_MS = 600 * 1e3;
|
|
681
703
|
/** Per-part PUT timeout (5 minutes). Matches the low-bandwidth tolerance. */
|
|
682
704
|
const PART_UPLOAD_TIMEOUT_MS = 3e5;
|
|
705
|
+
const PART_UPLOAD_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
|
683
706
|
/**
|
|
684
707
|
* Boundary used by `md5_10m` — first 10,002,432 bytes.
|
|
685
708
|
*
|
|
@@ -907,7 +930,7 @@ async function putToPresignedUrl(presignedUrl, data, partIndex, totalParts, logg
|
|
|
907
930
|
const requestId = response.headers.get("x-cos-request-id") ?? "-";
|
|
908
931
|
const etag = response.headers.get("ETag") ?? "-";
|
|
909
932
|
if (!response.ok) {
|
|
910
|
-
const body = await response
|
|
933
|
+
const body = await readResponseTextLimited(response, PART_UPLOAD_ERROR_BODY_LIMIT_BYTES).catch(() => "");
|
|
911
934
|
logger?.error?.(`${prefix} PUT part ${partIndex}/${totalParts}: HTTP ${response.status} ${response.statusText} (${elapsed}ms, requestId=${requestId}) body=${body.slice(0, 160)}`);
|
|
912
935
|
throw new Error(`COS PUT failed: ${response.status} ${response.statusText} - ${body.slice(0, 120)}`);
|
|
913
936
|
}
|
|
@@ -960,6 +983,7 @@ function sleep(ms) {
|
|
|
960
983
|
*/
|
|
961
984
|
const TOKEN_URL = "https://bots.qq.com/app/getAppAccessToken";
|
|
962
985
|
const DEFAULT_TOKEN_EXPIRES_IN_SECONDS = 7200;
|
|
986
|
+
const QQBOT_TOKEN_RESPONSE_LIMIT_BYTES = 8 * 1024;
|
|
963
987
|
/**
|
|
964
988
|
* Host-scoped SSRF policy for the QQ Bot token endpoint.
|
|
965
989
|
*
|
|
@@ -1102,11 +1126,6 @@ var TokenManager = class {
|
|
|
1102
1126
|
this.refreshControllers.clear();
|
|
1103
1127
|
}
|
|
1104
1128
|
}
|
|
1105
|
-
/** Check whether background refresh is running. */
|
|
1106
|
-
isBackgroundRefreshRunning(appId) {
|
|
1107
|
-
if (appId) return this.refreshControllers.has(appId);
|
|
1108
|
-
return this.refreshControllers.size > 0;
|
|
1109
|
-
}
|
|
1110
1129
|
async doFetchToken(appId, clientSecret) {
|
|
1111
1130
|
this.logger?.debug?.(`[qqbot:token:${appId}] >>> POST ${TOKEN_URL}`);
|
|
1112
1131
|
let response;
|
|
@@ -1140,7 +1159,7 @@ var TokenManager = class {
|
|
|
1140
1159
|
this.logger?.debug?.(`[qqbot:token:${appId}] <<< ${response.status}${traceId ? ` | TraceId: ${traceId}` : ""}`);
|
|
1141
1160
|
let rawBody;
|
|
1142
1161
|
try {
|
|
1143
|
-
rawBody = await response
|
|
1162
|
+
rawBody = await readResponseTextLimited(response, QQBOT_TOKEN_RESPONSE_LIMIT_BYTES);
|
|
1144
1163
|
} catch (err) {
|
|
1145
1164
|
throw new Error(`Failed to read access_token response: ${formatErrorMessage(err)}`, { cause: err });
|
|
1146
1165
|
}
|
|
@@ -909,10 +909,6 @@ var SlashCommandRegistry = class {
|
|
|
909
909
|
handler: cmd.handler
|
|
910
910
|
}));
|
|
911
911
|
}
|
|
912
|
-
/** Return all pre-dispatch commands. */
|
|
913
|
-
getPreDispatchCommands() {
|
|
914
|
-
return this.commands;
|
|
915
|
-
}
|
|
916
912
|
/** Return all registered commands (both maps) for help listing. */
|
|
917
913
|
getAllCommands() {
|
|
918
914
|
const all = /* @__PURE__ */ new Map();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { a as resolveQQBotAccount, r as listQQBotAccountIds } from "./config-ZEfgeoL4.js";
|
|
2
2
|
import { a as formatErrorMessage, n as debugLog, t as debugError } from "./log-SDfMMBWe.js";
|
|
3
3
|
import { t as getRequestContext } from "./request-context-Bm7PTBD1.js";
|
|
4
|
+
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
5
|
+
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
4
6
|
import { resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
5
7
|
import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
6
8
|
//#region extensions/qqbot/src/engine/tools/channel-api.ts
|
|
@@ -15,6 +17,13 @@ import { callGatewayTool } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
|
15
17
|
*/
|
|
16
18
|
const API_BASE = "https://api.sgroup.qq.com";
|
|
17
19
|
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
20
|
+
const CHANNEL_API_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
|
|
21
|
+
function resolveChannelApiSsrfPolicy(url) {
|
|
22
|
+
return {
|
|
23
|
+
hostnameAllowlist: [new URL(url).hostname],
|
|
24
|
+
allowRfc2544BenchmarkRange: true
|
|
25
|
+
};
|
|
26
|
+
}
|
|
18
27
|
/**
|
|
19
28
|
* JSON Schema for AI tool parameters (used by framework registration).
|
|
20
29
|
* AI Tool 参数的 JSON Schema 定义(供框架注册使用)。
|
|
@@ -124,8 +133,16 @@ async function executeChannelApi(params, options) {
|
|
|
124
133
|
].includes(method)) fetchOptions.body = JSON.stringify(params.body);
|
|
125
134
|
debugLog(`[qqbot-channel-api] >>> ${method} ${url} (timeout: ${DEFAULT_TIMEOUT_MS}ms)`);
|
|
126
135
|
let res;
|
|
136
|
+
let release;
|
|
127
137
|
try {
|
|
128
|
-
|
|
138
|
+
const guarded = await fetchWithSsrFGuard({
|
|
139
|
+
url,
|
|
140
|
+
init: fetchOptions,
|
|
141
|
+
auditContext: "qqbot-channel-api",
|
|
142
|
+
policy: resolveChannelApiSsrfPolicy(url)
|
|
143
|
+
});
|
|
144
|
+
res = guarded.response;
|
|
145
|
+
release = guarded.release;
|
|
129
146
|
} catch (err) {
|
|
130
147
|
clearTimeout(timeoutId);
|
|
131
148
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -143,42 +160,46 @@ async function executeChannelApi(params, options) {
|
|
|
143
160
|
} finally {
|
|
144
161
|
clearTimeout(timeoutId);
|
|
145
162
|
}
|
|
146
|
-
debugLog(`[qqbot-channel-api] <<< Status: ${res.status} ${res.statusText}`);
|
|
147
|
-
const rawBody = await res.text();
|
|
148
|
-
if (!rawBody || rawBody.trim() === "") {
|
|
149
|
-
if (res.ok) return json$1({
|
|
150
|
-
success: true,
|
|
151
|
-
status: res.status,
|
|
152
|
-
path: params.path
|
|
153
|
-
});
|
|
154
|
-
return json$1({
|
|
155
|
-
error: `API returned ${res.status} ${res.statusText}`,
|
|
156
|
-
status: res.status,
|
|
157
|
-
path: params.path
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
let parsed;
|
|
161
163
|
try {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
debugLog(`[qqbot-channel-api] <<< Status: ${res.status} ${res.statusText}`);
|
|
165
|
+
const rawBody = res.ok ? await res.text() : await readResponseTextLimited(res, CHANNEL_API_ERROR_BODY_LIMIT_BYTES);
|
|
166
|
+
if (!rawBody || rawBody.trim() === "") {
|
|
167
|
+
if (res.ok) return json$1({
|
|
168
|
+
success: true,
|
|
169
|
+
status: res.status,
|
|
170
|
+
path: params.path
|
|
171
|
+
});
|
|
172
|
+
return json$1({
|
|
173
|
+
error: `API returned ${res.status} ${res.statusText}`,
|
|
174
|
+
status: res.status,
|
|
175
|
+
path: params.path
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
let parsed;
|
|
179
|
+
try {
|
|
180
|
+
parsed = JSON.parse(rawBody);
|
|
181
|
+
} catch {
|
|
182
|
+
parsed = rawBody;
|
|
183
|
+
}
|
|
184
|
+
if (!res.ok) {
|
|
185
|
+
const errMsg = typeof parsed === "object" && parsed && "message" in parsed ? String(parsed.message) : `${res.status} ${res.statusText}`;
|
|
186
|
+
debugError(`[qqbot-channel-api] Error [${method} ${params.path}]: ${errMsg}`);
|
|
187
|
+
return json$1({
|
|
188
|
+
error: errMsg,
|
|
189
|
+
status: res.status,
|
|
190
|
+
path: params.path,
|
|
191
|
+
details: parsed
|
|
192
|
+
});
|
|
193
|
+
}
|
|
169
194
|
return json$1({
|
|
170
|
-
|
|
195
|
+
success: true,
|
|
171
196
|
status: res.status,
|
|
172
197
|
path: params.path,
|
|
173
|
-
|
|
198
|
+
data: parsed
|
|
174
199
|
});
|
|
200
|
+
} finally {
|
|
201
|
+
await release?.();
|
|
175
202
|
}
|
|
176
|
-
return json$1({
|
|
177
|
-
success: true,
|
|
178
|
-
status: res.status,
|
|
179
|
-
path: params.path,
|
|
180
|
-
data: parsed
|
|
181
|
-
});
|
|
182
203
|
} catch (err) {
|
|
183
204
|
return json$1({
|
|
184
205
|
error: formatErrorMessage(err),
|
|
@@ -209,7 +230,7 @@ function registerChannelTool(api) {
|
|
|
209
230
|
description: "Authenticated HTTP proxy for QQ Open Platform channel APIs. Common endpoints: list guilds GET /users/@me/guilds | list channels GET /guilds/{guild_id}/channels | get channel GET /channels/{channel_id} | create channel POST /guilds/{guild_id}/channels | list members GET /guilds/{guild_id}/members?after=0&limit=100 | get member GET /guilds/{guild_id}/members/{user_id} | list threads GET /channels/{channel_id}/threads | create thread PUT /channels/{channel_id}/threads | create announce POST /guilds/{guild_id}/announces | create schedule POST /channels/{channel_id}/schedules. See the qqbot-channel skill for full endpoint details.",
|
|
210
231
|
parameters: ChannelApiSchema,
|
|
211
232
|
async execute(_toolCallId, params) {
|
|
212
|
-
const { getAccessToken } = await import("./sender-
|
|
233
|
+
const { getAccessToken } = await import("./sender-CRZ2af7P.js").then((n) => n._);
|
|
213
234
|
return executeChannelApi(params, { accessToken: await getAccessToken(account.appId, account.clientSecret) });
|
|
214
235
|
}
|
|
215
236
|
}, { name: "qqbot_channel_api" });
|
package/dist/tools-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as registerQQBotTools } from "./tools-
|
|
1
|
+
import { t as registerQQBotTools } from "./tools-CV5Y0isN.js";
|
|
2
2
|
export { registerQQBotTools };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/qqbot",
|
|
3
|
-
"version": "2026.6.
|
|
3
|
+
"version": "2026.6.9",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/qqbot",
|
|
9
|
-
"version": "2026.6.
|
|
9
|
+
"version": "2026.6.9",
|
|
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.
|
|
18
|
+
"openclaw": ">=2026.6.9"
|
|
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.
|
|
3
|
+
"version": "2026.6.9",
|
|
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.
|
|
19
|
+
"openclaw": ">=2026.6.9"
|
|
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.
|
|
48
|
+
"pluginApi": ">=2026.6.9"
|
|
49
49
|
},
|
|
50
50
|
"build": {
|
|
51
|
-
"openclawVersion": "2026.6.
|
|
51
|
+
"openclawVersion": "2026.6.9"
|
|
52
52
|
},
|
|
53
53
|
"release": {
|
|
54
54
|
"publishToClawHub": true,
|