From dbf1cd820750e45805da66c2da0e3d29c38b3459 Mon Sep 17 00:00:00 2001 From: kennethcheng Date: Mon, 7 Sep 2026 00:54:34 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20README=20=E2=80=94?= =?UTF-8?q?=20=E9=A1=B9=E7=9B=AE=E4=BB=8B=E7=BB=8D=E3=80=81=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E3=80=81=E9=85=8D=E7=BD=AE=E3=80=81=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..5dabc6d --- /dev/null +++ b/README.md @@ -0,0 +1,202 @@ +# 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=` 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 吉祥物)—— 萌