cortico-world-hands 0.17.0 → 0.18.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 +240 -0
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -25,6 +25,8 @@ var HANDS_DEFAULTS = {
25
25
  hookGrams: "three",
26
26
  skinPackUrl: "",
27
27
  qqOwner: "",
28
+ ritualMorning: "08:00",
29
+ ritualNight: "23:00",
28
30
  mediaKeepDays: 1,
29
31
  mediaCleanHour: 4,
30
32
  mediaMaxMB: 0
@@ -1801,6 +1803,85 @@ var HANDS_TOOL_DECLS = [
1801
1803
  tags: ["flow"],
1802
1804
  description: "\u7ACB\u523B\u505C\u624B\uFF1A\u4E00\u5206\u949F\u4E4B\u5185\u4E0D\u518D\u52A8\u4EFB\u4F55\u4E00\u4E0B\uFF08\u5DF2\u7ECF\u53D1\u51FA\u7684\u4E0D\u7B97\uFF09\u3002\u5BF9\u65B9\u8BF4\u300C\u522B\u52A8\u4E86\u300D\u300C\u505C\u4E0B\u300D\u6216\u8005\u4F60\u53D1\u73B0\u81EA\u5DF1\u70B9\u9519\u5730\u65B9\u65F6\u7528\u5B83\u3002",
1803
1805
  parameters: { type: "object", properties: {}, required: [] }
1806
+ },
1807
+ {
1808
+ name: "agenda_add",
1809
+ tags: ["act"],
1810
+ description: '\u8BB0\u4E00\u6761\u65E5\u7A0B\uFF08\u5230\u70B9\u4F1A\u53EB\u9192\u5979\u4E00\u6B21\uFF09\u3002at \u7528 "2026-10-26 08:30" \u6216 "08:30"\uFF08\u4ECA\u5929/\u660E\u5929\u81EA\u52A8\u5224\u65AD\uFF09\u3002',
1811
+ parameters: {
1812
+ type: "object",
1813
+ properties: {
1814
+ title: { type: "string", description: "\u8981\u505A\u4EC0\u4E48" },
1815
+ at: { type: "string", description: '\u4EC0\u4E48\u65F6\u5019\uFF0C\u4F8B\u5982 "2026-10-26 08:30" \u6216 "20:30"' },
1816
+ who: { type: "string", description: "\u8C01\u8981\u6C42\u7684\uFF0C\u9ED8\u8BA4\u4E3B\u4EBA" }
1817
+ },
1818
+ required: ["title"]
1819
+ }
1820
+ },
1821
+ {
1822
+ name: "agenda_list",
1823
+ tags: ["read"],
1824
+ description: "\u770B\u8FD8\u6CA1\u505A\u7684\u65E5\u7A0B\u3002",
1825
+ parameters: { type: "object", properties: {} }
1826
+ },
1827
+ {
1828
+ name: "agenda_done",
1829
+ tags: ["act"],
1830
+ description: "\u628A\u4E00\u6761\u65E5\u7A0B\u6807\u8BB0\u5B8C\u6210\u3002",
1831
+ parameters: {
1832
+ type: "object",
1833
+ properties: { id: { type: "string", description: "agenda_list \u91CC\u7684 id" } },
1834
+ required: ["id"]
1835
+ }
1836
+ },
1837
+ {
1838
+ name: "affinity_get",
1839
+ tags: ["read"],
1840
+ description: "\u67E5\u67D0\u4E2A\u4EBA\uFF08QQ \u53F7\uFF09\u5728\u5979\u5FC3\u91CC\u7684\u5206\u6570\u3001\u6863\u4F4D\u3001\u8BE5\u7528\u4EC0\u4E48\u6001\u5EA6\u3002",
1841
+ parameters: {
1842
+ type: "object",
1843
+ properties: { qq: { type: "string", description: "\u5BF9\u65B9\u7684 QQ \u53F7\u6216\u540D\u5B57" } },
1844
+ required: ["qq"]
1845
+ }
1846
+ },
1847
+ {
1848
+ name: "affinity_adjust",
1849
+ tags: ["act"],
1850
+ description: "\u7ED9\u67D0\u4E2A\u4EBA\u8BB0\u4E00\u7B14\u5173\u7CFB\u53D8\u5316\uFF08\u53EF\u6B63\u53EF\u8D1F\uFF09\uFF0C\u5E76\u5199\u4E00\u53E5\u4E3A\u4EC0\u4E48\u3002",
1851
+ parameters: {
1852
+ type: "object",
1853
+ properties: {
1854
+ qq: { type: "string", description: "\u5BF9\u65B9\u7684 QQ \u53F7\u6216\u540D\u5B57" },
1855
+ delta: { type: "number", description: "\u53D8\u5316\u91CF\uFF0C\u6BD4\u5982 5 \u6216 -3" },
1856
+ why: { type: "string", description: "\u4E3A\u4EC0\u4E48" }
1857
+ },
1858
+ required: ["qq", "delta"]
1859
+ }
1860
+ },
1861
+ {
1862
+ name: "affinity_list",
1863
+ tags: ["read"],
1864
+ description: "\u770B\u5979\u5FC3\u91CC\u6392\u524D\u9762\u7684\u51E0\u4E2A\u4EBA\uFF0C\u8FDE\u540C\u5206\u6570\u548C\u6863\u4F4D\u3002",
1865
+ parameters: { type: "object", properties: {} }
1866
+ },
1867
+ {
1868
+ name: "ritual_set",
1869
+ tags: ["act"],
1870
+ description: "\u8BB0\u4E00\u4E2A\u7EAA\u5FF5\u65E5\uFF08\u6708-\u65E5\uFF09\u3002\u5230\u70B9\u4F1A\u63D0\u9192\u4E00\u6B21\u3002",
1871
+ parameters: {
1872
+ type: "object",
1873
+ properties: {
1874
+ date: { type: "string", description: '\u6708-\u65E5\uFF0C\u4F8B\u5982 "10-26"' },
1875
+ name: { type: "string", description: "\u662F\u4EC0\u4E48\u65E5\u5B50" }
1876
+ },
1877
+ required: ["date", "name"]
1878
+ }
1879
+ },
1880
+ {
1881
+ name: "ritual_list",
1882
+ tags: ["read"],
1883
+ description: "\u5217\u51FA\u5DF2\u8BB0\u7684\u7EAA\u5FF5\u65E5\u3002",
1884
+ parameters: { type: "object", properties: {} }
1804
1885
  }
1805
1886
  ];
1806
1887
 
@@ -1822,6 +1903,10 @@ var HandsWorld = class {
1822
1903
  pausedUntil = 0;
1823
1904
  recent = [];
1824
1905
  honchoClient = null;
1906
+ agendaTimer = null;
1907
+ agenda;
1908
+ affinity;
1909
+ ritual;
1825
1910
  pollTimer = null;
1826
1911
  dailyDiaryTimer = null;
1827
1912
  lastDiaryDay = "";
@@ -1863,6 +1948,13 @@ var HandsWorld = class {
1863
1948
  /* ---------- 记忆钩子 ---------- */
1864
1949
  startHooks() {
1865
1950
  if (this.pollTimer) return;
1951
+ const dir = this.opts.dataDir ?? ".";
1952
+ this.agenda = new JsonStore(dir, "agenda.json", []);
1953
+ this.affinity = new JsonStore(dir, "affinity.json", {});
1954
+ this.ritual = new JsonStore(dir, "ritual.json", { anniversaries: [] });
1955
+ this.agendaTimer = setInterval(() => {
1956
+ void this.checkAgenda();
1957
+ }, 3e4);
1866
1958
  this.pollTimer = setInterval(() => {
1867
1959
  void this.pollUserSpeech();
1868
1960
  }, 5e3);
@@ -1888,6 +1980,63 @@ var HandsWorld = class {
1888
1980
  this.pollTimer = null;
1889
1981
  this.mineTimer = null;
1890
1982
  }
1983
+ /** 每 30 秒看一次:日程到点投一条,早安/晚安/纪念日各一次。 */
1984
+ async checkAgenda() {
1985
+ const now = /* @__PURE__ */ new Date();
1986
+ const iso = now.toISOString();
1987
+ const items = this.agenda?.read() ?? [];
1988
+ let changed = false;
1989
+ for (const item of items) {
1990
+ if (item.done || item.fired || !item.at) continue;
1991
+ if (new Date(item.at).getTime() <= now.getTime()) {
1992
+ item.fired = true;
1993
+ changed = true;
1994
+ await this.pushEvent(`[\u65E5\u7A0B] \u5230\u70B9\u4E86\uFF1A${item.title}\uFF08${item.who} \u8981\u7684\uFF09`, "agenda.due", "agenda");
1995
+ }
1996
+ }
1997
+ if (changed) this.agenda?.write(items);
1998
+ try {
1999
+ const st = this.ritualStore().read();
2000
+ const pad = (n) => String(n).padStart(2, "0");
2001
+ const today = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
2002
+ const hhmm = `${pad(now.getHours())}:${pad(now.getMinutes())}`;
2003
+ const morning = this.opts.cfg.ritualMorning || "08:00";
2004
+ const night = this.opts.cfg.ritualNight || "23:00";
2005
+ let dirty = false;
2006
+ if (hhmm === morning && st.lastMorning !== today) {
2007
+ st.lastMorning = today;
2008
+ dirty = true;
2009
+ await this.pushEvent("[\u4EEA\u5F0F] \u65E9\u5B89\u65F6\u95F4\u5230\u4E86\uFF0C\u4E3B\u52A8\u8DDF\u4E3B\u4EBA\u8BF4\u53E5\u65E9\u5B89\u3002", "ritual.morning", "ritual");
2010
+ }
2011
+ if (hhmm === night && st.lastNight !== today) {
2012
+ st.lastNight = today;
2013
+ dirty = true;
2014
+ await this.pushEvent("[\u4EEA\u5F0F] \u665A\u5B89\u65F6\u95F4\u5230\u4E86\uFF0C\u8DDF\u4E3B\u4EBA\u9053\u4E2A\u665A\u5B89\u3002", "ritual.night", "ritual");
2015
+ }
2016
+ const md = `${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
2017
+ for (const a of st.anniversaries ?? []) {
2018
+ if (a.date === md && st.lastAnniversary !== today) {
2019
+ st.lastAnniversary = today;
2020
+ dirty = true;
2021
+ await this.pushEvent(`[\u4EEA\u5F0F] \u4ECA\u5929\u662F\u300C${a.name}\u300D\uFF0C\u8BB0\u5F97\u63D0\u4E00\u53E5\u3002`, "ritual.anniversary", "ritual");
2022
+ }
2023
+ }
2024
+ if (dirty) this.ritualStore().write(st);
2025
+ } catch {
2026
+ }
2027
+ }
2028
+ agendaStore() {
2029
+ if (!this.agenda) throw new Error("\u65E5\u7A0B\u8FD8\u6CA1\u51C6\u5907\u597D");
2030
+ return this.agenda;
2031
+ }
2032
+ affinityStore() {
2033
+ if (!this.affinity) throw new Error("\u5173\u7CFB\u518C\u8FD8\u6CA1\u51C6\u5907\u597D");
2034
+ return this.affinity;
2035
+ }
2036
+ ritualStore() {
2037
+ if (!this.ritual) throw new Error("\u4EEA\u5F0F\u518C\u8FD8\u6CA1\u51C6\u5907\u597D");
2038
+ return this.ritual;
2039
+ }
1891
2040
  async pushEvent(text, type, senderKey) {
1892
2041
  const host = this.host;
1893
2042
  if (!host) return;
@@ -2118,6 +2267,70 @@ ${lines.join("\n")}
2118
2267
  return await this.mediaClean(args);
2119
2268
  case "screen_wake":
2120
2269
  return await this.screenWake();
2270
+ case "agenda_add": {
2271
+ const title = typeof args.title === "string" ? args.title.trim() : "";
2272
+ if (!title) return failed("\u8981\u8BB0\u4EC0\u4E48\uFF1F");
2273
+ const at = normalizeWhen(typeof args.at === "string" ? args.at : "");
2274
+ const items = this.agendaStore().read();
2275
+ const id = "a" + Date.now().toString(36);
2276
+ items.push({ id, title, at, who: typeof args.who === "string" && args.who ? args.who : "\u4E3B\u4EBA", done: false });
2277
+ this.agendaStore().write(items);
2278
+ return at ? `\u8BB0\u4E0B\u4E86\uFF1A${title}\uFF0C${fmtWhen(at)} \u53EB\u4F60` : `\u8BB0\u4E0B\u4E86\uFF1A${title}\uFF08\u6CA1\u8BBE\u65F6\u95F4\uFF09`;
2279
+ }
2280
+ case "agenda_list": {
2281
+ const items = this.agendaStore().read().filter((i) => !i.done);
2282
+ if (items.length === 0) return "\u65E5\u7A0B\u662F\u7A7A\u7684\u3002";
2283
+ return items.map((i) => `- [${i.id}] ${i.title}${i.at ? " \xB7 " + fmtWhen(i.at) : ""}\uFF08${i.who}\uFF09`).join("\n");
2284
+ }
2285
+ case "agenda_done": {
2286
+ const id = typeof args.id === "string" ? args.id.trim() : "";
2287
+ const items = this.agendaStore().read();
2288
+ const hit = items.find((i) => i.id === id);
2289
+ if (!hit) return failed("\u6CA1\u6709\u8FD9\u6761\u65E5\u7A0B");
2290
+ hit.done = true;
2291
+ this.agendaStore().write(items);
2292
+ return `\u597D\u4E86\uFF1A\u300C${hit.title}\u300D\u5DF2\u5B8C\u6210`;
2293
+ }
2294
+ case "affinity_get": {
2295
+ const qq = typeof args.qq === "string" ? args.qq.trim() : "";
2296
+ if (!qq) return failed("\u8981\u67E5\u8C01\uFF1F");
2297
+ const book = this.affinityStore().read();
2298
+ const row = book[qq];
2299
+ if (!row) return `${qq} \u8FD8\u6CA1\u8BB0\u8FC7\uFF0C\u5206\u6570 0\uFF08\u4E00\u822C\uFF09\u3002`;
2300
+ return `${qq}\uFF1A${row.score} \u5206\uFF08${LEVELS(row.score)}\uFF09${row.slot ? "\uFF0C\u7B97\u300C" + row.slot + "\u300D" : ""}\u3002\u6700\u8FD1\uFF1A${(row.notes ?? []).slice(-3).map((n) => `${n.delta > 0 ? "+" : ""}${n.delta} ${n.why}`).join("\uFF1B") || "\u65E0"}`;
2301
+ }
2302
+ case "affinity_adjust": {
2303
+ const qq = typeof args.qq === "string" ? args.qq.trim() : "";
2304
+ const delta = Number(args.delta);
2305
+ if (!qq || !Number.isFinite(delta)) return failed("\u8981 qq \u548C delta");
2306
+ const why = typeof args.why === "string" ? args.why.trim() : "";
2307
+ const book = this.affinityStore().read();
2308
+ const row = book[qq] ?? { qq, score: 0, notes: [] };
2309
+ row.score = Math.max(-100, Math.min(100, row.score + delta));
2310
+ row.notes = [...row.notes ?? [], { at: (/* @__PURE__ */ new Date()).toISOString(), delta, why }].slice(-50);
2311
+ book[qq] = row;
2312
+ this.affinityStore().write(book);
2313
+ return `${qq} \u73B0\u5728\u662F ${row.score} \u5206\uFF08${LEVELS(row.score)}\uFF09`;
2314
+ }
2315
+ case "affinity_list": {
2316
+ const rows = Object.values(this.affinityStore().read()).sort((a, b) => b.score - a.score).slice(0, 8);
2317
+ if (rows.length === 0) return "\u5173\u7CFB\u518C\u8FD8\u662F\u7A7A\u7684\u3002";
2318
+ return rows.map((r) => `- ${r.qq}\uFF1A${r.score} \u5206\uFF08${LEVELS(r.score)}\uFF09${r.slot ? "\xB7 " + r.slot : ""}`).join("\n");
2319
+ }
2320
+ case "ritual_set": {
2321
+ const date = typeof args.date === "string" ? args.date.trim() : "";
2322
+ const name2 = typeof args.name === "string" ? args.name.trim() : "";
2323
+ if (!/^\d{1,2}-\d{1,2}$/.test(date) || !name2) return failed("date \u8981\u5199\u6210 \u6708-\u65E5\uFF0C\u6BD4\u5982 10-26");
2324
+ const st = this.ritualStore().read();
2325
+ st.anniversaries = [...(st.anniversaries ?? []).filter((a) => a.date !== date), { date, name: name2 }];
2326
+ this.ritualStore().write(st);
2327
+ return `\u8BB0\u4F4F\u4E86\uFF1A${date} \u662F\u300C${name2}\u300D`;
2328
+ }
2329
+ case "ritual_list": {
2330
+ const st = this.ritualStore().read();
2331
+ if (!st.anniversaries?.length) return "\u8FD8\u6CA1\u8BB0\u7EAA\u5FF5\u65E5\u3002";
2332
+ return st.anniversaries.map((a) => `- ${a.date} ${a.name}`).join("\n");
2333
+ }
2121
2334
  case "qq_reply":
2122
2335
  return await this.qqReply(args);
2123
2336
  case "screen_unlock":
@@ -2665,6 +2878,33 @@ ${this.recent.map((r) => `- ${r}`).join("\n")}`);
2665
2878
  function failed(text) {
2666
2879
  return { text, failed: true };
2667
2880
  }
2881
+ function normalizeWhen(input) {
2882
+ const text = input.trim();
2883
+ if (!text) return "";
2884
+ const full = /^(\d{4})-(\d{1,2})-(\d{1,2})[ T](\d{1,2}):(\d{2})$/.exec(text);
2885
+ if (full) {
2886
+ const d = new Date(Number(full[1]), Number(full[2]) - 1, Number(full[3]), Number(full[4]), Number(full[5]));
2887
+ return isNaN(d.getTime()) ? "" : d.toISOString();
2888
+ }
2889
+ const hm = /^(\d{1,2}):(\d{2})$/.exec(text);
2890
+ if (hm) {
2891
+ const now = /* @__PURE__ */ new Date();
2892
+ const d = new Date(now.getFullYear(), now.getMonth(), now.getDate(), Number(hm[1]), Number(hm[2]));
2893
+ if (d.getTime() <= now.getTime()) d.setDate(d.getDate() + 1);
2894
+ return d.toISOString();
2895
+ }
2896
+ const parsed = new Date(text);
2897
+ return isNaN(parsed.getTime()) ? "" : parsed.toISOString();
2898
+ }
2899
+ function fmtWhen(iso) {
2900
+ try {
2901
+ const d = new Date(iso);
2902
+ const pad = (n) => String(n).padStart(2, "0");
2903
+ return `${d.getMonth() + 1}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
2904
+ } catch {
2905
+ return iso;
2906
+ }
2907
+ }
2668
2908
 
2669
2909
  // src/definition.ts
2670
2910
  function handsDefinition() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortico-world-hands",
3
- "version": "0.17.0",
3
+ "version": "0.18.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",