The health endpoint returned 200 unconditionally from before dependencies were ready and never reflected MongoDB state. Handler now takes a readiness check; the endpoint returns 503 while starting or when MongoDB is unreachable and 200 only when ready.
Fixed-index truncation could split an HTML tag or entity or drop a closing tag, making Telegram reject the whole message with 'can't parse entities'. Cut on a newline boundary where tags are balanced, reserve room for a truncation notice, and strip any trailing partial markup.
A single retryable delivery failure aborted checkpoints for the whole poll batch, so fully-delivered events were re-collected and re-sent on the next poll, and an unrelated failure froze all change detection. Attach post-delivery checkpoints to each event and run them as soon as that event fully delivers; a failed event defers only its own checkpoints for retry. A subscriber skipped after an earlier retryable failure now counts as the event's failure so the event is not marked delivered to a subscriber that never received it.
Transport-level failures return *url.Error embedding the full request URL, which contains the bot token. Strip the token before the error propagates to logs.
Swap the Redis datastore for MongoDB via mongo-driver v2. New
internal/mongostore mirrors the old redisstore type/constant names so
consumers change only the import qualifier; document model collapses the
multi-key subscriber/settings and per-event delivery sets into single-doc
operations and a TTL-indexed delivery collection.
Config now requires MONGODB_URI (Atlas) with MONGODB_DATABASE selecting
dev vs prod on one cluster; REDIS_URL removed. Compose files run bot-only
against Atlas. Default tests stay Docker-free (poller/bot use fakes,
mongostore unit tests are pure-logic); a gated //go:build integration
suite covers the real backend via testcontainers.
Replace the per-subscriber SISMEMBER delivery check with a single
SMEMBERS per event plus in-memory lookups, cutting N round-trips to one.
Remove the component-label helpers duplicated between poller and bot by
exporting the canonical versions from poller; bot already depends on it.
Collapse the repeated component checkpoint closures into a shared helper.
Drop the redundant escapeAttr alias and the unused variadic on bot.New.
ListSubscribers fetched settings with one HGET per subscriber. Replace
the per-key fetch with a single HGETALL and resolve each subscriber from
an in-memory map, cutting N round-trips to one per delivery cycle.
Extract resolveSubscriberSettings so the batched and single-key paths
share identical decode/default/self-heal behavior.