mod affinity_decay; mod scoring; use anyhow::Result; use feedsignal_db::Db; use feedsignal_llm::Llm; use std::sync::Arc; use tokio_cron_scheduler::{Job, JobScheduler}; /// Wires up the two recurring jobs described in the design discussion: /// polling feeds + scoring new articles on a short interval, and decaying /// topic affinities once a day so stale signals fade. Call once at server /// startup. pub async fn start_scheduler(db: Arc, llm: Arc) -> Result { let scheduler = JobScheduler::new().await?; { let db = db.clone(); let llm = llm.clone(); scheduler .add(Job::new_async("0 */15 * * * *", move |_uuid, _lock| { let db = db.clone(); let llm = llm.clone(); Box::pin(async move { if let Err(err) = scoring::run(&db, &llm).await { tracing::error!(?err, "scoring pipeline run failed"); } }) })?) .await?; } { let db = db.clone(); scheduler .add(Job::new_async("0 0 4 * * *", move |_uuid, _lock| { let db = db.clone(); Box::pin(async move { if let Err(err) = affinity_decay::run(&db).await { tracing::error!(?err, "affinity decay run failed"); } }) })?) .await?; } scheduler.start().await?; Ok(scheduler) }