Compare commits

...

2 commits

Author SHA1 Message Date
ebbfdcea63
Split Memory Log into Log Levels, support different lenght limits (hardcoded for now)
All checks were successful
Run Tests on Code / run-tests (push) Successful in 17s
2024-07-21 14:10:52 +02:00
ff0360ac07
Implement Settings Updating inside Bot thread, Improve Status Ping behaviour 2024-07-21 14:10:21 +02:00
5 changed files with 123 additions and 51 deletions

View file

@ -12,9 +12,10 @@ use crate::settings::{BotSettings, SeriesSettings};
pub(crate) struct Bot { pub(crate) struct Bot {
settings: BotSettings, settings: BotSettings,
settings_id: u16,
health_signal: Arc<AtomicHealthSignal>, health_signal: Arc<AtomicHealthSignal>,
history: SeriesHistory, history: SeriesHistory,
login_time: Option<DateTime<Utc>> login_time: Option<DateTime<Utc>>,
} }
enum Wait { enum Wait {
@ -23,16 +24,26 @@ enum Wait {
} }
impl Bot { impl Bot {
pub(crate) fn new(health_signal: Arc<AtomicHealthSignal>, settings: BotSettings) -> Self { pub(crate) fn new(health_signal: Arc<AtomicHealthSignal>, settings_id: u16, settings: BotSettings) -> Self {
let history: SeriesHistory = SeriesHistory::load_history(); let history: SeriesHistory = SeriesHistory::load_history();
Bot { health_signal, settings, history, login_time: None }
Bot { health_signal, settings_id, settings, history, login_time: None }
} }
pub(crate) fn run(&mut self, series_settings: Arc<RwLock<HashMap<u16, SeriesSettings>>>, update_pending: Arc<AtomicBool>) { pub(crate) fn run(
&mut self,
series_settings: Arc<RwLock<HashMap<u16, SeriesSettings>>>,
bot_settings: Arc<RwLock<HashMap<u16, BotSettings>>>,
update_pending: Arc<AtomicBool>
) {
let mut last_ping = Utc::now();
if let Some(interval) = self.settings.status_post_interval() {
last_ping -= interval
}
loop { loop {
match self.health_signal.load(Ordering::SeqCst) { match self.health_signal.load(Ordering::SeqCst) {
HealthSignal::RequestAlive => { HealthSignal::RequestAlive => {
//self.health_signal.store(HealthSignal::ConfirmAlive, Ordering::SeqCst); self.health_signal.store(HealthSignal::ConfirmAlive, Ordering::SeqCst);
} }
HealthSignal::RequestStop => { HealthSignal::RequestStop => {
break; break;
@ -40,53 +51,64 @@ impl Bot {
_ => {} _ => {}
} }
while update_pending.load(Ordering::SeqCst) { if update_pending.load(Ordering::SeqCst) {
Logging::debug("Awaiting Config Update");
sleep(Duration::milliseconds(100).to_std().unwrap()); sleep(Duration::milliseconds(100).to_std().unwrap());
continue
} }
self.settings = bot_settings.read().get(&self.settings_id).unwrap().clone();
// perform status ping
if let Some(interval) = self.settings.status_post_interval() {
if Utc::now() + Duration::seconds(1) >= last_ping + interval {
if self.ping_status() {
last_ping = Utc::now();
}
else {
continue
}
}
}
if let Some(login_time) = self.login_time { if let Some(login_time) = self.login_time {
if Utc::now() - login_time >= Duration::minutes(60) { if Utc::now() - login_time >= Duration::minutes(60) {
self.logout(); //self.logout();
continue continue
} }
} }
else { else {
self.login(); //self.login();
} }
self.history = SeriesHistory::load_history(); self.history = SeriesHistory::load_history();
let run_start_time = Utc::now();
self.ping_status();
series_settings.read().iter().for_each(|(id, series)| { series_settings.read().iter().for_each(|(id, series)| {
let new_entries = series.check(); //let new_entries = series.check();
}); });
for series in series_settings.read().iter() { for series in series_settings.read().iter() {
//series.update(&mut self.history, &lemmy, &self.settings).await; //series.update(&mut self.history, &lemmy, &self.settings).await;
Logging::debug("Done Updating Series"); Logging::debug("Done Updating Series");
self.wait(1, Wait::Absolute); self.wait(1, Wait::Absolute);
} }
Logging::debug("Awaiting Timeout");
self.wait(30, Wait::Buffer(run_start_time));
Logging::debug("Pinging Server");
self.ping_status();
Logging::debug("Awaiting Timeout 2");
self.wait(30, Wait::Absolute);
} }
self.health_signal.store(HealthSignal::ConfirmStop, Ordering::SeqCst); self.health_signal.store(HealthSignal::ConfirmStop, Ordering::SeqCst);
} }
fn ping_status(&self) { fn ping_status(&self) -> bool {
if let Some(status_url) = &self.settings.status_post_url() { if let Some(status_url) = &self.settings.status_post_url() {
match HTTP_CLIENT.get(status_url).send() { return match HTTP_CLIENT.get(status_url).send() {
Ok(_) => {}, Ok(_) => {
true
},
Err(e) => { Err(e) => {
let err_msg = format!("While pinging status URL: {e}"); let err_msg = format!("While pinging status URL: {e}");
Logging::error(err_msg.as_str()); Logging::error(err_msg.as_str());
false
} }
} }
} }
false
} }
fn wait(&self, seconds: i64, start_time: Wait) { fn wait(&self, seconds: i64, start_time: Wait) {
@ -99,15 +121,14 @@ impl Bot {
sleep(Duration::milliseconds(100).to_std().unwrap()); sleep(Duration::milliseconds(100).to_std().unwrap());
} }
} }
fn login(&mut self) { fn login(&mut self) {
// todo!();
self.login_time = Some(Utc::now()); self.login_time = Some(Utc::now());
todo!()
} }
fn logout(&mut self) { fn logout(&mut self) {
self.login_time = None; self.login_time = None;
todo!() todo!();
} }
} }

View file

@ -1,22 +1,52 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use log::Level;
use parking_lot::Mutex; use parking_lot::Mutex;
use systemd_journal_logger::connected_to_journal; use systemd_journal_logger::connected_to_journal;
fn mem_log(msg: Option<String>) -> VecDeque<LogEvent> { fn mem_log(level: Level, msg: Option<String>) -> VecDeque<LogEvent> {
static MEM_LOG: Mutex<VecDeque<LogEvent>> = Mutex::new(VecDeque::new()); static MEM_LOG_DEBUG: Mutex<VecDeque<LogEvent>> = Mutex::new(VecDeque::new());
static MEM_LOG_INFO: Mutex<VecDeque<LogEvent>> = Mutex::new(VecDeque::new());
static MEM_LOG_WARN: Mutex<VecDeque<LogEvent>> = Mutex::new(VecDeque::new());
static MEM_LOG_ERROR: Mutex<VecDeque<LogEvent>> = Mutex::new(VecDeque::new());
let max_len: i8;
let mut list = match level {
Level::Debug => {
max_len = 10;
MEM_LOG_DEBUG.lock()
},
Level::Info => {
max_len = 20;
MEM_LOG_INFO.lock()
},
Level::Warn => {
max_len = 40;
MEM_LOG_WARN.lock()
},
Level::Error => {
max_len = -1;
MEM_LOG_ERROR.lock()
},
Level::Trace => {
max_len = 10;
MEM_LOG_DEBUG.lock()
}
};
if let Some(msg) = msg { if let Some(msg) = msg {
let now = Utc::now(); let now = Utc::now();
let log_event = LogEvent::new(now, msg); let log_event = LogEvent::new(now, msg);
let mut lock = MEM_LOG.lock(); list.push_back(log_event);
lock.push_back(log_event); if max_len != -1 {
while lock.len() > 10 { while list.len() > max_len as usize {
lock.pop_front(); list.pop_front();
}
} }
} }
return MEM_LOG.lock().clone(); list.clone()
} }
pub struct Logging { pub struct Logging {
@ -29,7 +59,7 @@ impl Logging {
true => log::debug!("{msg}"), true => log::debug!("{msg}"),
false => println!("{msg}"), false => println!("{msg}"),
} }
mem_log(Some(msg)); mem_log(Level::Debug, Some(msg));
} }
pub fn info(msg: &str) { pub fn info(msg: &str) {
@ -38,7 +68,7 @@ impl Logging {
true => log::info!("{msg}"), true => log::info!("{msg}"),
false => println!("{msg}"), false => println!("{msg}"),
} }
mem_log(Some(msg)); mem_log(Level::Info, Some(msg));
} }
pub fn warn(msg: &str) { pub fn warn(msg: &str) {
@ -47,7 +77,7 @@ impl Logging {
true => log::warn!("{msg}"), true => log::warn!("{msg}"),
false => println!("{msg}"), false => println!("{msg}"),
} }
mem_log(Some(msg)); mem_log(Level::Warn, Some(msg));
} }
pub fn error(msg: &str) { pub fn error(msg: &str) {
@ -56,11 +86,11 @@ impl Logging {
true => log::error!("{msg}"), true => log::error!("{msg}"),
false => eprintln!("{msg}"), false => eprintln!("{msg}"),
} }
mem_log(Some(msg)); mem_log(Level::Error, Some(msg));
} }
pub fn get_mem_log() -> VecDeque<LogEvent> { pub fn get_mem_log(level: Level) -> VecDeque<LogEvent> {
mem_log(None) mem_log(level, None)
} }
} }

View file

@ -59,18 +59,19 @@ fn main() {
let mut health_signals: Vec<Arc<AtomicHealthSignal>> = vec![]; let mut health_signals: Vec<Arc<AtomicHealthSignal>> = vec![];
let mut update_pending: Arc<AtomicBool> = Arc::new(AtomicBool::new(false)); let mut update_pending: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
let series_settings = settings.read().series_settings().clone();
settings.read().bot_settings().read().iter().for_each(|(id, bot_settings)| { settings.read().bot_settings().read().iter().for_each(|(id, bot_settings)| {
let health_signal = Arc::new(AtomicHealthSignal::new(HealthSignal::RequestAlive)); let health_signal = Arc::new(AtomicHealthSignal::new(HealthSignal::RequestAlive));
health_signals.push(health_signal.clone()); health_signals.push(health_signal.clone());
let bot_settings = bot_settings.clone(); let bot_settings = bot_settings.clone();
let series_settings = series_settings.clone(); let bot_id = *id;
let series_settings = settings.read().series_settings().clone();
let bot_settings_map = settings.read().bot_settings().clone();
let update_pending = update_pending.clone(); let update_pending = update_pending.clone();
spawn(move || { spawn(move || {
let mut bot = Bot::new(health_signal, bot_settings); let mut bot = Bot::new(health_signal, bot_id, bot_settings);
bot.run(series_settings, update_pending); bot.run(series_settings, bot_settings_map, update_pending);
}); });
}); });

View file

@ -69,6 +69,12 @@ impl ApplicationSettings {
} }
} }
#[derive(Clone)]
pub struct StatusPostSettings {
url: String,
interval: Duration,
}
#[derive(Clone)] #[derive(Clone)]
pub struct BotSettings { pub struct BotSettings {
id: u16, id: u16,
@ -76,7 +82,7 @@ pub struct BotSettings {
instance: String, instance: String,
username: SensitiveString, username: SensitiveString,
password: SensitiveString, password: SensitiveString,
status_post_url: Option<String>, status_post_settings: Option<StatusPostSettings>,
} }
impl BotSettings { impl BotSettings {
pub fn instance(&self) -> String { pub fn instance(&self) -> String {
@ -90,7 +96,17 @@ impl BotSettings {
} }
pub fn status_post_url(&self) -> Option<String> { pub fn status_post_url(&self) -> Option<String> {
self.status_post_url.clone() match &self.status_post_settings {
Some(settings) => Some(settings.url.clone()),
None => None
}
}
pub fn status_post_interval(&self) -> Option<Duration> {
match &self.status_post_settings {
Some(settings) => Some(settings.interval),
None => None
}
} }
} }

View file

@ -7,7 +7,7 @@ use crate::config::SeriesConfig;
use crate::fetchers::{Fetcher, FetcherTrait}; use crate::fetchers::{Fetcher, FetcherTrait};
use crate::fetchers::jnovel::JNovelFetcher; use crate::fetchers::jnovel::JNovelFetcher;
use crate::lemmy::PostType; use crate::lemmy::PostType;
use crate::settings::{BotSettings, PostSettings, SeriesSettings, SettingsSource}; use crate::settings::{BotSettings, PostSettings, SeriesSettings, SettingsSource, StatusPostSettings};
use crate::settings::toml::TomlSettings; use crate::settings::toml::TomlSettings;
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
@ -16,9 +16,10 @@ pub struct SingleBotTomlSettings {
username: SensitiveString, username: SensitiveString,
password: SensitiveString, password: SensitiveString,
status_post_url: Option<String>, status_post_url: Option<String>,
status_post_interval: Option<u32>,
pub protected_communities: Vec<String>, pub protected_communities: Vec<String>,
series: Vec<SeriesConfig>, series: Vec<SeriesConfig>,
fetch_interval: usize, fetch_interval: u32,
} }
impl SettingsSource for SingleBotTomlSettings { impl SettingsSource for SingleBotTomlSettings {
@ -41,7 +42,10 @@ impl SettingsSource for SingleBotTomlSettings {
instance: self.instance.clone(), instance: self.instance.clone(),
username: self.username.clone(), username: self.username.clone(),
password: self.password.clone(), password: self.password.clone(),
status_post_url: self.status_post_url.clone(), status_post_settings: Some(StatusPostSettings {
url: self.status_post_url.clone().unwrap(),
interval: Duration::from_secs(self.status_post_interval.clone().unwrap() as u64)
}),
}); });
map map
} }