外观
通知扩展:Telegram 等第三方通知
OMV 的告警默认只会发邮件,但邮件常常被淹没在收件箱里。读完这一篇,你能把同样的告警同时转发到 Telegram,并且明白这套机制是怎么工作的,以后换成别的渠道也只改最后一行。
开始之前
- 先在
系统 → 通知 → 设置(System → Notification → Settings)里把邮件通知配置好并发出一封测试邮件,见 配置邮件通知。这套机制建立在邮件通知之上,邮件通知没有启用,脚本就不会被触发。 - 你需要一个 Telegram 账号。
- 命令在 OMV 的 shell(root 身份)里执行,不写
sudo。 - OMV 本身依赖
wget和jq,下面的脚本只用它们,不需要额外安装软件。
脚本写错会让通知投递失败
影响范围:脚本在邮件投递过程中运行。如果它以非零状态退出,Postfix 会把这封内部邮件当作投递失败,可能重试或退信;脚本输出的内容也可能出现在退信里。
前置条件:脚本不管发送是否成功,都要以 exit 0 结束,并且不要向标准输出写东西,需要记录就用 logger。Token 不要写进脚本,也不要写进会被分享的任何地方。
怎么回退:删除脚本文件即可,OMV 自己的邮件通知不受影响。
通知是怎么触发脚本的
这套机制来自 OMV 的源码,/usr/share/openmediavault/notification/sink.d/README 里有说明:
- 启用邮件通知后,OMV 把 Postfix 配置成:所有从通知发件人发出的邮件,都密送一份给内部地址
[email protected]。 - 这个内部地址由一个专用的 Postfix 投递通道(
omvnotificationfilter)处理,它调用/usr/share/openmediavault/notification/postfix-runner.sh,并且以系统用户openmediavault-notify身份运行。 postfix-runner.sh读取邮件,拆出收件人、主题、日期和正文,然后用run-parts依次运行sink.d目录里的所有可执行文件。
所以 OMV 发出的每一封邮件,包括 S.M.A.R.T.、RAID、监测、更新、认证相关的告警,以及你自己的计划任务邮件,都会触发这个目录里的脚本一次。脚本拿到这些输入:
| 输入 | 内容 |
|---|---|
第 1 个参数 $1 | 正文文件的路径,已去掉邮件头 |
第 2 个参数 $2 | 发件人地址 |
第 3 个参数 $3 | 收件人地址 |
环境变量 OMV_NOTIFICATION_FROM、OMV_NOTIFICATION_RECIPIENT | 发件人、收件人 |
环境变量 OMV_NOTIFICATION_SUBJECT、OMV_NOTIFICATION_DATE | 邮件主题、日期 |
环境变量 OMV_NOTIFICATION_MESSAGE_FILE | 正文文件的路径,和 $1 相同 |
出于安全考虑,脚本以专用用户 openmediavault-notify 运行,这个用户的登录 shell 是 nologin,只能读它有权限读的文件。后面存放 Token 的配置文件就要给它读权限。
两个细节:
- 文件名请只用字母、数字、下划线和连字符,不要带
.sh这样的扩展名。运行脚本用的是run-parts --lsbsysinit,名字不符合它的规则就会被悄悄忽略,没有任何提示。 - 这套机制按「每封邮件一次」触发,不区分告警类型。想只转发一部分,在脚本里按主题过滤,见下文。
创建 Telegram 机器人
- 在 Telegram 里找到官方的
@BotFather,发送/newbot,按提示起名。它会给你一串 Token,格式像123456:ABC-DEF...。 - 打开你新建的机器人,点 Start,随便发一条消息。
- 在 OMV 的 shell 里查你的聊天 ID。先用
read把 Token 读进变量,这样它不会出现在命令历史里:
sh
read -rs TOKEN # 粘贴 Token 后按回车,屏幕上不会回显
wget -qO- "https://api.telegram.org/bot${TOKEN}/getUpdates" | jq '.result[].message.chat.id'
unset TOKEN输出的数字就是聊天 ID。如果结果是空数组,说明机器人还没收到消息,回到第 2 步再发一条。这个接口的详细说明见 Telegram Bot API。
保存 Token
把 Token 和聊天 ID 放在单独的配置文件里,脚本从它读取:
sh
install -m 640 -o root -g openmediavault-notify /dev/null /etc/omv-telegram.conf
nano /etc/omv-telegram.conf在文件里写入两行,把尖括号里的内容换成你自己的值:
sh
TG_TOKEN='<机器人 Token>'
TG_CHAT_ID='<聊天 ID>'文件的权限是 640,属主 root,属组 openmediavault-notify:root 能改,通知用户能读,其他用户读不到。
创建脚本
脚本放在 sink.d 目录里,文件名 50telegram:
sh
nano /usr/share/openmediavault/notification/sink.d/50telegram内容如下:
sh
#!/bin/sh
# 由 postfix-runner.sh 以 openmediavault-notify 用户调用
# $1 正文文件 $2 发件人 $3 收件人;主题等信息在 OMV_NOTIFICATION_* 环境变量里
CONF=/etc/omv-telegram.conf
[ -r "$CONF" ] || exit 0
. "$CONF"
[ -n "$TG_TOKEN" ] && [ -n "$TG_CHAT_ID" ] || exit 0
# 可选:只转发你关心的告警,其余忽略。去掉下面几行的注释,并按需修改关键词
# case "$OMV_NOTIFICATION_SUBJECT" in
# *SMART*|*"event on /dev/md"*|*"I/O error"*|"Monitoring "*|*"Errors were detected"*) ;;
# *) exit 0 ;;
# esac
text="[$(hostname)] ${OMV_NOTIFICATION_SUBJECT}
$(head -c 3500 "$1")"
payload=$(jq -n --arg chat "$TG_CHAT_ID" --arg text "$text" \
'{chat_id: $chat, text: $text}') || exit 0
wget -q -O /dev/null --timeout=15 --tries=1 \
--header='Content-Type: application/json' \
--post-data="$payload" \
"https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
|| logger -t omv-telegram "sending to Telegram failed"
exit 0保存后设置权限:
sh
chown root:root /usr/share/openmediavault/notification/sink.d/50telegram
chmod 755 /usr/share/openmediavault/notification/sink.d/50telegram几点说明:
head -c 3500把正文截到 3500 字节,因为 Telegram 单条消息的文本上限是 4096 个字符。- 没有设置
parse_mode,消息按纯文本发送,不用处理特殊字符的转义。 exit 0在每条路径上都保留,原因见前面的警示块。- Token 在
wget的命令行里,同一台机器上的其他用户在发送的那几秒钟内,可以通过进程列表看到它。NAS 上通常没有不受信任的本地用户,但你要知道这一点。
测试
先手动运行脚本,用通知用户的身份,不依赖邮件:
sh
printf '这是一条测试消息\n' > /tmp/omv-test-body
runuser -u openmediavault-notify -- env OMV_NOTIFICATION_SUBJECT='手动测试' \
/usr/share/openmediavault/notification/sink.d/50telegram /tmp/omv-test-body sender recipient
rm /tmp/omv-test-bodyTelegram 里收到消息,说明脚本、Token 和网络都没问题。
再走完整流程:到 系统 → 通知 → 设置,点页面下方的测试(Test)按钮,它会发一封测试邮件,同时应该触发 Telegram 消息。
检查结果
- Telegram 里收到了来自你机器人的消息,开头是
[主机名] 邮件主题。 ls -l /usr/share/openmediavault/notification/sink.d/50telegram显示-rwxr-xr-x。ls -l /etc/omv-telegram.conf显示-rw-r-----,属主 root,属组openmediavault-notify。- 没收到消息时,先看日志:
sh
journalctl -t omv-telegram --since "1 hour ago"
journalctl --since "10 minutes ago" | grep -i omvnotificationfilter第一条是脚本自己记录的发送失败,第二条是 Postfix 对内部投递通道的处理记录。
常见问题
脚本没有任何反应。 依次检查:邮件通知是否已启用(系统 → 通知 → 设置 里的已启动(Enabled)是否勾选);文件名是不是带了 .sh;文件是不是可执行;用上面的方法手动运行。
手动运行成功,但走邮件流程没反应。 最常见的原因是配置文件对 openmediavault-notify 不可读,检查它的属组和权限。
NAS 需要代理才能访问 Telegram。 网络 → 代理(Network → Proxy)里的设置通常不会传给 Postfix 调用的脚本。在脚本开头加一行 export https_proxy='<代理地址>',wget 会读取它。
消息太多,被日常邮件淹没。 打开脚本里的过滤那几行,只转发主题里含有关键词的告警。几类常见告警的主题格式如下,写过滤条件时参考,最终以你实际收到的邮件为准:
| 告警 | 主题 | 出处 |
|---|---|---|
| 系统监测 | Monitoring <动作> -- <事件> <服务> | OMV 的 monit 配置模板 |
| 块设备 I/O 错误(8.5.10 起) | I/O error detected on device '<设备>' | OMV 的 rsyslog 配置模板 |
| Btrfs 设备错误 | Errors were detected on the file system mounted at <挂载点> [UUID=...] | OMV 的每日 cron 脚本 |
| 需要重启 | Reboot required | OMV 的每日 cron 脚本 |
| 有待应用的配置更改 | Pending configuration changes | OMV 的每日 cron 脚本 |
| 有用户被锁定 | Locked users overview | OMV 的每日 cron 脚本 |
| 证书即将到期 | A SSL certificate expires in <N> days | OMV 的每日 cron 脚本 |
| S.M.A.R.T. | 主题里含 SMART | smartd 生成,格式由 smartmontools 决定 |
| 软 RAID | <事件> event on /dev/mdX:<主机名> | mdadm 的监控程序生成 |
想换成别的渠道。 只需替换脚本最后的发送请求,输入输出的部分不变。ntfy、Gotify、Discord Webhook 这类服务都提供简单的 HTTP 接口,具体格式以各自的官方文档为准。官方插件之外,omv-extras 的 openmediavault-snapraid-aio 自带了 Apprise 通知,可以直接发到 Telegram、Discord、Slack、Gotify 等很多服务,首次运行时它会用 pipx 自动安装 Apprise,需要联网。界面里的字段是 Apprise URLs、Attach report on warnings 等,没有官方中文译名,以界面为准。
想用自己的目录,而不是 OMV 的 sink.d。 在 /etc/default/openmediavault 里设置环境变量 OMV_NOTIFICATION_SINK_DIR(用 omv-env set -- OMV_NOTIFICATION_SINK_DIR <目录>),postfix-runner.sh 会改为运行这个目录里的脚本。注意这样会让 sink.d 里自带的脚本不再运行,它只在调试开关打开时才有作用,影响很小。目录和脚本同样要让 openmediavault-notify 能读、能执行。