- Python 71.2%
- HTML 22%
- CSS 6.4%
- Dockerfile 0.4%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| requirements.txt | ||
PatreonLink
A dockerized service that whitelists your Patreon supporters on a Minecraft server using their Xbox accounts, and removes them when their membership lapses.
- No environment variables. All settings (Patreon/Discord credentials,
Minecraft connection, sync interval) live in a SQLite database on the
/datavolume and are managed through the web GUI. - Web GUI with a first-run wizard: enter Patreon API credentials, connect your creator account via OAuth, pick a campaign, and test every connection.
- Sync loop runs every 5 minutes (configurable): new active patrons with a linked gamertag are whitelisted; lapsed patrons are removed.
- Patron self-service portal (
/link): Patreon doesn't expose Xbox gamertags, so patrons log in with Discord. If their Discord account is connected on Patreon they're matched automatically; otherwise they can claim their membership by Patreon email. They then enter their Xbox gamertag, which is verified and resolved to an XUID via the public GeyserMC API.
Run it
docker-compose.yml deploys the prebuilt image from the registry — use it
directly as a Portainer stack, or locally:
docker compose up -d # pulls git.thenymans.com/brian/patreonlink:latest
# or build from source:
docker build -t git.thenymans.com/brian/patreonlink:latest .
docker compose up -d
Open http://localhost:8000 — you'll be taken to the first-run setup to create an admin password. Everything else is configured from the admin pages.
CI/CD
Pushes to main run .forgejo/workflows/deploy.yml: build and push the
image to the Forgejo registry (:latest and :<sha>), then update the
Portainer stack pinned to that commit's image. Required repository secrets:
| Secret | Purpose |
|---|---|
REGISTRY_USER / REGISTRY_PASSWORD |
Push access to git.thenymans.com's container registry |
PORTAINER_TOKEN |
Portainer API key |
PORTAINER_STACK_ID |
Numeric ID of the PatreonLink stack |
PORTAINER_ENDPOINT_ID |
Numeric ID of the Portainer environment |
Create the stack once in Portainer (paste docker-compose.yml); the workflow
updates it on every push afterwards.
Setup walkthrough
- General settings — set the public base URL (the address patrons will
use, e.g.
https://link.example.com). OAuth redirect URLs are derived from it. Put the app behind HTTPS (reverse proxy) for production. - Patreon — create an API client at patreon.com/portal → Clients & API Keys, set its redirect URI to the one shown on the page, paste the client ID/secret, then click Connect Patreon account and Test connection.
- Discord — create an app at discord.com/developers, add the redirect shown on the page, paste the client ID/secret. This powers the patron portal login.
- Minecraft — pick one:
- RCON (Java servers, including Geyser/Floodgate setups): host, port,
password, and the add/remove command templates. For Floodgate use
fwhitelist add {gamertag}/fwhitelist remove {gamertag}. Enable RCON inserver.properties(enable-rcon,rcon.port,rcon.password). - allowlist.json (Bedrock Dedicated Server): mount your server
directory into the container (see
docker-compose.yml) and point atallowlist.json. BDS reloads the file automatically.
- RCON (Java servers, including Geyser/Floodgate setups): host, port,
password, and the add/remove command templates. For Floodgate use
- Share
https://your-base-url/linkwith your patrons.
How matching works
Each sync pulls all campaign members from Patreon (name, email, membership
status, and — when the patron has connected it — their Discord user ID).
A patron is whitelisted when their status is active_patron and a
gamertag is on file. Gamertags arrive three ways:
- Patron logs into
/linkwith Discord; their Discord ID matches the one Patreon reported → they enter their gamertag. - Patron's Discord isn't on Patreon → they claim by Patreon email, then enter their gamertag.
- The admin sets a gamertag directly on the dashboard.
Notes
- Data (settings + links + logs) persists in
./data/patreonlink.db. - The admin pages are password-protected, but there is no rate limiting or CSRF protection beyond same-site cookies — don't expose the admin UI to the open internet without a reverse proxy in front, and always use HTTPS.
- Local development:
pip install -r requirements.txtthenuvicorn app.main:app --reload(state goes to./data/).