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.
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.
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.
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.
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.
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.