@zhin.js/plugin-reminder 1.0.2 → 1.0.4

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
@@ -23,22 +23,28 @@ zhin install @zhin.js/plugin-reminder
23
23
  根应用从 `zhin.js` 继承 Stable Command Feature;上面的直接 Command 依赖确保子插件解析到同一物理 provider。安装命令会添加插件挂载;手动维护时同样使用上述清单。启用支持持久状态原子更新的 Database Host。缺少持久数据库或精确目标投递 Host 时启动明确失败,不会降级为内存保存。SQLite、PostgreSQL、MySQL 的状态能力由框架 Database Host 提供;本插件回归使用真实 SQLite 文件。
24
24
 
25
25
  ```yaml
26
+ configVersion: 2
26
27
  plugins:
27
- reminder:
28
- timezone: UTC
29
- maxTasks: 20
30
- maxTasksPerSender: 10
31
- batchSize: 5
32
- missedGraceSeconds: 15
28
+ reminder: "@zhin.js/plugin-reminder"
29
+ reminder:
30
+ timezone: UTC
31
+ maxTasks: 20
32
+ adminMaxTasks: 20
33
+ maxTasksPerSender: 10
34
+ batchSize: 5
35
+ missedGraceSeconds: 15
33
36
  ```
34
37
 
35
- 配置统一由 Schema 校验;修改仅重载插件。timezone 是有效的 IANA 时区,只影响显示,默认 UTC。绝对时间必须显式包含 `Z` 或 `±hh:mm`,没有隐式本地时间转换。多个实例应显式配置不同的 `commandNamespace`,例如 `personal` 和 `work`;使用时在指令前添加对应命名空间。Endpoint 的 `commandPrefix` 决定指令前缀。
38
+ 配置统一由 Schema 校验;修改仅重载插件。timezone 是有效的 IANA 时区,默认 UTC,用于 `create` 指令的本地日期解析和时间显示;不猜服务器或发送者时区。原有 `at`、Agent 工具和 Console 的绝对时间仍必须显式包含 `Z` 或 `±hh:mm`。多个实例应显式配置不同的 `commandNamespace`,例如 `personal` 和 `work`;使用时在指令前添加对应命名空间。Endpoint 的 `commandPrefix` 决定指令前缀。
36
39
 
37
40
  ## 指令
38
41
 
39
42
  以下示例省略 Endpoint 前缀与插件显式命名空间。各指令的 `--help` 提供参数说明。
40
43
 
41
44
  ```text
45
+ remind create 十分钟后 喝水
46
+ remind create "明天下午三点半" 提交周报
47
+ remind create
42
48
  remind after 600 十分钟后休息
43
49
  remind at 2027-01-01T09:00:00+08:00 新年会议
44
50
  remind every 3600 喝水
@@ -48,6 +54,10 @@ remind cancel <任务 ID>
48
54
 
49
55
  延迟是 1 秒至一年;周期是 60 秒至一年;内容是 1–500 字符。时间必须在未来一年内。指令权限经过框架 PermissionHost;插件没有额外权限旁路或取消其他发送者任务的管理员接口。
50
56
 
57
+ `create` 创建一次性个人提醒,不依赖 AI。缺少时间或正文时逐项补问,随后用纯文字显示绝对日期、IANA 时区、正文和完整原会话;只有明确回复 `确认` 才保存。后续回答不加命令前缀;输入 `取消` 或 `cancel` 退出后再发新命令。每步有时限且整个流程最多三分钟,输错不会无限续期。超时、取消或热重载结束旧流程,均不创建任务。确认时已过期的时间不会自动顺延。
58
+
59
+ 自然时间仅支持有限中文语法:如 `10分钟后`、`一天后`(固定 24 小时)、`今天18:30`、`明天九点`、`后天九点半`、`明天下午三点半`;也支持带 offset 的 ISO 时间。裸写 `九点`、日历周期和夏令时重复/不存在时刻不会猜测。按钮、Markdown 和图片均非必需;非聊天 CLI 没有真实发送者和交互能力时不能创建。完整操作与边界见[无需 AI 的提醒指南](https://zhin.js.org/solutions/reminders)。
60
+
51
61
  ## 执行与恢复
52
62
 
53
63
  `schedules/poll/index.ts` 每 5 秒通过当前 owner 的 ScheduleContext 解析服务,没有队列积压时通常在目标时间后 5 秒内扫描;平台投递耗时不计入此保证。服务不保存 Message、客户端或旧 snapshot。周期是固定秒数的间隔,不是墙上时钟的 cron,不受夏令时跳变影响。
@@ -60,7 +70,21 @@ remind cancel <任务 ID>
60
70
 
61
71
  这是保守的至多一次尝试策略,不是 outbox 或 exactly-once 投递。claim 后、实际发送前崩溃可能丢失一次提醒;周期任务遇未知投递也会暂停,需要用户处理。
62
72
 
63
- `maxTasks` 限制新建时每实例的活跃任务(待触发或未确认),不是降低配置后立即裁剪的记录总数;状态硬上限为 32 条记录,每发送者在会话内最多 10 条活跃任务;总编码大小限制为 60 KiB。完成或取消记录在新建任务时优先淘汰,未知任务持续占用限额,直到显式取消。减少配置上限不删除既有活跃任务,只限制继续创建。
73
+ `maxTasks` 限制每实例**聊天个人提醒池**的活跃任务(待触发或未确认);`adminMaxTasks` 单独限制**管理员提醒池**的活跃任务(待执行、暂停或未确认)。两池配额独立,默认各 20 条,活跃任务总量可以达到两项之和,不能把 `maxTasks` 当作两池合计上限。每池状态硬上限分别为 32 条、60 KiB,管理员池大小包含有界操作确认记录;个人池仍有每发送者在同一会话内最多 10 条的独立限制。完成或取消历史在新建时优先淘汰,未知任务持续占用配额。降低配置不删除既有活跃任务,只限制继续创建。
74
+
75
+ ## Console 管理员任务
76
+
77
+ 插件通过正式 `pluginTaskProviderToken` 提供独立的管理员提醒。管理员经已鉴权的 Console Host 新建时选择精确 Endpoint、会话、父容器及线程;插件不伪造聊天发送者。管理员任务不会进入 `remind list` 或个人工具列表,聊天用户即使知道管理员任务 ID 也不能用 `remind cancel` 取消;管理员任务入口同样不列出或管理聊天个人任务。
78
+
79
+ 创建和编辑参数为 `{ text, at, intervalSeconds? }`:内容 1–500 字,首次执行时间须为未来一年内、包含 `Z` 或 `±hh:mm` 的 ISO 时间;可选周期是 60 秒至一年的整数。`timezone` 按当前实例配置明确显示,固定秒数周期不按夏令时调整。更换目标必须取消旧任务并新建,不把在途或已投递消息迁移到其他会话。
80
+
81
+ - 待执行任务可暂停或取消;编辑须先暂停,编辑后仍暂停,确认后再恢复。
82
+ - 已过期的一次性暂停任务不能直接恢复,须先编辑未来时间;周期恢复会跳过错过轮次。
83
+ - 投递前持久记录为 `unknown`,仅平台确认消息 ID 后记为完成或进入下一周期。未确认任务不能恢复、编辑或重试,核对目标会话后可取消并另建。
84
+ - 取消不能撤回已经开始的外部投递,完成回写也不会覆盖在途取消。状态仅描述调度事实,不证明当前目标存在、平台已连接或消息可送达;暂停、取消不需要探测外部目标。
85
+ - 两池共用现有扫描 worker、`batchSize` 和统一 `sendTarget` 链路,不启动第二个定时调度;扫描轮转优先池,跨 tick 保留轮转位置,`batchSize: 1` 时也不会因另一池持续有待投递任务而饥饿。任务和操作确认持久化,HMR 与进程重启不会重试未确认投递。
86
+
87
+ 管理员操作带客户端 UUID `requestId` 与最近读取的 `expectedRevision`。创建时 `requestId` 即任务 ID;状态与操作确认在同一次持久 CAS 中写入。相同 ID、相同规范化请求可读取当前快照核对,ID 被用于其他请求则冲突;新操作使用旧 revision 也会冲突,需要刷新并重新确认。快照 `receipts` 只包含最近至多 128 次操作的 ID、任务 ID 和操作类型,**不是平台投递回执**,不包含请求摘要。历史任务淘汰不重置 revision;确认记录已淘汰时不能自动用旧 revision 重放。初次空列表的 revision 稳定,读取不会产生新的任务版本。
64
88
 
65
89
  ## 显式本地检查
66
90
 
@@ -0,0 +1,12 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { defineCommand } from 'zhin.js/command';
3
+ import { createPersonalReminder } from "../../../lib/create-reminder.js";
4
+ const params = {
5
+ when: { type: 'string', optional: true, max: 200, description: '提醒时间,例如 10分钟后、明天上午九点;含空格须加引号' },
6
+ text: { type: 'text', optional: true, max: 500, description: '个人提醒正文;缺省时会逐步补问' },
7
+ };
8
+ export default defineCommand({
9
+ description: '逐步补全时间和正文,明确确认后创建一次性个人提醒',
10
+ params,
11
+ execute: context => createPersonalReminder(context, context.params),
12
+ });
@@ -0,0 +1,14 @@
1
+ import { defineCommand, type CommandMessage } from 'zhin.js/command';
2
+ import type { ReminderConfig } from '../../../src/config.js';
3
+ import { createPersonalReminder } from '../../../src/create-reminder.js';
4
+
5
+ const params = {
6
+ when: { type: 'string', optional: true, max: 200, description: '提醒时间,例如 10分钟后、明天上午九点;含空格须加引号' },
7
+ text: { type: 'text', optional: true, max: 500, description: '个人提醒正文;缺省时会逐步补问' },
8
+ } as const;
9
+
10
+ export default defineCommand<ReminderConfig, string | undefined, CommandMessage, typeof params>({
11
+ description: '逐步补全时间和正文,明确确认后创建一次性个人提醒',
12
+ params,
13
+ execute: context => createPersonalReminder(context, context.params),
14
+ });
@@ -0,0 +1,82 @@
1
+ import { type DeliveryTarget, type OutboundHost, type PluginStateStore } from 'zhin.js';
2
+ import { generationAdmissionSource, type JsonValue, type PluginTask, type PluginTaskAction, type PluginTaskMutation, type PluginTaskProvider, type PluginTaskSnapshot } from 'zhin.js/plugin-runtime';
3
+ import type { ReminderConfig } from './config.js';
4
+ export declare const ADMIN_REMINDER_STATE_KEY = "admin-reminders/v1";
5
+ type AdminTask = {
6
+ readonly id: string;
7
+ readonly creation: string;
8
+ readonly target: DeliveryTarget;
9
+ readonly text: string;
10
+ readonly createdAt: number;
11
+ readonly nextRunAt: number;
12
+ readonly intervalMs: number | null;
13
+ readonly state: Exclude<PluginTask['state'], 'running'>;
14
+ readonly claimId: string | null;
15
+ readonly lastRunAt: number | null;
16
+ readonly detail: string;
17
+ };
18
+ type MutationReceipt = {
19
+ readonly id: string;
20
+ readonly fingerprint: string;
21
+ readonly taskId: string;
22
+ readonly operation: 'create' | PluginTaskAction;
23
+ };
24
+ export type AdminReminderBook = {
25
+ readonly version: 1;
26
+ readonly revision: string;
27
+ readonly tasks: readonly AdminTask[];
28
+ readonly receipts: readonly MutationReceipt[];
29
+ };
30
+ /** A separate administrator state pool; no sender identity and no access to chat reminders. */
31
+ export declare class AdministratorReminderTasks implements PluginTaskProvider {
32
+ #private;
33
+ private readonly state;
34
+ private readonly outbound;
35
+ private readonly config;
36
+ readonly title = "\u7BA1\u7406\u5458\u63D0\u9192";
37
+ readonly description = "\u72EC\u7ACB\u4E8E\u804A\u5929\u4E2A\u4EBA\u63D0\u9192\uFF0C\u5411\u6307\u5B9A\u8D26\u53F7\u3001\u4F1A\u8BDD\u53CA\u7EBF\u7A0B\u53D1\u9001\u4E00\u6B21\u6027\u6216\u56FA\u5B9A\u5468\u671F\u63D0\u9192";
38
+ readonly effects: string[];
39
+ readonly schema: {
40
+ readonly type: "object";
41
+ readonly additionalProperties: false;
42
+ readonly required: readonly ["text", "at"];
43
+ readonly properties: {
44
+ readonly text: {
45
+ readonly type: "string";
46
+ readonly minLength: 1;
47
+ readonly maxLength: 500;
48
+ readonly title: "提醒内容";
49
+ };
50
+ readonly at: {
51
+ readonly type: "string";
52
+ readonly format: "date-time";
53
+ readonly title: "首次执行时间";
54
+ readonly description: "未来一年内、显式包含 Z 或 ±hh:mm 的 ISO 时间";
55
+ };
56
+ readonly intervalSeconds: {
57
+ readonly type: "integer";
58
+ readonly minimum: 60;
59
+ readonly maximum: number;
60
+ readonly title: "周期秒数(可选)";
61
+ };
62
+ };
63
+ };
64
+ get [generationAdmissionSource](): import("zhin.js").GenerationAdmissionGate[];
65
+ constructor(state: PluginStateStore, outbound: OutboundHost, config: ReminderConfig);
66
+ stop(): void;
67
+ read(): Promise<PluginTaskSnapshot>;
68
+ create(input: PluginTaskMutation & Readonly<{
69
+ target: DeliveryTarget;
70
+ parameters: Readonly<Record<string, JsonValue>>;
71
+ }>): Promise<PluginTaskSnapshot>;
72
+ change(input: PluginTaskMutation & Readonly<{
73
+ id: string;
74
+ action: PluginTaskAction;
75
+ parameters?: Readonly<Record<string, JsonValue>>;
76
+ }>): Promise<PluginTaskSnapshot>;
77
+ /** Called only by the existing ReminderService worker, within its shared batch limit. */
78
+ claim(now: number, claimId: string): Promise<AdminTask | undefined>;
79
+ deliver(task: AdminTask): Promise<void>;
80
+ }
81
+ export {};
82
+ //# sourceMappingURL=admin-tasks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-tasks.d.ts","sourceRoot":"","sources":["../src/admin-tasks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,294 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { createDeliveryTarget } from 'zhin.js';
3
+ import { PluginTaskError, createGenerationAdmissionGate, generationAdmissionSource } from 'zhin.js/plugin-runtime';
4
+ import { EndpointDeliveryError } from '@zhin.js/im-contract';
5
+ import { MAX_DELAY_SECONDS, nextPeriodicTime, parseReminderInstant } from './timing.js';
6
+ export const ADMIN_REMINDER_STATE_KEY = 'admin-reminders/v1';
7
+ const activeStates = new Set(['scheduled', 'paused', 'unknown']);
8
+ const uuidPattern = /^[\da-f]{8}-(?:[\da-f]{4}-){3}[\da-f]{12}$/iu;
9
+ /** A separate administrator state pool; no sender identity and no access to chat reminders. */
10
+ export class AdministratorReminderTasks {
11
+ state;
12
+ outbound;
13
+ config;
14
+ title = '管理员提醒';
15
+ description = '独立于聊天个人提醒,向指定账号、会话及线程发送一次性或固定周期提醒';
16
+ effects = ['创建后将在指定时间通过当前插件实例向目标会话发送提醒;暂停或取消不能撤回已开始的投递', '未确认的投递不会自动重试或恢复,需核实目标会话后取消并另建任务'];
17
+ schema = { type: 'object', additionalProperties: false, required: ['text', 'at'], properties: {
18
+ text: { type: 'string', minLength: 1, maxLength: 500, title: '提醒内容' },
19
+ at: { type: 'string', format: 'date-time', title: '首次执行时间', description: '未来一年内、显式包含 Z 或 ±hh:mm 的 ISO 时间' },
20
+ intervalSeconds: { type: 'integer', minimum: 60, maximum: MAX_DELAY_SECONDS, title: '周期秒数(可选)' },
21
+ } };
22
+ #stopped = false;
23
+ #admission = createGenerationAdmissionGate();
24
+ get [generationAdmissionSource]() { return [this.#admission]; }
25
+ constructor(state, outbound, config) {
26
+ this.state = state;
27
+ this.outbound = outbound;
28
+ this.config = config;
29
+ }
30
+ stop() { this.#stopped = true; }
31
+ async read() {
32
+ return this.#admit(async () => this.#snapshot(readBook(await this.state.get(ADMIN_REMINDER_STATE_KEY))));
33
+ }
34
+ async create(input) {
35
+ return this.#admit(() => this.#create(input));
36
+ }
37
+ async #create(input) {
38
+ this.#assertLive();
39
+ assertMutation(input);
40
+ let target;
41
+ try {
42
+ target = createDeliveryTarget({ conversation: input.target });
43
+ }
44
+ catch {
45
+ throw invalid('请提供完整有效的目标账号、会话、父容器和线程');
46
+ }
47
+ const parameters = parseParameters(input.parameters);
48
+ const fingerprint = digest({ operation: 'create', target, parameters });
49
+ const revision = randomUUID();
50
+ const now = Date.now();
51
+ const task = { id: input.requestId, creation: fingerprint, target, text: parameters.text, createdAt: now,
52
+ nextRunAt: Date.parse(parameters.at), intervalMs: parameters.intervalSeconds === undefined ? null : parameters.intervalSeconds * 1000,
53
+ state: 'scheduled', claimId: null, lastRunAt: null, detail: '等待执行' };
54
+ const result = await this.state.update(ADMIN_REMINDER_STATE_KEY, current => {
55
+ this.#assertLive();
56
+ const book = readBook(current);
57
+ if (reconcile(book, input.requestId, fingerprint))
58
+ return book;
59
+ assertRevision(book, input.expectedRevision);
60
+ const existing = book.tasks.find(item => item.id === input.requestId);
61
+ if (existing) {
62
+ if (existing.creation !== fingerprint)
63
+ throw conflict('同一请求 ID 已用于其他内容');
64
+ return book;
65
+ }
66
+ assertFuture(task.nextRunAt, now);
67
+ const active = book.tasks.filter(item => activeStates.has(item.state));
68
+ if (active.length >= this.config.adminMaxTasks)
69
+ throw invalid('管理员提醒池已达上限,请先取消任务;聊天个人提醒使用独立配额');
70
+ const historyLimit = Math.max(0, this.config.adminMaxTasks - active.length - 1);
71
+ const history = historyLimit ? book.tasks.filter(item => !activeStates.has(item.state)).slice(-historyLimit) : [];
72
+ return bounded({ ...book, revision, tasks: [...active, ...history, task], receipts: receipt(book, input.requestId, fingerprint, task.id, 'create') });
73
+ });
74
+ return this.#snapshot(readBook(result));
75
+ }
76
+ async change(input) {
77
+ return this.#admit(() => this.#change(input));
78
+ }
79
+ async #change(input) {
80
+ this.#assertLive();
81
+ assertMutation(input);
82
+ if (typeof input.id !== 'string' || !uuidPattern.test(input.id) || !['pause', 'resume', 'cancel', 'update'].includes(input.action))
83
+ throw invalid('任务 ID 或操作无效');
84
+ if (input.action !== 'update' && input.parameters !== undefined)
85
+ throw invalid('仅编辑操作接受参数');
86
+ const parameters = input.action === 'update' ? parseParameters(input.parameters) : undefined;
87
+ const fingerprint = digest({ operation: 'change', id: input.id, action: input.action, parameters: parameters ?? null });
88
+ const revision = randomUUID();
89
+ const now = Date.now();
90
+ const result = await this.state.update(ADMIN_REMINDER_STATE_KEY, current => {
91
+ this.#assertLive();
92
+ const book = readBook(current);
93
+ if (reconcile(book, input.requestId, fingerprint))
94
+ return book;
95
+ assertRevision(book, input.expectedRevision);
96
+ const task = book.tasks.find(item => item.id === input.id);
97
+ if (!task)
98
+ throw new PluginTaskError('not_found', '未找到当前实例的管理员任务;聊天任务不在此入口管理');
99
+ let changed;
100
+ switch (input.action) {
101
+ case 'pause':
102
+ if (task.state !== 'scheduled')
103
+ throw invalid('仅待执行任务可以暂停;未确认投递不能暂停或恢复');
104
+ changed = { ...task, state: 'paused', detail: '管理员暂停' };
105
+ break;
106
+ case 'resume': {
107
+ if (task.state !== 'paused')
108
+ throw invalid('仅已暂停任务可以恢复;未确认投递不能恢复或重试');
109
+ if (task.intervalMs === null && task.nextRunAt <= now)
110
+ throw invalid('一次性任务时间已过,请先编辑为未来时间再恢复');
111
+ const nextRunAt = task.nextRunAt <= now && task.intervalMs !== null ? nextPeriodicTime(task.nextRunAt, task.intervalMs, now) : task.nextRunAt;
112
+ changed = { ...task, state: 'scheduled', nextRunAt, detail: nextRunAt !== task.nextRunAt ? '已恢复,并跳过错过的周期轮次' : '管理员恢复' };
113
+ break;
114
+ }
115
+ case 'update':
116
+ if (task.state !== 'paused')
117
+ throw invalid('编辑前请先暂停任务;未确认投递不能编辑');
118
+ assertFuture(Date.parse(parameters.at), now);
119
+ changed = { ...task, text: parameters.text, nextRunAt: Date.parse(parameters.at),
120
+ intervalMs: parameters.intervalSeconds === undefined ? null : parameters.intervalSeconds * 1000, detail: '已编辑,保持暂停;请确认后恢复' };
121
+ break;
122
+ case 'cancel':
123
+ if (!activeStates.has(task.state))
124
+ throw invalid('任务已结束,不能再次取消');
125
+ changed = { ...task, state: 'cancelled', detail: '管理员取消;已经开始的投递无法撤回' };
126
+ break;
127
+ }
128
+ return bounded({ ...book, revision, tasks: book.tasks.map(item => item.id === task.id ? changed : item), receipts: receipt(book, input.requestId, fingerprint, task.id, input.action) });
129
+ });
130
+ return this.#snapshot(readBook(result));
131
+ }
132
+ /** Called only by the existing ReminderService worker, within its shared batch limit. */
133
+ async claim(now, claimId) {
134
+ this.#assertLive();
135
+ const revision = randomUUID();
136
+ const book = await this.state.update(ADMIN_REMINDER_STATE_KEY, current => {
137
+ const book = readBook(current);
138
+ let claimed = false;
139
+ let changed = false;
140
+ const tasks = book.tasks.map(task => {
141
+ if (task.state !== 'scheduled' || task.nextRunAt > now)
142
+ return task;
143
+ if (task.intervalMs !== null && now - task.nextRunAt > this.config.missedGraceSeconds * 1000) {
144
+ changed = true;
145
+ return { ...task, nextRunAt: nextPeriodicTime(task.nextRunAt, task.intervalMs, now), detail: '已跳过错过的周期轮次' };
146
+ }
147
+ if (claimed)
148
+ return task;
149
+ changed = claimed = true;
150
+ return { ...task, state: 'unknown', claimId, lastRunAt: now, detail: '投递中或未确认;不会自动重试' };
151
+ });
152
+ return changed ? bounded({ ...book, revision, tasks }) : current;
153
+ });
154
+ return book?.tasks.find(task => task.claimId === claimId);
155
+ }
156
+ async deliver(task) {
157
+ if (this.#stopped)
158
+ return;
159
+ let state = 'unknown';
160
+ let detail = '平台未确认投递,请核实目标会话后取消并另建任务;不会自动重试';
161
+ try {
162
+ const id = await this.outbound.sendTarget(task.target, `⏰ 提醒:${task.text}`);
163
+ if (id) {
164
+ state = task.intervalMs === null ? 'succeeded' : 'scheduled';
165
+ detail = '已确认投递';
166
+ }
167
+ }
168
+ catch (error) {
169
+ if (error instanceof EndpointDeliveryError && error.disposition !== 'unknown') {
170
+ state = 'failed';
171
+ detail = '平台明确未投递或拒绝;任务已停止';
172
+ }
173
+ }
174
+ const revision = randomUUID();
175
+ await this.state.update(ADMIN_REMINDER_STATE_KEY, current => {
176
+ const book = readBook(current);
177
+ let changed = false;
178
+ const tasks = book.tasks.map(item => {
179
+ if (item.id !== task.id || item.claimId !== task.claimId || item.state !== 'unknown')
180
+ return item;
181
+ changed = true;
182
+ return { ...item, state, detail, ...(state === 'scheduled' && task.intervalMs !== null
183
+ ? { nextRunAt: nextPeriodicTime(task.nextRunAt, task.intervalMs, Date.now()) } : {}) };
184
+ });
185
+ return changed ? bounded({ ...book, revision, tasks }) : current;
186
+ });
187
+ }
188
+ #assertLive() { if (this.#stopped)
189
+ throw new PluginTaskError('unavailable', '管理员提醒服务属于已停止的插件实例'); }
190
+ async #admit(operation) {
191
+ this.#assertLive();
192
+ const release = this.#admission.acquire();
193
+ if (!release)
194
+ throw new PluginTaskError('unavailable', '管理员提醒服务属于未发布或已退役的 generation');
195
+ try {
196
+ return await operation();
197
+ }
198
+ finally {
199
+ release();
200
+ }
201
+ }
202
+ #snapshot(book) {
203
+ return { revision: book.revision, receipts: book.receipts.map(item => ({ requestId: item.id, taskId: item.taskId, operation: item.operation })), tasks: book.tasks.map(task => ({
204
+ id: task.id, ownership: 'administrator', title: taskTitle(task.text), target: task.target, state: task.state, createdAt: task.createdAt,
205
+ ...(task.state === 'scheduled' || task.state === 'paused' ? { nextRunAt: task.nextRunAt } : {}), ...(task.lastRunAt === null ? {} : { lastRunAt: task.lastRunAt }),
206
+ timezone: this.config.timezone, detail: `${task.detail};状态仅表示调度事实,不代表平台连接或投递健康`,
207
+ parameters: { text: task.text, at: new Date(task.nextRunAt).toISOString(), ...(task.intervalMs === null ? {} : { intervalSeconds: task.intervalMs / 1000 }) },
208
+ actions: task.state === 'scheduled' ? ['pause', 'cancel'] : task.state === 'paused' ? ['update', 'resume', 'cancel'] : task.state === 'unknown' ? ['cancel'] : [],
209
+ })) };
210
+ }
211
+ }
212
+ function assertMutation(input) {
213
+ if (typeof input.requestId !== 'string' || !uuidPattern.test(input.requestId) || typeof input.expectedRevision !== 'string' || !input.expectedRevision || input.expectedRevision.length > 128)
214
+ throw invalid('操作须包含 UUID requestId 和读取到的 expectedRevision');
215
+ }
216
+ function parseParameters(value) {
217
+ if (!value || Array.isArray(value) || Object.keys(value).some(key => !['text', 'at', 'intervalSeconds'].includes(key))
218
+ || typeof value.text !== 'string' || !value.text.trim() || value.text.length > 500 || typeof value.at !== 'string'
219
+ || (value.intervalSeconds !== undefined && (typeof value.intervalSeconds !== 'number' || !Number.isSafeInteger(value.intervalSeconds) || value.intervalSeconds < 60 || value.intervalSeconds > MAX_DELAY_SECONDS)))
220
+ throw invalid('参数须包含 1–500 字提醒内容、带 offset 的 ISO 时间及可选的 60 秒至一年整数周期');
221
+ let at;
222
+ try {
223
+ at = parseReminderInstant(value.at);
224
+ }
225
+ catch {
226
+ throw invalid('提醒时间须为有效且带 Z 或 ±hh:mm 的 ISO 时间');
227
+ }
228
+ return { text: value.text, at: new Date(at).toISOString(), ...(value.intervalSeconds === undefined ? {} : { intervalSeconds: value.intervalSeconds }) };
229
+ }
230
+ function assertFuture(at, now) {
231
+ if (!Number.isSafeInteger(at) || at <= now || at - now > MAX_DELAY_SECONDS * 1000)
232
+ throw invalid('提醒时间须在未来一年内');
233
+ }
234
+ function digest(value) { return createHash('sha256').update(JSON.stringify(value)).digest('hex'); }
235
+ function taskTitle(text) {
236
+ if (text.length <= 200)
237
+ return text;
238
+ let title = '';
239
+ for (const character of text) {
240
+ if (title.length + character.length > 199)
241
+ break;
242
+ title += character;
243
+ }
244
+ return `${title}…`;
245
+ }
246
+ function reconcile(book, id, fingerprint) {
247
+ const previous = book.receipts.find(item => item.id === id);
248
+ if (!previous)
249
+ return false;
250
+ if (previous.fingerprint !== fingerprint)
251
+ throw conflict('同一操作请求 ID 已用于其他内容,禁止重放');
252
+ return true;
253
+ }
254
+ function assertRevision(book, revision) { if (book.revision !== revision)
255
+ throw conflict('任务列表已变化,请刷新后重新确认操作'); }
256
+ function receipt(book, id, fingerprint, taskId, operation) {
257
+ return [...book.receipts, { id, fingerprint, taskId, operation }].slice(-128);
258
+ }
259
+ function invalid(message) { return new PluginTaskError('invalid_input', message); }
260
+ function conflict(message) { return new PluginTaskError('conflict', message); }
261
+ function bounded(book) {
262
+ if (book.tasks.length > 32 || new TextEncoder().encode(JSON.stringify(book)).byteLength > 60 * 1024)
263
+ throw invalid('管理员提醒池状态达到 32 条或 60 KiB 上限,请先取消并整理任务');
264
+ return book;
265
+ }
266
+ function readBook(value) {
267
+ if (value === undefined)
268
+ return { version: 1, revision: '0', tasks: [], receipts: [] };
269
+ try {
270
+ if (value.version !== 1 || typeof value.revision !== 'string' || !value.revision || !Array.isArray(value.tasks) || !Array.isArray(value.receipts) || value.receipts.length > 128)
271
+ throw new Error('invalid book');
272
+ for (const item of value.receipts)
273
+ if (!item || !uuidPattern.test(item.id) || !uuidPattern.test(item.taskId)
274
+ || !['create', 'pause', 'resume', 'cancel', 'update'].includes(item.operation) || !/^[\da-f]{64}$/u.test(item.fingerprint))
275
+ throw new Error('invalid receipt');
276
+ const ids = new Set();
277
+ for (const task of value.tasks) {
278
+ if (!task || !uuidPattern.test(task.id) || ids.has(task.id) || typeof task.creation !== 'string' || !/^[\da-f]{64}$/u.test(task.creation)
279
+ || typeof task.text !== 'string' || !task.text.trim() || task.text.length > 500 || !Number.isSafeInteger(task.createdAt) || !Number.isSafeInteger(task.nextRunAt)
280
+ || (task.intervalMs !== null && (!Number.isSafeInteger(task.intervalMs) || task.intervalMs < 60_000 || task.intervalMs > MAX_DELAY_SECONDS * 1000))
281
+ || !['scheduled', 'paused', 'unknown', 'succeeded', 'failed', 'cancelled'].includes(task.state)
282
+ || (task.claimId !== null && (typeof task.claimId !== 'string' || !task.claimId)) || (task.lastRunAt !== null && !Number.isSafeInteger(task.lastRunAt))
283
+ || typeof task.detail !== 'string' || !Number.isFinite(new Date(task.nextRunAt).getTime()))
284
+ throw new Error('invalid task');
285
+ createDeliveryTarget({ conversation: task.target });
286
+ ids.add(task.id);
287
+ }
288
+ return bounded(value);
289
+ }
290
+ catch {
291
+ throw new PluginTaskError('unavailable', '管理员提醒持久状态格式无效,已停止投递');
292
+ }
293
+ }
294
+ //# sourceMappingURL=admin-tasks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-tasks.js","sourceRoot":"","sources":["../src/admin-tasks.ts"],"names":[],"mappings":"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"}
package/lib/config.d.ts CHANGED
@@ -2,12 +2,14 @@ import { Schema } from 'zhin.js';
2
2
  export declare const reminderConfig: Schema<Schema.InputRecord<{
3
3
  timezone: Schema<string, string> & Schema.Present;
4
4
  maxTasks: Schema<number, number> & Schema.Present;
5
+ adminMaxTasks: Schema<number, number> & Schema.Present;
5
6
  maxTasksPerSender: Schema<number, number> & Schema.Present;
6
7
  batchSize: Schema<number, number> & Schema.Present;
7
8
  missedGraceSeconds: Schema<number, number> & Schema.Present;
8
9
  }>, {
9
10
  timezone: string;
10
11
  maxTasks: number;
12
+ adminMaxTasks: number;
11
13
  maxTasksPerSender: number;
12
14
  batchSize: number;
13
15
  missedGraceSeconds: number;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,eAAO,MAAM,cAAc;;;;;;;;;;;;OAMzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAElE,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG7D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;OAOzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAElE,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAM7D"}
package/lib/config.js CHANGED
@@ -1,13 +1,16 @@
1
1
  import { Schema } from 'zhin.js';
2
2
  export const reminderConfig = Schema.object({
3
- timezone: Schema.string().default('UTC').description('显示提醒时间的 IANA 时区;绝对时间仍必须提供 ISO offset').effect('reload'),
4
- maxTasks: Schema.number().min(1).max(32).step(1).default(20).description('每个插件实例新建时允许的活跃任务上限(待触发或未确认);降低上限不删除既有任务').effect('reload'),
3
+ timezone: Schema.string().default('UTC').description('解释 create 自然时间并显示提醒的 IANA 时区;ISO 绝对时间仍须显式 offset').effect('reload'),
4
+ maxTasks: Schema.number().min(1).max(32).step(1).default(20).description('每实例聊天个人提醒池的活跃任务上限;与管理员池独立,降低上限不删除既有任务').effect('reload'),
5
+ adminMaxTasks: Schema.number().min(1).max(32).step(1).default(20).description('每实例管理员提醒池的活跃任务上限(待执行、暂停或未确认);与聊天池独立,两池总量可达上限之和').effect('reload'),
5
6
  maxTasksPerSender: Schema.number().min(1).max(10).step(1).default(10).description('同一发送者在同一会话的任务上限').effect('reload'),
6
7
  batchSize: Schema.number().min(1).max(5).step(1).default(5).description('每次扫描最多投递的任务数').effect('reload'),
7
8
  missedGraceSeconds: Schema.number().min(5).max(60).step(1).default(15).description('周期轮次超过此延迟后跳过;停机期间不补历史轮次').effect('reload'),
8
9
  });
9
10
  export function assertReminderTimezone(timezone) {
10
11
  try {
12
+ if (!timezone || timezone.length > 100 || timezone !== timezone.trim() || /^[+-]/u.test(timezone))
13
+ throw new RangeError('Invalid IANA timezone');
11
14
  new Intl.DateTimeFormat('zh-CN', { timeZone: timezone }).format(0);
12
15
  }
13
16
  catch {
package/lib/config.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,sCAAsC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC7G,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,yCAAyC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACpI,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACrH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACxG,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;CAC/H,CAAC,CAAC;AAIH,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,IAAI,CAAC;QAAC,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAC3E,MAAM,CAAC;QAAC,MAAM,IAAI,SAAS,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAAC,CAAC;AAC1E,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,kDAAkD,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACzH,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,uCAAuC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAClI,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,gDAAgD,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAChJ,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACrH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACxG,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;CAC/H,CAAC,CAAC;AAIH,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,IAAI,CAAC;QACH,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,KAAK,QAAQ,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,uBAAuB,CAAC,CAAC;QACjJ,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,CAAC;QAAC,MAAM,IAAI,SAAS,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAAC,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { CommandContext } from 'zhin.js/command';
2
+ import type { ReminderConfig } from './config.js';
3
+ /** Personal reminder entry: no persistence is touched before explicit confirmation. */
4
+ export declare function createPersonalReminder(context: CommandContext<ReminderConfig>, parameters: Readonly<{
5
+ when?: string;
6
+ text?: string;
7
+ }>): Promise<string | undefined>;
8
+ //# sourceMappingURL=create-reminder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-reminder.d.ts","sourceRoot":"","sources":["../src/create-reminder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAalD,uFAAuF;AACvF,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,cAAc,CAAC,cAAc,CAAC,EACvC,UAAU,EAAE,QAAQ,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GACrD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuF7B"}