FnMessageBots:让 NAS 主动给你发消息

FnMessageBots:让 NAS 主动给你发消息

从「自己去看日志」到「日志主动找人」——这台小机器人的来龙去脉,以及 Docker 和 FPK 两条安装路线、怎么配、怎么用、怎么验证它真的在工作。

November 8, 20262026年11月8日
18 min read约 11 分钟

Read in English →

先说说为什么做这个东西

我在家里挂了一台飞牛 NAS,它 24 小时开机,跑着 Docker、存着照片、备份着几台电脑的数据。用久了会发现一个很别扭的地方:

它其实什么都知道,只是从不告诉我。

硬盘掉了一块、某个容器半夜崩了、有人在外面试着 SSH 登进来、UPS 断电切到电池供电——这些事飞牛全都记在系统日志里了。问题是,日志得我自己去翻。而人是不可能每天去看日志的,等你发现的时候,通常是「已经坏了两天」而不是「刚刚坏了」。

所以我当时就想要一个特别朴素的东西:别让我盯着日志,让日志来找我。 手机上的企业微信一响,我抬眼看一下,就知道 NAS 有没有事。

它是怎么长出来的

整件事的技术内核其实特别简单,一句话:飞牛把事件写进一个 SQLite 数据库,我定时去读它。

那个库在 /usr/trim/var/eventlogger_service/logger_data.db3,关心的表是 log。没有推送接口、没有事件总线,所以只能轮询——每几秒看一眼有没有新记录。

这里踩的第一个坑很典型:你不想每次重启都把历史全部推一遍。程序启动时先记住当前最大 id 作为基线,之后只处理比它新的记录。这条规则看着不起眼,但它决定了下班回家重启一次容器之后,你的手机是要安静,还是要被三百条历史消息轰炸。

数据是怎么流动的

一条事件从写进数据库到推到你手机,中间经过这四步。

之后是几个慢慢加上去的东西,每一样都是自己被烦到才做的:

  • 去重窗口:同一件事在 300 秒内只推一次,不然一块硬盘反复唤醒能把你手机震没电。
  • 磁盘事件合并:同类型磁盘唤醒/休眠在一个时间窗里合并成一条。
  • 勿扰时段:默认晚上十点到早上七点不推,到点只发一条「昨晚发生了什么」。
  • 影视/音乐汇总:入库和刮削本来就零碎,等它静默 60 秒再合成一条「本轮入库了这些」。
  • 轮询批汇总:主日志库那一路更狠——一轮轮询(5 秒)里读到的所有事件直接并成一条:类型计数 + 前 10 条摘要。逐条发会撞企业微信每分钟 20 条的上限,被限流时丢掉的恰好是最要紧的那条。
  • 待发通知持久化:推送发不出去的时候把批次存盘,重启后接着发,而不是默默丢掉。
  • 配置热加载:改完 Web 页上的设置直接生效,不用进容器重启。

再后来是两块「工程活」:一是加了个 Web 配置页(Docker 默认 18080,FPK 在安装向导里选端口),第一次访问要过一道密码关——Docker 用启动日志里的初始化码自设,FPK 在安装向导里直接设好,配置文件里只存盐值和哈希;二是做成两种安装形态——Docker 镜像(x86 和 arm 都有)和 FPK 应用包(飞牛应用中心直接搜得到)。因为 NAS 用户分两类:动手能力强的喜欢 compose,不想碰命令行的只想点「安装」。

现在它长什么样

装了之后大概是这样:

  • 有人登录 NAS、登录失败、退出登录 → 推
  • 硬盘发现/唤醒/休眠/IO 报错 → 推
  • 容器启动、停止、意外退出、更新失败 → 推
  • CPU 占用过高、温度过高 → 推
  • UPS 切电池、恢复市电 → 推(这个真的救命)
  • Samba / WebDAV / FTP / NFS / SSH 服务被开关 → 推
  • 影视入库、刮削完成、音乐入库 → 静默汇总成一条

跑起来需要什么

不多:

需要说明
一台飞牛 fnOS 主机Docker 或应用中心,二选一
能读日志库默认路径 /usr/trim/var/eventlogger_service/logger_data.db3
一个推送渠道企业微信群机器人最省事,也可以钉钉、飞书、Bark、PushPlus、魔法推送、SMTP 邮件、通用 Webhook
想折腾源码的话Python 3.9+

下面两条安装路线,选一条就行。

路线 A:Docker 部署(推荐给习惯 compose 的人)

在 NAS 上随便找个目录,比如 /vol1/1000/docker/fn-message-bot,然后写一个 .env:

UI_PORT=18080

再写 docker-compose.yml:

services:
  fn-message-bot:
    image: sunanang/fn-message-bots:latest
    container_name: fn-message-bot
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./data/logs:/app/data/logs:rw
      - ./data/cursor:/app/data/cursor:rw
      - ./config:/app/config:rw
      - /usr/trim/var/eventlogger_service:/usr/trim/var/eventlogger_service:ro
      - /var/log/journal:/var/log/journal:ro
      - /run/log/journal:/run/log/journal:ro
      - /etc/machine-id:/etc/machine-id:ro
      - /etc/os-release:/host/etc/os-release:ro
      - /var/lib/dpkg/status:/host/dpkg/status:ro
    environment:
      - TZ=Asia/Shanghai
      - UI_PORT=${UI_PORT:-18080}

几行关键的:

  • host 网络模式,是为了让巡检能看到 NAS 真实的网卡和 IP;端口由 UI_PORT 控制,不再写 ports。
  • /usr/trim/var/eventlogger_service 必须只读挂进去,路径别改。
  • TZ 别省。镜像默认北京时间,但如果你容器里被设成 UTC,勿扰时段整体会错八小时。
compose 文件

然后:

docker compose up -d
docker logs -f fn-message-bot

日志里会打印一行初始化码,先抄下来,下一步要用。

docker compose up -d

路线 B:FPK 安装(推荐给不想碰命令行的人)

打开飞牛应用中心,搜「日志推送」直接安装(应用中心里显示的名字就是这四个字,不是 FnMessageBots);或者去 GitHub Release 下载 .fpk 手动装。

应用中心里的日志推送

应用详情页长这样,点「安装」就行。这张是 2.6.0 时候截的,那会儿下载数才两万多,现在已经 7 万多了。

应用中心搜「日志推送」

搜「日志推送」,就这一个结果。

有两个包,长得像但是不一样:

  • 普通用户版(FnMessageBot-x.y.z.fpk):以应用账号 FnMessageBot 运行,权限最小,推荐。
  • root 版(-root.fpk):整个应用以 root 跑,名字后面带「(root)」。只在确实读不到某些数据时用。

两个包共用同一个应用标识和版本,所以只能装一个,是覆盖关系不是并存关系。从 root 版切回普通版时,root 留下的数据文件属主可能需要按安装提示修一下。

安装向导只有一页:填监听端口(预填 18230,改成你想要的)和访问密码。这里没有「选运行账号」这一步——账号就是你装的哪个包决定的。装完直接从应用中心打开。

配置:三步,别多做

第一步:过密码这一关

两条路的开场不一样,别弄混:

  • FPK 装的话,密码在安装向导里就设好了,打开配置页直接输密码就行,没有初始化码这一步。
  • Docker 装的话,第一次打开配置页要填一次初始化码——就是 docker logs 里打印的那行,或者容器里 config/.setup_code 这个文件。这么做是防止同一个局域网里别人抢先给你设了密码。设完密码后初始化码自动作废。

你也可以在部署时用环境变量 FNMB_WEB_PASSWORD 直接预置一个初始密码(至少 8 位),省掉这一步。

设置访问密码

这是 Docker 装机时的样子:填对初始化码才能设密码。FPK 装不会出现这一步。

第二步:勾事件

进配置页第一件事不是填 webhook,而是关掉你不需要的事件。

这话听起来怪,但很实际:全勾上,第一天很新鲜,第三天你就开始嫌吵,然后整个机器人的通知会被你习惯性忽略——那就等于没装。

我的习惯是:登录/登录失败、硬盘、容器崩溃、UPS 全留;CPU 告警只在持续偏高时留;文件删除这类高频事件关掉。

事件勾选

每个事件都有一句人话解释,不用查文档也能判断要不要开。

第三步:填推送渠道

以企业微信群机器人为例:在群里加一个机器人,复制它的 Webhook,粘贴到配置页对应那一栏,保存。

保存是热加载的——不用重启容器,几秒后就会生效。

推送渠道与系统设置

渠道就在最上面那一行;往下是系统设置——标题前缀、轮询间隔、访问密码、勿扰时段、巡检 Cron 都在这里。

配置页总览大概是这样,左边事件分组、右边推送和勿扰:

Web 配置页总览

打开就是这一页:上面是推送统计,下面按分组列出所有可监控的事件。

Corollary (如果你打算把配置页挂到外网)

记得把 FNMB_TRUSTED_PROXIES 设成代理的 IP/CIDR —— 只填你自己控制的那几个,并且代理要正确传 X-Forwarded-For。别图省事填 0.0.0.0/0,那等于把来源校验这层直接拆了。

日常怎么用

装完之后你其实不需要每天操作它,它自己在那跑。真正用得上的是三种时候:

一是收到消息的时候。 手机响了,看一眼是什么事:

真实推送

这是我手机上真实收到的样子:登录通知、巡检报告、服务恢复提醒都在一个会话里。

二是想看「过去几天发生过什么」。 库里有原始记录,也有我留的小工具:

python tools/log_manager.py stats
python tools/log_manager.py recent --hours 24
python tools/log_manager.py type LoginFail --limit 10

三是想安静的时候。 设好勿扰时段,它会攒着,早上给你一条汇总。影视入库那种「一口气入库二十集」的碎消息也是攒成一条,不会刷屏。

怎么验证它真的在工作

这一步别跳。装完不验证,等于不知道它到底有没有在跑。

最快的办法:故意制造一条事件。

比如在另一台机器上故意用错的密码 SSH 一下 NAS:

ssh wronguser@192.168.1.13

如果配置里勾了 SSH_AUTH_FAILED,几秒内手机就该响。手机响了,链路就是通的。

没响就往三个地方看,顺序别反:

  1. 容器活不活:docker ps,或者 docker inspect --format '{{.State.Health.Status}}' fn-message-bot。
  2. 应用自己在说什么:data/logs/monitor_YYYYMMDD.log,看有没有采集到那条事件、推送返回了什么。
  3. 配置页保存时的告警:如果某个数据源权限不够,保存按钮下面会直接告诉你原因,还会给出可复制的授权命令。
验证链路

想更彻底一点,源码里带了一套测试,不需要真机也能跑:

python -m pytest tests

覆盖了采集、权限诊断、配置接口,以及 FPK 安装/升级/权限脚本的生命周期。

几个我踩过、你大概率也会踩的坑

权限不足。 这是最常见的。程序不会替你去提权,也不会因为一个数据源读不到就整体罢工——它会在配置页告诉你缺什么、怎么给。普通用户版给的命令针对 FnMessageBot 账号,Docker 版会提示你检查挂载和容器 UID/GID。

功能检查结果

保存配置时它会逐项检查数据源,缺哪个权限、要执行什么命令,全写在上面。

时间不对。 推送时间整体偏了几个小时,八成是 TZ。容器改了环境变量要重建才生效,光 restart 不行。

时间戳本身有偏差。 默认按标准 Unix 时间戳处理,2.8.0 起不再默认额外加 8 小时。只有旧系统存库时间确有偏差时才用 LOGTIME_DISPLAY_OFFSET_SECONDS 补,比如 28800 表示补 +8 小时;如果你是从老版本升上来、日志时间反而早了 8 小时,那是老配置还留着,删掉或改成 0。这只是显示补偿,别拿它调勿扰时区。

别随手挂 docker.sock。 挂了就等于把宿主机 root 级控制能力交给容器。只有你确实要监控容器事件时才开,并且心里清楚这件事的代价。

SSH 事件读不到。 飞牛新版本主日志库里常常没有 Sshd* 记录,要挂 /var/log/journal、/run/log/journal 和 /etc/machine-id(仓库里的 docker-compose.yml 已经带了,README 里那份精简示例没带,照抄会漏)。

最后

这东西没什么宏大的野心,它就是为了解决一个很小的不爽:我明明有一台一直在工作的机器,却要靠运气才知道它出没出事。

Note (现在的成绩)

现在的成绩大概是:GitHub 上 85 个 star,飞牛应用中心 + Docker 下载 7 万+,稳定在用的用户估算一千人左右。数字不算大,但它是真实装在别人 NAS 上、每天在跑的东西——对我来说这比 star 数重要得多,因为这意味着一旦推错了,别人家的手机也会响。

如果你也在用飞牛,装一个试试。装完记得做那一步验证——制造一条事件,看着手机响一下,你才会真的相信它。