use std::path::PathBuf; use std::sync::{Arc, RwLock}; use chrono::{Timelike, Utc}; use crate::settings::PostBody::Description; use lemmy_db_schema::PostFeatureType; use lemmy_db_schema::sensitive::SensitiveString; use serde_derive::{Deserialize, Serialize}; use crate::lemmy::{Lemmy, PartInfo, PostType}; use crate::post_history::{SeriesHistory}; use crate::fetchers::{FetcherTrait, Fetcher}; use crate::fetchers::jnovel::{JNovelFetcher}; use crate::logging::Logging; use crate::settings::PostBody; #[derive(Serialize, Deserialize, Clone, Debug)] pub(crate) struct SingleBotConfig { pub(crate) instance: String, username: SensitiveString, password: SensitiveString, pub(crate) status_post_url: Option<String>, pub(crate) protected_communities: Vec<String>, pub(crate) series: Vec<SeriesConfig>, } impl SingleBotConfig { pub(crate) fn load(&self) -> Self { if self.instance.is_empty() { panic!("bot instance not set!") } if self.username.is_empty() { panic!("bot username not set!") } if self.password.is_empty() { panic!("bot password not provided!") } self.series.iter().for_each(|series| { if series.prepub_community.post_body == Description { panic!("'Description' type Post Body only supported for Volumes!") } }); self.clone() } pub(crate) fn get_path() -> PathBuf { confy::get_configuration_file_path(env!("CARGO_PKG_NAME"), "config").expect("Application will not without confy") } pub(crate) fn get_username(&self) -> SensitiveString { self.username.clone() } pub(crate) fn get_password(&self) -> SensitiveString { self.password.clone() } } impl Default for SingleBotConfig { fn default() -> Self { SingleBotConfig { instance: "".to_owned(), username: SensitiveString::from("".to_owned()), password: SensitiveString::from("".to_owned()), status_post_url: None, protected_communities: vec![], series: vec![], } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub(crate) struct SeriesConfig { pub(crate) slug: String, pub(crate) parted: bool, pub(crate) prepub_community: PostConfig, pub(crate) volume_community: PostConfig, pub(crate) fetcher: Fetcher } impl SeriesConfig { pub(crate) fn update(&self, history: &mut SeriesHistory, lemmy: &Lemmy, config: &Arc<RwLock<SingleBotConfig>>) { let info_msg = format!("Checking {} for Updates", self.slug); Logging::info(info_msg.as_str()); let mut fetcher: Fetcher = match &self.fetcher { Fetcher::Jnc(_) => { Fetcher::Jnc(JNovelFetcher::new()) }, /*default => { let err_msg = format!("Fetcher {default} not implemented"); error!(err_msg); return; }*/ }; match fetcher { Fetcher::Jnc(ref mut jnc) => { jnc.set_series(self.slug.clone()); jnc.set_part_option(self.parted); } } let post_list = match fetcher.check_feed() { Ok(data) => data, Err(_) => { let err_msg = format!("While checking feed for {}", self.slug); Logging::error(err_msg.as_str()); return; } }; if post_list.is_empty() && Utc::now().minute() % 10 == 0 { let info_msg = "No Updates found"; Logging::info(info_msg); } for post_info in post_list.iter() { if history.check_for_post( self.slug.as_str(), post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str(), post_info.get_info().title.as_str() ) { continue } let post_data = post_info.get_post_data(self, lemmy); let info = format!( "Posting '{}' to {}", post_info.get_info().title.as_str(), post_info.get_post_config(self).name.as_str() ); Logging::info(info.as_str()); let post_id = match lemmy.post(post_data) { Some(data) => data, None=> { Logging::error("Error posting chapter"); return; } }; let read_config = config.read().expect("Read Lock Failed").clone(); if post_info.get_post_config(self).pin_settings.pin_new_post_community && !read_config .protected_communities .contains(&post_info.get_post_config(self).name) { let info = format!( "Pinning '{}' to {}", post_info.get_info().title, post_info.get_post_config(self).name.as_str() ); Logging::info(info.as_str()); let pinned_posts = lemmy.get_community_pinned(lemmy.get_community_id(&post_info.get_post_config(self).name)).unwrap_or_else(|| { Logging::error("Pinning of Post to community failed"); vec![] }); if !pinned_posts.is_empty() { let community_pinned_post = &pinned_posts[0]; if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Community).is_none() { Logging::error("Error un-pinning post"); } } if lemmy.pin(post_id, PostFeatureType::Community).is_none() { Logging::error("Error pinning post"); } } else if read_config .protected_communities .contains(&post_info.get_post_config(self).name) { let message = format!( "Community '{}' for Series '{}' is protected. Is this intended?", &post_info.get_post_config(self).name, self.slug ); Logging::warn(message.as_str()); } if post_info.get_post_config(self).pin_settings.pin_new_post_local { let info = format!("Pinning '{}' to Instance", post_info.get_info().title); Logging::info(info.as_str()); let pinned_posts = match lemmy.get_local_pinned() { Some(data) => {data} None => { Logging::error("Error fetching pinned posts"); vec![] } }; if !pinned_posts.is_empty() { for pinned_post in pinned_posts { if read_config .protected_communities .contains(&pinned_post.community.name) { continue; } else { let community_pinned_post = &pinned_post; if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Local).is_none() { Logging::error("Error pinning post"); continue; } break; } } } if lemmy.pin(post_id, PostFeatureType::Local).is_none() { Logging::error("Error pinning post"); }; } let mut series_history = history.get_series(self.slug.as_str()); let mut part_history = series_history.get_part(post_info.get_part_info().unwrap_or(PartInfo::NoParts).as_string().as_str()); match post_info.post_type { Some(post_type) => { match post_type { PostType::Chapter => part_history.chapter = post_info.get_info().title, PostType::Volume => part_history.volume = post_info.get_info().title, } } None => part_history.chapter = post_info.get_info().title, } 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); Logging::debug("Saving History"); history.save_history(); } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub(crate) struct PostConfig { pub(crate) name: String, pub(crate) pin_settings: PinConfig, pub(crate) post_body: PostBody, } #[derive(Debug, Serialize, Deserialize, Clone)] pub(crate) struct PinConfig { pub(crate) pin_new_post_local: bool, pub(crate) pin_new_post_community: bool, }