Back to Blog

AstrBot / RSS / Automation

Cron Aggregate Delivery in RSS Forwarder

A record of cron triggers, batch aggregation, dynamic titles, local cards, and fallback summaries.

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

RSS Forwarder had already stabilized daily digest cards and ordinary media delivery last week. This week moved to another reading pattern: instead of sending every RSS update immediately, the plugin can collect a batch of new items at fixed times and turn them into one browseable aggregate message.

That fits personal reading, hobby channels, and low-interruption information streams. It keeps automatic RSS collection, but changes the delivery rhythm from source-driven notifications to scheduled summary windows.

Project Overview

The plugin is maintained publicly at RhoninSeiei/astrbot_plugin_rss_forwarder. The main configuration still centers on feeds, targets, jobs, and daily_digests, but jobs can now use a five-field cron expression as the trigger.

In the codebase, scheduler.py parses cron expressions and resolves the next trigger time, dispatcher.py keeps target-level confirmation and delivery behavior, pipeline.py builds aggregate titles and section summaries, and aggregate_card_image.py renders aggregate cards locally with Pillow. Tests cover configuration parsing, cron scheduling, LLM JSON parsing, fallback summaries, and image-card field preservation.

Cron Triggers

jobs[].cron now supports five fields: minute, hour, day, month, and weekday. For example, 0 9,18 * * * triggers at 09:00 and 18:00 every day. When both cron and interval_seconds are configured on the same job, scheduling follows cron first and keeps the interval field only as a compatibility value.

This allows one plugin to handle both high-frequency notifications and fixed-time summaries. For personal reading, one morning and one evening aggregate message are often easier to process than dozens of immediate pushes.

Aggregate Delivery

The new jobs[].aggregate_enabled mode sends a batch of filtered new items through an aggregate pipeline instead of delivering them one by one. Before aggregation, items still pass through historical filtering, exact deduplication, target-level confirmation, and semantic duplicate checks.

The aggregate model is asked to return a short title and a list of section summaries. The prompt asks for a compact header title, Simplified Chinese section titles and summaries, and no colon-style redundant prefixes in the title. The output should read like a card, not like a model analysis report.

If the LLM is unavailable, times out, or returns invalid JSON, the plugin falls back to a local numbered list. A follow-up change also lets the fallback path use the translation enhancement first, so an aggregate failure does not suddenly fall back to raw source titles and summaries.

Aggregate Cards

jobs[].aggregate_render_mode=image renders a local PNG card with Pillow. The card keeps a title, source metadata, item count, section titles, and summaries. When aggregate_include_images is enabled, the plugin tries to cache entry images locally and render them beside the relevant section.

This card has the same value as a daily digest card: it compresses multiple entries into one readable object. The difference is rhythm. A daily digest fits a once-a-day window, while a cron aggregate works better for reviewing updates every few hours.

Maintenance Notes

This update moves RSS Forwarder further from a single-item push tool toward a lightweight information organizer. Cron controls rhythm, aggregation controls expression, target-level confirmation keeps multi-target delivery predictable, and local cards provide a compact presentation surface.

For a personal interests site, this is worth recording separately. It is not just another configuration field; it recombines source reading, deduplication, summarization, image rendering, and send confirmation into a reading flow that fits everyday use.