- Elixir 98.1%
- CSS 0.7%
- JavaScript 0.5%
- HTML 0.4%
- Dockerfile 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .claude | ||
| .forgejo/workflows | ||
| assets | ||
| config | ||
| docs | ||
| lib | ||
| priv | ||
| rel/overlays/bin | ||
| test | ||
| .dockerignore | ||
| .formatter.exs | ||
| .gitignore | ||
| AGENTS.md | ||
| airlock.toml | ||
| Dockerfile | ||
| mise.toml | ||
| mix.exs | ||
| mix.lock | ||
| README.md | ||
Buddy Roulette 🎲
Compare Steam libraries, find the games everyone owns but nobody has really played, and let fate pick your group's next game night. Works solo too — load a single library to roulette your own backlog.
A Phoenix LiveView app grown out of the single-file backlog-roulette.html
experiment. Because all Steam API calls happen server-side, no CORS proxies
are involved.
How it works
- Add Steam profiles to the roster (SteamID64, custom URL name, or a full profile link — vanity names are resolved and persona names/avatars fetched automatically).
- Select one or more profiles — one loads that player's own backlog, two or more compares their libraries.
- Use the playtime slider to keep only games nobody in the group has played past the cutoff.
- Optionally require macOS support, SteamOS/Linux support, and/or full
controller support (any combination). Platform data comes from the Steam
store's
appdetailsendpoint and is cached in the database, since that endpoint is rate-limited and only answers one app per request. - Spin the roulette.
Each profile's game details must be public (Steam → Edit Profile → Privacy Settings → Game details).
Wishlists
The Wishlists page compares the roster's Steam wishlists the same way: pick profiles, see the combined wishlist with current store prices and sale discounts, filter to games on sale / on everyone's wishlist / under a price cap, sort by discount, price, name, or recency, and let fate suggest the group's next purchase. Wishlists and prices are cached in the database and refreshed automatically a few times a day (a private wishlist just shows up empty — wishlist visibility follows the same Steam privacy settings).
Setup
Requires Elixir 1.20 on Erlang/OTP 29 — exact versions are pinned in
mise.toml, so with mise installed the
right toolchain activates automatically.
You need a Steam Web API key — get a free one at steamcommunity.com/dev/apikey. One key works for looking up anyone's public library.
mix setup
STEAM_API_KEY=yourkeyhere ALLOWED_STEAM_IDS=76561198000000001,76561198000000002 mix phx.server
Now visit localhost:4000.
Profiles are stored in a local SQLite database; the API key is only read from
the STEAM_API_KEY environment variable (see config/runtime.exs).
Authentication
The app is invite-only: visitors sign in through Steam (OpenID via
Überauth), and only
SteamID64s listed in the ALLOWED_STEAM_IDS environment variable
(comma-separated) may enter. When it's unset, nobody can sign in. The allow
list is re-checked on every request, so removing an id locks that account out
immediately. The same STEAM_API_KEY is used to fetch the signed-in player's
persona name and avatar.
Tests
mix test
Tests stub the Steam API via Req.Test — nothing hits the network.
Deployment
Pushes to main build and push a container image via the Forgejo workflow in
.forgejo/workflows/build.yml. The Dockerfile builds a standard Phoenix
release that runs pending migrations on boot (bin/migrate && bin/server)
and listens on PORT (default 4000).
The container needs:
SECRET_KEY_BASE— generate withmix phx.gen.secretSTEAM_API_KEYandALLOWED_STEAM_IDS— see abovePHX_HOST— the public hostnameDATABASE_PATH— the SQLite file, on a persistent volume; single-writer, so run exactly one replica (e.g. aRecreatedeployment strategy)