@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.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: existsSync5,
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 = existsSync5 ? existsSync5(media.localPath) : fs3.existsSync(media.localPath);
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
- return mergeChannelConfig(cfg, "dingtalk", {
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 mediaLineResult = extractMediaLinesFromText({
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(localMediaResult.text);
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 mediaLineResult.mediaUrls) addMedia(url);
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(localMediaResult.text, cleanedText);
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