Feeds were already persisted, but nothing ever re-fetched them after the initial subscribe — scoring.rs had a TODO where feed polling was supposed to go. Add a feed_polling job that re-fetches every subscribed feed, relies on articles.url's unique constraint to skip ones already seen, and stamps last_fetched_at. Runs once on startup (so reopening the app catches up immediately) and every 15 minutes after, ahead of the scoring pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QmwX8eafbMstJvt8XPSqft
29 lines
997 B
Rust
29 lines
997 B
Rust
use anyhow::Result;
|
|
use feedsignal_db::Db;
|
|
|
|
/// Re-fetches every subscribed feed and inserts any articles published since
|
|
/// the last run. `articles.url` is unique, so re-fetching the same feed and
|
|
/// inserting its (mostly already-seen) entries is safe — `insert_article`
|
|
/// silently skips ones already in the database, leaving only genuinely new
|
|
/// articles behind.
|
|
pub async fn run(db: &Db) -> Result<()> {
|
|
let feeds = db.list_feeds().await?;
|
|
tracing::info!(count = feeds.len(), "polling subscribed feeds");
|
|
|
|
for (feed_id, url) in feeds {
|
|
let (_title, articles) = match feedsignal_feeds::fetch_feed(&url, feed_id).await {
|
|
Ok(result) => result,
|
|
Err(err) => {
|
|
tracing::warn!(?err, %url, "failed to poll feed, skipping");
|
|
continue;
|
|
}
|
|
};
|
|
|
|
for article in &articles {
|
|
db.insert_article(article).await?;
|
|
}
|
|
db.mark_feed_fetched(feed_id).await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|