Files

9.9 KiB

Windows Gotify Agent

Gotify → Windows 原生通知 · 应用内按应用分类查看历史 · 系统托盘常驻

icon

一个轻量级 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(推荐普通用户)

  1. 去 Releases 页面 下载最新 zip
  2. 解压到任意目录(比如 C:\Program Files\WindowsGotify\)
  3. 双击 WindowsGotify.exe 启动 → 托盘出现鸟图标
  4. 右键托盘 → 打开设置 / 查看历史
  5. 填 Gotify URL + Client Token → 保存并重连
  6. 默认勾选 开机自动启动,登录 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 / GotifyApplication
  • Services/ — ConfigService / Logger / StateService / GotifyApiService / GotifyWebSocketService / NotificationService / ApplicationCache / ThemeService
  • Utils/StartupManager.cs — 注册表 Run 项
  • Tray/TrayIconService.cs — WinForms NotifyIcon
  • Themes/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

🛣️ 路线图 (V2)

  • 多 Gotify Server(同时连 NAS / 云的多个 instance)
  • 按应用 / 优先级过滤历史(下拉框)
  • 通知分类(Do Not Disturb 时段、白名单)
  • 自定义通知图标(每个 app 一个图标)
  • MSI / MSIX 安装器
  • Gitea Actions CI:push tag 时自动 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 吉祥物)—— 萌