Files
discord_bot/specs/SPEC-013-news.md
T

2.3 KiB

SPEC-013 — News digest

Replaces the broken pre-1.0-openai news_feed.py. A CLI (python -m fjerkroa_bot.news --config <cfg>) fetches the news-feeds and writes a compact digest to the news file that AIResponder.message injects into the {news} slot. Feeds are external input and operator-configured.

NEWS-01 — RSS and Atom parse to items (coverage: test)

parse_feed(bytes, label) extracts {title, link, source} from both RSS (<item>) and Atom (<entry>) documents, tolerates malformed XML (returns an empty list, logs), and never raises.

NEWS-02 — Digest is sanitized and bounded (coverage: test)

render_digest caps at news-max-items, and every headline passes sanitize_external_text (SAF-03) — a feed cannot inject @everyone or control characters into the prompt via a headline.

NEWS-03 — Feeds are SSRF-guarded and deduped (coverage: test)

NewsFetcher.collect skips any feed URL the SSRF guard rejects, skips feeds that fail to fetch (one bad feed never sinks the run), and drops duplicate headlines across feeds.

Webhook posting (ggg model)

--post mode fetches feeds mapped to channels and posts NEW items to the channel's Discord webhook — replacing the py3.8 getnews.py (dead play3 feed, 35 MB substring-scan state file, HTML-redirect cruft). Config: news-post-feeds = [[url, label, channel], …], news-post-webhooks = {channel = url}, news-post-state.

NEWS-04 — Only unseen items post, then are marked seen (coverage: test)

NewsPoster.run_post posts each item whose key (link, else title) is not in the seen-set, adds it to the set, and posts to the mapped channel's webhook. Re-runs over the same feed post nothing new.

NEWS-05 — First run seeds without flooding (coverage: test)

With no prior state file (seed_only), every current item is marked seen but nothing is posted — migrating off getnews.py never dumps a backlog into the channels. news-post-max-per-run caps steady-state posts per run.

NEWS-06 — Post failures and bad channels are survived (coverage: test)

A feed the SSRF guard rejects, a feed that fails to fetch, an item whose channel has no configured webhook, and a webhook POST that raises are each logged and skipped — one failure never sinks the run, and the seen-set still advances for successfully-processed items.