From b6f5c38e4a2b65fb06e034e125333254ef162383 Mon Sep 17 00:00:00 2001 From: Neshura Date: Tue, 7 May 2024 23:49:55 +0200 Subject: [PATCH] Overhaul Error handling (Option instead of Result + Logging changes --- src/bot.rs | 15 +++++ src/config.rs | 68 +++++++++------------ src/fetchers/jnovel.rs | 12 ++-- src/lemmy.rs | 135 ++++++++++++++++++++++++++--------------- src/main.rs | 12 +++- 5 files changed, 146 insertions(+), 96 deletions(-) diff --git a/src/bot.rs b/src/bot.rs index 4539351..6a2890f 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -7,6 +7,15 @@ use notify::{Event, EventKind, event::{AccessKind, AccessMode}, RecursiveMode, W 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() { @@ -76,6 +85,8 @@ impl Bot { }; lemmy.get_communities().await; + + self.history = SeriesHistory::load_history(); let start: DateTime = Utc::now(); while Utc::now() - start <= Duration::minutes(60) { @@ -84,10 +95,14 @@ impl Bot { 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; } diff --git a/src/config.rs b/src/config.rs index 0bee169..a1da5e4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,6 +11,15 @@ use systemd_journal_logger::connected_to_journal; use crate::fetchers::{FetcherTrait, Fetcher}; use crate::fetchers::jnovel::{JNovelFetcher}; +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() { @@ -168,8 +177,8 @@ impl SeriesConfig { info!(info); let post_id = match lemmy.post(post_data).await { - Ok(data) => data, - Err(_) => { + Some(data) => data, + None=> { error!("Error posting chapter"); return; } @@ -188,31 +197,18 @@ impl SeriesConfig { post_info.get_post_config(self).name.as_str() ); info!(info); - let pinned_posts = match lemmy.get_community_pinned(lemmy.get_community_id(&post_info.get_post_config(self).name)).await { - Ok(data) => data, - Err(_) => { - error!("Pinning of Post to community failed"); - continue; - } - }; + let pinned_posts = lemmy.get_community_pinned(lemmy.get_community_id(&post_info.get_post_config(self).name)).await.unwrap_or_else(|| { + error!("Pinning of Post to community failed"); + vec![] + }); if !pinned_posts.is_empty() { let community_pinned_post = &pinned_posts[0]; - match lemmy - .unpin(community_pinned_post.post.id, PostFeatureType::Community) - .await { - Ok(_) => {} - Err(_) => { - error!("Error un-pinning post"); - return; - } + if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Community).await.is_none() { + error!("Error un-pinning post"); } } - match lemmy.pin(post_id, PostFeatureType::Community).await { - Ok(_) => {} - Err(_) => { - error!("Error pinning post"); - return; - } + if lemmy.pin(post_id, PostFeatureType::Community).await.is_none() { + error!("Error pinning post"); } } else if read_config .protected_communities @@ -229,10 +225,10 @@ impl SeriesConfig { let info = format!("Pinning '{}' to Instance", post_info.get_info().title); info!(info); let pinned_posts = match lemmy.get_local_pinned().await { - Ok(data) => {data} - Err(_) => { + Some(data) => {data} + None => { error!("Error fetching pinned posts"); - return; + vec![] } }; @@ -245,25 +241,16 @@ impl SeriesConfig { continue; } else { let community_pinned_post = &pinned_post; - match lemmy - .unpin(community_pinned_post.post.id, PostFeatureType::Local) - .await { - Ok(_) => {} - Err(_) => { - error!("Error pinning post"); - return; - } + if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Local).await.is_none() { + error!("Error pinning post"); + continue; } break; } } } - match lemmy.pin(post_id, PostFeatureType::Local).await { - Ok(_) => {} - Err(_) => { - error!("Error pinning post"); - return; - } + if lemmy.pin(post_id, PostFeatureType::Local).await.is_none() { + error!("Error pinning post"); }; } @@ -283,6 +270,7 @@ impl SeriesConfig { series_history.set_part(post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str(), part_history); history .set_series(self.slug.as_str(), series_history); + debug!("Saving History"); history.save_history(); } } diff --git a/src/fetchers/jnovel.rs b/src/fetchers/jnovel.rs index c13c626..69f6e6a 100644 --- a/src/fetchers/jnovel.rs +++ b/src/fetchers/jnovel.rs @@ -224,7 +224,7 @@ impl FetcherTrait for JNovelFetcher { .or_insert(new_post_info); } - if let Some(prepub_info) = get_latest_prepub(&volume.slug).await? { + if let Some(prepub_info) = get_latest_prepub(&volume.slug).await { let prepub_post_info = PostInfo { post_type: Some(PostType::Chapter), part: Some(new_part_info), @@ -252,7 +252,7 @@ impl FetcherTrait for JNovelFetcher { } -async fn get_latest_prepub(volume_slug: &str) -> Result, ()> { +async fn get_latest_prepub(volume_slug: &str) -> Option { let response = match HTTP_CLIENT .get(api_url!() + "/volumes/" + volume_slug + "/parts?format=json") .send() @@ -263,13 +263,13 @@ async fn get_latest_prepub(volume_slug: &str) -> Result, ( Err(e) => { let err_msg = format!("While getting latest PrePub: {e}"); error!(err_msg); - return Err(()); + return None; } }, Err(e) => { let err_msg = format!("{e}"); error!(err_msg); - return Err(()); + return None; } }; @@ -278,7 +278,7 @@ async fn get_latest_prepub(volume_slug: &str) -> Result, ( Err(e) => { let err_msg = format!("{e}"); error!(err_msg); - return Err(()); + return None; } }; volume_prepub_parts_data.parts.reverse(); // Makes breaking out of the parts loop easier @@ -300,5 +300,5 @@ async fn get_latest_prepub(volume_slug: &str) -> Result, ( }); } - Ok(post_details) + post_details } diff --git a/src/lemmy.rs b/src/lemmy.rs index 29b2408..67b8dd0 100644 --- a/src/lemmy.rs +++ b/src/lemmy.rs @@ -15,6 +15,15 @@ use serde::{Deserialize, Serialize}; use url::Url; 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! error { ($msg:tt) => { match connected_to_journal() { @@ -124,7 +133,7 @@ impl PostInfo { pub(crate) fn get_part_info(&self) -> Option { self.part } - + pub(crate) fn get_post_config(&self, series: &SeriesConfig) -> PostConfig { match self.post_type { Some(post_type) => { @@ -136,10 +145,10 @@ impl PostInfo { None => series.prepub_community.clone(), } } - + pub(crate) fn get_post_data(&self, series: &SeriesConfig, lemmy: &Lemmy) -> CreatePost { let post_config = self.get_post_config(series); - + let post_body = match &post_config.post_body { PostBody::None => None, PostBody::Description => self.get_description(), @@ -156,7 +165,7 @@ impl PostInfo { honeypot: None, nsfw: None, language_id: Some(LanguageId(37)), // TODO get this id once every few hours per API request, the ordering of IDs suggests that the EN Id might change in the future - } + } } } @@ -252,21 +261,33 @@ impl Lemmy { } - pub(crate) async fn post(&self, post: CreatePost) -> Result { - let response: String = self.post_data_json("/api/v3/post", &post).await?; - let json_data: PostView = self.parse_json_map(&response).await?; + pub(crate) async fn post(&self, post: CreatePost) -> Option { + let response: String = match self.post_data_json("/api/v3/post", &post).await { + Some(data) => data, + None => return None, + }; + let json_data: PostView = match self.parse_json_map(&response).await { + Some(data) => data, + None => return None, + }; - Ok(json_data.post.id) + Some(json_data.post.id) } - async fn feature(&self, params: FeaturePost) -> Result { - let response: String = self.post_data_json("/api/v3/post/feature", ¶ms).await?; - let json_data: PostView = self.parse_json_map(&response).await?; + async fn feature(&self, params: FeaturePost) -> Option { + let response: String = match self.post_data_json("/api/v3/post/feature", ¶ms).await { + Some(data) => data, + None => return None, + }; + let json_data: PostView = match self.parse_json_map(&response).await { + Some(data) => data, + None => return None, + }; - Ok(json_data) + Some(json_data) } - pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Result { + pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Option { let pin_params = FeaturePost { post_id, featured: false, @@ -275,7 +296,7 @@ impl Lemmy { self.feature(pin_params).await } - pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Result { + pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Option { let pin_params = FeaturePost { post_id, featured: true, @@ -284,17 +305,23 @@ impl Lemmy { self.feature(pin_params).await } - pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Result, ()> { + pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Option> { let list_params = GetPosts { community_id: Some(community), type_: Some(ListingType::Local), ..Default::default() }; - let response: String = self.get_data_query("/api/v3/post/list", &list_params).await?; - let json_data: GetPostsResponse = self.parse_json(&response).await?; + let response: String = match self.get_data_query("/api/v3/post/list", &list_params).await { + Some(data) => data, + None => return None, + }; + let json_data: GetPostsResponse = match self.parse_json(&response).await { + Some(data) => data, + None => return None, + }; - Ok(json_data + Some(json_data .posts .iter() .filter(|post| post.post.featured_community) @@ -302,16 +329,22 @@ impl Lemmy { .collect()) } - pub(crate) async fn get_local_pinned(&self) -> Result, ()> { + pub(crate) async fn get_local_pinned(&self) -> Option> { let list_params = GetPosts { type_: Some(ListingType::Local), ..Default::default() }; - let response: String = self.get_data_query("/api/v3/post/list", &list_params).await?; - let json_data: GetPostsResponse = self.parse_json(&response).await?; + let response: String = match self.get_data_query("/api/v3/post/list", &list_params).await { + Some(data) => data, + None => return None, + }; + let json_data: GetPostsResponse = match self.parse_json(&response).await { + Some(data) => data, + None => return None, + }; - Ok(json_data + Some(json_data .posts .iter() .filter(|post| post.post.featured_local) @@ -326,18 +359,12 @@ impl Lemmy { }; let response: String = match self.get_data_query("/api/v3/community/list", &list_params).await { - Ok(data) => data, - Err(_) => { - error!("Unable to extract data from request"); - return; - } + Some(data) => data, + None => return, }; let json_data: ListCommunitiesResponse = match self.parse_json::(&response).await { - Ok(data) => data, - Err(_) => { - error!("Unable to parse data from json"); - return; - }, + Some(data) => data, + None => return, }; let mut communities: HashMap = HashMap::new(); @@ -349,7 +376,7 @@ impl Lemmy { self.communities = communities; } - async fn post_data_json(&self, route: &str, json: &T ) -> Result { + async fn post_data_json(&self, route: &str, json: &T ) -> Option { let res = HTTP_CLIENT .post(format!("{}{route}", &self.instance)) .bearer_auth(&self.jwt_token.to_string()) @@ -359,7 +386,7 @@ impl Lemmy { self.extract_data(res).await } - async fn get_data_query(&self, route: &str, param: &T ) -> Result { + async fn get_data_query(&self, route: &str, param: &T ) -> Option { let res = HTTP_CLIENT .get(format!("{}{route}", &self.instance)) .bearer_auth(&self.jwt_token.to_string()) @@ -369,42 +396,52 @@ impl Lemmy { self.extract_data(res).await } - async fn extract_data(&self, response: Result) -> Result { + async fn extract_data(&self, response: Result) -> Option { match response { - Ok(data) => match data.text().await { - Ok(data) => Ok(data), - Err(e) => { - let err_msg = format!("{e}"); + Ok(data) => { + if data.status().is_success() { + match data.text().await { + Ok(data) => Some(data), + Err(e) => { + let err_msg = format!("{e}"); + error!(err_msg); + None + } + } + } + else { + let err_msg = format!("HTTP Request failed: {}", data.text().await.unwrap()); error!(err_msg); - Err(()) + None } }, Err(e) => { let err_msg = format!("{e}"); error!(err_msg); - Err(()) + None } } } - async fn parse_json<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Result { + async fn parse_json<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option { match serde_json::from_str::(response) { - Ok(data) => Ok(data), + Ok(data) => Some(data), Err(e) => { - let err_msg = format!("{e} while parsing JSON"); + let err_msg = format!("while parsing JSON: {e} "); error!(err_msg); - Err(()) + None } } } - async fn parse_json_map<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Result { + async fn parse_json_map<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option { + debug!(response); match serde_json::from_str::>(response) { - Ok(mut data) => Ok(data.remove("post_view").expect("Element should be present")), + Ok(mut data) => Some(data.remove("post_view").expect("Element should be present")), Err(e) => { - let err_msg = format!("{e} while parsing JSON HashMap"); + let err_msg = format!("while parsing JSON HashMap: {e}"); error!(err_msg); - Err(()) + None } } } diff --git a/src/main.rs b/src/main.rs index 5eae9ae..cc0e9cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,17 @@ async fn main() { .expect("Systemd-Logger crate error") .install() .expect("Systemd-Logger crate error"); - log::set_max_level(LevelFilter::Info); + match std::env::var("LOG_LEVEL") { + Ok(level) => { + match level.as_str() { + "debug" => log::set_max_level(LevelFilter::Debug), + "info" => log::set_max_level(LevelFilter::Info), + _ => log::set_max_level(LevelFilter::Info), + } + } + _ => log::set_max_level(LevelFilter::Info), + } + let mut bot = Bot::new(); bot.run().await; }