After getting my network and server dialed in, I wanted to put that stability to work doing something useful - something that solved a real problem I deal with every week.
For anyone in the Flesh and Blood TCG community, the official event site lists everything, but it is not designed for quick reference or notifications. I wanted one unified, automatically updating calendar - something I could subscribe to on my phone or share with my playgroup.
GitHub: https://github.com/chaco-win/fab-events-sync
Live calendar: https://fabevents.chaco.dev
Why This Project
- One place to see competitive events without digging through pages.
- Shareable, phone-friendly calendar that updates itself.
- Discord notifications so groups stay informed automatically.
- Built to run unattended: containers, health checks, and logs.
The Goal
Create a fully automated system that:
- Scrapes new Flesh and Blood events from FABTCG.com
- Parses and normalizes event data (name, date, location, format)
- Publishes them to a shared calendar and web frontend
- Notifies users through Discord when new events are added
- Monitors itself and reports any issues automatically
Containerized, version-controlled, and built to survive without babysitting.
The Stack
A small Docker Compose stack on my home server:
/srv/docker/fab-calendar/
- docker-compose.yml
- scraper/
- main.py
- requirements.txt
- logs/
The scraper handles scraping, parsing, and syncing via the Google Calendar API. It is lightweight, stateless, and auto-restarts if something fails.
services:
fab-calendar:
build: ./scraper
volumes:
- ./logs:/logs
environment:
- GOOGLE_CREDENTIALS_JSON=/config/creds.json
- DISCORD_WEBHOOK_URL=${DISCORD_WEBHOOK_URL}
restart: always
How It Works
- Scraping: pulls FABTCG’s event listings and parses them into structured JSON. Filters by format (ProQuest, Battle Hardened, Nationals) and skips duplicates.
- Calendar Sync: authenticates with a Google service account and updates the shared public calendar at fabevents.chaco.dev.
- Discord Notifications: posts a formatted message to Discord via webhook or bot token when new events are detected.
- Health Monitoring: logs to /logs/fab-calendar.log and records status for weekly health checks.
Weekly Health Checks
Every Sunday morning a separate process verifies:
- The scraper ran successfully within the past week
- The last calendar update timestamp is valid
- No repeated errors in the logs
If anything fails, a Discord notification fires automatically.
[HealthCheck] Last run: 2025-09-07 06:00 UTC
[HealthCheck] Calendar updated successfully (45 total events)
[HealthCheck] No errors detected
Discord Integration and Notifications
When events are updated:
FAB Calendar Updated
3 new events found:
- ProQuest: Dallas, TX
- Battle Hardened: Sydney, AUS
- Nationals: New Zealand
If the scraper fails:
FAB Calendar ERROR
Traceback: 'NoneType' object has no attribute 'find'
Future Plans: Public Discord Bot
Next phase: a public Discord bot anyone can invite. It will use the same backend but expose slash commands.
Planned features:
- /fab upcoming - list upcoming events by region or format
- /fab subscribe - subscribe to notifications for types/regions
- /fab remind - automated reminders (e.g., Monday before)
- /fab optout - unsubscribe from categories
- /fab health - scraper uptime and last update status
Challenges and Lessons Learned
- Cloudflare rate limits: randomized delays, caching, and backoff to avoid being flagged
- Timezones: everything converted to UTC to prevent offsets
- Token management: automatic key rotation for Google API tokens
- Webhook spam: deduplicate by event ID
Why It Works in My Setup
- Hosted in Docker on mirrored ZFS for redundancy
- Uses Pi-hole for DNS and logging visibility
- Exposed securely through Cloudflare Tunnel - no open ports
- Snapshotted and backed up weekly
It is stable, isolated, and low-maintenance - exactly what I wanted.
Looking Ahead
Source: https://github.com/chaco-win/fab-events-sync
Calendar: https://fabevents.chaco.dev
Next steps:
- Launch the public Discord bot
- Add customizable reminders for local stores/leagues
- Build a small dashboard for curation and feedback