Releases: shpaker/feedforbot
Releases · shpaker/feedforbot
5.1.0
Added
py.typedmarker (PEP 561) — downstream type checkers now pick up inline type annotations shipped with the package- PyPI classifiers populated in
pyproject.toml(Python 3.10–3.14, topics, license,Typing :: Typed) — improves discoverability on pypi.org - Lint CI status badge in README
Changed
- README feature list polished and restructured
tmfeed/deploy artifacts renamed for clarity:config.yml→feedforbot.config.yml,deploy.yml→deploy.playbook.yml; newfeedforbot.compose.yml.j2template; Justfile and deploy workflow updated to match- CI:
build.ymlconsolidated intopypi.yml; lint matrix tweaks
Full Changelog: 5.0.0...5.1.0
5.0.0
Added
--healthcheck-hostCLI option (defaults to127.0.0.1) — previously bound to0.0.0.0unconditionallyHttpClientProtocol,ListenerProtocol,TransportProtocolare now context managers (__enter__/__exit__) with a requiredclose()method — scheduler closes listener/transport (and their HTTP clients) cleanly on shutdown- Per-scheduler
cache_limitconfig option — caps the number of entries kept in cache (keeps the N most recent bygrabbed_at); unbounded when unset --cache-dsnCLI flag (defaultfile:) — cache backend is now configured on the command line via a URL-style DSN (file:persists at~/.feedforbot/,file:///custom/pathuses a custom directory,memory:is in-process). Designed to scale to future backends (e.g.redis://host:6379/0) without proliferating flagsRedisCachebackend +redis://…/rediss://…/unix://…DSN schemes — shared cache suitable for multi-instance deployments and docker-compose setups. Ships with thecliextra (pip install feedforbot[cli]) and the GHCR Docker image. Theredispackage is an optional dependency —RedisCache(...)raises a clearImportErrorwhen it isn't installed, so core installs (pip install feedforbot) stay minimal
Changed
- Breaking: healthcheck endpoint now responds only at
/health(previously matched every path) — Docker/Kubernetes probes configured against/must be updated - Breaking: YAML config is now a top-level list of listener+transport entries; the
cache:andschedulers:wrapper keys have been removed. CLI positional argument renamed fromCONFIGURATIONtoSCHEDULERS_FILE. Cache backend selection moved fromcache.typein the YAML to the new--cache-dsnCLI flag. - Breaking:
CacheProtocolredesigned aroundis_populated/known_ids/add(*articles)/trim(limit)(previouslyread/write/is_populated).FilesCacheon-disk schema is now[{"id", "grabbed_at"}](no title/url/text). Files written by earlier versions are detected on load, deleted, and treated as a first-run (no spam on upgrade). Custom cache implementations must be updated.
Fixed
FilesCachewrites atomically (tempfile +os.replace) — no more corrupted cache on crash/power loss mid-writeFilesCachetolerates corrupt/invalid cache files — logs a warning, deletes the file and treats it as a first-run instead of propagating to SentryFilesCacheno longer grows unbounded — use the newcache_limitscheduler option to cap retentionHttpClient.postraisesHttpResponseErroron non-JSON or non-object payloads (e.g. HTML error pages from Telegram API proxies) instead of leakingJSONDecodeError- Telegram default and custom templates now HTML-escape article fields (title/text/etc.) — prevents Telegram API rejecting messages with
&,<,>in content - Config with empty
schedulers: []is now rejected at parse time instead of crashing withValueErrorinThreadPoolExecutor RSSListenerskips individual malformed feed entries (missingsummary/title) with a warning instead of failing the whole tick- Telegram bot token masking now covers error-path log lines (
http_error:) — token could previously leak viastr(exc)when httpx included the URL in connection errors - HTTP 429 flood-wait responses are now retried after the
Retry-Afterdelay (capped bymax_retry_after, default 60s) instead of failing the send immediately — Telegram rate-limit bursts self-heal - CLI graceful shutdown:
shutdown_start/shutdown_completeare logged on SIGTERM/SIGINT,CancelledErroris no longer reported as a crash, a second signal forces an immediateloop.stop()escape hatch, and Windows (no POSIX signals) falls back toKeyboardInterruptwithout raising
Full Changelog: 4.0.0...5.0.0
4.0.0
Changed
- Migrated from Poetry to uv
- Python >=3.10 with CI matrix (3.10–3.14)
- Structured logging throughout core modules
- Domain layer made synchronous (async only in Scheduler)
- Cache merges articles instead of overwriting
- HTTP client with persistent connections and exponential backoff retries
- Jinja2 sandbox rendering (SSTI mitigation)
- Telegram Bot API token redacted from logs/tracebacks
- New articles sorted chronologically before sending
- All GitHub Actions pinned to commit SHAs
Added
- Healthcheck endpoint (
--healthcheck-port) - JSON Schema for YAML config
- HTTP request timeouts (30s total, 10s connect)
- Correlation ID per scheduler tick for log tracing
- Docker images published to GHCR
- Comprehensive test suite
- GitHub Actions deploy workflow for tmfeed
Fixed
- Scheduler silently dying on unhandled tick exceptions
TelegramBotTransportsilently ignoring API errorsArticleModel.__eq__raisingAttributeErroron non-model typesRSSListenercrashing on<img>tags withoutsrc- Cache diff performance, race conditions, and datetime serialization
- Docker image tag strategy and GHCR metadata
4.0.0rc7
Fixed
- Scheduler silently dying on unhandled tick exceptions —
_ticknow catchesExceptionbroadly, logs the full traceback, reports to Sentry, and continues to the next cron tick. Previously, any error other thanListenerReceiveError(cache I/O, parsing, validation) would kill the scheduler coroutine, silently swallowed byasyncio.gather(return_exceptions=True). The container stayed healthy via healthcheck but no feeds were polled. - Crashed scheduler tasks are now logged at ERROR level when
asyncio.gatherexits.
Added
flake8-bandit(S) rules enabled in ruff, with per-file-ignores for test assertions.- Deployment configs for tmfeed.
4.0.0rc6
4.0.0rc5
3.3.5
3.3.4
3.3.3
Full Changelog: 3.3.2...3.3.3
3.3.2
Full Changelog: 3.3.1...3.3.2