Commit Graph

29 Commits

Author SHA1 Message Date
Kamil 'Kamaś' Bruchal 488f0b9a9b feat: ignore unknown commands instead of replying
The bot now answers only its predefined commands and stays silent on
anything else, logging ignored commands at debug level.

Tests no longer assert exact reply/reminder wording (which broke on
copy tweaks like 37a81a2) — they check behavior instead: message
counts, the names/handles substituted in, and that /sync persists its
fetch and doesn't leak internal errors.
2026-08-19 14:44:26 +02:00
Kamil 'Kamaś' Bruchal 37a81a2710 style: minor refinings 2026-07-20 17:45:35 +02:00
Kamil 'Kamaś' Bruchal 7f5bc8454a style: thank duty crew in reminders instead of urging them to clean 2026-07-20 17:37:32 +02:00
Kamil 'Kamaś' Bruchal 885ccac5af docs: describe two-stage lead time in reminders section 2026-07-13 17:49:55 +02:00
Kamil 'Kamaś' Bruchal 66364c044a feat: warn 2 weeks early and auto-assign 1 week early for empty duty weeks 2026-07-13 17:45:41 +02:00
kamash 43b4b85de4 Update .forgejo/workflows/ci.yml 2026-07-06 19:05:11 +02:00
Kamil 'Kamaś' Bruchal c703507020 Asserts: logo.png and forgejo workflow added 2026-07-06 19:03:29 +02:00
Kamil 'Kamaś' Bruchal 5a10eda3e1 fix: only answer unknown /commands, not ordinary messages
The default handler fired for every update the command matchers didn't
claim, so plain chatter, service messages, media, and replies to the bot
all drew a "Nieznana komenda" response — spammy in group chats. Gate the
fallback on a leading bot_command entity so only real (unrecognized)
commands get the help reply.
2026-06-22 19:58:13 +02:00
Kamil 'Kamaś' Bruchal 41980e4f6f test: cover Telegram command handlers via httptest bot
Add tests for the previously-untested handlers (ktoSprzata, sync, unknown,
matchCommand, BotSendFunc, PublishCommands) using a real *bot.Bot pointed at
an httptest server that captures sendMessage replies. Covers the /sync
success, throttled, and failure paths plus the non-message fallback.
2026-06-19 19:04:04 +02:00
Kamil 'Kamaś' Bruchal 6573144b14 feat: auto-assign empty duty weeks via service-account sheet writes
Migrate Sheets auth from an API key to a service-account credential
(GOOGLE_CREDENTIALS_FILE) with the read+write spreadsheets scope, add a
WriteSheet primitive, and use it to actively manage empty duty weeks in
the weekly reminder.

Each fire now handles a fixed this-week/next-week model (replacing the
DUTY_AHEAD_DAYS announce):
- this week empty -> pick the two people with the fewest duties (random
  tie-break), write their names into cols B/C, and announce
- this week filled -> announce who is on duty
- next week empty -> ask people to volunteer before it is auto-assigned

Col G ("Liczba dyżurów") is a live formula, so names-only writes keep the
fewest-duties selection fair across weeks. Selection, row tracking, range
building, and every fire() branch are unit-tested; live tests for sheet
read/write are env-gated.
2026-06-19 03:48:59 +02:00
Kamil 'Kamaś' Bruchal 825331dce5 fix: match bot commands addressed with @botusername in groups
In group chats Telegram appends the bot addressee to the bot_command
entity (/sync@hs_dyzur_bot), and the library's MatchTypeCommand compares
the whole token verbatim, so commands never matched. Replace it with
custom MatchFuncs that read the bot_command entity span and strip any
@suffix before comparing the command name.
2026-06-19 02:48:29 +02:00
Kamil 'Kamaś' Bruchal 816aa45c86 style: gofmt scheduler_test.go 2026-06-19 02:05:56 +02:00
Kamil 'Kamaś' Bruchal 4f9cc6c359 feat: harden rota sync and throttle manual /sync
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.
2026-06-19 02:05:04 +02:00
Kamil 'Kamaś' Bruchal 15b2fc9093 feat: ping on-duty members by Telegram handle in reminders
Read each member's Telegram handle from the new sheet column F (duty
count moves to G) and mention on-duty people as @handle in the weekly
reminder, so they get a real notification. Names without a handle, or
not found in the people list, fall back to plain text.

- sheet.go: add colHandle, Person.Handle; parse handle column
- scheduler.go: resolve names to @mentions via handlesByName/mention
- service.go: widen default SHEET_RANGE to column G
2026-06-19 01:40:20 +02:00
Kamil 'Kamaś' Bruchal 74cb302f64 chore: track go.mod, go.sum, and missing test files
These were never committed (hidden by status.showUntrackedFiles=no): without
go.mod/go.sum a fresh clone would not build, and the source files committed in
abb38e3 were missing their accompanying tests.
2026-06-18 21:40:41 +02:00
Kamil 'Kamaś' Bruchal 2e75d15f9d refactor: address review findings (slog, fetch timeout, Docker)
- add per-request timeout to the Sheets fetch so a hung call can't block /sync
- stop leaking raw sync errors to chat; log detail, reply generically
- migrate logging from log to structured log/slog
- drop duplicate godotenv.Load from ReadSheet (LoadConfig loads .env at startup)
- add multi-stage distroless Dockerfile + .dockerignore; embed tzdata via import
- document single-instance long-polling constraint in README
- translate sheet.go comments to English
- move package doc to doc.go; remove superseded root main.go and live_test.go
2026-06-18 21:38:37 +02:00
Kamil 'Kamaś' Bruchal 881bd5be03 feat: add weekly duty reminder scheduler
Add Reminder that posts a weekly cleaning-duty reminder to a Telegram
group on a configurable schedule (REMINDER_WEEKDAY/REMINDER_HOUR), in
Europe/Warsaw time, announcing the duty DUTY_AHEAD_DAYS out. Reminders
are disabled unless GROUP_CHAT_ID is set. Wire ReminderConfig parsing
into LoadConfig with validation, document the commands and reminder
config in README, and cover config loading with tests.
2026-06-18 02:36:06 +02:00
Kamil 'Kamaś' Bruchal 03e029cd4d docs: updated README.md 2026-06-17 22:29:29 +02:00
Kamil 'Kamaś' Bruchal a7bbbdc084 refactor: files consolidation 2026-06-17 21:29:35 +02:00
Kamil 'Kamaś' Bruchal cb0cc679c8 fix: read GOOGLE_SHEETS_ID and default range to current-year tab
LoadConfig read SPREADSHEET_ID, but the .env defines GOOGLE_SHEETS_ID,
so config loading failed. The default SHEET_RANGE was also a hardcoded
'Dyżury!A1:F100', but the spreadsheet tabs are named per year (e.g.
'2026'), producing a 400 'Unable to parse range'. Read GOOGLE_SHEETS_ID
and derive the default range from the current year.

Add a RUN_LIVE-gated integration test that drives the full
LoadConfig -> Service -> Syncer.Sync -> Store path against the live
Sheet, and gitignore the generated rota.json cache.
2026-06-17 21:16:25 +02:00
Kamil 'Kamaś' Bruchal 572701106c Docs: README.md added 2026-06-17 21:01:01 +02:00
Kamil 'Kamaś' Bruchal 6b37b56736 fix: clean fetch-sheet lint, gitignore .env 2026-06-17 20:58:49 +02:00
Kamil 'Kamaś' Bruchal 6c177c9514 fix: lint, interval validation, corrupt-cache tolerance, background init sync, concurrent test 2026-06-17 20:45:29 +02:00
Kamil 'Kamaś' Bruchal 7d799c74f1 feat: add Config + Service wiring for cache and background sync 2026-06-17 20:35:05 +02:00
Kamil 'Kamaś' Bruchal 74f3c03013 feat: add Syncer that refreshes cache and tolerates fetch failure 2026-06-17 20:31:53 +02:00
Kamil 'Kamaś' Bruchal 4afe123a76 feat: add in-memory rota Store with atomic JSON persistence 2026-06-17 20:29:04 +02:00
Kamil 'Kamaś' Bruchal 32f07ce90b Feat: Sheet parsing 2026-06-17 20:08:16 +02:00
Kamil 'Kamaś' Bruchal 335e698e5e Feat: ReadSheet implemented 2026-06-17 19:25:24 +02:00
Kamil 'Kamaś' Bruchal 7828e11046 Initial commit 2026-06-17 18:53:54 +02:00