dsh-whale-post-gate
Verifieddsh-whale-post-gate · v0.4.0 · MIT
配额与计费闸 + 回环闸(示例②的挂点)
Install
dsh plugin add dsh-whale-post-gate Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Readme
dsh-whale-post-gate
配额与计费闸 + 回环闸(★可换的挂点 —— 示例②)
- 提供接口:
whale.gate - 依赖接口:
whale.types(★可选)、whale.roster(★可选,用于把"一个组"算成 1 单位 ✓) - 自测:
node selftest.mjs(只看退出码:0 过/非 0 不过)
接口带 apiVersion;核心不认识任何成员名与类型标识 —— 名单与类型一律从接口取。
三道闸 + 配额(★每道都写明为什么 ✗)
★★离线件豁免整套回环闸 ✗✓(2026-10-10 从正本移植;★正本判据 29-31 +「主人 2026-10-06 令」✓): ★三道闸拦的都是"别多叫醒人一次" ✓ —— ★而离线件根本不叫醒任何人(它躺着等对方来收 ✓) ⇒ ★拦它没有任何收益,只会把"想说的话"堵在发信人手里 ✓。 ★★这正是"信只会晚到,不会不到":★"闸"该拦的是代价**,不是表达** ✓✓。 ⚠️ ★豁免的只是回环闸 ✗ —— ★配额照旧 ✓(离线桶独立计 ✓);★在线件一切照旧 ✓。
| # | 闸 | 拦什么 |
|---|---|---|
| ① | ★纯回执拒发 | ★正文去掉空白标点后只剩"收到/好的/谢谢" ⇒ 拒发(回执本该由收信方自动写 ack ✓) |
| ② | ★同对回环闸 | ★同一对(你→对方)在 pairWindowMs 内已发 pairMax 封 ⇒ 拒发,先合并成一封 ✓ |
| ③ | ★链深闸 | ★带 re 的信按父信 hop 递推,到 hopMax 跳 ⇒ 拒发(链条必须落地成结论 ✓) |
| ④ | ★配额 | ★分桶计额度;★离线件独立计(按发信次数,1 次=1 条,组发不翻倍 ✓) |
| ⑤ | ★★在线件小日上限(S8) ✗✓ | ★"手机"这类成员:★每封在线件 = 叫醒它做一次满上下文推理(最贵的一步 ✓)⇒ 按 min(自报, 天花板) 拦 ✓;★离线件不受这条限 ✓;★--force 不豁免 ✓ |
接口
| 方法 | 一句话 |
|---|---|
check(letter, ctx) |
★拦不拦:'pass'/{ reject, reason } ✓ |
record(letter, targets) |
★投递成功后才记账(被拦下的信不占额度 ✓) |
report({ as, days }) |
★查账(只读 ✓) |
cfg/localDay()/quotaUnits() |
★生效配置/日界/"这封算几个单位" ✓ |
配置
| 字段 | 默认 | 含义 |
|---|---|---|
quota.onOver |
'reject' |
★'reject' 拒发(退出码非 0、不落信箱)/★'price' 照发但计费("闸是价格闸,不是封嘴闸"✓) |
quota.dayBoundaryHour |
0 |
★日界:0 = 自然日;★写 9 = 早九点到次日早九点算一天 ✓ |
quota.types |
四个样例桶 | ★每桶 limit;★离线桶可 perSend: true(按发信次数计 ✓) |
quota.defaultLimit |
120 |
★没在表里的类型落这个桶 ✓(★否则"注册一个新类型"在闸这层就不成立 ✗) |
★★quota.bucketRules ✗ |
不配 | ★★分桶规则(数组):[{ field, equals, bucket }] —— ★从上往下第一个命中的赢 ✓;equals 不写 ⇒ "该字段有值就命中" ✓。★字段名与桶名全由配置给 ✓(本件里不出现任何具体名字 ✓)。★不配 ⇒ 行为一字不变(离线走 offline、其余按 type ✓) |
★★now(顶层) ✗ |
不配 | ★★时钟可注入 ✗✓:★给一个有限数字(钉死时刻 ✓)或函数(() => ms ✓)⇒ ★台账的"哪一天"按它算 ✓(★不配 ⇒ 真时间 ✓)。★为什么要它 ✗:★"生效时刻"这类规则(★"09:00 前按旧口径、到点后才分桶" ✓)必须能拿假时间测 ✓ —— ★原来处处裸 Date.now() ⇒ 测不了 ✓ |
★★quota.defaultBucket ✗ |
不配 | ★一条规则都没命中时落哪个桶 ✓(★配了 bucketRules 才有意义 ✓ —— 必须有,否则"带新字段的信"会掉回 type 桶 ✓) |
★★quota.phoneFlag ✗ |
不配 | ★★谁是"手机" ✗(★属性名由配置给 ✓ —— 本件不认识任何具体名字 ✓):带此属性的收件人只算在线件的小日上限 ✓。★不配 ⇒ 这道闸完全不启用 ✓ |
★★quota.phoneOnlineCap ✗ |
3 |
★★天花板 ✗:★实际额度 = min(收件人自报的 onlineCapPerDay, 这个) ✓ —— ★"收件习惯由它自己声明,而声明只能更保守" ✓(它能把上限调到 1 ✓,调不到天花板之上 ✗)。★没自报 ⇒ 用天花板 ✓("没说" ≠ "可以一直叫醒它" ✓) |
loop.ackMaxBytes |
40 |
★多长的正文才可能被当回执 ✓ |
loop.ackOnly |
中英回执词 | ★中英都认 ✓(只认中文 ⇒ 英文回执成了绕过闸的后门 ✗) |
loop.pairWindowMs |
20 分钟 |
★同对回环的时间窗 ✓ |
loop.pairMax |
3 |
★同一对在这个窗内最多发几封 ✓ |
loop.hopMax |
3 |
★链深上限 ✓ |
边界(★两条老实话,免得下一个人把它当安全边界 ✗)
- ★★链深闸不是安全边界 ✗ —— 它依赖回信人老实带
re,不带就重置。它是礼貌闸/省米闸 ✓。 - ★★
--force会整条绕过这三道闸 ✗(这是有意留的逃生门 ✓),但留痕:台账当日记forced计数 ✓。 - ★★剔完组里没人 ⇒ 拒发 ✗(配额按组算时会遇到 ✓ —— 宁可拒发,也不发一封没有收件人的信 ✓)。
换掉它(★)
★想换一套计费口径(比如按字节计费、或者干脆不限额)⇒ ★只换这一件 ✓,
★核心与别的插件一行都不用动 ✓(它们只认 check/record/report 这三个 ✓)。