运维 | ANI-RSS 的微信通知:一个占位符引发的 400

有些事,做完只需要一分钟;有些坑,填平却要绕一个大圈。给 ANI-RSS 加上微信通知,本以为是「填个 Webhook 地址」的小事,最后却变成了一场跟源码较劲的短跑。

写在前面

家里的自动追番链路是 ANI-RSS 加 qBittorrent:蜜柑的 RSS 一更新,ANI-RSS 自动拉取、识别集数、推给下载器,番剧就安安静静地躺在媒体库里。链路跑通了大半年,唯一缺的是一双「眼睛」——新番开始下载了,人在外面看不到;下载失败了,也要等回家才发现。

于是想加个通知:番剧开始下载、下载完成、缺集、出错,都推送到微信。推送服务选了 WxPusher,免费、稳定、微信直达,注册应用拿一个 token,用户扫码关注拿到自己的 UID,就能开始推送。

配置过程

ANI-RSS 的通知设置里没有 WxPusher 的现成选项,但有一个通用的 WebHook 类型:可以自定义 Method、URL、Header 和 Body,本质上就是向任意 HTTP 接口 POST 一段 JSON。WxPusher 也恰好提供标准的消息推送接口——把 APP token 和收到人的 UID 放进请求体,就能把内容推到微信。

配置本身很简单:

  • 通知类型:WebHook
  • Method:POST
  • URL:WxPusher 的消息接口
  • Header:Content-Type: application/json
  • Body:一个 JSON,把通知模板作为消息内容填进去

第一版配置写完,点击测试——400。

那个占位符

ANI-RSS 的通知模板支持占位符替换:${title}、${episode}、${subgroup} 这些会被换成真实的番剧信息。官方文档里的 WebHook 示例写的是把 ${notification} 放到 Body 里,让 ANI-RSS 把渲染好的整段通知文本填进去。

但实测返回 400。单独用 curl 测 WxPusher 接口是通的,说明问题出在 ANI-RSS 生成的请求体上。

翻源码(WebHookNotification.java + BaseNotification.java)才看明白:

  1. ANI-RSS 会先走一遍 replaceNotificationTemplate,把 Body 里的 ${notification} 提前替换成一段包含了换行符的原始多行模板文本;
  2. 之后才轮到对 ${message} 这类占位符做 JSON 转义处理。

于是 Body 里如果写 ${notification},最终得到的是一段没有经过 JSON 转义的多行文本,直接把 JSON 结构撑破了——响应自然是 400。

解法只有一行字的区别:把 Body 里的占位符从 ${notification} 改成 ${message}。后者不会被提前替换,会在 JSON 转义之后才填入,生成的就是合法 JSON。

教训:官方文档示例的占位符不一定比另一条路径更正确。 同样是「整段通知文本」,两个占位符在代码里的处理时机完全不同。遇到 400 先怀疑占位符在流水线里的位置,而不是接口本身。

验证

改完配置后,从 ANI-RSS 容器内向 WxPusher 接口发了一条真实消息:

{
  "code": 1000,
  "msg": "处理成功",
  "data": [{ "status": "创建发送任务成功" }]
}

几秒钟后,手机微信上弹出了那条写着「链路已打通」的测试消息。

现在的通知模板保持精简——事件类型、标题、季、集、字幕组、进度、首播日期、事件说明、下载位置,去掉了一开始模板里附带的 TMDB / BGM 元数据(那部分信息在微信这种轻量场景里属于噪音)。开始下载、下载完成、缺集、出错四个事件,都会实时推过来。

以后新番开播,人不在家也知道它在安静地下载了。


全部操作由 Hermes Agent 辅助完成:读取配置、查 WxPusher 接口文档、翻 ANI-RSS 源码定位占位符时序、通过 API 写回配置并验证。

文章目录