feedmill
RSS/Atom to Discord and Telegram bridge with per-channel filters.
feedmill polls a list of RSS/Atom feeds, remembers what it has already seen in a small SQLite database and posts new entries to Discord webhooks or Telegram channels. Each target can have its own include/exclude filters, so one feed can fan out to several places.
Why
Most hosted feed-to-chat services either poll too rarely or want an account for every channel. This is a single process with a single config file, and it is happy on the smallest VPS you can buy.
Install
python3 -m venv /opt/feedmill
/opt/feedmill/bin/pip install feedmill-0.9.3-py3-none-any.whl
/opt/feedmill/bin/feedmill check -c /etc/feedmill.yaml
Config
interval: 10m
database: /var/lib/feedmill/state.db
user_agent: "feedmill/0.9 (+https://dev.tapun.site/projects/feedmill/)"
feeds:
- url: https://blog.rust-lang.org/feed.xml
targets: [discord:releases]
include: ["Announcing Rust"]
- url: https://lwn.net/headlines/rss
targets: [telegram:news]
exclude: ["[$]"]
targets:
discord:releases:
webhook: ${DISCORD_WEBHOOK}
telegram:news:
token: ${TG_TOKEN}
chat: "@example_channel"
Commands
feedmill run | poll forever, honouring interval |
feedmill check | validate the config and fetch every feed once, without posting |
feedmill replay --since 24h | re-post entries from the last day to a test target |
feedmill vacuum | drop state for feeds that are no longer configured |
Notes
- Entries are keyed by
guid, falling back to a hash of link + title when a feed has no stable ids. - Discord
429responses are retried afterRetry-After; other errors are logged and skipped. - The database uses WAL mode, so
feedmill checkcan run while the service is up.
Changes
- 0.9.3 — Fixed duplicate posts when a feed switches its guid format; retry Discord 429 after Retry-After.
- 0.9.0 — Per-target include/exclude filters and the replay command.
- 0.8.0 — Telegram target.