Watches Steam apps for build updates and dispatches a GitHub Actions workflow when one lands.
Uses SteamKit2 to poll Steam's PICS product info for the branches you care about. Instead of keeping its own "last seen build" cache, it reads the target repo's latest commit - the dump tooling in this org writes commit messages as BuildID - description..., so that number is the source of truth for what's already been processed. When Steam's build ID for a branch gets ahead of it, AppListener fires a workflow_dispatch to bring the repo up to date.
Grab an archive from the latest release:
| Archive | Needs .NET installed? | Use when |
|---|---|---|
AppListener-win-x64.zip | No | Windows, just run it |
AppListener-linux-x64.zip | No | Linux, just run it |
AppListener-win-x64-portable.zip | .NET 10 runtime | Windows, smaller download |
AppListener-linux-x64-portable.zip | .NET 10 runtime | Linux, smaller download |
Those links always resolve to the newest stable release. On Linux, chmod +x AppListener after unzipping.
Copy config.example.toml to config.toml next to the binary and fill it in:
[steam]
# Omit username/password entirely to log in anonymously.username = "your_steam_username"password = "your_steam_password"remember_password = true
[github]
token = "your_default_github_token"
[apps.730]
branch = "public"# Steam branch to watch, default "public"repo = "swiftly-tracker/cs2-dumps"# repo whose latest commit encodes the last-processed BuildIDdispatch_repo = "swiftly-tracker/cs2-dumps"# repo to send workflow_dispatch to, defaults to `repo`workflow_id = "update.yml"git_ref = "main"# branch of `repo` to read commits from / dispatch on# token = "..." # optional per-app override of [github].tokenOne [apps.<appid>] table per Steam app to watch. STEAM_USERNAME, STEAM_PASSWORD, and GITHUB_TOKEN environment variables override the config file. Leaving out [steam] username/password logs in anonymously, which is enough for public app info.
config.toml holds real credentials - it's git-ignored, keep it that way.
Run with no arguments to start the watcher daemon:
./AppListenerIt logs into Steam, checks every configured app's current build ID against its tracking repo once at startup, then keeps watching Steam's PICS changelist and re-checks whenever a watched app changes. Ctrl+C shuts it down cleanly (Steam logs off before the process exits).
Print an app's PICS branches and build IDs without running the daemon:
./AppListener app_info 730Counter-Strike 2 (app 730)
csgo buildid 12426195
csgo_legacy buildid 12426195
public buildid 24701871
...
Show usage:
./AppListener -help- Log into Steam (anonymous or username/password, with Steam Guard prompted on the console if needed).
- For each configured app, read the branch's
buildidvia SteamKit2's PICS API and compare it against the leading number in the tracking repo's latest commit message. - If Steam is ahead, POST a
workflow_dispatchtodispatch_repo'sworkflow_idongit_ref. - Poll Steam's PICS changelist on an interval; whenever a watched app shows up in a changelist, repeat step 2 for it.
A dispatch is only fired once per build ID per run - it won't re-dispatch on every poll tick while the tracking repo hasn't caught up yet.
AppListener/
├── src/
│ ├── Configuration/ # config.toml model + loader (Tomlyn)
│ ├── Steam/ # SteamKit2 session: login, PICS product info, changelist polling
│ ├── GitHub/ # commit BuildID parsing + workflow_dispatch
│ ├── Watcher/ # BackgroundService tying Steam + GitHub together
│ ├── Commands/ # app_info one-shot command
│ └── Entrypoint.cs # CLI dispatch / Generic Host bootstrap
└── config.example.toml
Requires the .NET 10 SDK.
git clone https://github.com/Swiftly-Tracker/AppListener.git
cd AppListener
dotnet build AppListener.slnx -c ReleaseRun it directly with dotnet run, or publish a standalone binary:
dotnet publish AppListener.csproj -c Release -r linux-x64 --self-contained true \
-p:PublishSingleFile=true -o out/linux-x64- Issues: Report bugs and request features
- Security: Report privately - never in a public issue
GPL-3.0. See LICENSE.