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.