@zhin.js/plugin-rss 1.1.6 → 1.1.8

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
@@ -10,15 +10,17 @@ pnpm exec zhin install @zhin.js/plugin-rss
10
10
 
11
11
  ## 配置
12
12
 
13
- 在应用目录运行安装命令,会安装依赖并挂载到 `package.json#zhin.plugins`,实例名为 `rss`。在 `zhin.config.yml` 的实例节点修改配置:
13
+ 在应用目录运行安装命令,会安装依赖并挂载到 主配置 `plugins` 映射,实例名为 `rss`。在 `zhin.config.yml` 的实例节点修改配置:
14
14
 
15
15
  ```yaml
16
+ configVersion: 2
16
17
  plugins:
17
- rss:
18
- pollCron: "0 */5 * * * *" # 六段 Cron(秒、分、时、日、月、周),每 5 分钟
19
- maxPerGroup: 30 # 每个会话最大订阅数(默认 30)
20
- maxItems: 5 # 单次推送最多展示条数(默认 5)
21
- timeout: 15000 # 拉取 feed 的超时时间 ms(默认 15s)
18
+ rss: "@zhin.js/plugin-rss"
19
+ rss:
20
+ pollCron: "0 */5 * * * *" # 六段 Cron(秒、分、时、日、月、周),每 5 分钟
21
+ maxPerGroup: 30 # 每个会话最大订阅数(默认 30)
22
+ maxItems: 5 # 单次推送最多展示条数(默认 5)
23
+ timeout: 15000 # 拉取 feed 的超时时间 ms(默认 15s)
22
24
  ```
23
25
 
24
26
  ## 命令
@@ -35,8 +37,64 @@ plugins:
35
37
 
36
38
  如果旧订阅记录保存了错误的端点或用户身份,系统无法可靠反推出原始会话。`rss-remove` 只删除当前 Bot、当前会话的订阅,不能清理归属不明的旧记录;需由管理员在数据库中核对插件实例和完整订阅字段,备份后按记录精确删除,再在真实会话重新订阅。不要仅凭 URL 或会话 ID 批量删除,以免影响其他 Bot。缺失或非法地址不会通过猜测进行投递;字段齐全但值错误的旧记录不会被自动识别或迁移。
37
39
 
40
+ ## Console 管理员订阅
41
+
42
+ 在 Console 的任务中心选择对应 RSS 实例的“管理员 RSS 订阅”,填写 RSS / Atom 的 HTTP(S) 地址,并选择精确的账号、会话、父容器和线程后创建。先确认页面所列的抓取与发送影响;地址不接受 URL 用户名或密码。任务保存时不会抓取源站或发送测试消息。
43
+
44
+ 该入口需要持久数据库状态、支持 `sendTarget` 的 Outbound Host 和 Schedule Host 同时可用;缺少其中任一能力时不显示管理员任务 Provider,原有聊天订阅和可选内存模式继续可用。管理员操作经 Console Host 的 full scope Bearer Token 鉴权。
45
+
46
+ 1. 新建后,等待下一次实例 `pollCron` 轮询。**首次成功抓取只建立已读基线,不推送历史文章**;后续新文章才发送到所选目标。
47
+ 2. 在任务中心暂停订阅后可修改源地址,再恢复;恢复后的首次成功抓取同样只建立新基线,**不补发暂停期间的文章**。
48
+ 3. 不再需要时取消。暂停或取消不能撤回已经开始的投递。任务状态、操作回执和每条订阅自己的去重游标随数据库持久保存,重启后仍可查看。
49
+
50
+ 管理员订阅使用独立的持久池,没有伪造的聊天发送者,不读取或管理聊天订阅的表与已读记录,也不出现在 `rss-list` 中。`rss-remove`、`rss-check` 和 AI 订阅列表工具仍只处理原有聊天订阅;它们不会取消、检查或触发管理员订阅。管理员订阅可以保留完整父容器和线程,原聊天命令仍遵循上文地址限制。
51
+
52
+ ### 可选静默摘要
53
+
54
+ 每条管理员 RSS 订阅可在任务参数中填写三项:`quietTimezone`(如 `Asia/Shanghai`)、
55
+ `quietStart`(如 `22:00`)和 `quietEnd`(如 `08:00`)。三项全部省略或全部留空时关闭静默;
56
+ 启用时必须全部填写,使用有效 IANA 时区和 `HH:mm` 的 24 小时格式,开始与结束不能相同。
57
+ 不能用 `+08:00` 代替时区。
58
+
59
+ 静默区间包含开始分钟、不包含结束分钟,也支持跨午夜。每次轮询按当时的当地分钟判断,
60
+ 不改变实例轮询频率或 Host 调度时区。静默期间仍检查新文章并持久积累;退出静默后的首轮
61
+ 轮询先发送已积累的摘要,该订阅下一轮再继续抓源。已有摘要的发送不依赖源站此时能否抓取。
62
+ 如果轮询总落在静默时段,摘要会继续等到首次非静默轮询,不保证在结束时刻精确发送。
63
+
64
+ 摘要仅合并当前 owner、当前订阅与完整目标下的更新,不跨源、账号、父容器或线程合并。
65
+ 最多保留 **3 条短标题和安全链接**,实际展示还受实例 `maxItems` 限制,不保存文章正文;其余只累计数量并提示查看源站。
66
+ 计数达到 **10000** 后仍可饱和记录,继续有更新时用“至少 10000 条”展示,不伪称精确总数。
67
+ 计数沿用最新 20 条抓取窗口与最近 64 个标识的有界去重,不是源站全部文章的永久统计。
68
+
69
+ 暂停会原子清空待发摘要;编辑仍须先暂停,恢复后重新建基线,不补发之前积累或暂停期间的内容。
70
+ 取消同样清空待发内容,不能撤回已开始的 I/O。摘要在发送前持久领取;结果不明、进程中断或
71
+ 确认无法保存时停止自动投递,不重试或自动恢复,可能需要到目标核实后取消并另建订阅。
72
+ 未启用的管理员订阅、聊天创建的订阅以及定时提醒保持原行为。
73
+
74
+ 状态表示实际已知的调度或投递事实:
75
+
76
+ | 状态 | 含义和可用操作 |
77
+ | --- | --- |
78
+ | 待轮询 | 等待建立基线或检查新文章;可暂停、取消。源站抓取失败尚未发生发送,会在下轮重查 |
79
+ | 已暂停 | 不再抓取;可编辑源地址、恢复或取消 |
80
+ | 投递中或未确认 | 发送前已经持久登记;没有确认消息 ID、结果不明或进程中断时均不自动重试,只能取消。先到目标会话核实,确需继续时另建订阅 |
81
+ | 失败 | 平台明确未投递或拒绝,订阅已停止;核查原因后另建 |
82
+ | 已取消 | 不再触发后续轮询 |
83
+
84
+ “待轮询”不证明账号在线或目标可发送;源站抓取成功也不等于平台投递成功。轮询遵循 Host 的实际调度时区,不猜测用户时区,不生成没有依据的“下次执行时间”。
85
+
86
+ 每实例最多保存 **8 条管理员任务**。取消或失败的历史任务可在新建时被整理;未确认投递仍占配额,不能自动丢弃。最近 **32 条操作回执**用于响应丢失后的对账,完整状态上限 **60 KiB**;静默订阅会预留有界摘要、去重游标和投递登记空间,实际数量也受此限制。空间不足时先拒绝创建或变更,不覆盖已有任务,也不先推进游标再丢掉待发摘要。
87
+
88
+ 每轮只检查 feed 返回顺序中的前 **20 条**,通常是源的最新内容,不翻页追溯更早文章;一次轮询间隔内更新超过这个窗口时,窗口外文章不会被补抓。首次和暂停恢复后的成功抓取,只把当轮窗口建立为基线,不发送历史。后续若当轮识别出 8 条新文章、`maxItems: 5`,只发送前 5 条明细,并附“还有 3 条,请查看源站”;8 条都会进入已检查游标,另外 3 条不会在下一轮继续补发。
89
+
90
+ 每条订阅保留最近 **64 个条目标识摘要**;长期移除后重新出现在源中的文章可能再次视为新内容。这些有界游标不提供永久归档或任意断线时长的无遗漏保证。
91
+
92
+ 管理员状态保存在当前插件 owner 的 `admin-rss/v1` 键,与聊天 SQL 表无需迁移。新版本只读兼容内部 v1 格式,首次实际变更或轮询后在同一键写为内部 v2,保留旧游标和操作确认。旧程序无法读取 v2,会保留数据并停止管理员操作,而不会忽略静默策略后立即推送;回滚前应备份并核对版本。格式损坏时同样停止管理员状态操作和投递,不自动清空或当作空订阅继续执行。
93
+
38
94
  ## 工作原理
39
95
 
96
+ 以下说明适用于聊天命令创建的订阅;管理员订阅的基线和逐任务游标见上一节。
97
+
40
98
  1. `rss-add` 时验证 feed 可达性,记录订阅关系和当前会话(适配器、Bot、群/私聊)
41
99
  2. 订阅成功后标记所有已有条目为"已读",避免立即推送历史内容
42
100
  3. Cron 定时任务按配置的频率轮询所有已订阅的 feed
@@ -0,0 +1,102 @@
1
+ import { type DeliveryTarget, type OutboundHost, type PluginStateStore } from 'zhin.js';
2
+ import { generationAdmissionSource, type JsonValue, type PluginTaskAction, type PluginTaskMutation, type PluginTaskProvider, type PluginTaskSnapshot } from 'zhin.js/plugin-runtime';
3
+ import { fetchFeed, type RssConfig } from './feed.js';
4
+ import { type RssQuietHours } from './quiet-hours.js';
5
+ export declare const ADMIN_RSS_STATE_KEY = "admin-rss/v1";
6
+ type Task = {
7
+ readonly id: string;
8
+ readonly creation: string;
9
+ readonly version: string;
10
+ readonly url: string;
11
+ readonly target: DeliveryTarget;
12
+ readonly createdAt: number;
13
+ readonly state: 'scheduled' | 'paused' | 'unknown' | 'failed' | 'cancelled';
14
+ readonly seen: readonly string[] | null;
15
+ readonly claimId: string | null;
16
+ readonly lastCheckAt: number | null;
17
+ readonly lastRunAt: number | null;
18
+ readonly detail: string;
19
+ readonly quiet?: RssQuietHours;
20
+ readonly pending?: PendingSummary | null;
21
+ };
22
+ type SummaryItem = {
23
+ readonly key: string;
24
+ readonly title: string;
25
+ readonly link: string;
26
+ };
27
+ type PendingSummary = {
28
+ readonly from: number;
29
+ readonly to: number;
30
+ readonly count: number;
31
+ readonly countCapped: boolean;
32
+ readonly items: readonly SummaryItem[];
33
+ };
34
+ type Receipt = {
35
+ readonly id: string;
36
+ readonly fingerprint: string;
37
+ readonly taskId: string;
38
+ readonly operation: 'create' | PluginTaskAction;
39
+ };
40
+ export type AdministratorRssBook = {
41
+ readonly version: 1 | 2;
42
+ readonly revision: string;
43
+ readonly tasks: readonly Task[];
44
+ readonly receipts: readonly Receipt[];
45
+ };
46
+ type FeedLoader = typeof fetchFeed;
47
+ /** Administrator subscriptions use their own owner-bound durable pool and per-task cursors. */
48
+ export declare class AdministratorRssTasks implements PluginTaskProvider {
49
+ #private;
50
+ readonly title = "\u7BA1\u7406\u5458 RSS \u8BA2\u9605";
51
+ readonly description = "\u5411\u7CBE\u786E\u8D26\u53F7\u3001\u4F1A\u8BDD\u4E0E\u7EBF\u7A0B\u63A8\u9001\u65B0\u6587\u7AE0\uFF0C\u72EC\u7ACB\u4E8E\u804A\u5929\u521B\u5EFA\u7684 RSS \u8BA2\u9605";
52
+ readonly schema: {
53
+ readonly type: "object";
54
+ readonly additionalProperties: false;
55
+ readonly required: readonly ["url"];
56
+ readonly properties: {
57
+ readonly url: {
58
+ readonly type: "string";
59
+ readonly minLength: 1;
60
+ readonly maxLength: 2048;
61
+ readonly title: "RSS / Atom 地址";
62
+ readonly description: "HTTP(S) 地址,不接受 URL 用户名或密码";
63
+ };
64
+ readonly quietTimezone: {
65
+ readonly type: "string";
66
+ readonly maxLength: 128;
67
+ readonly title: "静默时区(可选)";
68
+ readonly description: "如 Asia/Shanghai;三项全部留空关闭静默,启用须全部填写";
69
+ };
70
+ readonly quietStart: {
71
+ readonly type: "string";
72
+ readonly maxLength: 5;
73
+ readonly title: "静默开始(可选)";
74
+ readonly description: "HH:mm,包含此分钟,如 22:00;须同时填写时区和结束时间";
75
+ };
76
+ readonly quietEnd: {
77
+ readonly type: "string";
78
+ readonly maxLength: 5;
79
+ readonly title: "静默结束(可选)";
80
+ readonly description: "HH:mm,不包含此分钟,如 08:00;开始和结束不能相同";
81
+ };
82
+ };
83
+ };
84
+ readonly effects: string[];
85
+ get [generationAdmissionSource](): import("zhin.js").GenerationAdmissionGate[];
86
+ constructor(state: PluginStateStore, outbound: OutboundHost, config: Readonly<RssConfig>, load?: FeedLoader);
87
+ stop(): void;
88
+ read(): Promise<PluginTaskSnapshot>;
89
+ create(input: PluginTaskMutation & Readonly<{
90
+ target: DeliveryTarget;
91
+ parameters: Readonly<Record<string, JsonValue>>;
92
+ }>): Promise<PluginTaskSnapshot>;
93
+ change(input: PluginTaskMutation & Readonly<{
94
+ id: string;
95
+ action: PluginTaskAction;
96
+ parameters?: Readonly<Record<string, JsonValue>>;
97
+ }>): Promise<PluginTaskSnapshot>;
98
+ /** Called by the plugin's one existing RSS poll registration, never by chat rss-check. */
99
+ tick(): Promise<void>;
100
+ }
101
+ export {};
102
+ //# 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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"}