Files
Windows-Gotify-Client/AGENTS.md
T
KennethCheng 4c2575317e docs: 在 AGENTS.md 记录 WindowsAppSDK 2.4.0 self-contained 打包 bug + 修法
首次 build 后启动会报 AppNotificationManager.Register() 0x8007007E 找不到 Microsoft.WindowsAppRuntime.Insights.Resource.dll。

原因是 WindowsAppSDK 2.4.0 的 NuGet 包没把 Insights Resource DLL 打进 build 目录,只在系统级 WindowsAppRuntime 安装里才有。

提供两种修法:

- dev:从 C:\Program Files\WSL\wslsettings\(或任何已装 WindowsAppSDK 的程序)拷贝 5 个 DLL 到 build 目录

- prod:目标机器装 WindowsAppRuntime system-wide(winget install Microsoft.WindowsAppRuntime.2),或者 publish 后手工补这 5 个 DLL

已在 Gitea 的 build 验证里实测有效。
2026-09-06 20:30:48 +08:00

6.7 KiB

AGENTS.md

Windows Gotify Agent —— 在 Windows 10/11 上以托盘程序方式运行,通过 WebSocket 连接 Gotify Server,把新消息以 Windows 原生通知呈现。

用户场景

V1 核心场景(用户明确要求):

  • Windows 接收 Gotify 通知(WebSocket → Windows 原生通知)
  • 应用内查看历史消息(打开 MainWindow → 历史列表,自动拉最近 N 条 + 实时追加新消息)
  • 列表条目支持双击跳转:消息 extras 里有 client::notification.click.url 时,默认浏览器打开

V1 暂不做(避免范围蔓延):多 Gotify Server、消息过滤、GUI 完整配置编辑、MSI/MSIX 安装器、Linux/macOS 端。

Setup commands

  • 环境:Windows 10 19041+ / Windows 11、Visual Studio 2022、.NET 8 SDK、Desktop development with .NET 工作负载
  • 还原依赖: dotnet restore
  • 编译: dotnet build -c Release
  • 本地运行: dotnet run -c Release
  • 自包含发布(推荐): dotnet publish -c Release -r win-x64 --self-contained true
  • 发布产物: bin\Release\net8.0-windows\win-x64\publish\(整个目录一起发布,不要只复制 EXE)

Project layout

  • WindowsGotify.csproj — SDK 风格项目文件,net8.0-windows / x64,UseWPF + UseWindowsForms,WindowsPackageType=None(unpackaged)
  • app.manifest — asInvoker 兼容性清单(Win10/11 supportedOS)
  • App.xaml + App.xaml.cs — 应用入口、Mutex 单实例锁、AppNotificationManager 注册/注销、生命周期编排
  • MainWindow.xaml + MainWindow.xaml.cs — 设置窗(Gotify URL、Client Token、保存并重连、测试通知);关闭按钮只 Hide 不退出
  • Models/ — AppConfig(嵌套 Gotify/Notification/Reconnect/History/Startup 五个子配置) + GotifyMessage(+ GotifyMessagePage、PagingInfo)
  • Services/ConfigService.cs — config.json 读写,路径 %LOCALAPPDATA%\WindowsGotify\config.json
  • Services/Logger.cs — 文本日志写入 app.log,线程安全
  • Services/StateService.cs — state.json 持久化 LastMessageId,断线重连/历史同步的去重锚点
  • Services/GotifyApiService.cs — GET /message?limit=&since= 拉历史补偿;GetRecentMessagesAsync 拉最近 N 条(供 UI 历史查看)
  • Services/GotifyWebSocketService.cs — /stream?token=... WebSocket 循环、自动重连(指数退避 2→5→10→20→30→60)、History 补偿;新增 MessageReceived 事件供 UI 实时追加
  • Services/NotificationService.cs — 通过 AppNotificationBuilder 弹 Windows 原生通知,Priority ≥ 8 走 Alarm 音;若消息 extras 含 client::notification.click.url,通过 AddArgument("url=...") 透传给 OnNotificationInvoked
  • Services/NotificationService.cs — 通过 AppNotificationBuilder 弹 Windows 原生通知,Priority ≥ 8 走 Alarm 音
  • Utils/StartupManager.cs — 写入 HKCU\Software\Microsoft\Windows\CurrentVersion\Run 实现当前用户开机启动
  • Tray/TrayIconService.cs — WinForms NotifyIcon 托盘(打开设置/测试通知/重新连接/退出)

Code style

  • C# 12 / .NET 8,<Nullable>enable</Nullable> + <ImplicitUsings>enable</ImplicitUsings>
  • 顶层 sealed class,文件作用域命名空间,主命名空间 WindowsGotify.*
  • 异步 API:WebSocket、API、状态保存一律 async/await,UI 事件用 async void + try/catch
  • using System.*; 优先,然后第三方(Microsoft.Windows.AppNotifications.* / Microsoft.Win32),最后 WindowsGotify.*
  • WPF XAML 资源引用走 x:Name,代码隐藏用 _camelCase 私有字段
  • WebSocket 资源用 using 或 try/finally 保证释放,ClientWebSocket.Abort() 用于强制重连
  • 不要把 Application Token 写进此项目(只允许 Client Token)

Runtime data

  • 配置文件: %LOCALAPPDATA%\WindowsGotify\config.json
  • 状态文件: %LOCALAPPDATA%\WindowsGotify\state.json
  • 运行日志: %LOCALAPPDATA%\WindowsGotify\logs\app.log

Testing instructions

  • V1 不带自动化测试,依赖手动 + Gotify 端到端验证

  • 单元自测: 托盘菜单 → 「测试通知」,应弹出标题为「Gotify 测试」的 Windows 通知

  • 端到端发消息(PowerShell):

    $headers = @{ "X-Gotify-Key" = "<Application Token>" }
    $body = @{ title = "Hadoop"; message = "NameNode 启动完成"; priority = 3 } | ConvertTo-Json
    Invoke-RestMethod -Uri "https://<gotify-host>/message" -Method Post -Headers $headers -ContentType "application/json" -Body $body
    
  • 反向代理(Nginx / NPM)必须透传 WebSocket Upgrade 头,详见方案文档第 14 节

  • 任意代码改动后必须执行: dotnet restore && dotnet build -c Release,并在目标 Windows 10/11 上跑一次端到端

  • 已知 WindowsAppSDK 2.4.0 self-contained 打包 bug:dotnet build 出来的 bin\...\win-x64\ 缺 Microsoft.WindowsAppRuntime.Insights.Resource.dll,首次启动 AppNotificationManager.Register() 会报 0x8007007E 找不到指定的模块。

    • 修法 A(dev):从已装 WindowsAppSDK 的程序(如 C:\Program Files\WSL\wslsettings\)拷贝 5 个 DLL 到 build 目录:
      • Microsoft.WindowsAppRuntime.Bootstrap.dll、Microsoft.WindowsAppRuntime.Bootstrap.Net.dll、Microsoft.WindowsAppRuntime.dll、Microsoft.WindowsAppRuntime.Insights.Resource.dll、Microsoft.WindowsAppRuntime.pri
    • 修法 B(prod):dotnet publish 后同样需要补这 5 个文件,或者目标机器装 WindowsAppRuntime system-wide(winget install Microsoft.WindowsAppRuntime.2)
  • /stream 是 WebSocket 端点,/message?limit=&since= 是历史补偿端点,二者都只接受 Client Token

  • 消息去重锚点 = state.LastMessageId,只处理 Id > LastMessageId 的消息,启动时用 since=LastMessageId 拉增量

  • HTTP ClientToken 走 Authorization: Bearer <token>;WebSocket 走 query ?token=<token>(日志中要打码为 token=***)

  • 声音策略: priority >= 8 → AppNotificationSoundEvent.Alarm,否则 Default

PR & commit conventions

  • 分支策略: 默认分支 main,所有改动走 feature 分支 + PR
  • 提交信息: 简体中文 conventional commits,前缀使用 feat: / fix: / docs: / refactor: / chore: / build:
  • 改动 .csproj / Microsoft.WindowsAppSDK 版本号必须在 PR 描述中单独说明,不允许顺手升级
  • 涉及 Client Token / 真实 Gotify URL 的示例值用占位符 <token> / <gotify-host>,禁止提交真实凭据

Security

  • Client Token 只能进入 config.json,日志中只允许 token=*** 打码输出
  • 公网部署必须使用 HTTPS + wss;ws:// / http:// 仅允许内网
  • 通知点击仅允许打开经 Gotify extras 传入的 URL,不要任意打开消息文本里出现的链接
  • 启动项走 HKCU\...\Run(当前用户)而非 HKLM,避免提权与全员副作用
  • 发布前在干净 Windows 10/11 上验证通知注册、通知中心授权、WebSocket 反代、自包含目录依赖完整