9.9 KiB
9.9 KiB
Windows Gotify Agent
Gotify → Windows 原生通知 · 应用内按应用分类查看历史 · 系统托盘常驻
一个轻量级 Windows 桌面端,把 Gotify 推送的消息以 Windows 原生通知 呈现,并在应用内按 应用名 分类查看历史。
✨ 功能
| 模块 | 说明 |
|---|---|
| 🔔 实时通知 | WebSocket 长连接(/stream?token=),Gotify 一推消息就弹 Windows 原生 toast,优先级 ≥ 8 走告警音 |
| 📜 历史查看 | 主窗左栏 ListView 显示最近 50 条,TextWrapping 不被截断;实时新消息插到最前 |
| 🏷️ 按应用分类 | 启动时拉 GET /application 缓存到 ApplicationCache,历史每行显示应用名(自动按名字 hash 出固定颜色);不是"App 12345"那种无意义编号 |
| 🌗 浅色 / 深色主题 | 主窗右上角 ☼ 浅色 / ☾ 深色 按钮一键切换,所有颜色走 DynamicResource 实时刷新,选择自动存 config.json |
| 📋 复制 | 详情面板"消息正文"右侧 [复制] 按钮,一键把 [应用名] 标题 / 时间 / 优先级 / 正文 / URL 整段写进剪贴板 |
| 🛡️ 复制兜底 | 剪贴板被 UU远程 / RDP / 杀软长期占着时,自动弹一个预填 + 全选的小窗口,让你直接 Ctrl+C(绕开 OLE 锁) |
| 🖱️ 详情可选中 | 详情面板的标题 / 正文 / URL 全部用 TextBox IsReadOnly,鼠标拉黑、Ctrl+A、Ctrl+C 都能用 |
| 🧲 托盘常驻 | 关闭主窗只 Hide,进程留托盘;右键菜单:打开设置 / 测试通知 / 重新连接 / 退出 |
| 🚀 开机自启动 | 设置区 CheckBox 勾选后写 HKCU\...\Run(只当前用户,不需管理员);取消勾选会立刻从注册表删 |
| 🔗 通知跳链接 | 消息 extras.client::notification.click.url 透传到通知,点 toast 直接用默认浏览器打开 |
📸 截图
| 主窗(深色模式) | 主窗(浅色模式) |
|---|---|
| 托盘 + 应用内历史 + 详情面板 | 同上但浅色配色 |
| 优先级徽章彩色,应用名按名字分色 | 通知 hover 显示 |
截图占位 —— 跑起来后自行截图替换。
🛠️ 环境要求
- OS: Windows 10 (19041+) / Windows 11
- 运行时: .NET 8 Desktop Runtime(开发用 SDK,运行用 Runtime)
- 依赖: Windows App Runtime(
Microsoft.WindowsAppSDK 2.4.0同版本,本仓库dotnet publish --self-contained已自带) - Gotify: 任意 v2.x server,需要创建一个 Client(不是 Application,本程序只消费消息不发送)
🚀 快速开始
方式 1:下载 Release(推荐普通用户)
- 去 Releases 页面 下载最新 zip
- 解压到任意目录(比如
C:\Program Files\WindowsGotify\) - 双击
WindowsGotify.exe启动 → 托盘出现鸟图标 - 右键托盘 → 打开设置 / 查看历史
- 填
Gotify URL+Client Token→ 保存并重连 - 默认勾选 开机自动启动,登录 Windows 自动起
⚠️ 整个目录拷走,别只拷 EXE(WindowsAppSDK unpackaged 需要同一目录的 native DLLs)。
方式 2:源码编译(开发者)
git clone https://gitea.kennethcheng.cc/KennethCheng/Windows-Gotify-Client.git
cd Windows-Gotify-Client
# 编译 + 跑 dev 版
dotnet restore
dotnet build -c Release
dotnet run -c Release
# 自包含发布(拷整个 bin\Release\net8.0-windows10.0.19041.0\win-x64\publish\ 目录)
dotnet publish -c Release -r win-x64 --self-contained true
⚙️ 配置
配置文件:%LOCALAPPDATA%\WindowsGotify\config.json
首次启动自动用默认值生成,主窗 GUI 编辑后实时落盘。
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
Gotify.Url |
string | http://127.0.0.1:8080 |
Gotify server URL,不要带尾部斜杠 |
Gotify.ClientToken |
string | (空) | Gotify 后台 Clients 标签里那个 Cxxxx... token(不是 Application Token) |
Notification.Enabled |
bool | true |
总开关,关掉就不弹 toast |
Notification.Sound |
bool | true |
是否带声音(优先级 ≥ 8 强制告警音) |
Notification.MaxMessageLength |
int | 2000 |
通知正文最大字符数,超出截断 |
Reconnect.InitialDelaySeconds |
int | 2 |
WebSocket 断开首次重连延迟(秒) |
Reconnect.MaxDelaySeconds |
int | 60 |
指数退避上限(2 → 4 → 8 → ... → 60) |
History.Enabled |
bool | true |
启动时拉 since=lastMessageId 补齐离线期间消息 |
History.StartupSyncCount |
int | 20 |
启动时一次拉多少条增量 |
History.ViewerLimit |
int | 50 |
主窗历史列表最多显示多少条(UI 硬上限 200) |
Startup.Enabled |
bool | true |
开机自启动(写 HKCU\...\Run) |
Ui.Theme |
string | System |
Light / Dark / System(System 等同 Light) |
🏗️ 架构
┌─ App.xaml.cs ──────────┐
│ 单实例 Mutex │
│ AppNotificationManager │ ← Windows App SDK
│ 主题加载 │
└────────────┬─────────┘
│
┌─────────┴──────────┐
│ │
┌──▼─────────┐ ┌─────▼──────────┐
│ TrayIcon │ │ MainWindow │
│ (WinForms) │ │ (WPF) │
└────────────┘ └─┬─────────┬────┘
│ │
┌───────────┘ └────────────┐
│ │
┌───────▼─────────┐ ┌─────────▼────────┐
│ GotifyWebSocket │ │ GotifyApi │
│ Service │ │ Service │
│ - 自动重连 │ │ - GetMessages │
│ - 去重(lastId) │ │ - GetRecentMsg │
│ - History 补偿 │ │ - GetApplications│
└─────────────────┘ └──────────────────┘
│ │
│ ┌───────────────────┘
│ │
│ ┌──────▼──────┐ ┌──────────────┐
│ │ Application │ │ StateService │
└──────►│ Cache │ │ - lastId │
└─────────────┘ └──────────────┘
┌──────────────┐ ┌──────────────────┐
│ Notification │ │ ConfigService │
│ Service │ │ (config.json) │
└──────────────┘ └──────────────────┘
模块清单(参见 AGENTS.md 完整说明):
Models/—AppConfig/GotifyMessage/GotifyApplicationServices/—ConfigService/Logger/StateService/GotifyApiService/GotifyWebSocketService/NotificationService/ApplicationCache/ThemeServiceUtils/StartupManager.cs— 注册表 Run 项Tray/TrayIconService.cs— WinFormsNotifyIconThemes/Light.xaml/Dark.xaml— 主题资源Converters/ValueConverters.cs— 优先级色 / 应用名 hash 出色
🐛 已知问题
- WindowsAppSDK 2.4.0 self-contained 打包漏
Microsoft.WindowsAppRuntime.Insights.Resource.dll——dotnet build/publish输出目录里少了这个 DLL,首次启动AppNotificationManager.Register()会抛0x8007007E。修法:- dev:从已装 WindowsAppSDK 的程序(本机如
C:\Program Files\WSL\wslsettings\)拷贝 5 个 DLL 到 build 目录:Microsoft.WindowsAppRuntime.Bootstrap.dll、Bootstrap.Net.dll、Microsoft.WindowsAppRuntime.dll、Insights.Resource.dll、Microsoft.WindowsAppRuntime.pri
- prod:
winget install Microsoft.WindowsAppRuntime.2(或目标机器装 system-wide WindowsAppRuntime) - 详细见
AGENTS.md
- dev:从已装 WindowsAppSDK 的程序(本机如
🛣️ 路线图 (V2)
- 多 Gotify Server(同时连 NAS / 云的多个 instance)
- 按应用 / 优先级过滤历史(下拉框)
- 通知分类(Do Not Disturb 时段、白名单)
- 自定义通知图标(每个 app 一个图标)
- MSI / MSIX 安装器
- Gitea Actions CI:
pushtag 时自动 build + publish release - 通知消息聚合(同一应用 1 分钟内多条合并为"X 条新消息")
📜 协议
- Gotify 消息流:通过
/stream?token=<Client Token>WebSocket 订阅 - HTTP API:
/application(应用列表)、/message?limit=&since=(历史) - 点击链接:Gotify 消息
extras.client::notification.click.url,被透传到 toast 的AddArgument("url", ...),点击时用默认浏览器打开 - 本地状态:
%LOCALAPPDATA%\WindowsGotify\state.json记录LastMessageId防止重启后重复通知
🔒 安全
- 只读 Client Token:本程序只向 Gotify 订阅消息,不需要 Application Token;请勿在配置或日志里泄露任何 Application Token
- 当前用户 Run 项:开机启动走
HKCU,不写HKLM,不需要管理员权限,不影响其他用户 - Token 日志脱敏:WebSocket 连接日志把
?token=***段打码,不输出完整 token - HTTPS / wss:公网部署请用 HTTPS + wss;ws / http 仅适合内网
- 通知链接白名单:通知点击只能打开 Gotify
extras显式声明的 URL,不会自动从消息文本里抓链接
📄 License
TBD
🙏 致谢
- Gotify — 简单好用的自托管消息推送 server
- Windows App SDK — 现代 Windows 原生通知 API
- Ducker(Gotify 吉祥物)—— 萌
