use crate::{config::{Config}, HTTP_CLIENT}; use crate::lemmy::{Lemmy}; use crate::post_history::{SeriesHistory}; use chrono::{DateTime, Duration, Utc}; use std::sync::{Arc, RwLock}; use notify::{Event, EventKind, event::{AccessKind, AccessMode}, RecursiveMode, Watcher}; use tokio::time::sleep; use systemd_journal_logger::connected_to_journal; macro_rules! debug { ($msg:tt) => { match connected_to_journal() { true => log::debug!("[DEBUG] {}", $msg), false => println!("[DEBUG] {}", $msg), } }; } macro_rules! info { ($msg:tt) => { match connected_to_journal() { true => log::info!("[INFO] {}", $msg), false => println!("[INFO] {}", $msg), } }; } macro_rules! error { ($msg:tt) => { match connected_to_journal() { true => log::error!("[ERROR] {}", $msg), false => eprintln!("[ERROR] {}", $msg), } }; } pub(crate) struct Bot { shared_config: Arc<RwLock<Config>>, history: SeriesHistory, run_start_time: DateTime<Utc> } enum Wait { Absolute, Buffer } impl Bot { pub(crate) fn new() -> Self { let config = Config::load(); let shared_config: Arc<RwLock<Config>> = Arc::new(RwLock::new(config)); let shared_config_copy = shared_config.clone(); let mut watcher = notify::recommended_watcher(move |res: Result<Event, notify::Error>| { match res { Ok(event) => { if event.kind == EventKind::Access(AccessKind::Close(AccessMode::Write)) { let mut write = shared_config_copy.write().expect("Write Lock Failed"); let new_config = Config::load(); write.series = new_config.series; write.instance = new_config.instance; write.protected_communities = new_config.protected_communities; write.status_post_url = new_config.status_post_url; info!("Reloaded Configuration"); } }, Err(e) => { let msg = format!("Error watching files: {e}"); error!(msg); } } }).expect("Watcher Error"); watcher.watch(&Config::get_path(), RecursiveMode::NonRecursive).expect("Error in watcher"); let history: SeriesHistory = SeriesHistory::load_history(); Bot { shared_config, history, run_start_time: Utc::now() } } pub(crate) async fn run(&mut self) { loop { let mut lemmy = match Lemmy::new(&self.shared_config).await { Ok(data) => data, Err(_) => continue, }; lemmy.get_communities().await; self.history = SeriesHistory::load_history(); let start: DateTime<Utc> = Utc::now(); while Utc::now() - start <= Duration::minutes(60) { self.run_start_time = Utc::now(); self.ping_status().await; let read_copy = self.shared_config.read().expect("Read Lock Failed").clone(); for series in read_copy.series { series.update(&mut self.history, &lemmy, &self.shared_config).await; debug!("Done Updating Series"); self.wait(1, Wait::Absolute).await; } debug!("Awaiting Timeout"); self.wait(30, Wait::Buffer).await; debug!("Pinging Server"); self.ping_status().await; debug!("Awaiting Timeout 2"); self.wait(30, Wait::Absolute).await; } lemmy.logout().await; } } async fn ping_status(&self) { let read_config = &self.shared_config.read().expect("Read Lock Failed").clone(); if let Some(status_url) = &read_config.status_post_url { match HTTP_CLIENT.get(status_url).send().await { Ok(_) => {}, Err(e) => { let err_msg = format!("While pinging status URL: {e}"); error!(err_msg); } } } } async fn wait(&self, seconds: i64, start_time: Wait) { let duration: Duration = Duration::seconds(seconds); let start_time: DateTime<Utc> = match start_time { Wait::Absolute => Utc::now(), Wait::Buffer => self.run_start_time, }; while Utc::now() - start_time < duration { sleep(Duration::milliseconds(100).to_std().unwrap()).await } } }