Rss Digest

Other

Generate a structured daily or weekly markdown digest from an OPML list of RSS/Atom feeds, optionally filtered by keywords, for Obsidian or Discord.

Install

openclaw skills install nai-rss-digest

RSS Digest

Generate a markdown digest from an OPML feed list. No API keys required. Uses only Python stdlib.

Scripts

ScriptPurposeInputOutput
scripts/parse_opml.pyParse OPML → feed listOPML file pathJSON array (stdout)
scripts/fetch_feeds.pyFetch feeds → articlesJSON feed list (stdin or --feeds)JSON array (stdout)
scripts/build_digest.pyArticles → markdownJSON articles (stdin)Markdown (stdout or --output)

Reference OPML: references/opml-example.opml

All scripts write progress/errors to stderr, data to stdout — safe to pipe.

Setup

Make scripts executable once:

chmod +x ~/.openclaw/workspace/skills/rss-digest/scripts/*.py

Set a variable for convenience:

SKILL=~/.openclaw/workspace/skills/rss-digest

One-Time Digest

Run the full pipeline:

python3 $SKILL/scripts/parse_opml.py ~/feeds.opml \
  | python3 $SKILL/scripts/fetch_feeds.py --hours 24 \
  | python3 $SKILL/scripts/build_digest.py --period Daily --output ~/digest.md

Open ~/digest.md in Obsidian or cat to terminal.

Weekly Digest

python3 $SKILL/scripts/parse_opml.py ~/feeds.opml \
  | python3 $SKILL/scripts/fetch_feeds.py --hours 168 \
  | python3 $SKILL/scripts/build_digest.py --period Weekly --group-by category --output ~/weekly-digest.md

Keyword Filtering

Only include articles matching specific topics:

python3 $SKILL/scripts/parse_opml.py ~/feeds.opml \
  | python3 $SKILL/scripts/fetch_feeds.py --hours 24 --keywords "AI,machine learning,LLM" \
  | python3 $SKILL/scripts/build_digest.py --title "AI Digest"

--keywords is comma-separated, case-insensitive. Matches title or summary.

Cron Schedule

Add to crontab (crontab -e) for a daily digest at 7 AM ET:

0 7 * * * python3 /Users/openclaw/.openclaw/workspace/skills/rss-digest/scripts/parse_opml.py ~/feeds.opml | python3 /Users/openclaw/.openclaw/workspace/skills/rss-digest/scripts/fetch_feeds.py --hours 24 | python3 /Users/openclaw/.openclaw/workspace/skills/rss-digest/scripts/build_digest.py --period Daily --output /Users/openclaw/.openclaw/workspace/vault/daily-recap/rss-$(date +\%Y-\%m-\%d).md

Weekly digest every Monday at 8 AM:

0 8 * * 1 python3 /Users/openclaw/.openclaw/workspace/skills/rss-digest/scripts/parse_opml.py ~/feeds.opml | python3 /Users/openclaw/.openclaw/workspace/skills/rss-digest/scripts/fetch_feeds.py --hours 168 | python3 /Users/openclaw/.openclaw/workspace/skills/rss-digest/scripts/build_digest.py --period Weekly --group-by category --output /Users/openclaw/.openclaw/workspace/vault/daily-recap/rss-weekly-$(date +\%Y-\%m-\%d).md

Options Reference

fetch_feeds.py

FlagDefaultDescription
--hours N24Articles from last N hours
--keywords K(none)Comma-separated filter keywords
--feeds FILEstdinJSON feed list file (alternative to piping)
--timeout N10HTTP timeout per feed in seconds

build_digest.py

FlagDefaultDescription
--title STR"RSS Digest"Digest heading title
--period STR"Daily"Period label (Daily / Weekly)
--output FILEstdoutWrite to file instead of stdout
--group-by STR"feed"Group by: feed, category, or none
--max-summary N300Max chars of article summary (0 = omit)
--date STRtodayOverride the date label

OPML File

Export your OPML from any RSS reader (Feedly, NetNewsWire, Reeder, Inoreader, etc.) via their export settings. See references/opml-example.opml for the expected format. Feeds can be flat or nested inside category folders.

Output Format

The digest is clean markdown with:

  • H1 title with article count
  • H2 sections per feed/category
  • Linked article titles, publication time, and truncated summary
  • Footer with generation timestamp

Suitable for pasting into Discord (plain markdown), saving to Obsidian, or emailing.

Error Handling

  • Unreachable feeds are skipped with a stderr warning — the rest continue.
  • Articles with no publish date are included (can't apply time filter to them).
  • Malformed XML feeds are skipped gracefully.
  • Empty result sets produce a "No articles found" digest rather than crashing.