Files

203 lines
9.9 KiB
Markdown

# Windows Gotify Agent
> Gotify → Windows 原生通知 · 应用内按应用分类查看历史 · 系统托盘常驻
![icon](Assets/gotify-mascot.png)
一个轻量级 Windows 桌面端,把 [Gotify](https://gotify.net/) 推送的消息以 **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 页面](https://gitea.kennethcheng.cc/KennethCheng/Windows-Gotify-Client/releases) 下载最新 zip
2. 解压到任意目录(比如 `C:\Program Files\WindowsGotify\`)
3. 双击 `WindowsGotify.exe` 启动 → 托盘出现鸟图标
4. 右键托盘 → **打开设置 / 查看历史**
5. 填 `Gotify URL` + `Client Token` → **保存并重连**
6. 默认勾选 **开机自动启动**,登录 Windows 自动起
> ⚠️ 整个目录拷走,别只拷 EXE(WindowsAppSDK unpackaged 需要同一目录的 native DLLs)。
### 方式 2:源码编译(开发者)
```powershell
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](https://gotify.net/) — 简单好用的自托管消息推送 server
- [Windows App SDK](https://learn.microsoft.com/en-us/windows/apps/windows-app-sdk/) — 现代 Windows 原生通知 API
- Ducker(Gotify 吉祥物)—— 萌