dsh-whale-post-deliver
English: deliver README in English
投递策略:离线留在信箱等人/在线立刻投出(可换的挂点 —— 示例①)
- 提供接口:
whale.deliver
- 依赖接口:
whale.roster(可选 —— 用不到就不取)
- 自测:
node selftest.mjs(只看退出码:0 过/非 0 不过)
接口带 apiVersion;核心不认识任何成员名与类型标识 —— 名单与类型一律从接口取。
它只回答一个问题(别让它管别的)
这封信现在投进对方的会话,还是留在信箱里等人? —— 三态:
| 返回 | 什么时候 |
|---|
'delivered' | 在线件,且对方有活体会话 ⇒ 真投出去了 |
'kept' | 离线件;或在线件但对方没有活体会话 ⇒ 不投也不消费 |
'rejected' | 收件人在 blocked 名单里 ⇒ 信不进它的信箱 |
接口
| 方法 | 一句话 |
|---|
deliver(letter, ctx) | 就是上面那个问题的答案;ctx.targets 由核心给(组已展开) |
blocked(id) | 这个收件人被明确挡掉了吗 |
sessionOf(id) | 那个会话此刻活着吗(就是下面那个探针) |
dormancyOf(id) | { state, days, lastMs, source } —— 谁还醒着(见下"休眠判定") |
log | 判定留痕(自测与排障用) |
休眠判定(S10 v2 —— 2026-10-10 从班级的设计文档移植)
v1 的教训(设计文档原话):「不许拿「邮差还活着」当「它在读信」 ——
真局一照就穿帮:小毛咪的邮差每 10 分钟替她续 hello,可她本人不读信(量到的是邮差,不是她)」
所以判定走两路,都带 source,不许把推断说成声明:
| 路 | 依据 | source |
|---|
| ① 明示位 | 配置给的 declaredDormant(id) / 名单里那个标记 | 'declared' |
| ② 推断 | 它信箱里最老的一封没读的信,躺了多久 —— 这才是"没读信"的明证 | 'inferred' |
四档状态:awake(dormantSoftDays 以内)/quiet(soft~hard)/dormant(≥ hard)/
unknown —— "我们不知道" —— 不是"它休眠"
两条硬纪律:
- 没有积压 ⇒ 不许判休眠("没信可读 ≠ 不读信" —— 所以才会是
unknown)
- 只用落盘 mtime(不信内容里的时间戳 —— "这封信什么时候到的"是盘说了算)
行为:明示休眠的收件人 ⇒ deliver 返回 'rejected'(与核心同口径:--force 也不豁免);
只是 quiet/inferred ⇒ 一律不退回(不许自己猜休眠)。
接真引擎的唯一接线口:sessionOf
核心**不认识"引擎""会话"**这些概念 —— 它只认一个探针函数:
sessionOf(id) => ({ live: true, inject: (text) => { /* 把这段文字送进那个会话 */ } }) // 或 undefined
两种接法(等价):
createBus({ probes: { sessionOf } }) —— 直接喂给核心;
- 把本件配好
sessionOf —— 核心会自动取 services.deliver.sessionOf。
规则三句(别混):
- 它只影响"在线件能不能真投出去" —— 探针说"没这个会话"(或压根没接探针)⇒ 在线件判
'kept':
信留在 inbox/ 等人(只晚到,不会不到);
pump 的消费授权不看它 —— 要消费得给 inject 或声明 reader: true
("会话活着" ≠ "信交到了读者手里");
- 给了
inject 就会真调它,它抛异常 ⇒ 不消费 —— 交不出去的信,绝不当成交出去了。
配置
| 字段 | 默认 | 含义 |
|---|
sessionOf | 不接 | 上面那个探针(不接 ⇒ 在线件一律判 'kept') |
blocked | [] | 明确挡掉的收件人 id(信不进它的信箱) |
dormantSoftDays | 3 | "醒着"的天数上限(超过它 ⇒ quiet) |
dormantHardDays | 7 | "休眠"的门槛(≥ 它 ⇒ dormant) |
oldestPendingMs | 不接 | 推断路的探针:(id) => 该收件箱里"最老的一封没读的信"的落盘时间(ms;0 ⇒ 没有积压)。不接 ⇒ 如实报 unknown("不知道"就说不知道) |
declaredDormant | 不接 | 明示位探针:(id) => boolean(**"谁被标了休眠"**由名单/配置给 —— 本件不认识具体属性名) |
now | 不接 | 注入时钟(自测用) |
换掉它(这是"挂点"的意义)
想做别的投递策略(比如"在线件先发短信再投")⇒ 只换这一件,
核心与别的插件一行都不用动(它们只认 deliver(letter, ctx) 这个接口)。