Back to Blog

AstrBot / RSS / Images

Local Digest Cards and Media Delivery in RSS Forwarder

A record of improving daily digest cards, local assets, remote media caching, and delivery confirmation.

Synthetic RSS digest card with grouped entries and local media placeholders.

The recent RSS Forwarder work was not mainly about adding more sources. It was about making daily reading digests and ordinary RSS media delivery more predictable. In practice, a subscription plugin often fails not at fetching an item, but at sending mixed text and media, handling different display styles per target, and avoiding repeated delivery after a partial failure.

Project Overview

The plugin is maintained publicly at RhoninSeiei/astrbot_plugin_rss_forwarder. Its configuration is split into feeds, targets, jobs, and daily_digests: feeds provide RSS, Atom, or Twitter/Nitter items, targets describe platform sessions, jobs combine multiple feeds and targets, and daily digests summarize a time window.

The code follows the same division. fetcher.py handles source requests and proxies, parser.py normalizes entries, scheduler.py runs periodic tasks, dispatcher.py handles deduplication, translation, and delivery, and daily_digest_image.py renders local digest cards. assets/daily_digest/ keeps lightweight card assets and fonts, while tests cover configuration migration, parsing, scheduling, delivery, and card generation.

Local Digest Rendering

Daily digest images used to depend on an external rendering capability, which introduced several failure points: service availability, remote rendering latency, font differences, and message-chain wrapping. The new render_mode=image implementation renders cards locally with Pillow, using bundled RSS marks, corner accents, signature assets, and local fonts.

That makes the output repeatable and removes the need to wait on a remote image service during deployment. For a static digest card, clear hierarchy matters more than visual complexity: title, source, summary, and time window should be easy to scan. Local rendering also makes fallback behavior simpler, because a failed image render can fall back to a text digest.

Remote Media Caching and Confirmation

Remote images in ordinary RSS entries were also revised. In text mode, the plugin now prefers caching remote RSS images as local JPEG files before passing them to the platform adapter. For sources that need a proxy, feeds[].proxy_url is passed into original-image cache requests, reducing failures caused by anti-hotlinking, image format differences, and remote transfer issues.

Delivery confirmation also changed around real usage. One version split body text and media into separate sends, confirming text deduplication before sending original images and videos, so rich-media upload failures would not cause the body to repeat. A later text-mode revision returned to a single message chain for body plus images, after first normalizing images through local caching. The important design point is to confirm content identity first, then preserve the full media shape where the adapter can handle it.

Maintenance Notes

An RSS helper looks like scheduled fetching and forwarding, but the maintained shape is closer to a small publishing system. Sources, targets, jobs, deduplication, semantic merging, translation, digest rendering, media caching, and send confirmation all need clear boundaries.

After this update, the project structure is easier to maintain: digest cards render offline, remote media enters a local cache first, target-level confirmation prevents repeated delivery in multi-target jobs, and configuration can tune source, job, and target presentation separately. For personal reading and hobby channels, that is more useful than simply adding more subscriptions.