Resolve the current-year sheet tab per fetch instead of freezing it at startup, so a long-running process follows the New Year rollover without a restart. Guard against the regression this introduces: a successful fetch that returns no duty weeks (e.g. a freshly created, empty next-year tab) now keeps the last-good cache instead of clobbering it with empty data. Surface a silently-stale cache — which is not a crash, so process-level supervision can't catch it — by escalating to a loud error log once no sync has succeeded for 6h (new Store.LastSynced accessor backs the check). Throttle manual /sync to once per minute across the chat to avoid spamming the Google Sheets API; the check-and-set is mutex-guarded since Telegram dispatches each update in its own goroutine. Update README accordingly. |
||
|---|---|---|
| cmd/dyzur-bot | ||
| .dockerignore | ||
| .gitignore | ||
| .golangci.yml | ||
| .markdownlint-cli2.yaml | ||
| .pre-commit-config.yaml | ||
| Dockerfile | ||
| README.md | ||
| doc.go | ||
| go.mod | ||
| go.sum | ||
| scheduler.go | ||
| scheduler_test.go | ||
| service.go | ||
| service_test.go | ||
| sheet.go | ||
| sheet_test.go | ||
| store.go | ||
| store_test.go | ||
| telegram.go | ||
| telegram_test.go | ||
README.md
Dyżur-bot
A telegram bot for managing the cleaning rota in hackerspace. Bot offers weekly reminders about cleaning duties as well as on demand informations. Backend read the schedule from a Google sheet, store it in local JSON file.
Running
Requires GOOGLE_API_KEY, GOOGLE_SHEETS_ID, and BOT_TOKEN in the
environment or a .env file (see service.go/sheet.go for optional
SHEET_RANGE, ROTA_CACHE_PATH, SYNC_INTERVAL). Then:
go run ./cmd/dyzur-bot
The service loads the cached rota, syncs from Google Sheets in the background on
SYNC_INTERVAL (default 30m), and runs the Telegram bot via long polling until
interrupted (Ctrl-C / SIGTERM).
When SHEET_RANGE is unset, the bot reads the current year's tab (e.g.
2026!A1:G100), resolved on every sync — so it follows the New Year rollover
automatically without a restart. A sync that fails, or that returns no duty
weeks (e.g. a next-year tab that exists but hasn't been filled in yet), keeps
the last-good cache instead of clearing it; if no sync succeeds for 6h the bot
logs a loud stale-cache error.
Run exactly one instance per bot token. The bot uses long polling (
getUpdates); two concurrent pollers on the same token make Telegram return HTTP 409 Conflict and updates get dropped. Do not scale this to multiple replicas.
Docker
docker build -t dyzur-bot .
docker run --rm --env-file .env -v dyzur-data:/data dyzur-bot
The image is a static binary on distroless (timezone data is embedded, so
reminders work without system tzdata). The rota cache lives at
/data/rota.json (ROTA_CACHE_PATH); mount a volume at /data to persist it
across restarts.
Commands
/kto_sprzata— who is on cleaning duty this week./sync— refresh the rota from Google Sheets now (throttled to once per minute across the chat to avoid spamming the Sheets API).
Reminders
If GROUP_CHAT_ID is set, the bot posts a weekly duty reminder to that chat.
Schedule (all optional, with defaults): REMINDER_WEEKDAY (default Monday),
REMINDER_HOUR (default 9, 0–23), DUTY_AHEAD_DAYS (default 14). The hour
is interpreted in Europe/Warsaw time. The reminder announces who is on duty
DUTY_AHEAD_DAYS from the fire time — e.g. every Monday 09:00, who cleans two
weeks out. Leave GROUP_CHAT_ID unset to disable reminders.