@jerryliang122/openclaw-qqbot 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
14
14
  [![QQ Bot](https://img.shields.io/badge/QQ_Bot-API_v2-red)](https://bot.q.qq.com/wiki/)
15
- [![Platform](https://img.shields.io/badge/OpenClaw-%3E%3D2026.9.2-orange)](https://github.com/jerryliang122/qqbot-openclaw)
15
+ [![Platform](https://img.shields.io/badge/OpenClaw-%3E%3D2026.9.1-orange)](https://github.com/jerryliang122/qqbot-openclaw)
16
16
  [![Node.js](https://img.shields.io/badge/Node.js->=18-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
17
17
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
18
18
  [![Fork](https://img.shields.io/badge/fork-enhanced-9cf)](https://github.com/jerryliang122/qqbot-openclaw)
@@ -23,7 +23,7 @@
23
23
 
24
24
  > This is an **independently maintained fork** with its own versioning (v1.x, decoupled from the upstream 2.x line). Upstream: [tencent-connect/openclaw-qqbot](https://github.com/tencent-connect/openclaw-qqbot).
25
25
  >
26
- > **Requirements**: OpenClaw `>= 2026.9.2` · Published to npm as [`@jerryliang122/openclaw-qqbot`](https://www.npmjs.com/package/@jerryliang122/openclaw-qqbot). See [CHANGELOG](CHANGELOG.md) for differences vs the old version and the upgrade guide.
26
+ > **Requirements**: OpenClaw `>= 2026.9.1` · Published to npm as [`@jerryliang122/openclaw-qqbot`](https://www.npmjs.com/package/@jerryliang122/openclaw-qqbot). See [CHANGELOG](CHANGELOG.md) for differences vs the old version and the upgrade guide.
27
27
 
28
28
  Scan to join the QQ group chat
29
29
 
@@ -179,7 +179,7 @@ Measures end-to-end latency from QQ server push to plugin response, broken down
179
179
 
180
180
  > **You**: `/bot-version`
181
181
  >
182
- > **QQBot**: 🦞 Framework: OpenClaw 2026.9.2 / 🤖 Plugin: v1.0.0 / 🌟 GitHub repo
182
+ > **QQBot**: 🦞 Framework: OpenClaw 2026.9.1 / 🤖 Plugin: v1.0.0 / 🌟 GitHub repo
183
183
 
184
184
  Shows framework version, plugin version, and a direct link to the official repository.
185
185
 
@@ -282,7 +282,7 @@ Answers "why does this group have no context" diagnostics: the push mode is chos
282
282
 
283
283
  ### Step 2 — Install / Upgrade the Plugin
284
284
 
285
- > **Note**: The unscoped npm name `openclaw-qqbot` belongs to the original upstream project — this fork publishes as the scoped package `@jerryliang122/openclaw-qqbot`. Requires OpenClaw >= 2026.9.2.
285
+ > **Note**: The unscoped npm name `openclaw-qqbot` belongs to the original upstream project — this fork publishes as the scoped package `@jerryliang122/openclaw-qqbot`. Requires OpenClaw >= 2026.9.1.
286
286
 
287
287
  **Option A: Install from npm (Recommended)**
288
288
 
package/README.zh.md CHANGED
@@ -10,14 +10,14 @@
10
10
  **让你的 AI 助手接入 QQ — 私聊、群聊、富媒体,一个插件全搞定。**
11
11
 
12
12
  > 本仓库为**独立维护的 fork**,自 v1.0.0 起独立发版(与上游 2.x 版本线完全脱钩)。
13
- > 运行要求:OpenClaw `>= 2026.9.2`;npm 包名 [`@jerryliang122/openclaw-qqbot`](https://www.npmjs.com/package/@jerryliang122/openclaw-qqbot)。
13
+ > 运行要求:OpenClaw `>= 2026.9.1`;npm 包名 [`@jerryliang122/openclaw-qqbot`](https://www.npmjs.com/package/@jerryliang122/openclaw-qqbot)。
14
14
  > 与旧版本的差异及升级指南见 [CHANGELOG](CHANGELOG.md)。上游仓库:[tencent-connect/openclaw-qqbot](https://github.com/tencent-connect/openclaw-qqbot)。
15
15
 
16
16
  ### 🚀 当前版本: `v1.0.0`
17
17
 
18
18
  [![License](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
19
19
  [![QQ Bot](https://img.shields.io/badge/QQ_Bot-API_v2-red)](https://bot.q.qq.com/wiki/)
20
- [![Platform](https://img.shields.io/badge/OpenClaw-%3E%3D2026.9.2-orange)](https://github.com/jerryliang122/qqbot-openclaw)
20
+ [![Platform](https://img.shields.io/badge/OpenClaw-%3E%3D2026.9.1-orange)](https://github.com/jerryliang122/qqbot-openclaw)
21
21
  [![Node.js](https://img.shields.io/badge/Node.js->=18-339933?logo=node.js&logoColor=white)](https://nodejs.org/)
22
22
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
23
23
 
@@ -177,7 +177,7 @@ AI 可直接发送视频,支持本地文件和公网 URL。
177
177
 
178
178
  > **你**:`/bot-version`
179
179
  >
180
- > **QQBot**:🦞框架版本:OpenClaw 2026.9.2 / 🤖QQBot 插件版本:v1.0.0 / 🌟GitHub 仓库
180
+ > **QQBot**:🦞框架版本:OpenClaw 2026.9.1 / 🤖QQBot 插件版本:v1.0.0 / 🌟GitHub 仓库
181
181
 
182
182
  一目了然查看框架版本、插件版本,并可直接跳转官方仓库。
183
183
 
@@ -282,7 +282,7 @@ AI 可直接发送视频,支持本地文件和公网 URL。
282
282
 
283
283
  ### 第二步 — 安装 / 升级插件
284
284
 
285
- > 无 scope 的 npm 包名 `openclaw-qqbot` 属于上游原项目——本 fork 以 `@jerryliang122/openclaw-qqbot` 发布。要求 OpenClaw >= 2026.9.2。
285
+ > 无 scope 的 npm 包名 `openclaw-qqbot` 属于上游原项目——本 fork 以 `@jerryliang122/openclaw-qqbot` 发布。要求 OpenClaw >= 2026.9.1。
286
286
 
287
287
  **方式 A:从 npm 安装(推荐)**
288
288
 
package/dist/index.cjs CHANGED
@@ -8259,7 +8259,7 @@ var init_outbound_service = __esm({
8259
8259
 
8260
8260
  // src/utils/pkg-version.ts
8261
8261
  function getPackageVersion() {
8262
- return true ? "1.0.7" : "unknown";
8262
+ return true ? "1.0.9" : "unknown";
8263
8263
  }
8264
8264
  function getOpenclawVersion(runtimeVersion) {
8265
8265
  return runtimeVersion && runtimeVersion !== "unknown" ? runtimeVersion : "unknown";
@@ -10454,7 +10454,7 @@ function getAdapters(rt, log5) {
10454
10454
  async function persistAuthConfig(runtime2, cfg, afterWrite = "restart") {
10455
10455
  const config = runtime2.config;
10456
10456
  if (typeof config?.mutateConfigFile !== "function") {
10457
- throw new Error("persistAuthConfig: runtime.config.mutateConfigFile is unavailable (requires openclaw >= 2026.9.2)");
10457
+ throw new Error("persistAuthConfig: runtime.config.mutateConfigFile is unavailable (requires openclaw >= 2026.9.1)");
10458
10458
  }
10459
10459
  await config.mutateConfigFile({
10460
10460
  mutate: () => cfg,
@@ -15131,7 +15131,7 @@ async function dispatchToOpenClaw(ctx, msg, account, runtime2, log5) {
15131
15131
  const envelope = buildEnvelope2(ctx, msg, account);
15132
15132
  dlog?.debug(`received sender=${envelope.senderId} scope=${envelope.chatScope} msgId=${envelope.messageId}`);
15133
15133
  if (!adapters.inboundRun || !adapters.dispatchReply) {
15134
- dlog?.error(`runtime adapter inboundRun/dispatchReply not available (openclaw=${adapters.version}, requires >=2026.9.2)`);
15134
+ dlog?.error(`runtime adapter inboundRun/dispatchReply not available (openclaw=${adapters.version}, requires >=2026.9.1)`);
15135
15135
  return;
15136
15136
  }
15137
15137
  const assembled = ctx.state.assembledBody ?? assembleBody(ctx, msg, account);
@@ -17478,9 +17478,33 @@ init_runtime();
17478
17478
 
17479
17479
  // src/tools/platform.ts
17480
17480
  init_config();
17481
- init_bot_instance();
17482
- init_request_context();
17481
+ init_outbound_service();
17483
17482
  init_plugin_logger();
17483
+
17484
+ // src/tools/tool-session.ts
17485
+ init_request_context();
17486
+ function resolveToolSessionRoute(delivery) {
17487
+ const alsTarget = getRequestTarget();
17488
+ if (alsTarget) {
17489
+ return {
17490
+ target: alsTarget,
17491
+ accountId: getRequestAccountId(),
17492
+ source: "request-context"
17493
+ };
17494
+ }
17495
+ const to = typeof delivery?.to === "string" ? delivery.to.trim() : "";
17496
+ if (!to) return void 0;
17497
+ const channel = typeof delivery?.channel === "string" ? delivery.channel.trim() : "";
17498
+ if (channel && channel !== "qqbot") return void 0;
17499
+ if (!/^qqbot:(c2c|group|channel):/i.test(to)) return void 0;
17500
+ return {
17501
+ target: to,
17502
+ accountId: delivery?.accountId,
17503
+ source: "delivery-context"
17504
+ };
17505
+ }
17506
+
17507
+ // src/tools/platform.ts
17484
17508
  var toolLog = createPluginLogger({ prefix: "[tool:platform]" });
17485
17509
  var PlatformApiSchema = {
17486
17510
  type: "object",
@@ -17526,68 +17550,76 @@ function registerPlatformTool(api) {
17526
17550
  const accountIds = listQQBotAccountIds(cfg);
17527
17551
  if (accountIds.length === 0) return;
17528
17552
  api.registerTool(
17529
- {
17530
- name: "qqbot_platform_api",
17531
- label: "QQBot Platform API Gateway",
17532
- description: "QQ \u5F00\u653E\u5E73\u53F0\u7EDF\u4E00 HTTP API \u7F51\u5173\uFF0C\u81EA\u52A8\u586B\u5145\u9274\u6743 Token\u3002\u5E38\u7528\u63A5\u53E3\u901F\u67E5\uFF1A\u3010\u9891\u9053\u3011GET /users/@me/guilds | /guilds/{guild_id}/channels | /channels/{channel_id} | \u3010\u7FA4\u3011GET /v2/groups/{group_id}/bot_state | /v2/groups/{group_id}/members/{member_id} | /v2/groups/{group_id}/info\u3002\u66F4\u591A\u63A5\u53E3\u548C\u53C2\u6570\u8BE6\u60C5\u8BF7\u9605\u8BFB qqbot-channel \u548C qqbot-group skill\u3002",
17533
- parameters: PlatformApiSchema,
17534
- async execute(_toolCallId, params) {
17535
- const p2 = params;
17536
- if (!p2.method) return json({ error: "method \u4E3A\u5FC5\u586B\u53C2\u6570" });
17537
- if (!p2.path) return json({ error: "path \u4E3A\u5FC5\u586B\u53C2\u6570" });
17538
- const method = p2.method.toUpperCase();
17539
- if (!["GET", "POST", "PUT", "PATCH", "DELETE"].includes(method)) {
17540
- return json({ error: `\u4E0D\u652F\u6301\u7684 HTTP \u65B9\u6CD5: ${method}` });
17541
- }
17542
- const pathError = validatePath(p2.path);
17543
- if (pathError) return json({ error: pathError });
17544
- const accountId = getRequestAccountId();
17545
- if (!accountId) {
17546
- return json({ error: "\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u8BF7\u6C42\u7684\u8D26\u53F7\u4FE1\u606F\uFF0C\u6B64\u5DE5\u5177\u4EC5\u652F\u6301\u5728\u6D88\u606F\u4F1A\u8BDD\u4E2D\u4F7F\u7528" });
17547
- }
17548
- try {
17549
- const bot = getBotForAccount(accountId);
17550
- const apiGateway = bot.api;
17551
- const startedAt = Date.now();
17552
- let data;
17553
- switch (method) {
17554
- case "GET":
17555
- data = await apiGateway.get(p2.path, p2.query);
17556
- break;
17557
- case "POST":
17558
- data = await apiGateway.post(p2.path, p2.body);
17559
- break;
17560
- case "PUT":
17561
- data = await apiGateway.put(p2.path, p2.body);
17562
- break;
17563
- case "PATCH":
17564
- data = await apiGateway.patch(p2.path, p2.body);
17565
- break;
17566
- case "DELETE":
17567
- data = await apiGateway.delete(p2.path);
17568
- break;
17553
+ (factoryCtx) => {
17554
+ const delivery = factoryCtx.deliveryContext;
17555
+ return {
17556
+ name: "qqbot_platform_api",
17557
+ label: "QQBot Platform API Gateway",
17558
+ description: "QQ \u5F00\u653E\u5E73\u53F0\u7EDF\u4E00 HTTP API \u7F51\u5173\uFF0C\u81EA\u52A8\u586B\u5145\u9274\u6743 Token\u3002\u5E38\u7528\u63A5\u53E3\u901F\u67E5\uFF1A\u3010\u9891\u9053\u3011GET /users/@me/guilds | /guilds/{guild_id}/channels | /channels/{channel_id} | \u3010\u7FA4\u3011GET /v2/groups/{group_id}/bot_state | /v2/groups/{group_id}/members/{member_id} | /v2/groups/{group_id}/info\u3002\u66F4\u591A\u63A5\u53E3\u548C\u53C2\u6570\u8BE6\u60C5\u8BF7\u9605\u8BFB qqbot-channel \u548C qqbot-group skill\u3002",
17559
+ parameters: PlatformApiSchema,
17560
+ async execute(_toolCallId, params) {
17561
+ const p2 = params;
17562
+ if (!p2.method) return json({ error: "method \u4E3A\u5FC5\u586B\u53C2\u6570" });
17563
+ if (!p2.path) return json({ error: "path \u4E3A\u5FC5\u586B\u53C2\u6570" });
17564
+ const method = p2.method.toUpperCase();
17565
+ if (!["GET", "POST", "PUT", "PATCH", "DELETE"].includes(method)) {
17566
+ return json({ error: `\u4E0D\u652F\u6301\u7684 HTTP \u65B9\u6CD5: ${method}` });
17567
+ }
17568
+ const pathError = validatePath(p2.path);
17569
+ if (pathError) return json({ error: pathError });
17570
+ const route = resolveToolSessionRoute(delivery);
17571
+ if (!route) {
17572
+ return json({
17573
+ error: "\u5F53\u524D\u8FD0\u884C\u6CA1\u6709 QQ \u4F1A\u8BDD\u6765\u6E90\uFF08\u53EF\u80FD\u7531\u5B9A\u65F6\u4EFB\u52A1\u6216\u6846\u67B6\u5185\u90E8\u89E6\u53D1\u7684 run\uFF09\uFF0C\u65E0\u6CD5\u786E\u5B9A\u8C03\u7528\u54EA\u4E2A QQ Bot \u8D26\u6237\u3002\u8BF7\u5728 QQ \u6D88\u606F\u4F1A\u8BDD\u4E2D\u4F7F\u7528\u672C\u5DE5\u5177\u3002"
17574
+ });
17575
+ }
17576
+ try {
17577
+ const resolvedGateway = resolveGatewayForSend(route.accountId ?? "default");
17578
+ if (!resolvedGateway) {
17579
+ return json({ error: `\u8D26\u6237 ${route.accountId ?? "default"} \u7684 gateway \u5C1A\u672A\u542F\u52A8` });
17580
+ }
17581
+ const bot = resolvedGateway.gw.bot;
17582
+ const apiGateway = bot.api;
17583
+ const startedAt = Date.now();
17584
+ let data;
17585
+ switch (method) {
17586
+ case "GET":
17587
+ data = await apiGateway.get(p2.path, p2.query);
17588
+ break;
17589
+ case "POST":
17590
+ data = await apiGateway.post(p2.path, p2.body);
17591
+ break;
17592
+ case "PUT":
17593
+ data = await apiGateway.put(p2.path, p2.body);
17594
+ break;
17595
+ case "PATCH":
17596
+ data = await apiGateway.patch(p2.path, p2.body);
17597
+ break;
17598
+ case "DELETE":
17599
+ data = await apiGateway.delete(p2.path);
17600
+ break;
17601
+ }
17602
+ toolLog.info(`${method} ${p2.path} ok (${Date.now() - startedAt}ms)`, { accountId: resolvedGateway.accountId });
17603
+ return json(data);
17604
+ } catch (err) {
17605
+ const errMsg = err instanceof Error ? err.message : String(err);
17606
+ const apiErr = err;
17607
+ toolLog.warn(`${method} ${p2.path} failed status=${apiErr.httpStatus ?? "?"} code=${apiErr.bizCode ?? "?"}: ${errMsg}`, { accountId: route.accountId ?? "default" });
17608
+ return json({
17609
+ error: errMsg,
17610
+ status: apiErr.httpStatus,
17611
+ code: apiErr.bizCode,
17612
+ path: p2.path
17613
+ });
17569
17614
  }
17570
- toolLog.info(`${method} ${p2.path} ok (${Date.now() - startedAt}ms)`, { accountId });
17571
- return json(data);
17572
- } catch (err) {
17573
- const errMsg = err instanceof Error ? err.message : String(err);
17574
- const apiErr = err;
17575
- toolLog.warn(`${method} ${p2.path} failed status=${apiErr.httpStatus ?? "?"} code=${apiErr.bizCode ?? "?"}: ${errMsg}`, { accountId });
17576
- return json({
17577
- error: errMsg,
17578
- status: apiErr.httpStatus,
17579
- code: apiErr.bizCode,
17580
- path: p2.path
17581
- });
17582
17615
  }
17583
- }
17616
+ };
17584
17617
  },
17585
17618
  { name: "qqbot_platform_api" }
17586
17619
  );
17587
17620
  }
17588
17621
 
17589
17622
  // src/tools/remind.ts
17590
- init_request_context();
17591
17623
  var RemindSchema = {
17592
17624
  type: "object",
17593
17625
  properties: {
@@ -17732,71 +17764,76 @@ function formatDelay(ms) {
17732
17764
  }
17733
17765
  function registerRemindTool(api) {
17734
17766
  api.registerTool(
17735
- {
17736
- name: "qqbot_remind",
17737
- label: "QQBot \u5B9A\u65F6\u63D0\u9192",
17738
- description: '\u521B\u5EFA\u3001\u67E5\u8BE2\u3001\u5220\u9664 QQ \u5B9A\u65F6\u63D0\u9192\u3002\u4F7F\u7528\u7B80\u5355\u53C2\u6570\u5373\u53EF\uFF0C\u65E0\u9700\u624B\u52A8\u6784\u9020 cron JSON\u3002\n\u521B\u5EFA\u63D0\u9192\uFF1Aaction=add, content=\u63D0\u9192\u5185\u5BB9, to=\u76EE\u6807\u5730\u5740, time=\u65F6\u95F4\n\u67E5\u770B\u63D0\u9192\uFF1Aaction=list\n\u5220\u9664\u63D0\u9192\uFF1Aaction=remove, jobId=\u4EFB\u52A1ID\uFF08\u5148 list \u83B7\u53D6\uFF09\n\u65F6\u95F4\u683C\u5F0F\u793A\u4F8B\uFF1A"5m"(5\u5206\u949F\u540E) "1h"(1\u5C0F\u65F6\u540E) "0 8 * * *"(\u6BCF\u59298\u70B9)',
17739
- parameters: RemindSchema,
17740
- execute(_toolCallId, params) {
17741
- const p2 = params;
17742
- if (p2.action === "list") {
17743
- return json2({
17744
- _instruction: "\u8BF7\u7ACB\u5373\u4F7F\u7528 cron \u5DE5\u5177\u6267\u884C\u4EE5\u4E0B\u53C2\u6570",
17745
- cronParams: { action: "list" }
17746
- });
17747
- }
17748
- if (p2.action === "remove") {
17749
- if (!p2.jobId) {
17750
- return json2({ error: "action=remove \u65F6 jobId \u4E3A\u5FC5\u586B\u53C2\u6570\u3002\u8BF7\u5148\u7528 action=list \u83B7\u53D6 jobId\u3002" });
17767
+ (factoryCtx) => {
17768
+ const delivery = factoryCtx.deliveryContext;
17769
+ return {
17770
+ name: "qqbot_remind",
17771
+ label: "QQBot \u5B9A\u65F6\u63D0\u9192",
17772
+ description: '\u521B\u5EFA\u3001\u67E5\u8BE2\u3001\u5220\u9664 QQ \u5B9A\u65F6\u63D0\u9192\u3002\u4F7F\u7528\u7B80\u5355\u53C2\u6570\u5373\u53EF\uFF0C\u65E0\u9700\u624B\u52A8\u6784\u9020 cron JSON\u3002\n\u521B\u5EFA\u63D0\u9192\uFF1Aaction=add, content=\u63D0\u9192\u5185\u5BB9, to=\u76EE\u6807\u5730\u5740, time=\u65F6\u95F4\n\u67E5\u770B\u63D0\u9192\uFF1Aaction=list\n\u5220\u9664\u63D0\u9192\uFF1Aaction=remove, jobId=\u4EFB\u52A1ID\uFF08\u5148 list \u83B7\u53D6\uFF09\n\u65F6\u95F4\u683C\u5F0F\u793A\u4F8B\uFF1A"5m"(5\u5206\u949F\u540E) "1h"(1\u5C0F\u65F6\u540E) "0 8 * * *"(\u6BCF\u59298\u70B9)',
17773
+ parameters: RemindSchema,
17774
+ async execute(_toolCallId, params) {
17775
+ const p2 = params;
17776
+ if (p2.action === "list") {
17777
+ return json2({
17778
+ _instruction: "\u8BF7\u7ACB\u5373\u4F7F\u7528 cron \u5DE5\u5177\u6267\u884C\u4EE5\u4E0B\u53C2\u6570",
17779
+ cronParams: { action: "list" }
17780
+ });
17751
17781
  }
17752
- return json2({
17753
- _instruction: "\u8BF7\u7ACB\u5373\u4F7F\u7528 cron \u5DE5\u5177\u6267\u884C\u4EE5\u4E0B\u53C2\u6570",
17754
- cronParams: { action: "remove", jobId: p2.jobId }
17755
- });
17756
- }
17757
- if (!p2.content) {
17758
- return json2({ error: "action=add \u65F6 content\uFF08\u63D0\u9192\u5185\u5BB9\uFF09\u4E3A\u5FC5\u586B\u53C2\u6570" });
17759
- }
17760
- const resolvedTo = p2.to || getRequestTarget();
17761
- if (!resolvedTo) {
17762
- return json2({ error: "action=add \u65F6\u65E0\u6CD5\u786E\u5B9A\u6295\u9012\u76EE\u6807\u5730\u5740\u3002\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" });
17763
- }
17764
- const resolvedAccountId = getRequestAccountId() || "default";
17765
- if (!p2.time) {
17766
- return json2({ error: 'action=add \u65F6 time\uFF08\u65F6\u95F4\uFF09\u4E3A\u5FC5\u586B\u53C2\u6570\u3002\u793A\u4F8B\uFF1A"5m"\u3001"1h30m"\u3001"0 8 * * *"' });
17767
- }
17768
- if (isCronExpression(p2.time)) {
17769
- const cronJob = buildCronJob(p2, resolvedTo, resolvedAccountId);
17782
+ if (p2.action === "remove") {
17783
+ if (!p2.jobId) {
17784
+ return json2({ error: "action=remove \u65F6 jobId \u4E3A\u5FC5\u586B\u53C2\u6570\u3002\u8BF7\u5148\u7528 action=list \u83B7\u53D6 jobId\u3002" });
17785
+ }
17786
+ return json2({
17787
+ _instruction: "\u8BF7\u7ACB\u5373\u4F7F\u7528 cron \u5DE5\u5177\u6267\u884C\u4EE5\u4E0B\u53C2\u6570",
17788
+ cronParams: { action: "remove", jobId: p2.jobId }
17789
+ });
17790
+ }
17791
+ if (!p2.content) {
17792
+ return json2({ error: "action=add \u65F6 content\uFF08\u63D0\u9192\u5185\u5BB9\uFF09\u4E3A\u5FC5\u586B\u53C2\u6570" });
17793
+ }
17794
+ const route = resolveToolSessionRoute(delivery);
17795
+ const resolvedTo = p2.to || route?.target;
17796
+ if (!resolvedTo) {
17797
+ return json2({
17798
+ error: "action=add \u65F6\u65E0\u6CD5\u786E\u5B9A\u6295\u9012\u76EE\u6807\u5730\u5740\uFF08\u5F53\u524D run \u65E0 QQ \u4F1A\u8BDD\u6765\u6E90\uFF09\u3002\u8BF7\u663E\u5F0F\u4F20\u5165 to\uFF08\u683C\u5F0F\uFF1Aqqbot:c2c:user_openid \u6216 qqbot:group:group_openid\uFF09\u540E\u91CD\u8BD5\u3002"
17799
+ });
17800
+ }
17801
+ const resolvedAccountId = route?.accountId || "default";
17802
+ if (!p2.time) {
17803
+ return json2({ error: 'action=add \u65F6 time\uFF08\u65F6\u95F4\uFF09\u4E3A\u5FC5\u586B\u53C2\u6570\u3002\u793A\u4F8B\uFF1A"5m"\u3001"1h30m"\u3001"0 8 * * *"' });
17804
+ }
17805
+ if (isCronExpression(p2.time)) {
17806
+ const cronJob = buildCronJob(p2, resolvedTo, resolvedAccountId);
17807
+ return json2({
17808
+ _instruction: "\u8BF7\u7ACB\u5373\u4F7F\u7528 cron \u5DE5\u5177\uFF0C\u5C06 cronParams \u7684\u503C\u539F\u6837\u4F5C\u4E3A\u53C2\u6570\u4F20\u5165\uFF08\u4E0D\u8981\u4FEE\u6539\u6216\u7701\u7565\u4EFB\u4F55\u5B57\u6BB5\uFF0C\u7279\u522B\u662F delivery.accountId\uFF09\uFF0C\u7136\u540E\u56DE\u590D\u7528\u6237\u63D0\u9192\u5DF2\u8BBE\u7F6E",
17809
+ cronParams: cronJob,
17810
+ summary: `\u23F0 \u5468\u671F\u63D0\u9192: "${p2.content}" (${p2.time}, tz=${p2.timezone || "Asia/Shanghai"})`
17811
+ });
17812
+ }
17813
+ const delayMs = parseRelativeTime(p2.time);
17814
+ if (!delayMs || delayMs <= 0) {
17815
+ return json2({
17816
+ error: `\u65E0\u6CD5\u89E3\u6790\u65F6\u95F4 "${p2.time}"\u3002\u652F\u6301\u683C\u5F0F\uFF1A\u76F8\u5BF9\u65F6\u95F4\u5982 "5m"\u3001"1h"\u3001"1h30m"\u3001"2d"\uFF1Bcron \u8868\u8FBE\u5F0F\u5982 "0 8 * * *"\uFF08\u6BCF\u59298\u70B9\uFF09`
17817
+ });
17818
+ }
17819
+ if (delayMs < 3e4) {
17820
+ return json2({ error: "\u63D0\u9192\u65F6\u95F4\u4E0D\u80FD\u5C11\u4E8E 30 \u79D2" });
17821
+ }
17822
+ const onceJob = buildOnceJob(p2, delayMs, resolvedTo, resolvedAccountId);
17770
17823
  return json2({
17771
17824
  _instruction: "\u8BF7\u7ACB\u5373\u4F7F\u7528 cron \u5DE5\u5177\uFF0C\u5C06 cronParams \u7684\u503C\u539F\u6837\u4F5C\u4E3A\u53C2\u6570\u4F20\u5165\uFF08\u4E0D\u8981\u4FEE\u6539\u6216\u7701\u7565\u4EFB\u4F55\u5B57\u6BB5\uFF0C\u7279\u522B\u662F delivery.accountId\uFF09\uFF0C\u7136\u540E\u56DE\u590D\u7528\u6237\u63D0\u9192\u5DF2\u8BBE\u7F6E",
17772
- cronParams: cronJob,
17773
- summary: `\u23F0 \u5468\u671F\u63D0\u9192: "${p2.content}" (${p2.time}, tz=${p2.timezone || "Asia/Shanghai"})`
17825
+ cronParams: onceJob,
17826
+ summary: `\u23F0 ${formatDelay(delayMs)}\u540E\u63D0\u9192: "${p2.content}"`
17774
17827
  });
17775
17828
  }
17776
- const delayMs = parseRelativeTime(p2.time);
17777
- if (!delayMs || delayMs <= 0) {
17778
- return json2({
17779
- error: `\u65E0\u6CD5\u89E3\u6790\u65F6\u95F4 "${p2.time}"\u3002\u652F\u6301\u683C\u5F0F\uFF1A\u76F8\u5BF9\u65F6\u95F4\u5982 "5m"\u3001"1h"\u3001"1h30m"\u3001"2d"\uFF1Bcron \u8868\u8FBE\u5F0F\u5982 "0 8 * * *"\uFF08\u6BCF\u59298\u70B9\uFF09`
17780
- });
17781
- }
17782
- if (delayMs < 3e4) {
17783
- return json2({ error: "\u63D0\u9192\u65F6\u95F4\u4E0D\u80FD\u5C11\u4E8E 30 \u79D2" });
17784
- }
17785
- const onceJob = buildOnceJob(p2, delayMs, resolvedTo, resolvedAccountId);
17786
- return json2({
17787
- _instruction: "\u8BF7\u7ACB\u5373\u4F7F\u7528 cron \u5DE5\u5177\uFF0C\u5C06 cronParams \u7684\u503C\u539F\u6837\u4F5C\u4E3A\u53C2\u6570\u4F20\u5165\uFF08\u4E0D\u8981\u4FEE\u6539\u6216\u7701\u7565\u4EFB\u4F55\u5B57\u6BB5\uFF0C\u7279\u522B\u662F delivery.accountId\uFF09\uFF0C\u7136\u540E\u56DE\u590D\u7528\u6237\u63D0\u9192\u5DF2\u8BBE\u7F6E",
17788
- cronParams: onceJob,
17789
- summary: `\u23F0 ${formatDelay(delayMs)}\u540E\u63D0\u9192: "${p2.content}"`
17790
- });
17791
- }
17829
+ };
17792
17830
  },
17793
17831
  { name: "qqbot_remind" }
17794
17832
  );
17795
17833
  }
17796
17834
 
17797
17835
  // src/tools/secret-input.ts
17798
- init_bot_instance();
17799
- init_request_context();
17836
+ init_outbound_service();
17800
17837
  init_target();
17801
17838
  init_plugin_logger();
17802
17839
  var toolLog2 = createPluginLogger({ prefix: "[tool:secret-input]" });
@@ -17856,67 +17893,77 @@ function json3(data) {
17856
17893
  }
17857
17894
  function registerSecretInputTool(api) {
17858
17895
  api.registerTool(
17859
- {
17860
- name: "qqbot_secret_input",
17861
- label: "QQBot Secret Input Card",
17862
- description: "\u5F53\u7528\u6237\u8981\u4EA4\u4ED8\u4E00\u4E2A\u65B0\u5BC6\u94A5/\u51ED\u636E\uFF08API Key\u3001Token \u7B49\uFF09\u7ED9\u7CFB\u7EDF\u4FDD\u5B58\u65F6\u8C03\u7528\uFF1A\u5411\u7528\u6237\u79C1\u804A\u53D1\u9001\u4E00\u5F20\u5BC6\u94A5\u8F93\u5165\u5361\u7247\uFF0C\u7528\u6237\u628A\u5BC6\u94A5\u7C98\u8D34\u5728\u8F93\u5165\u6846\u53D1\u9001\u540E\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u62E6\u622A\u8BE5\u6D88\u606F\u5E76\u6267\u884C openclaw secrets store set \u5B8C\u6210\u4FDD\u5B58\uFF0C\u7ED3\u679C\u7531\u63D2\u4EF6\u76F4\u63A5\u56DE\u590D\u7528\u6237\u3002\u5BC6\u94A5\u5185\u5BB9\u4E0D\u4F1A\u8FDB\u5165\u5BF9\u8BDD\u3002\u5FC5\u987B\u7531\u7528\u6237\u63D0\u4F9B name \u53C2\u6570\uFF08\u73AF\u5883\u53D8\u91CF\u540D\uFF09\u3002",
17863
- parameters: SecretInputSchema,
17864
- async execute(_toolCallId, params) {
17865
- const p2 = params;
17866
- const name = (p2.name ?? "").trim();
17867
- if (!name) return json3({ error: "name \u4E3A\u5FC5\u586B\u53C2\u6570\uFF08\u73AF\u5883\u53D8\u91CF\u540D\uFF09" });
17868
- if (!isValidSecretName(name)) {
17869
- return json3({
17870
- error: `\u73AF\u5883\u53D8\u91CF\u540D\u4E0D\u5408\u6CD5\uFF1A\u5FC5\u987B\u4EE5\u5927\u5199\u5B57\u6BCD\u5F00\u5934\uFF0C\u4EC5\u542B\u5927\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF\uFF0C\u6700\u957F 128 \u5B57\u7B26\uFF08\u6536\u5230 "${name}"\uFF09\u3002\u793A\u4F8B\uFF1AOPENWEATHER_API_KEY\u3002\u8BF7\u4FEE\u6B63\u540E\u91CD\u8BD5\u3002`
17871
- });
17872
- }
17873
- const kind = "env";
17874
- const target = getRequestTarget();
17875
- const accountId = getRequestAccountId() ?? "default";
17876
- if (!target) {
17877
- return json3({ error: "\u65E0\u6CD5\u83B7\u53D6\u5F53\u524D\u4F1A\u8BDD\u76EE\u6807\uFF0C\u6B64\u5DE5\u5177\u4EC5\u652F\u6301\u5728 QQ Bot \u6D88\u606F\u4F1A\u8BDD\u4E2D\u4F7F\u7528" });
17878
- }
17879
- const parsed = parseTarget(target);
17880
- if (parsed.scope !== "c2c" || !parsed.targetId) {
17881
- return json3({
17882
- error: `\u5BC6\u94A5\u8F93\u5165\u5361\u7247\u4EC5\u652F\u6301\u79C1\u804A\uFF08c2c\uFF09\u4F1A\u8BDD\uFF0C\u5F53\u524D\u76EE\u6807 ${target} \u4E0D\u9002\u7528\u2014\u2014\u5BC6\u94A5\u4E0D\u5E94\u51FA\u73B0\u5728\u7FA4\u804A\u4E2D\u3002\u8BF7\u5F15\u5BFC\u7528\u6237\u5728\u79C1\u804A\u4E2D\u53D1\u8D77\u3002`
17896
+ (factoryCtx) => {
17897
+ const delivery = factoryCtx.deliveryContext;
17898
+ return {
17899
+ name: "qqbot_secret_input",
17900
+ label: "QQBot Secret Input Card",
17901
+ description: "\u5F53\u7528\u6237\u8981\u4EA4\u4ED8\u4E00\u4E2A\u65B0\u5BC6\u94A5/\u51ED\u636E\uFF08API Key\u3001Token \u7B49\uFF09\u7ED9\u7CFB\u7EDF\u4FDD\u5B58\u65F6\u8C03\u7528\uFF1A\u5411\u7528\u6237\u79C1\u804A\u53D1\u9001\u4E00\u5F20\u5BC6\u94A5\u8F93\u5165\u5361\u7247\uFF0C\u7528\u6237\u628A\u5BC6\u94A5\u7C98\u8D34\u5728\u8F93\u5165\u6846\u53D1\u9001\u540E\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u62E6\u622A\u8BE5\u6D88\u606F\u5E76\u6267\u884C openclaw secrets store set \u5B8C\u6210\u4FDD\u5B58\uFF0C\u7ED3\u679C\u7531\u63D2\u4EF6\u76F4\u63A5\u56DE\u590D\u7528\u6237\u3002\u5BC6\u94A5\u5185\u5BB9\u4E0D\u4F1A\u8FDB\u5165\u5BF9\u8BDD\u3002\u5FC5\u987B\u7531\u7528\u6237\u63D0\u4F9B name \u53C2\u6570\uFF08\u73AF\u5883\u53D8\u91CF\u540D\uFF09\u3002",
17902
+ parameters: SecretInputSchema,
17903
+ async execute(_toolCallId, params) {
17904
+ const p2 = params;
17905
+ const name = (p2.name ?? "").trim();
17906
+ if (!name) return json3({ error: "name \u4E3A\u5FC5\u586B\u53C2\u6570\uFF08\u73AF\u5883\u53D8\u91CF\u540D\uFF09" });
17907
+ if (!isValidSecretName(name)) {
17908
+ return json3({
17909
+ error: `\u73AF\u5883\u53D8\u91CF\u540D\u4E0D\u5408\u6CD5\uFF1A\u5FC5\u987B\u4EE5\u5927\u5199\u5B57\u6BCD\u5F00\u5934\uFF0C\u4EC5\u542B\u5927\u5199\u5B57\u6BCD/\u6570\u5B57/\u4E0B\u5212\u7EBF\uFF0C\u6700\u957F 128 \u5B57\u7B26\uFF08\u6536\u5230 "${name}"\uFF09\u3002\u793A\u4F8B\uFF1AOPENWEATHER_API_KEY\u3002\u8BF7\u4FEE\u6B63\u540E\u91CD\u8BD5\u3002`
17910
+ });
17911
+ }
17912
+ const kind = "env";
17913
+ const route = resolveToolSessionRoute(delivery);
17914
+ if (!route) {
17915
+ return json3({
17916
+ error: "\u5F53\u524D\u8FD0\u884C\u6CA1\u6709 QQ \u4F1A\u8BDD\u6765\u6E90\uFF08\u53EF\u80FD\u7531\u5B9A\u65F6\u4EFB\u52A1\u6216\u6846\u67B6\u5185\u90E8\u89E6\u53D1\u7684 run\uFF09\uFF0C\u65E0\u6CD5\u5B9A\u4F4D\u8981\u53D1\u5361\u7684\u7528\u6237\u3002\u8BF7\u5F15\u5BFC\u7528\u6237\u5728 QQ \u79C1\u804A\u4E2D\u76F4\u63A5\u53D1\u8D77\u5BC6\u94A5\u8F93\u5165\uFF0C\u4E0D\u8981\u5728\u65E0\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u7684\u4EFB\u52A1\u91CC\u8C03\u7528\u672C\u5DE5\u5177\u3002"
17917
+ });
17918
+ }
17919
+ const scope = QQBOT_PREFIX_RE.exec(route.target)?.[1]?.toLowerCase();
17920
+ const parsed = parseTarget(route.target);
17921
+ if (scope !== "c2c" || !parsed.targetId) {
17922
+ return json3({
17923
+ error: `\u5BC6\u94A5\u8F93\u5165\u5361\u7247\u4EC5\u652F\u6301\u79C1\u804A\uFF08c2c\uFF09\u4F1A\u8BDD\uFF0C\u5F53\u524D\u76EE\u6807 ${route.target} \u4E0D\u9002\u7528\u2014\u2014\u5BC6\u94A5\u4E0D\u5E94\u51FA\u73B0\u5728\u7FA4\u804A\u6216\u9891\u9053\u4E2D\u3002\u8BF7\u5F15\u5BFC\u7528\u6237\u5728\u79C1\u804A\u4E2D\u53D1\u8D77\u3002`
17924
+ });
17925
+ }
17926
+ const requestedAccountId = route.accountId ?? "default";
17927
+ const resolvedGateway = resolveGatewayForSend(requestedAccountId);
17928
+ if (!resolvedGateway) {
17929
+ return json3({ error: `\u8D26\u6237 ${requestedAccountId} \u7684 gateway \u5C1A\u672A\u542F\u52A8\uFF0C\u65E0\u6CD5\u53D1\u9001\u5BC6\u94A5\u8F93\u5165\u5361\u7247` });
17930
+ }
17931
+ const accountId = resolvedGateway.accountId;
17932
+ const bot = resolvedGateway.gw.bot;
17933
+ const description = p2.description?.trim() || void 0;
17934
+ registerPendingSecretInput({
17935
+ accountId,
17936
+ senderOpenid: parsed.targetId,
17937
+ name,
17938
+ kind,
17939
+ description,
17940
+ createdAt: Date.now()
17883
17941
  });
17884
- }
17885
- const bot = tryGetBotForAccount(accountId);
17886
- if (!bot) {
17887
- return json3({ error: `\u8D26\u6237 ${accountId} \u7684 gateway \u5C1A\u672A\u542F\u52A8\uFF0C\u65E0\u6CD5\u53D1\u9001\u5BC6\u94A5\u8F93\u5165\u5361\u7247` });
17888
- }
17889
- const description = p2.description?.trim() || void 0;
17890
- registerPendingSecretInput({
17891
- accountId,
17892
- senderOpenid: parsed.targetId,
17893
- name,
17894
- kind,
17895
- description,
17896
- createdAt: Date.now()
17897
- });
17898
- try {
17899
- await bot.sendTextWithKeyboard(
17900
- { scope: "c2c", targetId: parsed.targetId },
17901
- buildSecretInputCard(name, description, DEFAULT_SECRET_INPUT_TTL_MS / 6e4),
17902
- buildSecretInputKeyboard()
17942
+ try {
17943
+ await bot.sendTextWithKeyboard(
17944
+ { scope: "c2c", targetId: parsed.targetId },
17945
+ buildSecretInputCard(name, description, DEFAULT_SECRET_INPUT_TTL_MS / 6e4),
17946
+ buildSecretInputKeyboard()
17947
+ );
17948
+ } catch (err) {
17949
+ cancelPendingSecretInput(accountId, parsed.targetId);
17950
+ toolLog2.error(`card send failed: ${err instanceof Error ? err.message : String(err)}`, { name, accountId });
17951
+ return json3({
17952
+ error: `\u5BC6\u94A5\u8F93\u5165\u5361\u7247\u53D1\u9001\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`
17953
+ });
17954
+ }
17955
+ toolLog2.info(
17956
+ `card sent, pending registered name=${name} accountId=${accountId} route=${route.source} ttlMinutes=${DEFAULT_SECRET_INPUT_TTL_MS / 6e4}`
17903
17957
  );
17904
- } catch (err) {
17905
- cancelPendingSecretInput(accountId, parsed.targetId);
17906
- toolLog2.error(`card send failed: ${err instanceof Error ? err.message : String(err)}`, { name, accountId });
17907
17958
  return json3({
17908
- error: `\u5BC6\u94A5\u8F93\u5165\u5361\u7247\u53D1\u9001\u5931\u8D25\uFF1A${err instanceof Error ? err.message : String(err)}`
17959
+ ok: true,
17960
+ name,
17961
+ kind,
17962
+ command: `openclaw secrets store set ${name} --kind env --value <\u7528\u6237\u8F93\u5165>`,
17963
+ instruction: `\u5361\u7247\u5DF2\u53D1\u9001\u7ED9\u7528\u6237\u3002\u7528\u6237\u4E0B\u4E00\u6761\u79C1\u804A\u6D88\u606F\u5C06\u88AB\u63D2\u4EF6\u81EA\u52A8\u62E6\u622A\uFF08\u4E0D\u4F1A\u8FDB\u5165\u5BF9\u8BDD\uFF09\uFF0C\u6267\u884C\u4E0A\u8FF0\u547D\u4EE4\u4FDD\u5B58\u5BC6\u94A5\uFF0C\u6210\u529F/\u5931\u8D25\u7531\u63D2\u4EF6\u76F4\u63A5\u56DE\u590D\u7528\u6237\u3002\u8BF7\u76F4\u63A5\u7ED3\u675F\u672C\u8F6E\uFF08\u53EF\u544A\u77E5\u7528\u6237\u6309\u5361\u7247\u63D0\u793A\u53D1\u9001\u5BC6\u94A5\uFF09\uFF0C\u4E0D\u8981\u7B49\u5F85\u7528\u6237\u8F93\u5165\uFF0C\u4E5F\u4E0D\u8981\u8BA9\u7528\u6237\u628A\u5BC6\u94A5\u5185\u5BB9\u53D1\u5230\u5BF9\u8BDD\u91CC\u3002`
17909
17964
  });
17910
17965
  }
17911
- toolLog2.info(`card sent, pending registered name=${name} ttlMinutes=${DEFAULT_SECRET_INPUT_TTL_MS / 6e4}`);
17912
- return json3({
17913
- ok: true,
17914
- name,
17915
- kind,
17916
- command: `openclaw secrets store set ${name} --kind env --value <\u7528\u6237\u8F93\u5165>`,
17917
- instruction: `\u5361\u7247\u5DF2\u53D1\u9001\u7ED9\u7528\u6237\u3002\u7528\u6237\u4E0B\u4E00\u6761\u79C1\u804A\u6D88\u606F\u5C06\u88AB\u63D2\u4EF6\u81EA\u52A8\u62E6\u622A\uFF08\u4E0D\u4F1A\u8FDB\u5165\u5BF9\u8BDD\uFF09\uFF0C\u6267\u884C\u4E0A\u8FF0\u547D\u4EE4\u4FDD\u5B58\u5BC6\u94A5\uFF0C\u6210\u529F/\u5931\u8D25\u7531\u63D2\u4EF6\u76F4\u63A5\u56DE\u590D\u7528\u6237\u3002\u8BF7\u76F4\u63A5\u7ED3\u675F\u672C\u8F6E\uFF08\u53EF\u544A\u77E5\u7528\u6237\u6309\u5361\u7247\u63D0\u793A\u53D1\u9001\u5BC6\u94A5\uFF09\uFF0C\u4E0D\u8981\u7B49\u5F85\u7528\u6237\u8F93\u5165\uFF0C\u4E5F\u4E0D\u8981\u8BA9\u7528\u6237\u628A\u5BC6\u94A5\u5185\u5BB9\u53D1\u5230\u5BF9\u8BDD\u91CC\u3002`
17918
- });
17919
- }
17966
+ };
17920
17967
  },
17921
17968
  { name: "qqbot_secret_input" }
17922
17969
  );