@openclaw-china/qqbot 2026.3.11 → 2026.3.12
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/index.d.ts +2 -1
- package/dist/index.js +687 -86
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { homedir, tmpdir } from 'os';
|
|
|
3
3
|
import * as path2 from 'path';
|
|
4
4
|
import { join, dirname } from 'path';
|
|
5
5
|
import * as fs3 from 'fs';
|
|
6
|
-
import { existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from 'fs';
|
|
6
|
+
import { existsSync, readFileSync, rmSync, writeFileSync, renameSync, copyFileSync, mkdirSync, appendFileSync } from 'fs';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import * as fsPromises from 'fs/promises';
|
|
9
9
|
import { createHmac } from 'crypto';
|
|
@@ -651,8 +651,8 @@ function getErrorMap() {
|
|
|
651
651
|
|
|
652
652
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
653
653
|
var makeIssue = (params) => {
|
|
654
|
-
const { data, path:
|
|
655
|
-
const fullPath = [...
|
|
654
|
+
const { data, path: path5, errorMaps, issueData } = params;
|
|
655
|
+
const fullPath = [...path5, ...issueData.path || []];
|
|
656
656
|
const fullIssue = {
|
|
657
657
|
...issueData,
|
|
658
658
|
path: fullPath
|
|
@@ -768,11 +768,11 @@ var errorUtil;
|
|
|
768
768
|
|
|
769
769
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
770
770
|
var ParseInputLazyPath = class {
|
|
771
|
-
constructor(parent, value,
|
|
771
|
+
constructor(parent, value, path5, key) {
|
|
772
772
|
this._cachedPath = [];
|
|
773
773
|
this.parent = parent;
|
|
774
774
|
this.data = value;
|
|
775
|
-
this._path =
|
|
775
|
+
this._path = path5;
|
|
776
776
|
this._key = key;
|
|
777
777
|
}
|
|
778
778
|
get path() {
|
|
@@ -4876,7 +4876,7 @@ function extractMediaFromText(text, options = {}) {
|
|
|
4876
4876
|
const {
|
|
4877
4877
|
removeFromText = true,
|
|
4878
4878
|
checkExists = false,
|
|
4879
|
-
existsSync:
|
|
4879
|
+
existsSync: existsSync7,
|
|
4880
4880
|
parseMediaLines = false,
|
|
4881
4881
|
parseMarkdownImages = true,
|
|
4882
4882
|
parseHtmlImages = true,
|
|
@@ -4891,7 +4891,7 @@ function extractMediaFromText(text, options = {}) {
|
|
|
4891
4891
|
const key = media.localPath || media.source;
|
|
4892
4892
|
if (seenSources.has(key)) return false;
|
|
4893
4893
|
if (checkExists && media.isLocal && media.localPath) {
|
|
4894
|
-
const exists =
|
|
4894
|
+
const exists = existsSync7 ? existsSync7(media.localPath) : fs3.existsSync(media.localPath);
|
|
4895
4895
|
if (!exists) return false;
|
|
4896
4896
|
}
|
|
4897
4897
|
seenSources.add(key);
|
|
@@ -7380,8 +7380,8 @@ async function getAccessToken(appId, clientSecret, options) {
|
|
|
7380
7380
|
tokenPromiseMap.set(normalizedAppId, promise);
|
|
7381
7381
|
return promise;
|
|
7382
7382
|
}
|
|
7383
|
-
async function apiGet(accessToken,
|
|
7384
|
-
const url = `${API_BASE}${
|
|
7383
|
+
async function apiGet(accessToken, path5, options) {
|
|
7384
|
+
const url = `${API_BASE}${path5}`;
|
|
7385
7385
|
return httpGet(url, {
|
|
7386
7386
|
...options,
|
|
7387
7387
|
headers: {
|
|
@@ -7390,8 +7390,8 @@ async function apiGet(accessToken, path4, options) {
|
|
|
7390
7390
|
}
|
|
7391
7391
|
});
|
|
7392
7392
|
}
|
|
7393
|
-
async function apiPost(accessToken,
|
|
7394
|
-
const url = `${API_BASE}${
|
|
7393
|
+
async function apiPost(accessToken, path5, body, options) {
|
|
7394
|
+
const url = `${API_BASE}${path5}`;
|
|
7395
7395
|
return httpPost(url, body, {
|
|
7396
7396
|
...options,
|
|
7397
7397
|
headers: {
|
|
@@ -7490,7 +7490,7 @@ async function sendChannelMessage(params) {
|
|
|
7490
7490
|
});
|
|
7491
7491
|
}
|
|
7492
7492
|
async function sendC2CInputNotify(params) {
|
|
7493
|
-
await postPassiveMessage({
|
|
7493
|
+
const response = await postPassiveMessage({
|
|
7494
7494
|
accessToken: params.accessToken,
|
|
7495
7495
|
path: `/v2/users/${params.openid}/messages`,
|
|
7496
7496
|
sequenceKey: resolveMsgSeqKey(params.messageId, params.eventId),
|
|
@@ -7505,6 +7505,8 @@ async function sendC2CInputNotify(params) {
|
|
|
7505
7505
|
...params.messageId ? { msg_id: params.messageId } : params.eventId ? { event_id: params.eventId } : {}
|
|
7506
7506
|
})
|
|
7507
7507
|
});
|
|
7508
|
+
const refIdx = response.ext_info?.ref_idx?.trim();
|
|
7509
|
+
return refIdx ? { refIdx } : {};
|
|
7508
7510
|
}
|
|
7509
7511
|
async function uploadC2CMedia(params) {
|
|
7510
7512
|
const body = {
|
|
@@ -7574,6 +7576,233 @@ async function sendGroupMediaMessage(params) {
|
|
|
7574
7576
|
})
|
|
7575
7577
|
});
|
|
7576
7578
|
}
|
|
7579
|
+
var REF_INDEX_FILE = join(homedir(), ".openclaw", "qqbot", "data", "ref-index.jsonl");
|
|
7580
|
+
var MAX_CONTENT_LENGTH = 500;
|
|
7581
|
+
var MAX_ENTRIES = 5e4;
|
|
7582
|
+
var TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
7583
|
+
var COMPACT_THRESHOLD_RATIO = 2;
|
|
7584
|
+
var cache = null;
|
|
7585
|
+
var totalLinesOnDisk = 0;
|
|
7586
|
+
function normalizeRefIdx(refIdx) {
|
|
7587
|
+
const next = refIdx.trim();
|
|
7588
|
+
return next ? next : void 0;
|
|
7589
|
+
}
|
|
7590
|
+
function ensureStorageDir() {
|
|
7591
|
+
mkdirSync(dirname(REF_INDEX_FILE), { recursive: true });
|
|
7592
|
+
}
|
|
7593
|
+
function truncateContent(content) {
|
|
7594
|
+
return content.trim().slice(0, MAX_CONTENT_LENGTH);
|
|
7595
|
+
}
|
|
7596
|
+
function sanitizeAttachmentSummary(attachment) {
|
|
7597
|
+
const type = attachment.type;
|
|
7598
|
+
const filename = attachment.filename?.trim();
|
|
7599
|
+
const contentType = attachment.contentType?.trim();
|
|
7600
|
+
const localPath = attachment.localPath?.trim();
|
|
7601
|
+
const url = attachment.url?.trim();
|
|
7602
|
+
const transcript = attachment.transcript?.trim();
|
|
7603
|
+
if (!filename && !contentType && !localPath && !url && !transcript && type === "unknown") {
|
|
7604
|
+
return void 0;
|
|
7605
|
+
}
|
|
7606
|
+
return {
|
|
7607
|
+
type,
|
|
7608
|
+
...filename ? { filename } : {},
|
|
7609
|
+
...contentType ? { contentType } : {},
|
|
7610
|
+
...localPath ? { localPath } : {},
|
|
7611
|
+
...url ? { url } : {},
|
|
7612
|
+
...transcript ? { transcript } : {},
|
|
7613
|
+
...transcript && attachment.transcriptSource ? { transcriptSource: attachment.transcriptSource } : {}
|
|
7614
|
+
};
|
|
7615
|
+
}
|
|
7616
|
+
function sanitizeEntry(entry) {
|
|
7617
|
+
const senderId = entry.senderId.trim() || "unknown";
|
|
7618
|
+
const senderName = entry.senderName?.trim();
|
|
7619
|
+
const timestamp = Number.isFinite(entry.timestamp) ? Math.trunc(entry.timestamp) : Date.now();
|
|
7620
|
+
const attachments = entry.attachments?.map((attachment) => sanitizeAttachmentSummary(attachment)).filter((attachment) => Boolean(attachment));
|
|
7621
|
+
return {
|
|
7622
|
+
content: truncateContent(entry.content),
|
|
7623
|
+
senderId,
|
|
7624
|
+
...senderName ? { senderName } : {},
|
|
7625
|
+
timestamp,
|
|
7626
|
+
...entry.isBot ? { isBot: true } : {},
|
|
7627
|
+
...attachments && attachments.length > 0 ? { attachments } : {}
|
|
7628
|
+
};
|
|
7629
|
+
}
|
|
7630
|
+
function shouldCompact() {
|
|
7631
|
+
if (!cache) return false;
|
|
7632
|
+
return totalLinesOnDisk > cache.size * COMPACT_THRESHOLD_RATIO && totalLinesOnDisk > 1e3;
|
|
7633
|
+
}
|
|
7634
|
+
function compactFile() {
|
|
7635
|
+
if (!cache) return;
|
|
7636
|
+
try {
|
|
7637
|
+
ensureStorageDir();
|
|
7638
|
+
const tempPath = `${REF_INDEX_FILE}.tmp`;
|
|
7639
|
+
const lines = [];
|
|
7640
|
+
for (const [key, entry] of cache.entries()) {
|
|
7641
|
+
lines.push(
|
|
7642
|
+
JSON.stringify({
|
|
7643
|
+
k: key,
|
|
7644
|
+
v: sanitizeEntry(entry),
|
|
7645
|
+
t: entry._createdAt
|
|
7646
|
+
})
|
|
7647
|
+
);
|
|
7648
|
+
}
|
|
7649
|
+
writeFileSync(tempPath, lines.length > 0 ? `${lines.join("\n")}
|
|
7650
|
+
` : "", "utf8");
|
|
7651
|
+
renameSync(tempPath, REF_INDEX_FILE);
|
|
7652
|
+
totalLinesOnDisk = cache.size;
|
|
7653
|
+
} catch {
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
function evictIfNeeded() {
|
|
7657
|
+
if (!cache || cache.size < MAX_ENTRIES) return;
|
|
7658
|
+
const now = Date.now();
|
|
7659
|
+
for (const [key, entry] of cache.entries()) {
|
|
7660
|
+
if (now - entry._createdAt > TTL_MS) {
|
|
7661
|
+
cache.delete(key);
|
|
7662
|
+
}
|
|
7663
|
+
}
|
|
7664
|
+
if (cache.size < MAX_ENTRIES) {
|
|
7665
|
+
return;
|
|
7666
|
+
}
|
|
7667
|
+
const sorted = [...cache.entries()].sort((left, right) => left[1]._createdAt - right[1]._createdAt);
|
|
7668
|
+
const removeCount = cache.size - MAX_ENTRIES + 1;
|
|
7669
|
+
for (let index = 0; index < removeCount; index += 1) {
|
|
7670
|
+
const key = sorted[index]?.[0];
|
|
7671
|
+
if (key) {
|
|
7672
|
+
cache.delete(key);
|
|
7673
|
+
}
|
|
7674
|
+
}
|
|
7675
|
+
}
|
|
7676
|
+
function loadCache() {
|
|
7677
|
+
if (cache) {
|
|
7678
|
+
return cache;
|
|
7679
|
+
}
|
|
7680
|
+
cache = /* @__PURE__ */ new Map();
|
|
7681
|
+
totalLinesOnDisk = 0;
|
|
7682
|
+
try {
|
|
7683
|
+
if (!existsSync(REF_INDEX_FILE)) {
|
|
7684
|
+
return cache;
|
|
7685
|
+
}
|
|
7686
|
+
const now = Date.now();
|
|
7687
|
+
const raw = readFileSync(REF_INDEX_FILE, "utf8");
|
|
7688
|
+
const lines = raw.split(/\r?\n/);
|
|
7689
|
+
for (const line of lines) {
|
|
7690
|
+
const trimmed = line.trim();
|
|
7691
|
+
if (!trimmed) continue;
|
|
7692
|
+
totalLinesOnDisk += 1;
|
|
7693
|
+
try {
|
|
7694
|
+
const parsed = JSON.parse(trimmed);
|
|
7695
|
+
const key = typeof parsed.k === "string" ? normalizeRefIdx(parsed.k) : void 0;
|
|
7696
|
+
const createdAt = typeof parsed.t === "number" && Number.isFinite(parsed.t) ? parsed.t : void 0;
|
|
7697
|
+
if (!key || createdAt === void 0 || !parsed.v || typeof parsed.v !== "object") {
|
|
7698
|
+
continue;
|
|
7699
|
+
}
|
|
7700
|
+
if (now - createdAt > TTL_MS) {
|
|
7701
|
+
continue;
|
|
7702
|
+
}
|
|
7703
|
+
const entry = sanitizeEntry(parsed.v);
|
|
7704
|
+
cache.set(key, {
|
|
7705
|
+
...entry,
|
|
7706
|
+
_createdAt: createdAt
|
|
7707
|
+
});
|
|
7708
|
+
} catch {
|
|
7709
|
+
}
|
|
7710
|
+
}
|
|
7711
|
+
if (shouldCompact()) {
|
|
7712
|
+
compactFile();
|
|
7713
|
+
}
|
|
7714
|
+
} catch {
|
|
7715
|
+
cache = /* @__PURE__ */ new Map();
|
|
7716
|
+
totalLinesOnDisk = 0;
|
|
7717
|
+
}
|
|
7718
|
+
return cache;
|
|
7719
|
+
}
|
|
7720
|
+
function appendLine(line) {
|
|
7721
|
+
try {
|
|
7722
|
+
ensureStorageDir();
|
|
7723
|
+
appendFileSync(REF_INDEX_FILE, `${JSON.stringify(line)}
|
|
7724
|
+
`, "utf8");
|
|
7725
|
+
totalLinesOnDisk += 1;
|
|
7726
|
+
} catch {
|
|
7727
|
+
}
|
|
7728
|
+
}
|
|
7729
|
+
function restoreEntry(entry) {
|
|
7730
|
+
const sanitized = sanitizeEntry(entry);
|
|
7731
|
+
return {
|
|
7732
|
+
content: sanitized.content,
|
|
7733
|
+
senderId: sanitized.senderId,
|
|
7734
|
+
...sanitized.senderName ? { senderName: sanitized.senderName } : {},
|
|
7735
|
+
timestamp: sanitized.timestamp,
|
|
7736
|
+
...sanitized.isBot ? { isBot: true } : {},
|
|
7737
|
+
...sanitized.attachments ? { attachments: sanitized.attachments } : {}
|
|
7738
|
+
};
|
|
7739
|
+
}
|
|
7740
|
+
function formatAttachmentSummary(attachment) {
|
|
7741
|
+
const sourceParts = [
|
|
7742
|
+
attachment.localPath?.trim() ? `\u672C\u5730: ${attachment.localPath.trim()}` : void 0,
|
|
7743
|
+
attachment.url?.trim() ? `\u94FE\u63A5: ${attachment.url.trim()}` : void 0
|
|
7744
|
+
].filter((value) => Boolean(value));
|
|
7745
|
+
const sourceSuffix = sourceParts.length > 0 ? ` (${sourceParts.join(" | ")})` : "";
|
|
7746
|
+
if (attachment.type === "image") {
|
|
7747
|
+
return `[\u56FE\u7247${attachment.filename?.trim() ? `: ${attachment.filename.trim()}` : ""}]${sourceSuffix}`;
|
|
7748
|
+
}
|
|
7749
|
+
if (attachment.type === "voice") {
|
|
7750
|
+
if (attachment.transcript?.trim()) {
|
|
7751
|
+
const sourceLabel = attachment.transcriptSource === "asr" ? "\u5B98\u65B9\u8BC6\u522B" : attachment.transcriptSource === "stt" ? "\u672C\u5730\u8BC6\u522B" : attachment.transcriptSource === "tts" ? "TTS \u539F\u6587" : attachment.transcriptSource === "fallback" ? "\u515C\u5E95\u6587\u672C" : void 0;
|
|
7752
|
+
return `[\u8BED\u97F3\u6D88\u606F: ${attachment.transcript.trim()}${sourceLabel ? ` (${sourceLabel})` : ""}]${sourceSuffix}`;
|
|
7753
|
+
}
|
|
7754
|
+
return `[\u8BED\u97F3\u6D88\u606F]${sourceSuffix}`;
|
|
7755
|
+
}
|
|
7756
|
+
if (attachment.type === "video") {
|
|
7757
|
+
return `[\u89C6\u9891${attachment.filename?.trim() ? `: ${attachment.filename.trim()}` : ""}]${sourceSuffix}`;
|
|
7758
|
+
}
|
|
7759
|
+
if (attachment.type === "file") {
|
|
7760
|
+
return `[\u6587\u4EF6${attachment.filename?.trim() ? `: ${attachment.filename.trim()}` : ""}]${sourceSuffix}`;
|
|
7761
|
+
}
|
|
7762
|
+
return `[\u9644\u4EF6${attachment.filename?.trim() ? `: ${attachment.filename.trim()}` : ""}]${sourceSuffix}`;
|
|
7763
|
+
}
|
|
7764
|
+
function setRefIndex(refIdx, entry) {
|
|
7765
|
+
const key = normalizeRefIdx(refIdx);
|
|
7766
|
+
if (!key) return;
|
|
7767
|
+
const store = loadCache();
|
|
7768
|
+
evictIfNeeded();
|
|
7769
|
+
const nextEntry = sanitizeEntry(entry);
|
|
7770
|
+
const createdAt = Date.now();
|
|
7771
|
+
store.set(key, {
|
|
7772
|
+
...nextEntry,
|
|
7773
|
+
_createdAt: createdAt
|
|
7774
|
+
});
|
|
7775
|
+
appendLine({
|
|
7776
|
+
k: key,
|
|
7777
|
+
v: nextEntry,
|
|
7778
|
+
t: createdAt
|
|
7779
|
+
});
|
|
7780
|
+
if (shouldCompact()) {
|
|
7781
|
+
compactFile();
|
|
7782
|
+
}
|
|
7783
|
+
}
|
|
7784
|
+
function getRefIndex(refIdx) {
|
|
7785
|
+
const key = normalizeRefIdx(refIdx);
|
|
7786
|
+
if (!key) return null;
|
|
7787
|
+
const store = loadCache();
|
|
7788
|
+
const entry = store.get(key);
|
|
7789
|
+
if (!entry) {
|
|
7790
|
+
return null;
|
|
7791
|
+
}
|
|
7792
|
+
if (Date.now() - entry._createdAt > TTL_MS) {
|
|
7793
|
+
store.delete(key);
|
|
7794
|
+
return null;
|
|
7795
|
+
}
|
|
7796
|
+
return restoreEntry(entry);
|
|
7797
|
+
}
|
|
7798
|
+
function formatRefEntryForAgent(entry) {
|
|
7799
|
+
const content = entry.content.trim();
|
|
7800
|
+
const parts = content ? [content] : [];
|
|
7801
|
+
for (const attachment of entry.attachments ?? []) {
|
|
7802
|
+
parts.push(formatAttachmentSummary(attachment));
|
|
7803
|
+
}
|
|
7804
|
+
return parts.join("\n") || "[\u7A7A\u6D88\u606F]";
|
|
7805
|
+
}
|
|
7577
7806
|
var require2 = createRequire(import.meta.url);
|
|
7578
7807
|
function resolveQQBotMediaFileType(fileName) {
|
|
7579
7808
|
const mediaType = detectMediaType(fileName);
|
|
@@ -7729,7 +7958,7 @@ async function sendFileQQBot(params) {
|
|
|
7729
7958
|
}
|
|
7730
7959
|
}
|
|
7731
7960
|
try {
|
|
7732
|
-
|
|
7961
|
+
const result = await sendC2CMediaMessage({
|
|
7733
7962
|
accessToken,
|
|
7734
7963
|
openid: target.id,
|
|
7735
7964
|
fileInfo,
|
|
@@ -7737,6 +7966,12 @@ async function sendFileQQBot(params) {
|
|
|
7737
7966
|
...messageId ? { messageId } : {},
|
|
7738
7967
|
...eventId ? { eventId } : {}
|
|
7739
7968
|
});
|
|
7969
|
+
const refIdx = result.ext_info?.ref_idx?.trim();
|
|
7970
|
+
return {
|
|
7971
|
+
id: result.id,
|
|
7972
|
+
timestamp: result.timestamp,
|
|
7973
|
+
...refIdx ? { refIdx } : {}
|
|
7974
|
+
};
|
|
7740
7975
|
} catch (err) {
|
|
7741
7976
|
const message = formatQQBotError(err);
|
|
7742
7977
|
throw new Error(`QQBot C2C media send failed: ${message}`);
|
|
@@ -7853,6 +8088,85 @@ function shouldRetryWithEventId(err) {
|
|
|
7853
8088
|
function shouldSendTextAsFollowupForMedia(mediaUrl) {
|
|
7854
8089
|
return detectMediaType(stripTitleFromUrl(mediaUrl)) === "file";
|
|
7855
8090
|
}
|
|
8091
|
+
function isHttpUrl2(value) {
|
|
8092
|
+
return /^https?:\/\//i.test(value);
|
|
8093
|
+
}
|
|
8094
|
+
function resolveResponseRefIdx(response) {
|
|
8095
|
+
if (!response || typeof response !== "object") {
|
|
8096
|
+
return void 0;
|
|
8097
|
+
}
|
|
8098
|
+
const direct = response.refIdx;
|
|
8099
|
+
if (typeof direct === "string" && direct.trim()) {
|
|
8100
|
+
return direct.trim();
|
|
8101
|
+
}
|
|
8102
|
+
const extInfo = response.ext_info;
|
|
8103
|
+
if (typeof extInfo?.ref_idx === "string" && extInfo.ref_idx.trim()) {
|
|
8104
|
+
return extInfo.ref_idx.trim();
|
|
8105
|
+
}
|
|
8106
|
+
return void 0;
|
|
8107
|
+
}
|
|
8108
|
+
function resolveOutboundAttachmentType(mediaUrl) {
|
|
8109
|
+
const detected = detectMediaType(stripTitleFromUrl(mediaUrl));
|
|
8110
|
+
if (detected === "image") return "image";
|
|
8111
|
+
if (detected === "video") return "video";
|
|
8112
|
+
if (detected === "audio") return "voice";
|
|
8113
|
+
if (detected === "file") return "file";
|
|
8114
|
+
return "unknown";
|
|
8115
|
+
}
|
|
8116
|
+
function resolveOutboundAttachmentFileName(mediaUrl) {
|
|
8117
|
+
const source = stripTitleFromUrl(mediaUrl).trim();
|
|
8118
|
+
if (!source) return void 0;
|
|
8119
|
+
if (isHttpUrl2(source)) {
|
|
8120
|
+
try {
|
|
8121
|
+
const base2 = path2.posix.basename(new URL(source).pathname);
|
|
8122
|
+
return base2 && base2 !== "/" ? base2 : void 0;
|
|
8123
|
+
} catch {
|
|
8124
|
+
return void 0;
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
const base = path2.basename(source);
|
|
8128
|
+
return base || void 0;
|
|
8129
|
+
}
|
|
8130
|
+
function buildOutboundAttachmentSummary(params) {
|
|
8131
|
+
const source = stripTitleFromUrl(params.mediaUrl).trim();
|
|
8132
|
+
const type = resolveOutboundAttachmentType(source);
|
|
8133
|
+
const filename = resolveOutboundAttachmentFileName(source);
|
|
8134
|
+
const text = params.text?.trim();
|
|
8135
|
+
return {
|
|
8136
|
+
type,
|
|
8137
|
+
...filename ? { filename } : {},
|
|
8138
|
+
...isHttpUrl2(source) ? { url: source } : { localPath: source },
|
|
8139
|
+
...type === "voice" && text ? {
|
|
8140
|
+
transcript: text,
|
|
8141
|
+
transcriptSource: "tts"
|
|
8142
|
+
} : {}
|
|
8143
|
+
};
|
|
8144
|
+
}
|
|
8145
|
+
function recordOutboundC2CRefIndex(params) {
|
|
8146
|
+
const refIdx = params.refIdx?.trim();
|
|
8147
|
+
if (!refIdx) return;
|
|
8148
|
+
const text = params.text?.trim() ?? "";
|
|
8149
|
+
const attachments = params.mediaUrl?.trim() ? [buildOutboundAttachmentSummary({ mediaUrl: params.mediaUrl, text })] : void 0;
|
|
8150
|
+
if (!text && !attachments) {
|
|
8151
|
+
return;
|
|
8152
|
+
}
|
|
8153
|
+
try {
|
|
8154
|
+
const accountLabel = params.accountId?.trim() || DEFAULT_ACCOUNT_ID;
|
|
8155
|
+
setRefIndex(refIdx, {
|
|
8156
|
+
content: text,
|
|
8157
|
+
senderId: accountLabel,
|
|
8158
|
+
senderName: accountLabel,
|
|
8159
|
+
timestamp: Date.now(),
|
|
8160
|
+
isBot: true,
|
|
8161
|
+
...attachments ? { attachments } : {}
|
|
8162
|
+
});
|
|
8163
|
+
console.info(
|
|
8164
|
+
`[qqbot] cached outbound ref_idx=${refIdx} accountId=${accountLabel} textLen=${text.length} media=${params.mediaUrl?.trim() ? "yes" : "no"}`
|
|
8165
|
+
);
|
|
8166
|
+
} catch (err) {
|
|
8167
|
+
console.warn(`[qqbot] failed to cache outbound ref_idx=${refIdx}: ${String(err)}`);
|
|
8168
|
+
}
|
|
8169
|
+
}
|
|
7856
8170
|
function buildPassiveReplyRefs(params) {
|
|
7857
8171
|
if (params.replyToId) {
|
|
7858
8172
|
return { messageId: params.replyToId };
|
|
@@ -7998,7 +8312,14 @@ var qqbotOutbound = {
|
|
|
7998
8312
|
content: text,
|
|
7999
8313
|
markdown
|
|
8000
8314
|
});
|
|
8001
|
-
|
|
8315
|
+
const refIdx2 = resolveResponseRefIdx(result2);
|
|
8316
|
+
recordOutboundC2CRefIndex({ refIdx: refIdx2, accountId, text });
|
|
8317
|
+
return {
|
|
8318
|
+
channel: "qqbot",
|
|
8319
|
+
messageId: result2.id,
|
|
8320
|
+
timestamp: result2.timestamp,
|
|
8321
|
+
...refIdx2 ? { refIdx: refIdx2 } : {}
|
|
8322
|
+
};
|
|
8002
8323
|
}
|
|
8003
8324
|
let result;
|
|
8004
8325
|
try {
|
|
@@ -8065,7 +8386,14 @@ var qqbotOutbound = {
|
|
|
8065
8386
|
throw retryErr;
|
|
8066
8387
|
}
|
|
8067
8388
|
}
|
|
8068
|
-
|
|
8389
|
+
const refIdx = resolveResponseRefIdx(result);
|
|
8390
|
+
recordOutboundC2CRefIndex({ refIdx, accountId, text });
|
|
8391
|
+
return {
|
|
8392
|
+
channel: "qqbot",
|
|
8393
|
+
messageId: result.id,
|
|
8394
|
+
timestamp: result.timestamp,
|
|
8395
|
+
...refIdx ? { refIdx } : {}
|
|
8396
|
+
};
|
|
8069
8397
|
} catch (err) {
|
|
8070
8398
|
const message = summarizeError(err);
|
|
8071
8399
|
return { channel: "qqbot", error: message };
|
|
@@ -8174,7 +8502,21 @@ ${mediaUrl}` : mediaUrl;
|
|
|
8174
8502
|
};
|
|
8175
8503
|
}
|
|
8176
8504
|
}
|
|
8177
|
-
|
|
8505
|
+
const refIdx = target.kind === "c2c" ? resolveResponseRefIdx(result) : void 0;
|
|
8506
|
+
if (target.kind === "c2c") {
|
|
8507
|
+
recordOutboundC2CRefIndex({
|
|
8508
|
+
refIdx,
|
|
8509
|
+
accountId,
|
|
8510
|
+
text: sendTextAsFollowup ? void 0 : trimmedText,
|
|
8511
|
+
mediaUrl
|
|
8512
|
+
});
|
|
8513
|
+
}
|
|
8514
|
+
return {
|
|
8515
|
+
channel: "qqbot",
|
|
8516
|
+
messageId: result.id,
|
|
8517
|
+
timestamp: result.timestamp,
|
|
8518
|
+
...refIdx ? { refIdx } : {}
|
|
8519
|
+
};
|
|
8178
8520
|
} catch (err) {
|
|
8179
8521
|
const message = summarizeError(err);
|
|
8180
8522
|
return { channel: "qqbot", error: message };
|
|
@@ -8193,8 +8535,9 @@ ${mediaUrl}` : mediaUrl;
|
|
|
8193
8535
|
}
|
|
8194
8536
|
try {
|
|
8195
8537
|
const accessToken = await getAccessToken(credentials.appId, credentials.clientSecret);
|
|
8538
|
+
let typingResult;
|
|
8196
8539
|
try {
|
|
8197
|
-
await sendC2CInputNotify({
|
|
8540
|
+
typingResult = await sendC2CInputNotify({
|
|
8198
8541
|
accessToken,
|
|
8199
8542
|
openid: target.id,
|
|
8200
8543
|
messageId: replyToId,
|
|
@@ -8216,7 +8559,7 @@ ${mediaUrl}` : mediaUrl;
|
|
|
8216
8559
|
reason: summarizeError(err)
|
|
8217
8560
|
});
|
|
8218
8561
|
try {
|
|
8219
|
-
await sendC2CInputNotify({
|
|
8562
|
+
typingResult = await sendC2CInputNotify({
|
|
8220
8563
|
accessToken,
|
|
8221
8564
|
openid: target.id,
|
|
8222
8565
|
eventId: replyEventId,
|
|
@@ -8245,7 +8588,10 @@ ${mediaUrl}` : mediaUrl;
|
|
|
8245
8588
|
throw retryErr;
|
|
8246
8589
|
}
|
|
8247
8590
|
}
|
|
8248
|
-
return {
|
|
8591
|
+
return {
|
|
8592
|
+
channel: "qqbot",
|
|
8593
|
+
...typingResult?.refIdx ? { refIdx: typingResult.refIdx } : {}
|
|
8594
|
+
};
|
|
8249
8595
|
} catch (err) {
|
|
8250
8596
|
const message = summarizeError(err);
|
|
8251
8597
|
return { channel: "qqbot", error: message };
|
|
@@ -8394,13 +8740,13 @@ async function replaceAsync(input2, pattern, replacer) {
|
|
|
8394
8740
|
return result;
|
|
8395
8741
|
}
|
|
8396
8742
|
async function getResolvedImageSize(params) {
|
|
8397
|
-
const { url, cache, resolveImageSize } = params;
|
|
8398
|
-
const existing =
|
|
8743
|
+
const { url, cache: cache2, resolveImageSize } = params;
|
|
8744
|
+
const existing = cache2.get(url);
|
|
8399
8745
|
if (existing) {
|
|
8400
8746
|
return existing;
|
|
8401
8747
|
}
|
|
8402
8748
|
const pending = resolveImageSize(url).then((size) => normalizeImageSize(size) ?? DEFAULT_QQBOT_MARKDOWN_IMAGE_SIZE).catch(() => DEFAULT_QQBOT_MARKDOWN_IMAGE_SIZE);
|
|
8403
|
-
|
|
8749
|
+
cache2.set(url, pending);
|
|
8404
8750
|
return pending;
|
|
8405
8751
|
}
|
|
8406
8752
|
async function normalizeTextSegment(params) {
|
|
@@ -8527,13 +8873,29 @@ async function normalizeQQBotMarkdownImages(params) {
|
|
|
8527
8873
|
|
|
8528
8874
|
${appendedImages.join("\n")}`;
|
|
8529
8875
|
}
|
|
8530
|
-
var DEFAULT_KNOWN_TARGETS_PATH = join(homedir(), ".openclaw", "
|
|
8876
|
+
var DEFAULT_KNOWN_TARGETS_PATH = join(homedir(), ".openclaw", "qqbot", "data", "known-targets.json");
|
|
8877
|
+
var LEGACY_KNOWN_TARGETS_PATH = join(homedir(), ".openclaw", "data", "qqbot", "known-targets.json");
|
|
8531
8878
|
function resolveKnownTargetsFilePath(options) {
|
|
8532
8879
|
return options?.filePath?.trim() || DEFAULT_KNOWN_TARGETS_PATH;
|
|
8533
8880
|
}
|
|
8534
8881
|
function ensureKnownTargetsDir(filePath) {
|
|
8535
8882
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
8536
8883
|
}
|
|
8884
|
+
function migrateLegacyKnownTargets(filePath) {
|
|
8885
|
+
if (filePath !== DEFAULT_KNOWN_TARGETS_PATH) {
|
|
8886
|
+
return;
|
|
8887
|
+
}
|
|
8888
|
+
if (existsSync(filePath) || !existsSync(LEGACY_KNOWN_TARGETS_PATH)) {
|
|
8889
|
+
return;
|
|
8890
|
+
}
|
|
8891
|
+
ensureKnownTargetsDir(filePath);
|
|
8892
|
+
try {
|
|
8893
|
+
renameSync(LEGACY_KNOWN_TARGETS_PATH, filePath);
|
|
8894
|
+
} catch {
|
|
8895
|
+
copyFileSync(LEGACY_KNOWN_TARGETS_PATH, filePath);
|
|
8896
|
+
rmSync(LEGACY_KNOWN_TARGETS_PATH, { force: true });
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8537
8899
|
function compareTargetsByLastSeenDesc(a, b) {
|
|
8538
8900
|
if (b.lastSeenAt !== a.lastSeenAt) {
|
|
8539
8901
|
return b.lastSeenAt - a.lastSeenAt;
|
|
@@ -8569,6 +8931,7 @@ function parseKnownTargets(raw, filePath) {
|
|
|
8569
8931
|
}
|
|
8570
8932
|
function readKnownTargets(options) {
|
|
8571
8933
|
const filePath = resolveKnownTargetsFilePath(options);
|
|
8934
|
+
migrateLegacyKnownTargets(filePath);
|
|
8572
8935
|
if (!existsSync(filePath)) {
|
|
8573
8936
|
return [];
|
|
8574
8937
|
}
|
|
@@ -8580,6 +8943,7 @@ function readKnownTargets(options) {
|
|
|
8580
8943
|
}
|
|
8581
8944
|
function writeKnownTargets(targets, options) {
|
|
8582
8945
|
const filePath = resolveKnownTargetsFilePath(options);
|
|
8946
|
+
migrateLegacyKnownTargets(filePath);
|
|
8583
8947
|
if (targets.length === 0) {
|
|
8584
8948
|
if (existsSync(filePath)) {
|
|
8585
8949
|
rmSync(filePath, { force: true });
|
|
@@ -8713,9 +9077,50 @@ function getQQBotRuntime() {
|
|
|
8713
9077
|
return runtime;
|
|
8714
9078
|
}
|
|
8715
9079
|
var sessionDispatchQueue = /* @__PURE__ */ new Map();
|
|
9080
|
+
function resolveQQBotRouteSessionKey(route) {
|
|
9081
|
+
const effectiveSessionKey = route.effectiveSessionKey?.trim();
|
|
9082
|
+
if (effectiveSessionKey) {
|
|
9083
|
+
return effectiveSessionKey;
|
|
9084
|
+
}
|
|
9085
|
+
return route.sessionKey;
|
|
9086
|
+
}
|
|
8716
9087
|
function buildSessionDispatchQueueKey(route) {
|
|
8717
9088
|
const accountId = route.accountId?.trim() || DEFAULT_ACCOUNT_ID;
|
|
8718
|
-
return `${accountId}:${route
|
|
9089
|
+
return `${accountId}:${resolveQQBotRouteSessionKey(route)}`;
|
|
9090
|
+
}
|
|
9091
|
+
function normalizeQQBotSessionKeyPart(value) {
|
|
9092
|
+
const trimmed = value.trim();
|
|
9093
|
+
return trimmed ? trimmed.toLowerCase() : "unknown";
|
|
9094
|
+
}
|
|
9095
|
+
function buildQQBotDirectSessionKey(params) {
|
|
9096
|
+
const normalizedAccountId = normalizeQQBotSessionKeyPart(params.accountId);
|
|
9097
|
+
const normalizedSenderId = normalizeQQBotSessionKeyPart(params.senderStableId);
|
|
9098
|
+
const trimmedRouteSessionKey = params.routeSessionKey.trim();
|
|
9099
|
+
if (!trimmedRouteSessionKey) {
|
|
9100
|
+
return `agent:main:qqbot:dm:${normalizedAccountId}:${normalizedSenderId}`;
|
|
9101
|
+
}
|
|
9102
|
+
const qqAgentRouteMatch = trimmedRouteSessionKey.match(/^(agent:[^:]+:qqbot:)(?:direct|dm):.+$/i);
|
|
9103
|
+
if (qqAgentRouteMatch?.[1]) {
|
|
9104
|
+
return `${qqAgentRouteMatch[1]}dm:${normalizedAccountId}:${normalizedSenderId}`;
|
|
9105
|
+
}
|
|
9106
|
+
return `${trimmedRouteSessionKey}:dm:${normalizedAccountId}:${normalizedSenderId}`;
|
|
9107
|
+
}
|
|
9108
|
+
function normalizeQQBotReplyTarget(value) {
|
|
9109
|
+
if (typeof value !== "string") {
|
|
9110
|
+
return void 0;
|
|
9111
|
+
}
|
|
9112
|
+
let trimmed = value.trim();
|
|
9113
|
+
if (!trimmed) {
|
|
9114
|
+
return void 0;
|
|
9115
|
+
}
|
|
9116
|
+
if (/^qqbot:/i.test(trimmed)) {
|
|
9117
|
+
trimmed = trimmed.slice("qqbot:".length).trim();
|
|
9118
|
+
}
|
|
9119
|
+
if (/^c2c:/i.test(trimmed)) {
|
|
9120
|
+
const openid = trimmed.slice("c2c:".length).trim();
|
|
9121
|
+
return openid ? `user:${openid}` : void 0;
|
|
9122
|
+
}
|
|
9123
|
+
return /^(user|group|channel):/i.test(trimmed) ? trimmed : void 0;
|
|
8719
9124
|
}
|
|
8720
9125
|
async function runSerializedSessionDispatch(queueKey, task) {
|
|
8721
9126
|
const previous = sessionDispatchQueue.get(queueKey) ?? Promise.resolve();
|
|
@@ -8780,6 +9185,33 @@ function parseTextWithAttachments(payload) {
|
|
|
8780
9185
|
attachments
|
|
8781
9186
|
};
|
|
8782
9187
|
}
|
|
9188
|
+
function parseQQBotRefIndices(payload) {
|
|
9189
|
+
const scene = payload.message_scene;
|
|
9190
|
+
if (!scene || typeof scene !== "object") {
|
|
9191
|
+
return {};
|
|
9192
|
+
}
|
|
9193
|
+
const ext = scene.ext;
|
|
9194
|
+
if (!Array.isArray(ext)) {
|
|
9195
|
+
return {};
|
|
9196
|
+
}
|
|
9197
|
+
let refMsgIdx;
|
|
9198
|
+
let msgIdx;
|
|
9199
|
+
for (const value of ext) {
|
|
9200
|
+
const item = toString(value);
|
|
9201
|
+
if (!item) continue;
|
|
9202
|
+
if (item.startsWith("ref_msg_idx=")) {
|
|
9203
|
+
refMsgIdx = toString(item.slice("ref_msg_idx=".length));
|
|
9204
|
+
continue;
|
|
9205
|
+
}
|
|
9206
|
+
if (item.startsWith("msg_idx=")) {
|
|
9207
|
+
msgIdx = toString(item.slice("msg_idx=".length));
|
|
9208
|
+
}
|
|
9209
|
+
}
|
|
9210
|
+
return {
|
|
9211
|
+
...refMsgIdx ? { refMsgIdx } : {},
|
|
9212
|
+
...msgIdx ? { msgIdx } : {}
|
|
9213
|
+
};
|
|
9214
|
+
}
|
|
8783
9215
|
function resolveEventId(payload, fallbackEventId) {
|
|
8784
9216
|
return toString(payload.event_id) ?? toString(payload.eventId) ?? toString(fallbackEventId);
|
|
8785
9217
|
}
|
|
@@ -8789,6 +9221,7 @@ var VOICE_ASR_ERROR_MAX_LENGTH = 500;
|
|
|
8789
9221
|
var LONG_TASK_NOTICE_TEXT = "\u4EFB\u52A1\u5904\u7406\u65F6\u95F4\u8F83\u957F\uFF0C\u8BF7\u7A0D\u7B49\uFF0C\u6211\u8FD8\u5728\u7EE7\u7EED\u5904\u7406\u3002";
|
|
8790
9222
|
var DEFAULT_LONG_TASK_NOTICE_DELAY_MS = 3e4;
|
|
8791
9223
|
var QQ_GROUP_NO_REPLY_FALLBACK_TEXT = "\u6211\u5728\u3002\u4F60\u53EF\u4EE5\u76F4\u63A5\u8BF4\u5177\u4F53\u4E00\u70B9\u3002";
|
|
9224
|
+
var QQ_QUOTE_BODY_UNAVAILABLE_TEXT = "\u539F\u59CB\u5185\u5BB9\u4E0D\u53EF\u7528";
|
|
8792
9225
|
function startLongTaskNoticeTimer(params) {
|
|
8793
9226
|
const { delayMs, logger, sendNotice } = params;
|
|
8794
9227
|
let completed = false;
|
|
@@ -8823,7 +9256,7 @@ function startLongTaskNoticeTimer(params) {
|
|
|
8823
9256
|
}
|
|
8824
9257
|
};
|
|
8825
9258
|
}
|
|
8826
|
-
function
|
|
9259
|
+
function isHttpUrl3(value) {
|
|
8827
9260
|
return /^https?:\/\//i.test(value);
|
|
8828
9261
|
}
|
|
8829
9262
|
function isImageAttachment(att) {
|
|
@@ -8896,7 +9329,7 @@ async function resolveInboundAttachmentsForAgent(params) {
|
|
|
8896
9329
|
let asrErrorMessage;
|
|
8897
9330
|
for (const att of list) {
|
|
8898
9331
|
const next = { attachment: att };
|
|
8899
|
-
if (isImageAttachment(att) &&
|
|
9332
|
+
if (isImageAttachment(att) && isHttpUrl3(att.url)) {
|
|
8900
9333
|
try {
|
|
8901
9334
|
const downloaded = await downloadToTempFile(att.url, {
|
|
8902
9335
|
timeout,
|
|
@@ -8925,7 +9358,7 @@ async function resolveInboundAttachmentsForAgent(params) {
|
|
|
8925
9358
|
logger.info("voice attachment received but ASR is disabled");
|
|
8926
9359
|
} else if (!asrCredentials) {
|
|
8927
9360
|
logger.warn("voice ASR enabled but credentials are missing or invalid");
|
|
8928
|
-
} else if (!
|
|
9361
|
+
} else if (!isHttpUrl3(att.url)) {
|
|
8929
9362
|
logger.warn("voice ASR skipped: attachment URL is not an HTTP URL");
|
|
8930
9363
|
} else {
|
|
8931
9364
|
try {
|
|
@@ -8996,6 +9429,74 @@ function buildInboundContentWithAttachments(params) {
|
|
|
8996
9429
|
parts.push(block);
|
|
8997
9430
|
return parts.join("\n\n");
|
|
8998
9431
|
}
|
|
9432
|
+
function resolveRefAttachmentType(attachment) {
|
|
9433
|
+
const contentType = attachment.contentType?.trim().toLowerCase() ?? "";
|
|
9434
|
+
if (contentType.startsWith("image/") || isImageAttachment(attachment)) {
|
|
9435
|
+
return "image";
|
|
9436
|
+
}
|
|
9437
|
+
if (contentType === "voice" || contentType.startsWith("audio/") || isVoiceAttachment(attachment)) {
|
|
9438
|
+
return "voice";
|
|
9439
|
+
}
|
|
9440
|
+
if (contentType.startsWith("video/")) {
|
|
9441
|
+
return "video";
|
|
9442
|
+
}
|
|
9443
|
+
const mediaType = detectMediaType(attachment.filename?.trim() || attachment.url);
|
|
9444
|
+
if (mediaType === "image") return "image";
|
|
9445
|
+
if (mediaType === "audio") return "voice";
|
|
9446
|
+
if (mediaType === "video") return "video";
|
|
9447
|
+
if (mediaType === "file") return "file";
|
|
9448
|
+
return "unknown";
|
|
9449
|
+
}
|
|
9450
|
+
function buildInboundRefAttachmentSummaries(attachments) {
|
|
9451
|
+
if (attachments.length === 0) {
|
|
9452
|
+
return void 0;
|
|
9453
|
+
}
|
|
9454
|
+
return attachments.map((item) => ({
|
|
9455
|
+
type: resolveRefAttachmentType(item.attachment),
|
|
9456
|
+
...item.attachment.filename?.trim() ? { filename: item.attachment.filename.trim() } : {},
|
|
9457
|
+
...item.attachment.contentType?.trim() ? { contentType: item.attachment.contentType.trim() } : {},
|
|
9458
|
+
...item.localImagePath?.trim() ? { localPath: item.localImagePath.trim() } : {},
|
|
9459
|
+
...item.attachment.url?.trim() ? { url: item.attachment.url.trim() } : {},
|
|
9460
|
+
...item.voiceTranscript?.trim() ? {
|
|
9461
|
+
transcript: item.voiceTranscript.trim(),
|
|
9462
|
+
transcriptSource: "asr"
|
|
9463
|
+
} : {}
|
|
9464
|
+
}));
|
|
9465
|
+
}
|
|
9466
|
+
function buildQuotedAgentBody(params) {
|
|
9467
|
+
const quoteBlock = `[\u5F15\u7528\u6D88\u606F\u5F00\u59CB]
|
|
9468
|
+
${params.replyToBody}
|
|
9469
|
+
[\u5F15\u7528\u6D88\u606F\u7ED3\u675F]`;
|
|
9470
|
+
return params.baseBody ? `${quoteBlock}
|
|
9471
|
+
|
|
9472
|
+
${params.baseBody}` : quoteBlock;
|
|
9473
|
+
}
|
|
9474
|
+
function resolveAgentBodyBase(ctx) {
|
|
9475
|
+
if (typeof ctx.BodyForAgent === "string" && ctx.BodyForAgent.trim()) {
|
|
9476
|
+
return ctx.BodyForAgent;
|
|
9477
|
+
}
|
|
9478
|
+
if (typeof ctx.RawBody === "string" && ctx.RawBody.trim()) {
|
|
9479
|
+
return ctx.RawBody;
|
|
9480
|
+
}
|
|
9481
|
+
if (typeof ctx.Body === "string" && ctx.Body.trim()) {
|
|
9482
|
+
return ctx.Body;
|
|
9483
|
+
}
|
|
9484
|
+
if (typeof ctx.CommandBody === "string" && ctx.CommandBody.trim()) {
|
|
9485
|
+
return ctx.CommandBody;
|
|
9486
|
+
}
|
|
9487
|
+
return "";
|
|
9488
|
+
}
|
|
9489
|
+
function uniqueRefIndexKeys(...values) {
|
|
9490
|
+
const keys = [];
|
|
9491
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9492
|
+
for (const value of values) {
|
|
9493
|
+
const next = value?.trim();
|
|
9494
|
+
if (!next || seen.has(next)) continue;
|
|
9495
|
+
seen.add(next);
|
|
9496
|
+
keys.push(next);
|
|
9497
|
+
}
|
|
9498
|
+
return keys;
|
|
9499
|
+
}
|
|
8999
9500
|
function resolveInboundLogContent(params) {
|
|
9000
9501
|
const text = params.content.trim();
|
|
9001
9502
|
if (text) return text;
|
|
@@ -9017,6 +9518,7 @@ function sanitizeInboundLogText(text) {
|
|
|
9017
9518
|
function parseC2CMessage(data, fallbackEventId) {
|
|
9018
9519
|
const payload = data;
|
|
9019
9520
|
const { text, attachments } = parseTextWithAttachments(payload);
|
|
9521
|
+
const refIndices = parseQQBotRefIndices(payload);
|
|
9020
9522
|
const id = toString(payload.id);
|
|
9021
9523
|
const eventId = resolveEventId(payload, fallbackEventId);
|
|
9022
9524
|
const timestamp = toNumber2(payload.timestamp) ?? Date.now();
|
|
@@ -9033,6 +9535,7 @@ function parseC2CMessage(data, fallbackEventId) {
|
|
|
9033
9535
|
messageId: id,
|
|
9034
9536
|
eventId,
|
|
9035
9537
|
timestamp,
|
|
9538
|
+
...refIndices,
|
|
9036
9539
|
mentionedBot: false
|
|
9037
9540
|
};
|
|
9038
9541
|
}
|
|
@@ -9146,6 +9649,22 @@ function resolveChatTarget(event) {
|
|
|
9146
9649
|
peerKind: "dm"
|
|
9147
9650
|
};
|
|
9148
9651
|
}
|
|
9652
|
+
function resolveQQBotEffectiveSessionKey(params) {
|
|
9653
|
+
const { inbound, route, accountId } = params;
|
|
9654
|
+
if (inbound.type !== "direct") {
|
|
9655
|
+
return route.sessionKey;
|
|
9656
|
+
}
|
|
9657
|
+
const senderStableId = inbound.c2cOpenid?.trim() || inbound.senderId?.trim();
|
|
9658
|
+
if (!senderStableId) {
|
|
9659
|
+
return route.sessionKey;
|
|
9660
|
+
}
|
|
9661
|
+
const resolvedAccountId = route.accountId?.trim() || accountId.trim() || DEFAULT_ACCOUNT_ID;
|
|
9662
|
+
return buildQQBotDirectSessionKey({
|
|
9663
|
+
routeSessionKey: route.sessionKey,
|
|
9664
|
+
accountId: resolvedAccountId,
|
|
9665
|
+
senderStableId
|
|
9666
|
+
});
|
|
9667
|
+
}
|
|
9149
9668
|
function resolveEnvelopeFrom(event) {
|
|
9150
9669
|
if (event.type === "group") {
|
|
9151
9670
|
return `group:${(event.groupOpenid ?? "unknown").toLowerCase()}`;
|
|
@@ -9474,7 +9993,7 @@ function normalizeQQBotRenderedMarkdown(text) {
|
|
|
9474
9993
|
return changed ? next.trim() : text.trim();
|
|
9475
9994
|
}
|
|
9476
9995
|
async function sendQQBotMediaWithFallback(params) {
|
|
9477
|
-
const { qqCfg, to, mediaQueue, replyToId, replyEventId, logger, onDelivered, onError } = params;
|
|
9996
|
+
const { qqCfg, to, mediaQueue, replyToId, replyEventId, accountId, logger, onDelivered, onError } = params;
|
|
9478
9997
|
const outbound = params.outbound ?? qqbotOutbound;
|
|
9479
9998
|
for (const mediaUrl of mediaQueue) {
|
|
9480
9999
|
const result = await outbound.sendMedia({
|
|
@@ -9482,7 +10001,8 @@ async function sendQQBotMediaWithFallback(params) {
|
|
|
9482
10001
|
to,
|
|
9483
10002
|
mediaUrl,
|
|
9484
10003
|
replyToId,
|
|
9485
|
-
replyEventId
|
|
10004
|
+
replyEventId,
|
|
10005
|
+
accountId
|
|
9486
10006
|
});
|
|
9487
10007
|
if (result.error) {
|
|
9488
10008
|
logger.error(`sendMedia failed: ${result.error}`);
|
|
@@ -9496,7 +10016,8 @@ async function sendQQBotMediaWithFallback(params) {
|
|
|
9496
10016
|
to,
|
|
9497
10017
|
text: fallback,
|
|
9498
10018
|
replyToId,
|
|
9499
|
-
replyEventId
|
|
10019
|
+
replyEventId,
|
|
10020
|
+
accountId
|
|
9500
10021
|
});
|
|
9501
10022
|
if (fallbackResult.error) {
|
|
9502
10023
|
logger.error(`sendText fallback failed: ${fallbackResult.error}`);
|
|
@@ -9541,17 +10062,23 @@ function buildInboundContext(params) {
|
|
|
9541
10062
|
async function dispatchToAgent(params) {
|
|
9542
10063
|
const { inbound, cfg, qqCfg, accountId, logger, route } = params;
|
|
9543
10064
|
const runtime2 = getQQBotRuntime();
|
|
10065
|
+
const routeSessionKey = resolveQQBotRouteSessionKey(route);
|
|
9544
10066
|
const target = resolveChatTarget(inbound);
|
|
10067
|
+
const outboundAccountId = route.accountId ?? accountId;
|
|
10068
|
+
let typingRefIdx;
|
|
9545
10069
|
if (inbound.c2cOpenid) {
|
|
9546
10070
|
const typing = await qqbotOutbound.sendTyping({
|
|
9547
10071
|
cfg: { channels: { qqbot: qqCfg } },
|
|
9548
10072
|
to: `user:${inbound.c2cOpenid}`,
|
|
9549
10073
|
replyToId: inbound.messageId,
|
|
9550
10074
|
replyEventId: inbound.eventId,
|
|
9551
|
-
inputSecond: 60
|
|
10075
|
+
inputSecond: 60,
|
|
10076
|
+
accountId: outboundAccountId
|
|
9552
10077
|
});
|
|
9553
10078
|
if (typing.error) {
|
|
9554
10079
|
logger.warn(`sendTyping failed: ${typing.error}`);
|
|
10080
|
+
} else {
|
|
10081
|
+
typingRefIdx = typing.refIdx;
|
|
9555
10082
|
}
|
|
9556
10083
|
}
|
|
9557
10084
|
const replyApi = runtime2.channel?.reply;
|
|
@@ -9582,7 +10109,8 @@ async function dispatchToAgent(params) {
|
|
|
9582
10109
|
to: target.to,
|
|
9583
10110
|
text: LONG_TASK_NOTICE_TEXT,
|
|
9584
10111
|
replyToId: inbound.messageId,
|
|
9585
|
-
replyEventId: inbound.eventId
|
|
10112
|
+
replyEventId: inbound.eventId,
|
|
10113
|
+
accountId: outboundAccountId
|
|
9586
10114
|
});
|
|
9587
10115
|
if (result.error) {
|
|
9588
10116
|
logger.warn(`send long-task notice failed: ${result.error}`);
|
|
@@ -9602,7 +10130,7 @@ async function dispatchToAgent(params) {
|
|
|
9602
10130
|
{ agentId: route.agentId }
|
|
9603
10131
|
);
|
|
9604
10132
|
const envelopeOptions = replyApi.resolveEnvelopeFormatOptions?.(cfg);
|
|
9605
|
-
const previousTimestamp = storePath && sessionApi?.readSessionUpdatedAt ? sessionApi.readSessionUpdatedAt({ storePath, sessionKey:
|
|
10133
|
+
const previousTimestamp = storePath && sessionApi?.readSessionUpdatedAt ? sessionApi.readSessionUpdatedAt({ storePath, sessionKey: routeSessionKey }) : null;
|
|
9606
10134
|
const resolvedAttachmentResult = await resolveInboundAttachmentsForAgent({
|
|
9607
10135
|
attachments: inbound.attachments,
|
|
9608
10136
|
qqCfg,
|
|
@@ -9614,7 +10142,8 @@ async function dispatchToAgent(params) {
|
|
|
9614
10142
|
to: target.to,
|
|
9615
10143
|
text: buildVoiceASRFallbackReply(resolvedAttachmentResult.asrErrorMessage),
|
|
9616
10144
|
replyToId: inbound.messageId,
|
|
9617
|
-
replyEventId: inbound.eventId
|
|
10145
|
+
replyEventId: inbound.eventId,
|
|
10146
|
+
accountId: outboundAccountId
|
|
9618
10147
|
});
|
|
9619
10148
|
if (fallback.error) {
|
|
9620
10149
|
logger.error(`sendText ASR fallback failed: ${fallback.error}`);
|
|
@@ -9629,6 +10158,39 @@ async function dispatchToAgent(params) {
|
|
|
9629
10158
|
if (localImageCount > 0) {
|
|
9630
10159
|
logger.info(`prepared ${localImageCount} local image attachment(s) for agent`);
|
|
9631
10160
|
}
|
|
10161
|
+
let replyToId;
|
|
10162
|
+
let replyToBody;
|
|
10163
|
+
let replyToSender;
|
|
10164
|
+
let replyToIsQuote = false;
|
|
10165
|
+
if (inbound.c2cOpenid && inbound.refMsgIdx) {
|
|
10166
|
+
replyToId = inbound.refMsgIdx;
|
|
10167
|
+
replyToIsQuote = true;
|
|
10168
|
+
const refEntry = getRefIndex(inbound.refMsgIdx);
|
|
10169
|
+
if (refEntry) {
|
|
10170
|
+
replyToBody = formatRefEntryForAgent(refEntry);
|
|
10171
|
+
replyToSender = refEntry.senderName ?? refEntry.senderId;
|
|
10172
|
+
logger.info(`quote context resolved refMsgIdx=${inbound.refMsgIdx}`);
|
|
10173
|
+
} else {
|
|
10174
|
+
replyToBody = QQ_QUOTE_BODY_UNAVAILABLE_TEXT;
|
|
10175
|
+
logger.warn(`quote context missing refMsgIdx=${inbound.refMsgIdx}`);
|
|
10176
|
+
}
|
|
10177
|
+
}
|
|
10178
|
+
const refAttachmentSummaries = buildInboundRefAttachmentSummaries(resolvedAttachments);
|
|
10179
|
+
const currentRefIndexKeys = inbound.c2cOpenid ? uniqueRefIndexKeys(inbound.msgIdx, typingRefIdx) : [];
|
|
10180
|
+
if (currentRefIndexKeys.length > 0) {
|
|
10181
|
+
for (const currentRefIndexKey of currentRefIndexKeys) {
|
|
10182
|
+
setRefIndex(currentRefIndexKey, {
|
|
10183
|
+
content: inbound.content,
|
|
10184
|
+
senderId: inbound.senderId,
|
|
10185
|
+
...inbound.senderName ? { senderName: inbound.senderName } : {},
|
|
10186
|
+
timestamp: inbound.timestamp,
|
|
10187
|
+
...refAttachmentSummaries ? { attachments: refAttachmentSummaries } : {}
|
|
10188
|
+
});
|
|
10189
|
+
}
|
|
10190
|
+
logger.info(
|
|
10191
|
+
`cached inbound ref_idx keys=${currentRefIndexKeys.join(",")} msgIdx=${inbound.msgIdx ?? "-"} typingRefIdx=${typingRefIdx ?? "-"}`
|
|
10192
|
+
);
|
|
10193
|
+
}
|
|
9632
10194
|
const rawBody = buildInboundContentWithAttachments({
|
|
9633
10195
|
content: inbound.content,
|
|
9634
10196
|
attachments: resolvedAttachments
|
|
@@ -9654,40 +10216,48 @@ async function dispatchToAgent(params) {
|
|
|
9654
10216
|
}) : rawBody;
|
|
9655
10217
|
const inboundCtx = buildInboundContext({
|
|
9656
10218
|
event: inbound,
|
|
9657
|
-
sessionKey:
|
|
9658
|
-
accountId:
|
|
10219
|
+
sessionKey: routeSessionKey,
|
|
10220
|
+
accountId: outboundAccountId,
|
|
9659
10221
|
body: inboundBody,
|
|
9660
10222
|
rawBody,
|
|
9661
10223
|
commandBody: rawBody
|
|
9662
10224
|
});
|
|
9663
10225
|
const finalizeInboundContext = replyApi?.finalizeInboundContext;
|
|
9664
10226
|
const finalCtx = finalizeInboundContext ? finalizeInboundContext(inboundCtx) : inboundCtx;
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
10227
|
+
const ctxTo = normalizeQQBotReplyTarget(finalCtx.To);
|
|
10228
|
+
const ctxOriginatingTo = normalizeQQBotReplyTarget(finalCtx.OriginatingTo);
|
|
10229
|
+
const stableTo = ctxOriginatingTo ?? ctxTo ?? target.to;
|
|
10230
|
+
finalCtx.To = stableTo;
|
|
10231
|
+
finalCtx.OriginatingTo = stableTo;
|
|
10232
|
+
if (replyToId) {
|
|
10233
|
+
finalCtx.ReplyToId = replyToId;
|
|
10234
|
+
finalCtx.ReplyToBody = replyToBody;
|
|
10235
|
+
finalCtx.ReplyToSender = replyToSender;
|
|
10236
|
+
finalCtx.ReplyToIsQuote = replyToIsQuote;
|
|
10237
|
+
}
|
|
10238
|
+
const isSlashCommand = typeof finalCtx.CommandBody === "string" ? finalCtx.CommandBody.trim().startsWith("/") : typeof finalCtx.RawBody === "string" ? finalCtx.RawBody.trim().startsWith("/") : false;
|
|
10239
|
+
if (!isSlashCommand) {
|
|
10240
|
+
let agentBody = resolveAgentBodyBase(finalCtx);
|
|
10241
|
+
if (replyToIsQuote && replyToBody && replyToBody !== QQ_QUOTE_BODY_UNAVAILABLE_TEXT) {
|
|
10242
|
+
agentBody = buildQuotedAgentBody({
|
|
10243
|
+
baseBody: agentBody,
|
|
10244
|
+
replyToBody
|
|
10245
|
+
});
|
|
9677
10246
|
}
|
|
10247
|
+
finalCtx.BodyForAgent = appendCronHiddenPrompt(agentBody);
|
|
9678
10248
|
}
|
|
9679
10249
|
if (storePath && sessionApi?.recordInboundSession) {
|
|
9680
10250
|
try {
|
|
9681
|
-
const mainSessionKeyRaw = route
|
|
9682
|
-
const mainSessionKey = typeof mainSessionKeyRaw === "string" && mainSessionKeyRaw.trim() ? mainSessionKeyRaw : void 0;
|
|
10251
|
+
const mainSessionKeyRaw = route.mainSessionKey;
|
|
10252
|
+
const mainSessionKey = typeof mainSessionKeyRaw === "string" && mainSessionKeyRaw.trim() ? mainSessionKeyRaw.trim() : void 0;
|
|
9683
10253
|
const isGroup = inbound.type === "group" || inbound.type === "channel";
|
|
9684
10254
|
const updateLastRoute = !isGroup ? {
|
|
9685
10255
|
sessionKey: mainSessionKey ?? route.sessionKey,
|
|
9686
10256
|
channel: "qqbot",
|
|
9687
|
-
to:
|
|
9688
|
-
accountId:
|
|
10257
|
+
to: stableTo,
|
|
10258
|
+
accountId: outboundAccountId
|
|
9689
10259
|
} : void 0;
|
|
9690
|
-
const recordSessionKey = typeof finalCtx.SessionKey === "string" && finalCtx.SessionKey.trim() ? finalCtx.SessionKey :
|
|
10260
|
+
const recordSessionKey = typeof finalCtx.SessionKey === "string" && finalCtx.SessionKey.trim() ? finalCtx.SessionKey : routeSessionKey;
|
|
9691
10261
|
await sessionApi.recordInboundSession({
|
|
9692
10262
|
storePath,
|
|
9693
10263
|
sessionKey: recordSessionKey,
|
|
@@ -9711,7 +10281,7 @@ async function dispatchToAgent(params) {
|
|
|
9711
10281
|
const tableMode = textApi?.resolveMarkdownTableMode?.({
|
|
9712
10282
|
cfg,
|
|
9713
10283
|
channel: "qqbot",
|
|
9714
|
-
accountId:
|
|
10284
|
+
accountId: outboundAccountId
|
|
9715
10285
|
});
|
|
9716
10286
|
const resolvedTableMode = tableMode ?? "bullets";
|
|
9717
10287
|
const chunkText = (text) => {
|
|
@@ -9736,27 +10306,16 @@ async function dispatchToAgent(params) {
|
|
|
9736
10306
|
bufferedC2CMarkdownMediaSeen.add(next);
|
|
9737
10307
|
bufferedC2CMarkdownMediaUrls.push(next);
|
|
9738
10308
|
};
|
|
9739
|
-
const
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
bufferedC2CMarkdownMediaUrls = [];
|
|
9743
|
-
bufferedC2CMarkdownMediaSeen.clear();
|
|
9744
|
-
return;
|
|
9745
|
-
}
|
|
9746
|
-
const combinedText = bufferedC2CMarkdownTexts.join("\n\n").trim();
|
|
9747
|
-
const combinedMediaUrls = [...bufferedC2CMarkdownMediaUrls];
|
|
9748
|
-
bufferedC2CMarkdownTexts = [];
|
|
9749
|
-
bufferedC2CMarkdownMediaUrls = [];
|
|
9750
|
-
bufferedC2CMarkdownMediaSeen.clear();
|
|
9751
|
-
const normalizedCombinedText = normalizeQQBotRenderedMarkdown(combinedText);
|
|
9752
|
-
const { markdownImageUrls, mediaQueue } = splitQQBotMarkdownTransportMediaUrls(combinedMediaUrls);
|
|
10309
|
+
const sendC2CMarkdownTransportPayload = async (params2) => {
|
|
10310
|
+
const normalizedText = normalizeQQBotRenderedMarkdown(params2.text);
|
|
10311
|
+
const { markdownImageUrls, mediaQueue } = splitQQBotMarkdownTransportMediaUrls(params2.mediaUrls);
|
|
9753
10312
|
const finalMarkdownText = await normalizeQQBotMarkdownImages({
|
|
9754
|
-
text:
|
|
10313
|
+
text: normalizedText,
|
|
9755
10314
|
appendImageUrls: markdownImageUrls
|
|
9756
10315
|
});
|
|
9757
10316
|
const textReplyRefs = resolveQQBotTextReplyRefs({
|
|
9758
10317
|
to: target.to,
|
|
9759
|
-
text: finalMarkdownText ||
|
|
10318
|
+
text: finalMarkdownText || normalizedText,
|
|
9760
10319
|
markdownSupport,
|
|
9761
10320
|
c2cMarkdownDeliveryMode,
|
|
9762
10321
|
replyToId: inbound.messageId,
|
|
@@ -9765,7 +10324,7 @@ async function dispatchToAgent(params) {
|
|
|
9765
10324
|
const textSegments = finalMarkdownText ? [finalMarkdownText] : [];
|
|
9766
10325
|
const deliveryLabel = textReplyRefs.forceProactive ? "c2c-markdown-proactive" : "c2c-markdown-passive";
|
|
9767
10326
|
logger.info(
|
|
9768
|
-
`delivery=${deliveryLabel} to=${target.to} segments=${textSegments.length} media=${mediaQueue.length} replyToId=${textReplyRefs.replyToId ? "yes" : "no"} replyEventId=${textReplyRefs.replyEventId ? "yes" : "no"} tableMode=${String(resolvedTableMode)} chunkMode=${String(chunkMode ?? "default")}`
|
|
10327
|
+
`delivery=${deliveryLabel} to=${target.to} segments=${textSegments.length} media=${mediaQueue.length} replyToId=${textReplyRefs.replyToId ? "yes" : "no"} replyEventId=${textReplyRefs.replyEventId ? "yes" : "no"} phase=${params2.phase} tableMode=${String(resolvedTableMode)} chunkMode=${String(chunkMode ?? "default")}`
|
|
9769
10328
|
);
|
|
9770
10329
|
await sendQQBotMediaWithFallback({
|
|
9771
10330
|
qqCfg,
|
|
@@ -9773,6 +10332,7 @@ async function dispatchToAgent(params) {
|
|
|
9773
10332
|
mediaQueue,
|
|
9774
10333
|
replyToId: textReplyRefs.replyToId,
|
|
9775
10334
|
replyEventId: textReplyRefs.replyEventId,
|
|
10335
|
+
accountId: outboundAccountId,
|
|
9776
10336
|
logger,
|
|
9777
10337
|
onDelivered: () => {
|
|
9778
10338
|
markReplyDelivered();
|
|
@@ -9790,25 +10350,44 @@ async function dispatchToAgent(params) {
|
|
|
9790
10350
|
for (let chunkIndex = 0; chunkIndex < chunks.length; chunkIndex += 1) {
|
|
9791
10351
|
const chunk = chunks[chunkIndex] ?? "";
|
|
9792
10352
|
logger.info(
|
|
9793
|
-
`delivery=${deliveryLabel} segment=${segmentIndex + 1}/${textSegments.length} chunk=${chunkIndex + 1}/${chunks.length} preview=${formatQQBotOutboundPreview(chunk)}`
|
|
10353
|
+
`delivery=${deliveryLabel} segment=${segmentIndex + 1}/${textSegments.length} chunk=${chunkIndex + 1}/${chunks.length} phase=${params2.phase} preview=${formatQQBotOutboundPreview(chunk)}`
|
|
9794
10354
|
);
|
|
9795
10355
|
const result = await qqbotOutbound.sendText({
|
|
9796
10356
|
cfg: { channels: { qqbot: qqCfg } },
|
|
9797
10357
|
to: target.to,
|
|
9798
10358
|
text: chunk,
|
|
9799
10359
|
replyToId: textReplyRefs.replyToId,
|
|
9800
|
-
replyEventId: textReplyRefs.replyEventId
|
|
10360
|
+
replyEventId: textReplyRefs.replyEventId,
|
|
10361
|
+
accountId: outboundAccountId
|
|
9801
10362
|
});
|
|
9802
10363
|
if (result.error) {
|
|
9803
|
-
logger.error(`send
|
|
10364
|
+
logger.error(`send QQ markdown reply failed: ${result.error}`);
|
|
9804
10365
|
markGroupMessageInterfaceBlocked(result.error);
|
|
9805
10366
|
} else {
|
|
9806
|
-
logger.info(`sent
|
|
10367
|
+
logger.info(`sent QQ markdown reply (phase=${params2.phase}, len=${chunk.length})`);
|
|
9807
10368
|
markReplyDelivered();
|
|
9808
10369
|
}
|
|
9809
10370
|
}
|
|
9810
10371
|
}
|
|
9811
10372
|
};
|
|
10373
|
+
const flushBufferedC2CMarkdownReply = async () => {
|
|
10374
|
+
if (!useC2CMarkdownTransport || bufferedC2CMarkdownTexts.length === 0 && bufferedC2CMarkdownMediaUrls.length === 0) {
|
|
10375
|
+
bufferedC2CMarkdownTexts = [];
|
|
10376
|
+
bufferedC2CMarkdownMediaUrls = [];
|
|
10377
|
+
bufferedC2CMarkdownMediaSeen.clear();
|
|
10378
|
+
return;
|
|
10379
|
+
}
|
|
10380
|
+
const combinedText = bufferedC2CMarkdownTexts.join("\n\n").trim();
|
|
10381
|
+
const combinedMediaUrls = [...bufferedC2CMarkdownMediaUrls];
|
|
10382
|
+
bufferedC2CMarkdownTexts = [];
|
|
10383
|
+
bufferedC2CMarkdownMediaUrls = [];
|
|
10384
|
+
bufferedC2CMarkdownMediaSeen.clear();
|
|
10385
|
+
await sendC2CMarkdownTransportPayload({
|
|
10386
|
+
text: combinedText,
|
|
10387
|
+
mediaUrls: combinedMediaUrls,
|
|
10388
|
+
phase: "buffered"
|
|
10389
|
+
});
|
|
10390
|
+
};
|
|
9812
10391
|
const deliver = async (payload, info) => {
|
|
9813
10392
|
const typed = payload;
|
|
9814
10393
|
const extractedTextMedia = extractQQBotReplyMedia({
|
|
@@ -9840,12 +10419,21 @@ async function dispatchToAgent(params) {
|
|
|
9840
10419
|
const suppressText = deliveryDecision.suppressText || suppressEchoText;
|
|
9841
10420
|
const textToSend = suppressText ? "" : cleanedText;
|
|
9842
10421
|
if (useC2CMarkdownTransport) {
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
9847
|
-
|
|
10422
|
+
const shouldBufferFinalOnlyPayload = replyFinalOnly && (!info?.kind || info.kind === "final");
|
|
10423
|
+
if (shouldBufferFinalOnlyPayload) {
|
|
10424
|
+
if (textToSend) {
|
|
10425
|
+
bufferedC2CMarkdownTexts = appendQQBotBufferedText(bufferedC2CMarkdownTexts, textToSend);
|
|
10426
|
+
}
|
|
10427
|
+
for (const url of mediaQueue) {
|
|
10428
|
+
bufferC2CMarkdownMedia(url);
|
|
10429
|
+
}
|
|
10430
|
+
return;
|
|
9848
10431
|
}
|
|
10432
|
+
await sendC2CMarkdownTransportPayload({
|
|
10433
|
+
text: textToSend,
|
|
10434
|
+
mediaUrls: mediaQueue,
|
|
10435
|
+
phase: "immediate"
|
|
10436
|
+
});
|
|
9849
10437
|
return;
|
|
9850
10438
|
}
|
|
9851
10439
|
if (textToSend) {
|
|
@@ -9865,7 +10453,8 @@ async function dispatchToAgent(params) {
|
|
|
9865
10453
|
to: target.to,
|
|
9866
10454
|
text: chunk,
|
|
9867
10455
|
replyToId: textReplyRefs.replyToId,
|
|
9868
|
-
replyEventId: textReplyRefs.replyEventId
|
|
10456
|
+
replyEventId: textReplyRefs.replyEventId,
|
|
10457
|
+
accountId: outboundAccountId
|
|
9869
10458
|
});
|
|
9870
10459
|
if (result.error) {
|
|
9871
10460
|
logger.error(`sendText failed: ${result.error}`);
|
|
@@ -9881,6 +10470,7 @@ async function dispatchToAgent(params) {
|
|
|
9881
10470
|
mediaQueue,
|
|
9882
10471
|
replyToId: inbound.messageId,
|
|
9883
10472
|
replyEventId: inbound.eventId,
|
|
10473
|
+
accountId: outboundAccountId,
|
|
9884
10474
|
logger,
|
|
9885
10475
|
onDelivered: () => {
|
|
9886
10476
|
markReplyDelivered();
|
|
@@ -9952,7 +10542,8 @@ async function dispatchToAgent(params) {
|
|
|
9952
10542
|
to: target.to,
|
|
9953
10543
|
text: noReplyFallback,
|
|
9954
10544
|
replyToId: inbound.messageId,
|
|
9955
|
-
replyEventId: inbound.eventId
|
|
10545
|
+
replyEventId: inbound.eventId,
|
|
10546
|
+
accountId: outboundAccountId
|
|
9956
10547
|
});
|
|
9957
10548
|
if (fallbackResult.error) {
|
|
9958
10549
|
logger.error(`sendText no-reply fallback failed: ${fallbackResult.error}`);
|
|
@@ -10057,9 +10648,19 @@ async function handleQQBotDispatch(params) {
|
|
|
10057
10648
|
accountId,
|
|
10058
10649
|
peer: { kind: target.peerKind, id: target.peerId }
|
|
10059
10650
|
});
|
|
10060
|
-
const
|
|
10651
|
+
const effectiveSessionKey = resolveQQBotEffectiveSessionKey({
|
|
10652
|
+
inbound,
|
|
10653
|
+
route,
|
|
10654
|
+
accountId
|
|
10655
|
+
});
|
|
10656
|
+
const resolvedRoute = effectiveSessionKey === route.sessionKey ? route : {
|
|
10657
|
+
...route,
|
|
10658
|
+
mainSessionKey: route.mainSessionKey?.trim() || route.sessionKey,
|
|
10659
|
+
effectiveSessionKey
|
|
10660
|
+
};
|
|
10661
|
+
const queueKey = buildSessionDispatchQueueKey(resolvedRoute);
|
|
10061
10662
|
if (sessionDispatchQueue.has(queueKey)) {
|
|
10062
|
-
logger.info(`session busy; queueing inbound dispatch sessionKey=${
|
|
10663
|
+
logger.info(`session busy; queueing inbound dispatch sessionKey=${resolveQQBotRouteSessionKey(resolvedRoute)}`);
|
|
10063
10664
|
}
|
|
10064
10665
|
await runSerializedSessionDispatch(
|
|
10065
10666
|
queueKey,
|
|
@@ -10069,7 +10670,7 @@ async function handleQQBotDispatch(params) {
|
|
|
10069
10670
|
qqCfg,
|
|
10070
10671
|
accountId,
|
|
10071
10672
|
logger,
|
|
10072
|
-
route
|
|
10673
|
+
route: resolvedRoute
|
|
10073
10674
|
})
|
|
10074
10675
|
);
|
|
10075
10676
|
}
|
|
@@ -10381,7 +10982,7 @@ var qqbotPlugin = {
|
|
|
10381
10982
|
...meta
|
|
10382
10983
|
},
|
|
10383
10984
|
capabilities: {
|
|
10384
|
-
chatTypes: ["direct", "channel"],
|
|
10985
|
+
chatTypes: ["direct", "group", "channel"],
|
|
10385
10986
|
media: true,
|
|
10386
10987
|
reactions: false,
|
|
10387
10988
|
threads: false,
|