use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::task::JoinHandle;
use tokio::time::{self, Duration};
use crate::client::TopTL;
use crate::types::StatsPayload;
pub type StatsCallback = Arc<dyn Fn() -> StatsPayload + Send + Sync>;
pub struct Autoposter {
client: TopTL,
username: String,
interval: Duration,
callback: Option<StatsCallback>,
handle: Option<JoinHandle<()>>,
running: Arc<Mutex<bool>>,
}
impl Autoposter {
pub fn new(client: TopTL, username: impl Into<String>) -> Self {
Self {
client,
username: username.into(),
interval: Duration::from_secs(1800), callback: None,
handle: None,
running: Arc::new(Mutex::new(false)),
}
}
pub fn interval(mut self, interval: Duration) -> Self {
self.interval = interval;
self
}
pub fn callback(mut self, cb: StatsCallback) -> Self {
self.callback = Some(cb);
self
}
pub fn start(mut self) -> Self {
let callback = self
.callback
.clone()
.expect("A stats callback must be set before starting the autoposter");
let client = self.client.clone();
let username = self.username.clone();
let interval = self.interval;
let running = self.running.clone();
let handle = tokio::spawn(async move {
*running.lock().await = true;
let mut ticker = time::interval(interval);
loop {
ticker.tick().await;
if !*running.lock().await {
break;
}
let stats = (callback)();
if let Err(e) = client.post_stats(&username, &stats).await {
eprintln!("[toptl::autoposter] post_stats failed: {e}");
}
}
});
self.handle = Some(handle);
self
}
pub async fn stop(self) {
*self.running.lock().await = false;
if let Some(handle) = self.handle {
handle.abort();
let _ = handle.await;
}
}
}