bin/rss-digest.py is the 87-line Python script you’re modifying today to generate personalized daily RSS digests for your subscriber list, no paid third-party tools required. The build includes a configurable feed list, a responsive HTML digest template, and a dry-run flag that lets you test every part of the workflow without sending accidental emails to your list.
Command Structure for bin/rss-digest.py
The script uses Python’s built-in argparse library to handle command-line inputs, with three core optional flags to adjust its behavior. The `–config` flag lets you specify a custom feed configuration file if you’re testing a new list of feeds separate from your production setup. The `–date` flag accepts a YYYY-MM-DD string to override the default 24-hour window (the current calendar day) if you want to generate a digest for a past date, or build a weekend roundup that pulls 48 hours of entries. The `–dry-run` flag is the most critical for safe testing, and when enabled, follows this exact execution flow:

- Parses all input flags and validates that your feed config file has no syntax errors, duplicate feed URLs, or missing required fields, exiting immediately with a descriptive error if any issues are found.
- Fetches all RSS and Atom feeds in your config, adding a 1-second delay between requests to avoid hitting common rate limits, and caches all responses to a `.rss-cache` directory that persists for 24 hours to eliminate redundant network calls during template testing.
- Filters feed entries to only include items published within your target date window, and removes any entries that include terms on your global blocklist, or on a feed-specific blocklist you define in your config.
- Renders the filtered, categorized entries into your responsive HTML template, populating placeholders for the digest date, entry titles, publish dates, source feed names, and entry summaries.
- Writes the fully rendered digest to a `digest-preview.html` file in your working directory, prints a summary of fetched and filtered entries to your terminal, and skips all SMTP email send steps entirely.
Persistent Output Generated by bin/rss-digest.py
Not all output from the script is temporary, and you can retain specific files to speed up future testing and debug issues with your feed list. The `.rss-cache` directory remains on your local machine for 24 hours after a run, so you can tweak your template or blocklist as many times as you want without re-fetching the same feed data over and over. The `rss-digest-errors.log` file writes a timestamped entry for every feed that fails to load, including the status code returned, the feed URL, and any error messages from the parser, so you can distinguish between temporary server outages and permanently dead feed links. The `digest-preview.html` file you generate during dry runs can be saved to compare digest designs over time, or shared with a small group of test reviewers to get feedback on layout or content curation before you send the full digest to your entire list. Illustrative example: 12 of your 17 configured feeds return valid entries on a typical dry run, with 2 feeds returning 404 errors, 1 feed returning a 429 rate limit error, and 2 feeds with no new entries in the target 24-hour window. You can use this error breakdown to prioritize fixes: update dead URLs first, adjust request delays for rate-limited feeds, and remove feeds that never publish new content to keep your digest running efficiently.
Committed Feed Configuration for bin/rss-digest.py
You store all your feed metadata in a `feeds.toml` file that lives in the same directory as the script, which you commit to your Git repo alongside the script to track changes to your feed list over time. The TOML format is simple to edit even for non-technical contributors, so you can let other curators add or remove feeds without granting them access to modify the core script logic. Each entry in the config includes a display name for the feed, its full URL, a category tag for grouping entries in the digest, and a boolean flag to control if it is included in daily sends, with optional fields for custom authentication headers and feed-specific blocklists. A sample of your production config looks like this:
| Display Name | Feed URL | Category | Include In Daily Digest |
|---|---|---|---|
| TechCrunch Startups | https://techcrunch.com/category/startups/feed/ | Business | True |
| Open Source Security Updates | https://www.oss-security.com/feed.xml | Security | True |
| Local City Arts Events | https://yourcityartscouncil.org/events/rss | Community | False |
| Indie Game Dev Weekly | https://indiegamedevweekly.com/rss | Hobby | True |
You use the category tag to sort entries in the final digest, so business and security updates appear first, followed by hobby content, and you use the include flag to reserve low-frequency feeds like the local arts events list for your weekly Sunday roundup instead of cluttering daily sends with empty sections.

Next Run of bin/rss-digest.py
When you run this script next, you will have patched the two dead feed URLs from your last dry run’s error log, adjusted the rate limit delay to 2 seconds for the feed that returned a 429 error, and tweaked the HTML template to add a small header with your digest name and a link to your personal site. If you run the script within 24 hours of your last dry run, it will pull all feed data from the existing cache, so you can test your template changes in 2 seconds flat instead of waiting for all 17 feeds to load again. If you’re satisfied with the preview, you can run the script without the `–dry-run` flag, and it will send the digest to every email address on your subscriber list, which is stored in a separate `subscribers.csv` file that you never commit to Git to protect user privacy. You can also schedule the script to run automatically via cron at 7AM local time every day, so you never have to remember to trigger it manually, and you can configure the script to send a copy of the error log to your personal email if any feeds fail to load on a scheduled run. This lightweight, low-maintenance automation workflow aligns with the no-fluff tooling patterns taught on Build Next Stack, so you don’t have to rely on expensive third-party RSS digest tools that lock your feed list and subscriber data behind a paywall. You can extend the script over time to add features like subscriber preference management, custom digest frequencies, or social media previews for popular entries, all without rewriting the core logic you’ve already built. If you ever want to test a new set of feeds for a niche digest, you just copy the existing script, swap out the `feeds.toml` file, adjust the template branding, and you have a fully functional new digest workflow in minutes.