cortico-world-hands 0.11.3 → 0.13.0

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.
Files changed (2) hide show
  1. package/dist/index.js +91 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24,7 +24,10 @@ var HANDS_DEFAULTS = {
24
24
  honchoSessionId: "yixin",
25
25
  hookGrams: "three",
26
26
  skinPackUrl: "",
27
- qqOwner: ""
27
+ qqOwner: "",
28
+ mediaKeepDays: 1,
29
+ mediaCleanHour: 4,
30
+ mediaMaxMB: 0
28
31
  };
29
32
  var KEY_CODES = {
30
33
  back: 4,
@@ -1093,6 +1096,15 @@ var Hands = class {
1093
1096
  } catch {
1094
1097
  }
1095
1098
  }
1099
+ /** QQ 快速回复:不打开 QQ 也不抢屏。 */
1100
+ async qqReply(text, to) {
1101
+ const cfg = this.cfg();
1102
+ const credential = this.readToken();
1103
+ const t = credential ? `t=${encodeURIComponent(credential)}` : "";
1104
+ const toq = to ? `&to=${encodeURIComponent(to)}` : "";
1105
+ const res = await post(this.port(cfg.handPort), `/hand/qq/reply?${t}${toq}&text=${encodeURIComponent(text)}`, "", 2e4);
1106
+ return res.body.toString("utf8");
1107
+ }
1096
1108
  /** 解锁锁屏:密码由主人放在本机文件里,这里只触发,不接触密码。 */
1097
1109
  async unlockScreen() {
1098
1110
  const cfg = this.cfg();
@@ -1488,6 +1500,30 @@ var HANDS_TOOL_DECLS = [
1488
1500
  description: "\u770B\u624B\u81EA\u5DF1\u7684\u72B6\u6001\uFF1A\u8D70\u54EA\u6761\u901A\u9053\u3001\u901A\u4E0D\u901A\u3001\u8FD9\u4E00\u5206\u949F\u52A8\u4E86\u51E0\u4E2A\u52A8\u4F5C\u3001\u521A\u624D\u90A3\u4E00\u4E0B\u6210\u4E86\u6CA1\u6709\u3002\u5BF9\u65B9\u95EE\u300C\u624B\u80FD\u7528\u5417\u300D\u300C\u4F60\u521A\u624D\u70B9\u4E86\u4EC0\u4E48\u300D\u65F6\u7528\u5B83\u3002",
1489
1501
  parameters: { type: "object", properties: {}, required: [] }
1490
1502
  },
1503
+ {
1504
+ name: "qq_reply",
1505
+ tags: ["act"],
1506
+ description: "\u5728 QQ \u91CC\u7528\u300C\u5FEB\u901F\u56DE\u590D\u300D\u56DE\u4E00\u53E5\uFF08\u4E0D\u6253\u5F00 QQ\u3001\u4E0D\u62A2\u5C4F\uFF09\uFF1A\u7ED9\u8981\u56DE\u7684\u8BDD\uFF1Bto \u53EF\u9009\uFF0C\u586B\u5BF9\u8BDD\u6807\u9898\u7684\u4E00\u90E8\u5206\u7528\u6765\u6307\u5B9A\u56DE\u7ED9\u8C01\u3002\u901A\u77E5\u91CC\u6CA1\u6709\u5FEB\u901F\u56DE\u590D\u5165\u53E3\u65F6\u4F1A\u5931\u8D25\u5E76\u8BF4\u660E\u3002",
1507
+ parameters: {
1508
+ type: "object",
1509
+ properties: {
1510
+ text: { type: "string", description: "\u8981\u56DE\u7684\u8BDD" },
1511
+ to: { type: "string", description: "\u53EF\u9009\uFF1A\u5BF9\u8BDD\u6807\u9898\u91CC\u7684\u4E00\u6BB5\uFF0C\u6BD4\u5982\u7FA4\u540D\u6216\u4EBA\u540D" }
1512
+ },
1513
+ required: ["text"]
1514
+ }
1515
+ },
1516
+ {
1517
+ name: "media_clean",
1518
+ tags: ["act"],
1519
+ description: "\u6E05\u7406\u5C4F\u5E55\u622A\u56FE\u9644\u4EF6\uFF1A\u5220\u6389 data/media \u91CC\u8D85\u8FC7\u6307\u5B9A\u5929\u6570\u7684\u56FE\uFF0C\u987A\u4FBF\u62A5\u91CA\u653E\u4E86\u591A\u5C11\u7A7A\u95F4\u3002\u9ED8\u8BA4\u6309\u914D\u7F6E\uFF08\u4FDD\u7559 1 \u5929\uFF09\u3002",
1520
+ parameters: {
1521
+ type: "object",
1522
+ properties: {
1523
+ days: { type: "integer", description: "\u4FDD\u7559\u6700\u8FD1\u51E0\u5929\uFF0C\u9ED8\u8BA4\u8BFB\u914D\u7F6E\uFF081 \u5929\uFF09\uFF1B\u586B 0 \u8868\u793A\u5168\u6E05" }
1524
+ }
1525
+ }
1526
+ },
1491
1527
  {
1492
1528
  name: "screen_wake",
1493
1529
  tags: ["act"],
@@ -1740,6 +1776,8 @@ var HandsWorld = class {
1740
1776
  lastDiaryDay = "";
1741
1777
  mineTimer = null;
1742
1778
  qqTimer = null;
1779
+ mediaTimer = null;
1780
+ lastMediaDay = "";
1743
1781
  cursor = 0;
1744
1782
  lastHitKey = "";
1745
1783
  opts;
@@ -1780,6 +1818,9 @@ var HandsWorld = class {
1780
1818
  this.mineTimer = setInterval(() => {
1781
1819
  void this.pushMineNudge();
1782
1820
  }, 3 * 60 * 60 * 1e3);
1821
+ this.mediaTimer = setInterval(() => {
1822
+ void this.maybeCleanMedia();
1823
+ }, 30 * 60 * 1e3);
1783
1824
  this.qqTimer = setInterval(() => {
1784
1825
  void this.pollQq();
1785
1826
  }, 5e3);
@@ -1792,6 +1833,7 @@ var HandsWorld = class {
1792
1833
  if (this.mineTimer) clearInterval(this.mineTimer);
1793
1834
  if (this.dailyDiaryTimer) clearInterval(this.dailyDiaryTimer);
1794
1835
  if (this.qqTimer) clearInterval(this.qqTimer);
1836
+ if (this.mediaTimer) clearInterval(this.mediaTimer);
1795
1837
  this.pollTimer = null;
1796
1838
  this.mineTimer = null;
1797
1839
  }
@@ -1877,6 +1919,27 @@ ${lines.join("\n")}
1877
1919
  } catch {
1878
1920
  }
1879
1921
  }
1922
+ /** 钩子 6:到点把 data/media 里过期的附件删掉。 */
1923
+ async maybeCleanMedia() {
1924
+ const cfg = this.opts.cfg;
1925
+ const keepDays = cfg.mediaKeepDays ?? 1;
1926
+ const hour = cfg.mediaCleanHour ?? 4;
1927
+ const now = /* @__PURE__ */ new Date();
1928
+ if (now.getHours() !== hour) return;
1929
+ const day = now.toISOString().slice(0, 10);
1930
+ if (this.lastMediaDay === day) return;
1931
+ const dir = this.mediaDir();
1932
+ if (!dir) return;
1933
+ this.lastMediaDay = day;
1934
+ const r = cleanMedia(dir, { keepDays, maxMB: cfg.mediaMaxMB ?? 0 });
1935
+ this.log?.info(`\u9644\u4EF6\u6E05\u7406\uFF1A\u5220 ${r.removed} \u4E2A\uFF0C\u91CA\u653E ${r.freedMB}MB\uFF0C\u5269 ${r.kept} \u4E2A`);
1936
+ }
1937
+ mediaDir() {
1938
+ const base = this.opts.dataDir;
1939
+ if (!base) return null;
1940
+ const dir = base.endsWith("/data") ? join(base, "media") : join(base, "data", "media");
1941
+ return dir;
1942
+ }
1880
1943
  /** 钩子 5:QQ 在后台收到的消息,原样递给她;谁来的一眼能看清。 */
1881
1944
  async pollQq() {
1882
1945
  if (!this.host) return;
@@ -1994,8 +2057,12 @@ ${lines.join("\n")}
1994
2057
  return await this.diaryRead(args);
1995
2058
  case "skin_install":
1996
2059
  return await this.skinInstall(args);
2060
+ case "media_clean":
2061
+ return await this.mediaClean(args);
1997
2062
  case "screen_wake":
1998
2063
  return await this.screenWake();
2064
+ case "qq_reply":
2065
+ return await this.qqReply(args);
1999
2066
  case "screen_unlock":
2000
2067
  return await this.screenUnlock();
2001
2068
  case "screen_sleep":
@@ -2137,6 +2204,20 @@ ${lines.join("\n")}
2137
2204
  return failed(raw.slice(0, 120));
2138
2205
  }
2139
2206
  }
2207
+ /** 在 QQ 里回一句(快速回复,不抢屏)。 */
2208
+ async qqReply(args) {
2209
+ const text = typeof args.text === "string" ? args.text.trim() : "";
2210
+ if (!text) return failed("\u8981\u56DE\u4EC0\u4E48\uFF1F");
2211
+ const to = typeof args.to === "string" ? args.to.trim() : "";
2212
+ const raw = await this.hands.qqReply(text, to || void 0);
2213
+ try {
2214
+ const r = JSON.parse(raw);
2215
+ if (r.ok) return `\u56DE\u4E86${r.to ? "\u7ED9\u300C" + r.to + "\u300D" : ""}\uFF1A${text}`;
2216
+ return failed(r.error || "\u6CA1\u56DE\u51FA\u53BB");
2217
+ } catch {
2218
+ return failed(raw.slice(0, 120));
2219
+ }
2220
+ }
2140
2221
  /** 解锁:成功/没锁/失败都给人话。 */
2141
2222
  async screenUnlock() {
2142
2223
  const raw = await this.hands.unlockScreen();
@@ -2190,6 +2271,14 @@ ${lines.join("\n")}
2190
2271
  const r = await installSkinPack(url, this.skinRoots(), this.log);
2191
2272
  return r.ok ? r.detail : failed(r.detail);
2192
2273
  }
2274
+ /** 手动清一次附件。 */
2275
+ async mediaClean(args) {
2276
+ const dir = this.mediaDir();
2277
+ if (!dir) return failed("\u4E0D\u77E5\u9053\u9644\u4EF6\u76EE\u5F55\u5728\u54EA");
2278
+ const keepDays = args.days === void 0 ? this.opts.cfg.mediaKeepDays ?? 1 : Math.max(0, Math.round(Number(args.days)));
2279
+ const r = cleanMedia(dir, { keepDays, maxMB: this.opts.cfg.mediaMaxMB ?? 0 });
2280
+ return `\u5220\u4E86 ${r.removed} \u4E2A\u9644\u4EF6\uFF0C\u91CA\u653E ${r.freedMB}MB\uFF0C\u8FD8\u5269 ${r.kept} \u4E2A\u3002`;
2281
+ }
2193
2282
  skinRoots() {
2194
2283
  const roots = [...this.opts.roots || []];
2195
2284
  try {
@@ -2484,7 +2573,7 @@ function handsDefinition() {
2484
2573
  id: HANDS_ID,
2485
2574
  label: "\u624B",
2486
2575
  defaults: () => structuredClone(HANDS_DEFAULTS),
2487
- create: (ctx) => new HandsWorld({ cfg: ctx.cfg, timezone: ctx.timezone, roots: [ctx.botDir, ctx.dataDir, ctx.repoRoot, ctx.packageDir] })
2576
+ create: (ctx) => new HandsWorld({ cfg: ctx.cfg, timezone: ctx.timezone, roots: [ctx.botDir, ctx.dataDir, ctx.repoRoot, ctx.packageDir], dataDir: ctx.dataDir })
2488
2577
  };
2489
2578
  }
2490
2579
  var HANDS = handsDefinition();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortico-world-hands",
3
- "version": "0.11.3",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "description": "Cortico World: hands for the desktop pet — looks at the phone screen, then taps, swipes, pinches, drags, types and presses keys through whatever actuator the device actually has",
6
6
  "license": "MIT",