@openclaw/qqbot 2026.7.1 → 2026.7.2-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +8 -7
- package/dist/{channel-entry-C5YdhX3Y.js → channel-entry-CA2T2sf8.js} +4 -4
- package/dist/channel-entry-api.js +1 -1
- package/dist/{channel-D1UztsnG.js → channel-nz3Mkify.js} +99 -54
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-2ItDYKhz.js → channel.setup-CNoBtKXQ.js} +1 -1
- package/dist/{config-C1qZbh0K.js → config-CpOXnoEc.js} +2 -2
- package/dist/{config-schema-JZEf1dvB.js → config-schema-D7MaH5X5.js} +61 -76
- package/dist/doctor-contract-api.js +4 -10
- package/dist/{gateway-pJQppxe4.js → gateway-D8uYPtoy.js} +286 -174
- package/dist/{group-o0GmovSf.js → group-BVHG8qUZ.js} +40 -23
- package/dist/{handler-runtime-zQvT6SrI.js → handler-runtime-S1_XF8on.js} +8 -12
- package/dist/{log-DEtcoDWe.js → log-Da4jz75I.js} +4 -8
- package/dist/{outbound-BIrfvvFJ.js → outbound-FOG4zNLY.js} +8 -142
- package/dist/{runtime-DodcT_fQ.js → runtime-CyjBiGD2.js} +2 -2
- package/dist/runtime-api.js +1 -1
- package/dist/secret-contract-api.js +6 -22
- package/dist/{sender-CjDuU-uz.js → sender-BAUHZqDW.js} +225 -66
- package/dist/setup-plugin-api.js +1 -1
- package/dist/state-keys-jLJ2SmJA.js +225 -0
- package/dist/{tools-UJJ-tLHP.js → tools-CC5CKQig.js} +71 -26
- package/dist/tools-api.js +1 -1
- package/node_modules/p-map/index.d.ts +155 -0
- package/node_modules/p-map/index.js +284 -0
- package/node_modules/p-map/license +9 -0
- package/node_modules/p-map/package.json +57 -0
- package/node_modules/p-map/readme.md +190 -0
- package/node_modules/parse-ms/index.d.ts +30 -0
- package/node_modules/parse-ms/index.js +45 -0
- package/node_modules/parse-ms/license +9 -0
- package/node_modules/parse-ms/package.json +47 -0
- package/node_modules/parse-ms/readme.md +46 -0
- package/node_modules/pretty-ms/index.d.ts +157 -0
- package/node_modules/pretty-ms/index.js +149 -0
- package/node_modules/pretty-ms/license +9 -0
- package/node_modules/pretty-ms/package.json +55 -0
- package/node_modules/pretty-ms/readme.md +179 -0
- package/npm-shrinkwrap.json +44 -3
- package/openclaw.plugin.json +105 -133
- package/package.json +8 -4
- package/dist/state-keys-CQKlAFyo.js +0 -141
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { t as asOptionalObjectRecord } from "./string-normalize-R_0cKO7Q.js";
|
|
2
|
-
import { d as resolveAccountBase } from "./config-
|
|
3
|
-
import { n as debugLog } from "./log-
|
|
4
|
-
import {
|
|
2
|
+
import { d as resolveAccountBase } from "./config-CpOXnoEc.js";
|
|
3
|
+
import { n as debugLog } from "./log-Da4jz75I.js";
|
|
4
|
+
import { O as getHomeDir, P as isWindows, j as getQQBotMediaPath, k as getQQBotDataDir } from "./outbound-FOG4zNLY.js";
|
|
5
5
|
import { asBoolean, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
6
|
import fs from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { formatByteSize } from "openclaw/plugin-sdk/number-runtime";
|
|
9
|
+
import { resolveScopeRequireMention } from "openclaw/plugin-sdk/channel-policy";
|
|
9
10
|
import { clearRuntimeConfigSnapshot } from "openclaw/plugin-sdk/runtime-config-snapshot";
|
|
10
11
|
import { loadJsonFile } from "openclaw/plugin-sdk/json-store";
|
|
11
12
|
//#region extensions/qqbot/src/engine/commands/builtin/state.ts
|
|
@@ -666,10 +667,15 @@ function tailFileLines(filePath, maxLines) {
|
|
|
666
667
|
const readSize = Math.min(CHUNK_SIZE, position);
|
|
667
668
|
position -= readSize;
|
|
668
669
|
const buf = Buffer.alloc(readSize);
|
|
669
|
-
|
|
670
|
+
let actualRead = 0;
|
|
671
|
+
while (actualRead < readSize) {
|
|
672
|
+
const justRead = fs.readSync(fd, buf, actualRead, readSize - actualRead, position + actualRead);
|
|
673
|
+
if (justRead === 0) throw new Error(`Could not complete log read for ${filePath}`);
|
|
674
|
+
actualRead += justRead;
|
|
675
|
+
}
|
|
670
676
|
chunks.unshift(buf);
|
|
671
|
-
bytesRead +=
|
|
672
|
-
for (let i = 0; i <
|
|
677
|
+
bytesRead += actualRead;
|
|
678
|
+
for (let i = 0; i < actualRead; i++) if (buf[i] === 10) newlineCount++;
|
|
673
679
|
}
|
|
674
680
|
const allLines = Buffer.concat(chunks).toString("utf8").split("\n");
|
|
675
681
|
const tail = allLines.slice(-maxLines);
|
|
@@ -766,15 +772,10 @@ function registerLogCommands(registry) {
|
|
|
766
772
|
//#endregion
|
|
767
773
|
//#region extensions/qqbot/src/engine/commands/builtin/register-streaming.ts
|
|
768
774
|
function isStreamingConfigEnabled(streaming) {
|
|
769
|
-
if (streaming
|
|
770
|
-
|
|
771
|
-
if (
|
|
772
|
-
|
|
773
|
-
if (o.c2cStreamApi === true) return true;
|
|
774
|
-
if (o.mode === "off") return false;
|
|
775
|
-
return true;
|
|
776
|
-
}
|
|
777
|
-
return false;
|
|
775
|
+
if (!streaming || typeof streaming !== "object") return false;
|
|
776
|
+
const o = streaming;
|
|
777
|
+
if (o.nativeTransport === true) return true;
|
|
778
|
+
return o.mode !== "off";
|
|
778
779
|
}
|
|
779
780
|
function registerStreamingCommands(registry) {
|
|
780
781
|
registry.register({
|
|
@@ -820,7 +821,7 @@ function registerStreamingCommands(registry) {
|
|
|
820
821
|
``,
|
|
821
822
|
`\`\`\`shell`,
|
|
822
823
|
`# 1. 开启流式消息`,
|
|
823
|
-
`openclaw config set channels.qqbot.streaming true`,
|
|
824
|
+
`openclaw config set channels.qqbot.streaming.nativeTransport true`,
|
|
824
825
|
``,
|
|
825
826
|
`# 2. 重启网关使配置生效`,
|
|
826
827
|
`openclaw gateway restart`,
|
|
@@ -833,7 +834,10 @@ function registerStreamingCommands(registry) {
|
|
|
833
834
|
const qqbot = (currentCfg.channels ?? {}).qqbot;
|
|
834
835
|
if (!qqbot) return `❌ 配置文件中未找到 qqbot 通道配置`;
|
|
835
836
|
const accountId = ctx.accountId;
|
|
836
|
-
const newVal = wantOn
|
|
837
|
+
const newVal = wantOn ? {
|
|
838
|
+
mode: "partial",
|
|
839
|
+
nativeTransport: true
|
|
840
|
+
} : { mode: "off" };
|
|
837
841
|
if (accountId !== "default") {
|
|
838
842
|
const nextAccounts = { ...qqbot.accounts ?? {} };
|
|
839
843
|
const acct = { ...nextAccounts[accountId] };
|
|
@@ -912,7 +916,7 @@ const PRIVATE_ONLY_CORE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
912
916
|
"tools",
|
|
913
917
|
"skill",
|
|
914
918
|
"diagnostics",
|
|
915
|
-
"
|
|
919
|
+
"openclaw",
|
|
916
920
|
"tasks",
|
|
917
921
|
"allowlist",
|
|
918
922
|
"approve",
|
|
@@ -1097,13 +1101,17 @@ function getPluginVersion() {
|
|
|
1097
1101
|
function getFrameworkVersion() {
|
|
1098
1102
|
return getFrameworkVersionString();
|
|
1099
1103
|
}
|
|
1104
|
+
//#endregion
|
|
1105
|
+
//#region extensions/qqbot/src/engine/config/group.ts
|
|
1106
|
+
const DEFAULT_GROUP_HISTORY_LIMIT = 50;
|
|
1107
|
+
const DEFAULT_GROUP_COMMAND_LEVEL = "all";
|
|
1100
1108
|
const DEFAULT_GROUP_PROMPT = "If the sender is a bot, respond only when they explicitly @mention you to ask a question or request assistance with a specific task; keep your replies concise and clear, avoiding the urge to race other bots to answer or engage in lengthy, unproductive exchanges. In group chats, prioritize responding to messages from human users; bots should maintain a collaborative rather than competitive dynamic to ensure the conversation remains orderly and does not result in message flooding.";
|
|
1101
1109
|
const DEFAULT_GROUP_CONFIG = {
|
|
1102
1110
|
requireMention: true,
|
|
1103
1111
|
ignoreOtherMentions: false,
|
|
1104
|
-
commandLevel:
|
|
1112
|
+
commandLevel: DEFAULT_GROUP_COMMAND_LEVEL,
|
|
1105
1113
|
name: "",
|
|
1106
|
-
historyLimit:
|
|
1114
|
+
historyLimit: DEFAULT_GROUP_HISTORY_LIMIT
|
|
1107
1115
|
};
|
|
1108
1116
|
function readGroupsMap(cfg, accountId) {
|
|
1109
1117
|
const groups = asOptionalObjectRecord(resolveAccountBase(cfg, accountId).config.groups);
|
|
@@ -1134,11 +1142,20 @@ function readHistoryLimit(obj, key) {
|
|
|
1134
1142
|
function resolveGroupConfig(cfg, groupOpenid, accountId) {
|
|
1135
1143
|
const account = resolveAccountBase(cfg, accountId);
|
|
1136
1144
|
const groups = readGroupsMap(cfg, accountId);
|
|
1137
|
-
const
|
|
1145
|
+
const { "*": wildcard = {}, ...scopes } = groups;
|
|
1138
1146
|
const specific = groupOpenid ? groups[groupOpenid] ?? {} : {};
|
|
1139
|
-
const accountDefaultRequireMention = asBoolean(account.config.defaultRequireMention)
|
|
1147
|
+
const accountDefaultRequireMention = asBoolean(account.config.defaultRequireMention);
|
|
1148
|
+
const mentionTree = {
|
|
1149
|
+
defaults: { requireMention: readBoolean(wildcard, "requireMention") },
|
|
1150
|
+
scopes: Object.fromEntries(Object.entries(scopes).map(([key, entry]) => [key, { requireMention: readBoolean(entry, "requireMention") }]))
|
|
1151
|
+
};
|
|
1140
1152
|
return {
|
|
1141
|
-
requireMention:
|
|
1153
|
+
requireMention: resolveScopeRequireMention({
|
|
1154
|
+
tree: mentionTree,
|
|
1155
|
+
path: groupOpenid && Object.hasOwn(mentionTree.scopes, groupOpenid) ? [groupOpenid] : [],
|
|
1156
|
+
requireMentionOverride: accountDefaultRequireMention,
|
|
1157
|
+
overrideOrder: "after-config"
|
|
1158
|
+
}),
|
|
1142
1159
|
ignoreOtherMentions: readBoolean(specific, "ignoreOtherMentions") ?? readBoolean(wildcard, "ignoreOtherMentions") ?? DEFAULT_GROUP_CONFIG.ignoreOtherMentions,
|
|
1143
1160
|
commandLevel: readCommandLevel(specific, "commandLevel") ?? readCommandLevel(wildcard, "commandLevel") ?? DEFAULT_GROUP_CONFIG.commandLevel,
|
|
1144
1161
|
name: readString(specific, "name") ?? readString(wildcard, "name") ?? DEFAULT_GROUP_CONFIG.name,
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { c as getMessageApi, t as accountToCreds } from "./sender-
|
|
2
|
-
import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-
|
|
3
|
-
import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-
|
|
4
|
-
import { a as resolveQQBotAccount } from "./config-
|
|
1
|
+
import { c as getMessageApi, t as accountToCreds } from "./sender-BAUHZqDW.js";
|
|
2
|
+
import { o as ensurePlatformAdapter, s as getBridgeLogger } from "./config-schema-D7MaH5X5.js";
|
|
3
|
+
import { c as resolveQQBotExecApprovalConfig, d as buildExecApprovalText, f as buildPluginApprovalText, l as shouldHandleQQBotExecApprovalRequest, m as resolveApprovalTarget, o as isQQBotExecApprovalClientEnabled, s as matchesQQBotApprovalAccount, u as buildApprovalKeyboard } from "./channel-nz3Mkify.js";
|
|
4
|
+
import { a as resolveQQBotAccount } from "./config-CpOXnoEc.js";
|
|
5
5
|
import { resolveApprovalRequestSessionConversation } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
6
6
|
import { createChannelApprovalNativeRuntimeAdapter } from "openclaw/plugin-sdk/approval-handler-runtime";
|
|
7
7
|
//#region extensions/qqbot/src/bridge/approval/handler-runtime.ts
|
|
8
|
-
function isExecRequest(request) {
|
|
9
|
-
return "expiresAtMs" in request;
|
|
10
|
-
}
|
|
11
8
|
function resolveQQTarget(request) {
|
|
12
9
|
const sessionConversation = resolveApprovalRequestSessionConversation({
|
|
13
10
|
request,
|
|
@@ -70,11 +67,10 @@ const qqbotApprovalNativeRuntime = createChannelApprovalNativeRuntimeAdapter({
|
|
|
70
67
|
}
|
|
71
68
|
},
|
|
72
69
|
presentation: {
|
|
73
|
-
buildPendingPayload: ({
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
getBridgeLogger().debug?.(`[qqbot:approval-runtime] buildPendingPayload requestId=${req.id} kind=${isExecRequest(req) ? "exec" : "plugin"}`);
|
|
70
|
+
buildPendingPayload: ({ view, nowMs }) => {
|
|
71
|
+
const text = view.approvalKind === "exec" ? buildExecApprovalText(view, nowMs) : buildPluginApprovalText(view, nowMs);
|
|
72
|
+
const keyboard = buildApprovalKeyboard(view.approvalId, view.approvalKind, view.actions.map((action) => action.decision));
|
|
73
|
+
getBridgeLogger().debug?.(`[qqbot:approval-runtime] buildPendingPayload requestId=${view.approvalId} kind=${view.approvalKind}`);
|
|
78
74
|
return {
|
|
79
75
|
text,
|
|
80
76
|
keyboard
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
2
|
+
import prettyMilliseconds from "pretty-ms";
|
|
2
3
|
//#region extensions/qqbot/src/engine/utils/format.ts
|
|
3
4
|
/**
|
|
4
5
|
* General formatting and string utilities.
|
|
5
6
|
* 通用格式化与字符串工具。
|
|
6
7
|
*
|
|
7
|
-
* Pure utility functions with
|
|
8
|
-
* Replaces `openclaw/plugin-sdk/error-runtime` and `text-runtime`
|
|
9
|
-
* helpers for use inside engine/.
|
|
8
|
+
* Pure utility functions, with duration presentation from the plugin-local dependency.
|
|
10
9
|
*
|
|
11
10
|
* NOTE: The framework `formatErrorMessage` also applies `redactSensitiveText()`
|
|
12
11
|
* for token masking. We intentionally omit that here — the framework's log
|
|
@@ -46,11 +45,8 @@ function formatErrorMessage(err) {
|
|
|
46
45
|
}
|
|
47
46
|
/** Format a millisecond duration into a human-readable string (e.g. "5m 30s"). */
|
|
48
47
|
function formatDuration(durationMs) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const minutes = Math.floor(seconds / 60);
|
|
52
|
-
const remainSeconds = seconds % 60;
|
|
53
|
-
return remainSeconds > 0 ? `${minutes}m ${remainSeconds}s` : `${minutes}m`;
|
|
48
|
+
if (durationMs <= 0) return "0s";
|
|
49
|
+
return prettyMilliseconds(durationMs < 1e3 ? Math.round(durationMs) : Math.round(durationMs / 1e3) * 1e3, { unitCount: 2 });
|
|
54
50
|
}
|
|
55
51
|
//#endregion
|
|
56
52
|
//#region extensions/qqbot/src/engine/utils/log.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as UPLOAD_PREPARE_FALLBACK_CODE, C as checkMessageReplyLimit, D as recordMessageReply, E as getMessageReplyStats, F as formatFileSize, I as getFileTypeName, L as getImageMimeType, M as checkFileSize, N as downloadFile, O as UploadDailyLimitExceededError, P as fileExistsAsync, R as getMaxUploadSize, T as getMessageReplyConfig, U as __exportAll, g as sendText$1, h as sendMedia$1, t as accountToCreds, u as initApiConfig, z as readFileAsync } from "./sender-BAUHZqDW.js";
|
|
2
2
|
import { c as getPlatformAdapter, i as normalizeOptionalString, n as normalizeLowercaseStringOrEmpty, s as sanitizeFileName } from "./string-normalize-R_0cKO7Q.js";
|
|
3
|
-
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-
|
|
3
|
+
import { a as formatErrorMessage, n as debugLog, r as debugWarn, t as debugError } from "./log-Da4jz75I.js";
|
|
4
4
|
import { r as parseTarget$1 } from "./target-parser-DagYRQkP.js";
|
|
5
5
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
6
6
|
import * as fs$2 from "node:fs";
|
|
@@ -262,122 +262,6 @@ const OUTBOUND_ERROR_CODES = {
|
|
|
262
262
|
};
|
|
263
263
|
const DEFAULT_MEDIA_SEND_ERROR = "发送失败,请稍后重试。";
|
|
264
264
|
//#endregion
|
|
265
|
-
//#region extensions/qqbot/src/engine/messaging/reply-limiter.ts
|
|
266
|
-
const DEFAULT_LIMIT = 4;
|
|
267
|
-
const DEFAULT_TTL_MS = 3600 * 1e3;
|
|
268
|
-
const DEFAULT_MAX_TRACKED = 1e4;
|
|
269
|
-
/**
|
|
270
|
-
* Per-account reply limiter with automatic eviction.
|
|
271
|
-
*
|
|
272
|
-
* Usage:
|
|
273
|
-
* ```ts
|
|
274
|
-
* const limiter = new ReplyLimiter({ limit: 4, ttlMs: 3600000 });
|
|
275
|
-
* const check = limiter.checkLimit(messageId);
|
|
276
|
-
* if (check.allowed) {
|
|
277
|
-
* await sendPassiveReply(...);
|
|
278
|
-
* limiter.record(messageId);
|
|
279
|
-
* } else if (check.shouldFallbackToProactive) {
|
|
280
|
-
* await sendProactiveMessage(...);
|
|
281
|
-
* }
|
|
282
|
-
* ```
|
|
283
|
-
*/
|
|
284
|
-
var ReplyLimiter = class {
|
|
285
|
-
constructor(config) {
|
|
286
|
-
this.tracker = /* @__PURE__ */ new Map();
|
|
287
|
-
this.limit = config?.limit ?? DEFAULT_LIMIT;
|
|
288
|
-
this.ttlMs = config?.ttlMs ?? DEFAULT_TTL_MS;
|
|
289
|
-
this.maxTracked = config?.maxTrackedMessages ?? DEFAULT_MAX_TRACKED;
|
|
290
|
-
}
|
|
291
|
-
/** Check whether a passive reply is allowed for the given message. */
|
|
292
|
-
checkLimit(messageId) {
|
|
293
|
-
const now = Date.now();
|
|
294
|
-
this.evictIfNeeded(now);
|
|
295
|
-
const record = this.tracker.get(messageId);
|
|
296
|
-
if (!record) return {
|
|
297
|
-
allowed: true,
|
|
298
|
-
remaining: this.limit,
|
|
299
|
-
shouldFallbackToProactive: false
|
|
300
|
-
};
|
|
301
|
-
if (now - record.firstReplyAt > this.ttlMs) return {
|
|
302
|
-
allowed: false,
|
|
303
|
-
remaining: 0,
|
|
304
|
-
shouldFallbackToProactive: true,
|
|
305
|
-
fallbackReason: "expired",
|
|
306
|
-
message: `Message is older than ${this.ttlMs / (3600 * 1e3)}h; sending as a proactive message instead`
|
|
307
|
-
};
|
|
308
|
-
const remaining = this.limit - record.count;
|
|
309
|
-
if (remaining <= 0) return {
|
|
310
|
-
allowed: false,
|
|
311
|
-
remaining: 0,
|
|
312
|
-
shouldFallbackToProactive: true,
|
|
313
|
-
fallbackReason: "limit_exceeded",
|
|
314
|
-
message: `Passive reply limit reached (${this.limit} per hour); sending proactively instead`
|
|
315
|
-
};
|
|
316
|
-
return {
|
|
317
|
-
allowed: true,
|
|
318
|
-
remaining,
|
|
319
|
-
shouldFallbackToProactive: false
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
/** Record one passive reply against a message. */
|
|
323
|
-
record(messageId) {
|
|
324
|
-
const now = Date.now();
|
|
325
|
-
const existing = this.tracker.get(messageId);
|
|
326
|
-
if (!existing) this.tracker.set(messageId, {
|
|
327
|
-
count: 1,
|
|
328
|
-
firstReplyAt: now
|
|
329
|
-
});
|
|
330
|
-
else if (now - existing.firstReplyAt > this.ttlMs) this.tracker.set(messageId, {
|
|
331
|
-
count: 1,
|
|
332
|
-
firstReplyAt: now
|
|
333
|
-
});
|
|
334
|
-
else existing.count++;
|
|
335
|
-
}
|
|
336
|
-
/** Return diagnostic stats. */
|
|
337
|
-
getStats() {
|
|
338
|
-
let totalReplies = 0;
|
|
339
|
-
for (const record of this.tracker.values()) totalReplies += record.count;
|
|
340
|
-
return {
|
|
341
|
-
trackedMessages: this.tracker.size,
|
|
342
|
-
totalReplies
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
/** Return limiter configuration. */
|
|
346
|
-
getConfig() {
|
|
347
|
-
return {
|
|
348
|
-
limit: this.limit,
|
|
349
|
-
ttlMs: this.ttlMs,
|
|
350
|
-
ttlHours: this.ttlMs / (3600 * 1e3)
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
/** Clear all tracked records. */
|
|
354
|
-
clear() {
|
|
355
|
-
this.tracker.clear();
|
|
356
|
-
}
|
|
357
|
-
/** Opportunistically evict expired records to keep the tracker bounded. */
|
|
358
|
-
evictIfNeeded(now) {
|
|
359
|
-
if (this.tracker.size <= this.maxTracked) return;
|
|
360
|
-
for (const [id, rec] of this.tracker) if (now - rec.firstReplyAt > this.ttlMs) this.tracker.delete(id);
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
//#endregion
|
|
364
|
-
//#region extensions/qqbot/src/engine/messaging/outbound-reply.ts
|
|
365
|
-
const replyLimiter = new ReplyLimiter();
|
|
366
|
-
const MESSAGE_REPLY_LIMIT = 4;
|
|
367
|
-
function checkMessageReplyLimit(messageId) {
|
|
368
|
-
return replyLimiter.checkLimit(messageId);
|
|
369
|
-
}
|
|
370
|
-
function recordMessageReply(messageId) {
|
|
371
|
-
replyLimiter.record(messageId);
|
|
372
|
-
debugLog(`[qqbot] recordMessageReply: ${messageId}, count=${replyLimiter.getStats().totalReplies}`);
|
|
373
|
-
}
|
|
374
|
-
function getMessageReplyStats() {
|
|
375
|
-
return replyLimiter.getStats();
|
|
376
|
-
}
|
|
377
|
-
function getMessageReplyConfig() {
|
|
378
|
-
return replyLimiter.getConfig();
|
|
379
|
-
}
|
|
380
|
-
//#endregion
|
|
381
265
|
//#region extensions/qqbot/src/engine/messaging/outbound-result-helpers.ts
|
|
382
266
|
/**
|
|
383
267
|
* Convert a media send result into a user-facing message.
|
|
@@ -1491,7 +1375,7 @@ function decodeMediaPath(raw, log) {
|
|
|
1491
1375
|
const code = decoded.charCodeAt(i);
|
|
1492
1376
|
if (code <= 255) bytes.push(code);
|
|
1493
1377
|
else {
|
|
1494
|
-
const charBytes = Buffer.from(decoded
|
|
1378
|
+
const charBytes = Buffer.from(decoded.charAt(i), "utf8");
|
|
1495
1379
|
bytes.push(...charBytes);
|
|
1496
1380
|
}
|
|
1497
1381
|
}
|
|
@@ -1545,7 +1429,7 @@ function isVideoFile$1(filePath, mimeType) {
|
|
|
1545
1429
|
//#region extensions/qqbot/src/engine/messaging/outbound.ts
|
|
1546
1430
|
var outbound_exports = /* @__PURE__ */ __exportAll({
|
|
1547
1431
|
DEFAULT_MEDIA_SEND_ERROR: () => DEFAULT_MEDIA_SEND_ERROR,
|
|
1548
|
-
MESSAGE_REPLY_LIMIT: () =>
|
|
1432
|
+
MESSAGE_REPLY_LIMIT: () => 5,
|
|
1549
1433
|
OUTBOUND_ERROR_CODES: () => OUTBOUND_ERROR_CODES,
|
|
1550
1434
|
buildMediaTarget: () => buildMediaTarget,
|
|
1551
1435
|
checkMessageReplyLimit: () => checkMessageReplyLimit,
|
|
@@ -1579,8 +1463,8 @@ const mediaPathDecodeLog = {
|
|
|
1579
1463
|
*/
|
|
1580
1464
|
async function sendText(ctx) {
|
|
1581
1465
|
const { to, account } = ctx;
|
|
1582
|
-
|
|
1583
|
-
let
|
|
1466
|
+
const { replyToId } = ctx;
|
|
1467
|
+
let { text } = ctx;
|
|
1584
1468
|
initApiConfig(account.appId, { markdownSupport: account.markdownSupport });
|
|
1585
1469
|
debugLog("[qqbot] sendText ctx:", JSON.stringify({
|
|
1586
1470
|
to,
|
|
@@ -1588,21 +1472,6 @@ async function sendText(ctx) {
|
|
|
1588
1472
|
replyToId,
|
|
1589
1473
|
accountId: account.accountId
|
|
1590
1474
|
}, null, 2));
|
|
1591
|
-
if (replyToId) {
|
|
1592
|
-
const limitCheck = checkMessageReplyLimit(replyToId);
|
|
1593
|
-
if (!limitCheck.allowed) if (limitCheck.shouldFallbackToProactive) {
|
|
1594
|
-
debugWarn(`[qqbot] sendText: passive reply unavailable, falling back to proactive send - ${limitCheck.message}`);
|
|
1595
|
-
fallbackToProactive = true;
|
|
1596
|
-
replyToId = null;
|
|
1597
|
-
} else {
|
|
1598
|
-
debugError(`[qqbot] sendText: passive reply was blocked without a fallback path - ${limitCheck.message}`);
|
|
1599
|
-
return {
|
|
1600
|
-
channel: "qqbot",
|
|
1601
|
-
error: limitCheck.message
|
|
1602
|
-
};
|
|
1603
|
-
}
|
|
1604
|
-
else debugLog(`[qqbot] sendText: remaining passive replies for ${replyToId}: ${limitCheck.remaining}/4`);
|
|
1605
|
-
}
|
|
1606
1475
|
text = normalizeMediaTags(text);
|
|
1607
1476
|
const mediaTagMatches = text.match(/<(qqimg|qqvoice|qqvideo|qqfile|qqmedia)>([^<>]+)<\/(?:qqimg|qqvoice|qqvideo|qqfile|qqmedia|img)>/gi);
|
|
1608
1477
|
if (mediaTagMatches && mediaTagMatches.length > 0) {
|
|
@@ -1675,7 +1544,6 @@ async function sendText(ctx) {
|
|
|
1675
1544
|
type: target.type === "channel" ? "channel" : target.type,
|
|
1676
1545
|
id: target.id
|
|
1677
1546
|
}, item.content, creds, { msgId: replyToId ?? void 0 });
|
|
1678
|
-
if (replyToId) recordMessageReply(replyToId);
|
|
1679
1547
|
lastResult = {
|
|
1680
1548
|
channel: "qqbot",
|
|
1681
1549
|
messageId: result.id,
|
|
@@ -1716,8 +1584,7 @@ async function sendText(ctx) {
|
|
|
1716
1584
|
error: "Proactive messages require non-empty content (--message cannot be empty)"
|
|
1717
1585
|
};
|
|
1718
1586
|
}
|
|
1719
|
-
|
|
1720
|
-
else debugLog(`[qqbot] sendText: sending proactive message to ${to}, length=${text.length}`);
|
|
1587
|
+
debugLog(`[qqbot] sendText: sending proactive message to ${to}, length=${text.length}`);
|
|
1721
1588
|
}
|
|
1722
1589
|
if (!account.appId || !account.clientSecret) return {
|
|
1723
1590
|
channel: "qqbot",
|
|
@@ -1732,7 +1599,6 @@ async function sendText(ctx) {
|
|
|
1732
1599
|
};
|
|
1733
1600
|
debugLog("[qqbot] sendText target:", JSON.stringify(target));
|
|
1734
1601
|
const result = await sendText$1(deliveryTarget, text, creds, { msgId: replyToId ?? void 0 });
|
|
1735
|
-
if (replyToId) recordMessageReply(replyToId);
|
|
1736
1602
|
return {
|
|
1737
1603
|
channel: "qqbot",
|
|
1738
1604
|
messageId: result.id,
|
|
@@ -1864,4 +1730,4 @@ async function sendCronMessage(account, to, message) {
|
|
|
1864
1730
|
});
|
|
1865
1731
|
}
|
|
1866
1732
|
//#endregion
|
|
1867
|
-
export {
|
|
1733
|
+
export { getQQBotMediaDir as A, resolveUserFacingMediaError as C, checkSilkWasmAvailable as D, setOutboundAudioPort as E, normalizePath$1 as F, getTempDir as M, isLocalPath as N, getHomeDir as O, isWindows as P, resolveWorkspaceScopedLocalRoots as S, OUTBOUND_ERROR_CODES as T, sendVideoMsg as _, sendText as a, resolveOutboundMediaLocalRoots as b, isCronReminderPayload as c, normalizeMediaTags as d, buildMediaTarget as f, sendPhoto as g, sendDocument as h, sendProactiveMessage as i, getQQBotMediaPath as j, getQQBotDataDir as k, 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, DEFAULT_MEDIA_SEND_ERROR as w, resolveWorkspacePathCandidates as x, resolveTrustedOutboundMediaPath as y };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { v as setOpenClawVersion } from "./sender-
|
|
1
|
+
import { v as setOpenClawVersion } from "./sender-BAUHZqDW.js";
|
|
2
2
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
3
3
|
//#region extensions/qqbot/src/bridge/runtime.ts
|
|
4
|
-
const { setRuntime: _setRuntime,
|
|
4
|
+
const { setRuntime: _setRuntime, getRuntime: getQQBotRuntime } = createPluginRuntimeStore({
|
|
5
5
|
pluginId: "qqbot",
|
|
6
6
|
errorMessage: "QQBot runtime not initialized"
|
|
7
7
|
});
|
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-CyjBiGD2.js";
|
|
2
2
|
export { getQQBotRuntime, setQQBotRuntime };
|
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
import { collectConditionalChannelFieldAssignments, getChannelSurface, hasConfiguredSecretInputValue } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
1
|
+
import { collectConditionalChannelFieldAssignments, createChannelSecretTargetRegistryEntries, getChannelSurface, hasConfiguredSecretInputValue } from "openclaw/plugin-sdk/channel-secret-basic-runtime";
|
|
2
2
|
//#region extensions/qqbot/src/secret-contract.ts
|
|
3
3
|
const DEFAULT_ACCOUNT_ID = "default";
|
|
4
|
-
const secretTargetRegistryEntries =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
secretShape: "secret_input",
|
|
10
|
-
expectedResolvedValue: "string",
|
|
11
|
-
includeInPlan: true,
|
|
12
|
-
includeInConfigure: true,
|
|
13
|
-
includeInAudit: true
|
|
14
|
-
}, {
|
|
15
|
-
id: "channels.qqbot.clientSecret",
|
|
16
|
-
targetType: "channels.qqbot.clientSecret",
|
|
17
|
-
configFile: "openclaw.json",
|
|
18
|
-
pathPattern: "channels.qqbot.clientSecret",
|
|
19
|
-
secretShape: "secret_input",
|
|
20
|
-
expectedResolvedValue: "string",
|
|
21
|
-
includeInPlan: true,
|
|
22
|
-
includeInConfigure: true,
|
|
23
|
-
includeInAudit: true
|
|
24
|
-
}];
|
|
4
|
+
const secretTargetRegistryEntries = createChannelSecretTargetRegistryEntries({
|
|
5
|
+
channelKey: "qqbot",
|
|
6
|
+
account: ["clientSecret"],
|
|
7
|
+
channel: ["clientSecret"]
|
|
8
|
+
});
|
|
25
9
|
function hasTopLevelAppId(qqbot) {
|
|
26
10
|
if (typeof qqbot.appId === "string") return qqbot.appId.trim().length > 0;
|
|
27
11
|
return typeof qqbot.appId === "number";
|