@openclaw-china/qqbot 2026.3.9 → 2026.3.10
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 +112 -1
- package/dist/index.js +430 -18
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +1 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as os from 'os';
|
|
2
2
|
import { homedir, tmpdir } from 'os';
|
|
3
3
|
import * as path2 from 'path';
|
|
4
|
-
import { join } from 'path';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
5
5
|
import * as fs3 from 'fs';
|
|
6
|
-
import { existsSync } from 'fs';
|
|
6
|
+
import { existsSync, readFileSync, rmSync, writeFileSync, mkdirSync } from 'fs';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import * as fsPromises from 'fs/promises';
|
|
9
9
|
import { createHmac } from 'crypto';
|
|
@@ -4245,6 +4245,7 @@ var QQBotAccountSchema = external_exports.object({
|
|
|
4245
4245
|
longTaskNoticeDelayMs: external_exports.number().int().min(0).optional().default(3e4),
|
|
4246
4246
|
maxFileSizeMB: external_exports.number().positive().optional().default(100),
|
|
4247
4247
|
mediaTimeoutMs: external_exports.number().int().positive().optional().default(3e4),
|
|
4248
|
+
autoSendLocalPathMedia: external_exports.boolean().optional().default(true),
|
|
4248
4249
|
inboundMedia: external_exports.object({
|
|
4249
4250
|
dir: external_exports.string().optional(),
|
|
4250
4251
|
keepDays: external_exports.number().optional()
|
|
@@ -4264,6 +4265,9 @@ function resolveInboundMediaKeepDays(config) {
|
|
|
4264
4265
|
const value = config?.inboundMedia?.keepDays;
|
|
4265
4266
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : DEFAULT_INBOUND_MEDIA_KEEP_DAYS;
|
|
4266
4267
|
}
|
|
4268
|
+
function resolveQQBotAutoSendLocalPathMedia(config) {
|
|
4269
|
+
return config?.autoSendLocalPathMedia ?? true;
|
|
4270
|
+
}
|
|
4267
4271
|
function resolveInboundMediaTempDir() {
|
|
4268
4272
|
return DEFAULT_INBOUND_MEDIA_TEMP_DIR;
|
|
4269
4273
|
}
|
|
@@ -4316,6 +4320,156 @@ function resolveQQBotASRCredentials(config) {
|
|
|
4316
4320
|
};
|
|
4317
4321
|
}
|
|
4318
4322
|
|
|
4323
|
+
// src/onboarding.ts
|
|
4324
|
+
function isPromptCancelled(value) {
|
|
4325
|
+
return typeof value === "symbol";
|
|
4326
|
+
}
|
|
4327
|
+
function setQQBotCredentials(params) {
|
|
4328
|
+
const existing = params.cfg.channels?.qqbot ?? {};
|
|
4329
|
+
if (params.accountId === DEFAULT_ACCOUNT_ID) {
|
|
4330
|
+
return {
|
|
4331
|
+
...params.cfg,
|
|
4332
|
+
channels: {
|
|
4333
|
+
...params.cfg.channels,
|
|
4334
|
+
qqbot: {
|
|
4335
|
+
...existing,
|
|
4336
|
+
enabled: true,
|
|
4337
|
+
appId: params.appId,
|
|
4338
|
+
clientSecret: params.clientSecret
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
};
|
|
4342
|
+
}
|
|
4343
|
+
const accounts = existing.accounts ?? {};
|
|
4344
|
+
return {
|
|
4345
|
+
...params.cfg,
|
|
4346
|
+
channels: {
|
|
4347
|
+
...params.cfg.channels,
|
|
4348
|
+
qqbot: {
|
|
4349
|
+
...existing,
|
|
4350
|
+
enabled: true,
|
|
4351
|
+
accounts: {
|
|
4352
|
+
...accounts,
|
|
4353
|
+
[params.accountId]: {
|
|
4354
|
+
...accounts[params.accountId],
|
|
4355
|
+
enabled: true,
|
|
4356
|
+
appId: params.appId,
|
|
4357
|
+
clientSecret: params.clientSecret
|
|
4358
|
+
}
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
};
|
|
4363
|
+
}
|
|
4364
|
+
async function noteQQBotCredentialHelp(prompter) {
|
|
4365
|
+
await prompter.note(
|
|
4366
|
+
[
|
|
4367
|
+
"1) \u6253\u5F00 QQ \u5F00\u653E\u5E73\u53F0 (https://q.qq.com/)",
|
|
4368
|
+
"2) \u521B\u5EFA\u673A\u5668\u4EBA\u5E94\u7528\uFF0C\u83B7\u53D6 AppID \u548C ClientSecret",
|
|
4369
|
+
"3) \u5728\u5F00\u53D1\u8BBE\u7F6E\u4E2D\u914D\u7F6E\u6C99\u7BB1\u6210\u5458\u6216\u6D4B\u8BD5\u7FA4",
|
|
4370
|
+
"4) \u914D\u7F6E\u5B8C\u6210\u540E\u53EF\u4F7F\u7528 openclaw gateway \u542F\u52A8\u8FDE\u63A5",
|
|
4371
|
+
"",
|
|
4372
|
+
'\u547D\u4EE4\u884C\u4E5F\u652F\u6301\uFF1Aopenclaw channels add --channel qqbot --token "AppID:ClientSecret"'
|
|
4373
|
+
].join("\n"),
|
|
4374
|
+
"QQ Bot \u914D\u7F6E"
|
|
4375
|
+
);
|
|
4376
|
+
}
|
|
4377
|
+
function resolveOnboardingAccountId(params) {
|
|
4378
|
+
const override = params.accountOverrides?.qqbot?.trim();
|
|
4379
|
+
if (override) return override;
|
|
4380
|
+
const defaultAccountId = resolveDefaultQQBotAccountId(params.cfg);
|
|
4381
|
+
const accountIds = listQQBotAccountIds(params.cfg);
|
|
4382
|
+
if (!params.shouldPromptAccountIds || accountIds.length <= 1) {
|
|
4383
|
+
return defaultAccountId;
|
|
4384
|
+
}
|
|
4385
|
+
return params.prompter.select({
|
|
4386
|
+
message: "\u9009\u62E9\u8981\u914D\u7F6E\u7684 QQ Bot \u8D26\u6237",
|
|
4387
|
+
options: accountIds.map((accountId) => ({
|
|
4388
|
+
value: accountId,
|
|
4389
|
+
label: accountId === DEFAULT_ACCOUNT_ID ? "\u9ED8\u8BA4\u8D26\u6237" : accountId
|
|
4390
|
+
})),
|
|
4391
|
+
initialValue: defaultAccountId
|
|
4392
|
+
}).then((selected) => isPromptCancelled(selected) ? defaultAccountId : selected);
|
|
4393
|
+
}
|
|
4394
|
+
var qqbotOnboardingAdapter = {
|
|
4395
|
+
channel: "qqbot",
|
|
4396
|
+
getStatus: async (params) => {
|
|
4397
|
+
const accountIds = listQQBotAccountIds(params.cfg);
|
|
4398
|
+
const configuredAccountId = accountIds.find(
|
|
4399
|
+
(accountId) => Boolean(resolveQQBotCredentials(mergeQQBotAccountConfig(params.cfg, accountId)))
|
|
4400
|
+
);
|
|
4401
|
+
const configured = Boolean(configuredAccountId);
|
|
4402
|
+
const defaultAccountId = resolveDefaultQQBotAccountId(params.cfg);
|
|
4403
|
+
const statusLines = configured ? [
|
|
4404
|
+
configuredAccountId && configuredAccountId !== DEFAULT_ACCOUNT_ID ? `QQ Bot: \u5DF2\u914D\u7F6E (${configuredAccountId})` : `QQ Bot: \u5DF2\u914D\u7F6E${defaultAccountId !== DEFAULT_ACCOUNT_ID ? ` (default=${defaultAccountId})` : ""}`
|
|
4405
|
+
] : ["QQ Bot: \u9700\u8981 AppID \u548C ClientSecret"];
|
|
4406
|
+
return {
|
|
4407
|
+
channel: "qqbot",
|
|
4408
|
+
configured,
|
|
4409
|
+
statusLines,
|
|
4410
|
+
selectionHint: configured ? "\u5DF2\u914D\u7F6E" : "\u9700\u8981 AppID \u548C ClientSecret",
|
|
4411
|
+
quickstartScore: configured ? 2 : 0
|
|
4412
|
+
};
|
|
4413
|
+
},
|
|
4414
|
+
configure: async (params) => {
|
|
4415
|
+
const accountId = await resolveOnboardingAccountId(params);
|
|
4416
|
+
const merged = mergeQQBotAccountConfig(params.cfg, accountId);
|
|
4417
|
+
const configured = Boolean(resolveQQBotCredentials(merged));
|
|
4418
|
+
let next = params.cfg;
|
|
4419
|
+
let appId = null;
|
|
4420
|
+
let clientSecret = null;
|
|
4421
|
+
if (!configured) {
|
|
4422
|
+
await noteQQBotCredentialHelp(params.prompter);
|
|
4423
|
+
} else {
|
|
4424
|
+
const keepCurrent = await params.prompter.confirm({
|
|
4425
|
+
message: accountId === DEFAULT_ACCOUNT_ID ? "QQ Bot \u51ED\u8BC1\u5DF2\u914D\u7F6E\uFF0C\u662F\u5426\u4FDD\u7559\u5F53\u524D\u914D\u7F6E\uFF1F" : `\u8D26\u6237 ${accountId} \u7684 QQ Bot \u51ED\u8BC1\u5DF2\u914D\u7F6E\uFF0C\u662F\u5426\u4FDD\u7559\u5F53\u524D\u914D\u7F6E\uFF1F`,
|
|
4426
|
+
initialValue: true
|
|
4427
|
+
});
|
|
4428
|
+
if (keepCurrent) {
|
|
4429
|
+
return { cfg: next, accountId };
|
|
4430
|
+
}
|
|
4431
|
+
}
|
|
4432
|
+
const nextAppId = await params.prompter.text({
|
|
4433
|
+
message: "\u8BF7\u8F93\u5165 QQ Bot AppID",
|
|
4434
|
+
placeholder: "\u4F8B\u5982: 102146862",
|
|
4435
|
+
initialValue: typeof merged.appId === "string" ? merged.appId : void 0,
|
|
4436
|
+
validate: (value) => String(value ?? "").trim() ? void 0 : "AppID \u4E0D\u80FD\u4E3A\u7A7A"
|
|
4437
|
+
});
|
|
4438
|
+
if (isPromptCancelled(nextAppId)) {
|
|
4439
|
+
return { cfg: next, accountId };
|
|
4440
|
+
}
|
|
4441
|
+
appId = String(nextAppId).trim();
|
|
4442
|
+
const nextClientSecret = await params.prompter.text({
|
|
4443
|
+
message: "\u8BF7\u8F93\u5165 QQ Bot ClientSecret",
|
|
4444
|
+
placeholder: "\u4F60\u7684 ClientSecret",
|
|
4445
|
+
validate: (value) => String(value ?? "").trim() ? void 0 : "ClientSecret \u4E0D\u80FD\u4E3A\u7A7A"
|
|
4446
|
+
});
|
|
4447
|
+
if (isPromptCancelled(nextClientSecret)) {
|
|
4448
|
+
return { cfg: next, accountId };
|
|
4449
|
+
}
|
|
4450
|
+
clientSecret = String(nextClientSecret).trim();
|
|
4451
|
+
if (appId && clientSecret) {
|
|
4452
|
+
next = setQQBotCredentials({
|
|
4453
|
+
cfg: next,
|
|
4454
|
+
accountId,
|
|
4455
|
+
appId,
|
|
4456
|
+
clientSecret
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4459
|
+
return { cfg: next, accountId };
|
|
4460
|
+
},
|
|
4461
|
+
disable: (cfg) => ({
|
|
4462
|
+
...cfg,
|
|
4463
|
+
channels: {
|
|
4464
|
+
...cfg.channels,
|
|
4465
|
+
qqbot: {
|
|
4466
|
+
...cfg.channels?.qqbot ?? {},
|
|
4467
|
+
enabled: false
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
})
|
|
4471
|
+
};
|
|
4472
|
+
|
|
4319
4473
|
// ../../packages/shared/src/logger/logger.ts
|
|
4320
4474
|
function createLogger(prefix, opts) {
|
|
4321
4475
|
const logFn = opts?.log ?? console.log;
|
|
@@ -4719,7 +4873,7 @@ function extractMediaFromText(text, options = {}) {
|
|
|
4719
4873
|
const {
|
|
4720
4874
|
removeFromText = true,
|
|
4721
4875
|
checkExists = false,
|
|
4722
|
-
existsSync:
|
|
4876
|
+
existsSync: existsSync6,
|
|
4723
4877
|
parseMediaLines = false,
|
|
4724
4878
|
parseMarkdownImages = true,
|
|
4725
4879
|
parseHtmlImages = true,
|
|
@@ -4734,7 +4888,7 @@ function extractMediaFromText(text, options = {}) {
|
|
|
4734
4888
|
const key = media.localPath || media.source;
|
|
4735
4889
|
if (seenSources.has(key)) return false;
|
|
4736
4890
|
if (checkExists && media.isLocal && media.localPath) {
|
|
4737
|
-
const exists =
|
|
4891
|
+
const exists = existsSync6 ? existsSync6(media.localPath) : fs3.existsSync(media.localPath);
|
|
4738
4892
|
if (!exists) return false;
|
|
4739
4893
|
}
|
|
4740
4894
|
seenSources.add(key);
|
|
@@ -6529,6 +6683,13 @@ function getChannelConfig(cfg, channelId) {
|
|
|
6529
6683
|
const existing = channels[channelId];
|
|
6530
6684
|
return isRecord(existing) ? existing : {};
|
|
6531
6685
|
}
|
|
6686
|
+
function getGatewayAuthToken(cfg) {
|
|
6687
|
+
if (!isRecord(cfg.gateway)) {
|
|
6688
|
+
return void 0;
|
|
6689
|
+
}
|
|
6690
|
+
const auth = isRecord(cfg.gateway.auth) ? cfg.gateway.auth : void 0;
|
|
6691
|
+
return toTrimmedString2(auth?.token);
|
|
6692
|
+
}
|
|
6532
6693
|
function getPreferredAccountConfig(channelCfg) {
|
|
6533
6694
|
const accounts = channelCfg.accounts;
|
|
6534
6695
|
if (!isRecord(accounts)) {
|
|
@@ -6690,11 +6851,22 @@ async function configureDingtalk(prompter, cfg) {
|
|
|
6690
6851
|
"\u542F\u7528 AI Card \u6D41\u5F0F\u56DE\u590D\uFF08\u63A8\u8350\u5173\u95ED\uFF0C\u4F7F\u7528\u975E\u6D41\u5F0F\uFF09",
|
|
6691
6852
|
toBoolean(existing.enableAICard, false)
|
|
6692
6853
|
);
|
|
6693
|
-
|
|
6854
|
+
const patch = {
|
|
6694
6855
|
clientId,
|
|
6695
6856
|
clientSecret,
|
|
6696
6857
|
enableAICard
|
|
6697
|
-
}
|
|
6858
|
+
};
|
|
6859
|
+
if (enableAICard) {
|
|
6860
|
+
const gatewayToken = await prompter.askSecret({
|
|
6861
|
+
label: "OpenClaw Gateway Token\uFF08\u6D41\u5F0F\u8F93\u51FA\u5FC5\u9700\uFF1B\u7559\u7A7A\u5219\u4F7F\u7528\u5168\u5C40 gateway.auth.token\uFF09",
|
|
6862
|
+
existingValue: toTrimmedString2(existing.gatewayToken) ?? getGatewayAuthToken(cfg),
|
|
6863
|
+
required: false
|
|
6864
|
+
});
|
|
6865
|
+
if (gatewayToken.trim()) {
|
|
6866
|
+
patch.gatewayToken = gatewayToken;
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
return mergeChannelConfig(cfg, "dingtalk", patch);
|
|
6698
6870
|
}
|
|
6699
6871
|
async function configureFeishu(prompter, cfg) {
|
|
6700
6872
|
section("\u914D\u7F6E Feishu\uFF08\u98DE\u4E66\uFF09");
|
|
@@ -7904,6 +8076,179 @@ ${mediaUrl}` : mediaUrl;
|
|
|
7904
8076
|
|
|
7905
8077
|
// src/logger.ts
|
|
7906
8078
|
createLogger("qqbot");
|
|
8079
|
+
var DEFAULT_KNOWN_TARGETS_PATH = join(homedir(), ".openclaw", "data", "qqbot", "known-targets.json");
|
|
8080
|
+
function resolveKnownTargetsFilePath(options) {
|
|
8081
|
+
return options?.filePath?.trim() || DEFAULT_KNOWN_TARGETS_PATH;
|
|
8082
|
+
}
|
|
8083
|
+
function ensureKnownTargetsDir(filePath) {
|
|
8084
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
8085
|
+
}
|
|
8086
|
+
function compareTargetsByLastSeenDesc(a, b) {
|
|
8087
|
+
if (b.lastSeenAt !== a.lastSeenAt) {
|
|
8088
|
+
return b.lastSeenAt - a.lastSeenAt;
|
|
8089
|
+
}
|
|
8090
|
+
return a.target.localeCompare(b.target);
|
|
8091
|
+
}
|
|
8092
|
+
function normalizeKnownQQBotTarget(target) {
|
|
8093
|
+
const accountId = target.accountId.trim() || DEFAULT_ACCOUNT_ID;
|
|
8094
|
+
const normalized = {
|
|
8095
|
+
accountId,
|
|
8096
|
+
kind: target.kind,
|
|
8097
|
+
target: target.target.trim(),
|
|
8098
|
+
sourceChatType: target.sourceChatType,
|
|
8099
|
+
firstSeenAt: Math.trunc(target.firstSeenAt),
|
|
8100
|
+
lastSeenAt: Math.trunc(target.lastSeenAt)
|
|
8101
|
+
};
|
|
8102
|
+
const displayName = target.displayName?.trim();
|
|
8103
|
+
if (displayName) {
|
|
8104
|
+
normalized.displayName = displayName;
|
|
8105
|
+
}
|
|
8106
|
+
return normalized;
|
|
8107
|
+
}
|
|
8108
|
+
function parseKnownTargets(raw, filePath) {
|
|
8109
|
+
const parsed = JSON.parse(raw);
|
|
8110
|
+
if (!Array.isArray(parsed)) {
|
|
8111
|
+
throw new Error(`Invalid known QQBot targets file: ${filePath}`);
|
|
8112
|
+
}
|
|
8113
|
+
return parsed.filter((entry) => {
|
|
8114
|
+
if (!entry || typeof entry !== "object") return false;
|
|
8115
|
+
const candidate = entry;
|
|
8116
|
+
return typeof candidate.accountId === "string" && typeof candidate.kind === "string" && typeof candidate.target === "string" && typeof candidate.sourceChatType === "string" && typeof candidate.firstSeenAt === "number" && typeof candidate.lastSeenAt === "number";
|
|
8117
|
+
}).map((entry) => normalizeKnownQQBotTarget(entry)).filter((entry) => entry.target.length > 0);
|
|
8118
|
+
}
|
|
8119
|
+
function readKnownTargets(options) {
|
|
8120
|
+
const filePath = resolveKnownTargetsFilePath(options);
|
|
8121
|
+
if (!existsSync(filePath)) {
|
|
8122
|
+
return [];
|
|
8123
|
+
}
|
|
8124
|
+
const raw = readFileSync(filePath, "utf8");
|
|
8125
|
+
if (!raw.trim()) {
|
|
8126
|
+
return [];
|
|
8127
|
+
}
|
|
8128
|
+
return parseKnownTargets(raw, filePath).sort(compareTargetsByLastSeenDesc);
|
|
8129
|
+
}
|
|
8130
|
+
function writeKnownTargets(targets, options) {
|
|
8131
|
+
const filePath = resolveKnownTargetsFilePath(options);
|
|
8132
|
+
if (targets.length === 0) {
|
|
8133
|
+
if (existsSync(filePath)) {
|
|
8134
|
+
rmSync(filePath, { force: true });
|
|
8135
|
+
}
|
|
8136
|
+
return;
|
|
8137
|
+
}
|
|
8138
|
+
ensureKnownTargetsDir(filePath);
|
|
8139
|
+
writeFileSync(filePath, `${JSON.stringify(targets, null, 2)}
|
|
8140
|
+
`, "utf8");
|
|
8141
|
+
}
|
|
8142
|
+
function upsertKnownQQBotTarget(params) {
|
|
8143
|
+
const next = normalizeKnownQQBotTarget(params.target);
|
|
8144
|
+
if (!next.target) {
|
|
8145
|
+
throw new Error("Known QQBot target requires a non-empty target");
|
|
8146
|
+
}
|
|
8147
|
+
const targets = readKnownTargets(params);
|
|
8148
|
+
const index = targets.findIndex(
|
|
8149
|
+
(entry) => entry.accountId === next.accountId && entry.target === next.target
|
|
8150
|
+
);
|
|
8151
|
+
if (index >= 0) {
|
|
8152
|
+
const existing = targets[index];
|
|
8153
|
+
targets[index] = {
|
|
8154
|
+
...existing,
|
|
8155
|
+
kind: next.kind,
|
|
8156
|
+
sourceChatType: next.sourceChatType,
|
|
8157
|
+
displayName: next.displayName ?? existing.displayName,
|
|
8158
|
+
lastSeenAt: next.lastSeenAt
|
|
8159
|
+
};
|
|
8160
|
+
} else {
|
|
8161
|
+
targets.push(next);
|
|
8162
|
+
}
|
|
8163
|
+
targets.sort(compareTargetsByLastSeenDesc);
|
|
8164
|
+
writeKnownTargets(targets, params);
|
|
8165
|
+
return index >= 0 ? targets.find((entry) => entry.accountId === next.accountId && entry.target === next.target) : next;
|
|
8166
|
+
}
|
|
8167
|
+
function listKnownQQBotTargets(params = {}) {
|
|
8168
|
+
let targets = readKnownTargets(params);
|
|
8169
|
+
if (params.accountId?.trim()) {
|
|
8170
|
+
targets = targets.filter((entry) => entry.accountId === params.accountId?.trim());
|
|
8171
|
+
}
|
|
8172
|
+
if (params.kind) {
|
|
8173
|
+
targets = targets.filter((entry) => entry.kind === params.kind);
|
|
8174
|
+
}
|
|
8175
|
+
if (typeof params.limit === "number" && params.limit > 0) {
|
|
8176
|
+
targets = targets.slice(0, params.limit);
|
|
8177
|
+
}
|
|
8178
|
+
return targets;
|
|
8179
|
+
}
|
|
8180
|
+
function getKnownQQBotTarget(params) {
|
|
8181
|
+
const target = params.target.trim();
|
|
8182
|
+
if (!target) return void 0;
|
|
8183
|
+
const matches = readKnownTargets(params).filter((entry) => {
|
|
8184
|
+
if (entry.target !== target) return false;
|
|
8185
|
+
if (params.accountId?.trim()) {
|
|
8186
|
+
return entry.accountId === params.accountId.trim();
|
|
8187
|
+
}
|
|
8188
|
+
return true;
|
|
8189
|
+
});
|
|
8190
|
+
return matches[0];
|
|
8191
|
+
}
|
|
8192
|
+
function removeKnownQQBotTarget(params) {
|
|
8193
|
+
const target = params.target.trim();
|
|
8194
|
+
if (!target) return false;
|
|
8195
|
+
const before = readKnownTargets(params);
|
|
8196
|
+
const filtered = before.filter((entry) => {
|
|
8197
|
+
if (entry.target !== target) return true;
|
|
8198
|
+
if (params.accountId?.trim()) {
|
|
8199
|
+
return entry.accountId !== params.accountId.trim();
|
|
8200
|
+
}
|
|
8201
|
+
return false;
|
|
8202
|
+
});
|
|
8203
|
+
if (filtered.length === before.length) {
|
|
8204
|
+
return false;
|
|
8205
|
+
}
|
|
8206
|
+
writeKnownTargets(filtered, params);
|
|
8207
|
+
return true;
|
|
8208
|
+
}
|
|
8209
|
+
function clearKnownQQBotTargets(params = {}) {
|
|
8210
|
+
const before = readKnownTargets(params);
|
|
8211
|
+
const filtered = before.filter((entry) => {
|
|
8212
|
+
if (params.accountId?.trim() && entry.accountId !== params.accountId.trim()) {
|
|
8213
|
+
return true;
|
|
8214
|
+
}
|
|
8215
|
+
if (params.kind && entry.kind !== params.kind) {
|
|
8216
|
+
return true;
|
|
8217
|
+
}
|
|
8218
|
+
return false;
|
|
8219
|
+
});
|
|
8220
|
+
const removed = before.length - filtered.length;
|
|
8221
|
+
if (removed === 0) {
|
|
8222
|
+
return 0;
|
|
8223
|
+
}
|
|
8224
|
+
writeKnownTargets(filtered, params);
|
|
8225
|
+
return removed;
|
|
8226
|
+
}
|
|
8227
|
+
async function sendProactiveQQBotMessage(params) {
|
|
8228
|
+
const to = params.to.trim();
|
|
8229
|
+
if (!to) {
|
|
8230
|
+
return { channel: "qqbot", error: "to is required for proactive send" };
|
|
8231
|
+
}
|
|
8232
|
+
if (params.mediaUrl?.trim()) {
|
|
8233
|
+
return qqbotOutbound.sendMedia({
|
|
8234
|
+
cfg: params.cfg,
|
|
8235
|
+
to,
|
|
8236
|
+
mediaUrl: params.mediaUrl.trim(),
|
|
8237
|
+
text: params.text,
|
|
8238
|
+
accountId: params.accountId
|
|
8239
|
+
});
|
|
8240
|
+
}
|
|
8241
|
+
const text = params.text?.trim();
|
|
8242
|
+
if (!text) {
|
|
8243
|
+
return { channel: "qqbot", error: "text or mediaUrl is required for proactive send" };
|
|
8244
|
+
}
|
|
8245
|
+
return qqbotOutbound.sendText({
|
|
8246
|
+
cfg: params.cfg,
|
|
8247
|
+
to,
|
|
8248
|
+
text,
|
|
8249
|
+
accountId: params.accountId
|
|
8250
|
+
});
|
|
8251
|
+
}
|
|
7907
8252
|
|
|
7908
8253
|
// src/runtime.ts
|
|
7909
8254
|
var runtime = null;
|
|
@@ -8341,6 +8686,46 @@ function resolveEnvelopeFrom(event) {
|
|
|
8341
8686
|
}
|
|
8342
8687
|
return event.senderName?.trim() || event.senderId;
|
|
8343
8688
|
}
|
|
8689
|
+
function resolveKnownQQBotTargetFromInbound(params) {
|
|
8690
|
+
const { inbound, accountId } = params;
|
|
8691
|
+
if (inbound.type === "direct") {
|
|
8692
|
+
if (!inbound.c2cOpenid?.trim()) {
|
|
8693
|
+
return void 0;
|
|
8694
|
+
}
|
|
8695
|
+
return {
|
|
8696
|
+
accountId,
|
|
8697
|
+
kind: "user",
|
|
8698
|
+
target: `user:${inbound.c2cOpenid}`,
|
|
8699
|
+
displayName: inbound.senderName,
|
|
8700
|
+
sourceChatType: "direct",
|
|
8701
|
+
firstSeenAt: inbound.timestamp,
|
|
8702
|
+
lastSeenAt: inbound.timestamp
|
|
8703
|
+
};
|
|
8704
|
+
}
|
|
8705
|
+
if (inbound.type === "group" && inbound.groupOpenid?.trim()) {
|
|
8706
|
+
return {
|
|
8707
|
+
accountId,
|
|
8708
|
+
kind: "group",
|
|
8709
|
+
target: `group:${inbound.groupOpenid}`,
|
|
8710
|
+
displayName: inbound.senderName,
|
|
8711
|
+
sourceChatType: "group",
|
|
8712
|
+
firstSeenAt: inbound.timestamp,
|
|
8713
|
+
lastSeenAt: inbound.timestamp
|
|
8714
|
+
};
|
|
8715
|
+
}
|
|
8716
|
+
if (inbound.type === "channel" && inbound.channelId?.trim()) {
|
|
8717
|
+
return {
|
|
8718
|
+
accountId,
|
|
8719
|
+
kind: "channel",
|
|
8720
|
+
target: `channel:${inbound.channelId}`,
|
|
8721
|
+
displayName: inbound.senderName,
|
|
8722
|
+
sourceChatType: "channel",
|
|
8723
|
+
firstSeenAt: inbound.timestamp,
|
|
8724
|
+
lastSeenAt: inbound.timestamp
|
|
8725
|
+
};
|
|
8726
|
+
}
|
|
8727
|
+
return void 0;
|
|
8728
|
+
}
|
|
8344
8729
|
function extractLocalMediaFromText(params) {
|
|
8345
8730
|
const { text, logger } = params;
|
|
8346
8731
|
const result = extractMediaFromText(text, {
|
|
@@ -8383,6 +8768,23 @@ function extractMediaLinesFromText(params) {
|
|
|
8383
8768
|
const mediaUrls = result.all.map((m) => m.isLocal ? m.localPath ?? m.source : m.source).filter((m) => typeof m === "string" && m.trim().length > 0);
|
|
8384
8769
|
return { text: result.text, mediaUrls };
|
|
8385
8770
|
}
|
|
8771
|
+
function extractQQBotReplyMedia(params) {
|
|
8772
|
+
const mediaLineResult = extractMediaLinesFromText({
|
|
8773
|
+
text: params.text,
|
|
8774
|
+
logger: params.logger
|
|
8775
|
+
});
|
|
8776
|
+
if (!params.autoSendLocalPathMedia) {
|
|
8777
|
+
return mediaLineResult;
|
|
8778
|
+
}
|
|
8779
|
+
const localMediaResult = extractLocalMediaFromText({
|
|
8780
|
+
text: mediaLineResult.text,
|
|
8781
|
+
logger: params.logger
|
|
8782
|
+
});
|
|
8783
|
+
return {
|
|
8784
|
+
text: localMediaResult.text,
|
|
8785
|
+
mediaUrls: [.../* @__PURE__ */ new Set([...mediaLineResult.mediaUrls, ...localMediaResult.mediaUrls])]
|
|
8786
|
+
};
|
|
8787
|
+
}
|
|
8386
8788
|
function buildMediaFallbackText(mediaUrl) {
|
|
8387
8789
|
if (!/^https?:\/\//i.test(mediaUrl)) {
|
|
8388
8790
|
return void 0;
|
|
@@ -8716,15 +9118,12 @@ async function dispatchToAgent(params) {
|
|
|
8716
9118
|
const replyFinalOnly = qqCfg.replyFinalOnly ?? false;
|
|
8717
9119
|
const deliver = async (payload, info) => {
|
|
8718
9120
|
const typed = payload;
|
|
8719
|
-
const
|
|
9121
|
+
const extractedTextMedia = extractQQBotReplyMedia({
|
|
8720
9122
|
text: typed?.text ?? "",
|
|
8721
|
-
logger
|
|
8722
|
-
|
|
8723
|
-
const localMediaResult = extractLocalMediaFromText({
|
|
8724
|
-
text: mediaLineResult.text,
|
|
8725
|
-
logger
|
|
9123
|
+
logger,
|
|
9124
|
+
autoSendLocalPathMedia: resolveQQBotAutoSendLocalPathMedia(qqCfg)
|
|
8726
9125
|
});
|
|
8727
|
-
const cleanedText = sanitizeQQBotOutboundText(
|
|
9126
|
+
const cleanedText = sanitizeQQBotOutboundText(extractedTextMedia.text);
|
|
8728
9127
|
const payloadMediaUrls = Array.isArray(typed?.mediaUrls) ? typed?.mediaUrls : typed?.mediaUrl ? [typed.mediaUrl] : [];
|
|
8729
9128
|
const mediaQueue = [];
|
|
8730
9129
|
const seenMedia = /* @__PURE__ */ new Set();
|
|
@@ -8736,8 +9135,7 @@ async function dispatchToAgent(params) {
|
|
|
8736
9135
|
mediaQueue.push(next);
|
|
8737
9136
|
};
|
|
8738
9137
|
for (const url of payloadMediaUrls) addMedia(url);
|
|
8739
|
-
for (const url of
|
|
8740
|
-
for (const url of localMediaResult.mediaUrls) addMedia(url);
|
|
9138
|
+
for (const url of extractedTextMedia.mediaUrls) addMedia(url);
|
|
8741
9139
|
const deliveryDecision = evaluateReplyFinalOnlyDelivery({
|
|
8742
9140
|
replyFinalOnly,
|
|
8743
9141
|
kind: info?.kind,
|
|
@@ -8745,7 +9143,7 @@ async function dispatchToAgent(params) {
|
|
|
8745
9143
|
sanitizedText: cleanedText
|
|
8746
9144
|
});
|
|
8747
9145
|
if (deliveryDecision.skipDelivery) return;
|
|
8748
|
-
const suppressEchoText = mediaQueue.length > 0 && shouldSuppressQQBotTextWhenMediaPresent(
|
|
9146
|
+
const suppressEchoText = mediaQueue.length > 0 && shouldSuppressQQBotTextWhenMediaPresent(extractedTextMedia.text, cleanedText);
|
|
8749
9147
|
const suppressText = deliveryDecision.suppressText || suppressEchoText;
|
|
8750
9148
|
const textToSend = suppressText ? "" : cleanedText;
|
|
8751
9149
|
if (textToSend) {
|
|
@@ -8919,6 +9317,14 @@ async function handleQQBotDispatch(params) {
|
|
|
8919
9317
|
if (!shouldHandleMessage(inbound, qqCfg, logger)) {
|
|
8920
9318
|
return;
|
|
8921
9319
|
}
|
|
9320
|
+
const knownTarget = resolveKnownQQBotTargetFromInbound({ inbound, accountId });
|
|
9321
|
+
if (knownTarget) {
|
|
9322
|
+
try {
|
|
9323
|
+
upsertKnownQQBotTarget({ target: knownTarget });
|
|
9324
|
+
} catch (err) {
|
|
9325
|
+
logger.warn(`failed to record known qqbot target: ${String(err)}`);
|
|
9326
|
+
}
|
|
9327
|
+
}
|
|
8922
9328
|
const attachmentCount = inbound.attachments?.length ?? 0;
|
|
8923
9329
|
if (attachmentCount > 0) {
|
|
8924
9330
|
logger.info(`inbound message includes ${attachmentCount} attachment(s)`);
|
|
@@ -9248,7 +9654,8 @@ var qqbotPlugin = {
|
|
|
9248
9654
|
edit: false,
|
|
9249
9655
|
reply: true,
|
|
9250
9656
|
polls: false,
|
|
9251
|
-
blockStreaming: false
|
|
9657
|
+
blockStreaming: false,
|
|
9658
|
+
activeSend: true
|
|
9252
9659
|
},
|
|
9253
9660
|
messaging: {
|
|
9254
9661
|
normalizeTarget: (raw) => {
|
|
@@ -9331,6 +9738,7 @@ var qqbotPlugin = {
|
|
|
9331
9738
|
longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
|
|
9332
9739
|
maxFileSizeMB: { type: "number" },
|
|
9333
9740
|
mediaTimeoutMs: { type: "number" },
|
|
9741
|
+
autoSendLocalPathMedia: { type: "boolean" },
|
|
9334
9742
|
inboundMedia: {
|
|
9335
9743
|
type: "object",
|
|
9336
9744
|
additionalProperties: false,
|
|
@@ -9371,6 +9779,7 @@ var qqbotPlugin = {
|
|
|
9371
9779
|
longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
|
|
9372
9780
|
maxFileSizeMB: { type: "number" },
|
|
9373
9781
|
mediaTimeoutMs: { type: "number" },
|
|
9782
|
+
autoSendLocalPathMedia: { type: "boolean" },
|
|
9374
9783
|
inboundMedia: {
|
|
9375
9784
|
type: "object",
|
|
9376
9785
|
additionalProperties: false,
|
|
@@ -9386,6 +9795,7 @@ var qqbotPlugin = {
|
|
|
9386
9795
|
}
|
|
9387
9796
|
},
|
|
9388
9797
|
reload: { configPrefixes: ["channels.qqbot"] },
|
|
9798
|
+
onboarding: qqbotOnboardingAdapter,
|
|
9389
9799
|
config: {
|
|
9390
9800
|
listAccountIds: (cfg) => listQQBotAccountIds(cfg),
|
|
9391
9801
|
resolveAccount: (cfg, accountId) => resolveQQBotAccount({ cfg, accountId }),
|
|
@@ -9567,6 +9977,7 @@ var plugin = {
|
|
|
9567
9977
|
longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
|
|
9568
9978
|
maxFileSizeMB: { type: "number" },
|
|
9569
9979
|
mediaTimeoutMs: { type: "number" },
|
|
9980
|
+
autoSendLocalPathMedia: { type: "boolean" },
|
|
9570
9981
|
inboundMedia: {
|
|
9571
9982
|
type: "object",
|
|
9572
9983
|
additionalProperties: false,
|
|
@@ -9607,6 +10018,7 @@ var plugin = {
|
|
|
9607
10018
|
longTaskNoticeDelayMs: { type: "integer", minimum: 0 },
|
|
9608
10019
|
maxFileSizeMB: { type: "number" },
|
|
9609
10020
|
mediaTimeoutMs: { type: "number" },
|
|
10021
|
+
autoSendLocalPathMedia: { type: "boolean" },
|
|
9610
10022
|
inboundMedia: {
|
|
9611
10023
|
type: "object",
|
|
9612
10024
|
additionalProperties: false,
|
|
@@ -9631,6 +10043,6 @@ var plugin = {
|
|
|
9631
10043
|
};
|
|
9632
10044
|
var index_default = plugin;
|
|
9633
10045
|
|
|
9634
|
-
export { DEFAULT_ACCOUNT_ID, index_default as default, getQQBotRuntime, qqbotPlugin, setQQBotRuntime };
|
|
10046
|
+
export { DEFAULT_ACCOUNT_ID, clearKnownQQBotTargets, index_default as default, getKnownQQBotTarget, getQQBotRuntime, listKnownQQBotTargets, qqbotPlugin, removeKnownQQBotTarget, sendProactiveQQBotMessage, setQQBotRuntime };
|
|
9635
10047
|
//# sourceMappingURL=index.js.map
|
|
9636
10048
|
//# sourceMappingURL=index.js.map
|