2024-05-07 22:10:21 +02:00
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
use chrono::{Timelike, Utc};
|
2023-12-30 01:27:11 +01:00
|
|
|
use crate::config::PostBody::Description;
|
2023-12-30 00:22:21 +01:00
|
|
|
use lemmy_api_common::sensitive::Sensitive;
|
2024-05-07 22:10:21 +02:00
|
|
|
use lemmy_db_schema::PostFeatureType;
|
2023-06-19 00:26:50 +02:00
|
|
|
use serde_derive::{Deserialize, Serialize};
|
2024-05-07 22:10:21 +02:00
|
|
|
use crate::lemmy::{Lemmy, PartInfo, PostType};
|
|
|
|
use crate::post_history::{SeriesHistory};
|
|
|
|
use systemd_journal_logger::connected_to_journal;
|
|
|
|
use crate::fetchers::{FetcherTrait, Fetcher};
|
|
|
|
use crate::fetchers::jnovel::{JNovelFetcher};
|
|
|
|
|
2024-05-07 23:49:55 +02:00
|
|
|
macro_rules! debug {
|
|
|
|
($msg:tt) => {
|
|
|
|
match connected_to_journal() {
|
|
|
|
true => log::debug!("[DEBUG] {}", $msg),
|
|
|
|
false => println!("[DEBUG] {}", $msg),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2024-05-07 22:10:21 +02:00
|
|
|
macro_rules! info {
|
|
|
|
($msg:tt) => {
|
|
|
|
match connected_to_journal() {
|
|
|
|
true => log::info!("[INFO] {}", $msg),
|
|
|
|
false => println!("[INFO] {}", $msg),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! warn {
|
|
|
|
($msg:tt) => {
|
|
|
|
match connected_to_journal() {
|
|
|
|
true => log::warn!("[WARN] {}", $msg),
|
|
|
|
false => println!("[WARN] {}", $msg),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! error {
|
|
|
|
($msg:tt) => {
|
|
|
|
match connected_to_journal() {
|
|
|
|
true => log::error!("[ERROR] {}", $msg),
|
|
|
|
false => eprintln!("[ERROR] {}", $msg),
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2023-06-19 00:26:50 +02:00
|
|
|
|
2023-12-17 18:02:51 +01:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
2023-09-18 21:00:55 +02:00
|
|
|
pub(crate) struct Config {
|
2023-12-17 14:43:00 +01:00
|
|
|
pub(crate) instance: String,
|
2023-12-30 00:22:21 +01:00
|
|
|
username: String,
|
|
|
|
password: String,
|
2023-12-17 14:43:00 +01:00
|
|
|
pub(crate) status_post_url: Option<String>,
|
|
|
|
pub(crate) config_reload_seconds: u32,
|
2023-12-18 11:23:47 +01:00
|
|
|
pub(crate) protected_communities: Vec<String>,
|
2023-12-17 14:43:00 +01:00
|
|
|
pub(crate) series: Vec<SeriesConfig>,
|
2023-09-18 21:00:55 +02:00
|
|
|
}
|
2023-06-19 00:26:50 +02:00
|
|
|
|
|
|
|
impl Config {
|
2023-12-29 22:35:16 +01:00
|
|
|
pub(crate) fn load() -> Self {
|
2023-12-29 14:35:07 +01:00
|
|
|
let cfg: Self = match confy::load(env!("CARGO_PKG_NAME"), "config") {
|
2023-12-17 22:29:18 +01:00
|
|
|
Ok(data) => data,
|
2023-12-29 14:35:07 +01:00
|
|
|
Err(e) => panic!("config.toml not found: {e}"),
|
2023-12-17 22:29:18 +01:00
|
|
|
};
|
2023-12-29 14:35:07 +01:00
|
|
|
|
2023-12-17 01:42:41 +01:00
|
|
|
if cfg.instance.is_empty() {
|
2023-12-29 14:35:07 +01:00
|
|
|
panic!("bot instance not set!")
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.username.is_empty() {
|
|
|
|
panic!("bot username not set!")
|
|
|
|
}
|
|
|
|
|
|
|
|
if cfg.password.is_empty() {
|
|
|
|
panic!("bot password not provided!")
|
2023-09-18 21:00:55 +02:00
|
|
|
}
|
2023-12-17 14:43:21 +01:00
|
|
|
|
|
|
|
cfg.series.iter().for_each(|series| {
|
|
|
|
if series.prepub_community.post_body == Description {
|
|
|
|
panic!("'Description' type Post Body only supported for Volumes!")
|
|
|
|
}
|
|
|
|
});
|
2023-12-29 22:35:16 +01:00
|
|
|
cfg
|
2023-06-22 22:08:10 +02:00
|
|
|
}
|
2023-12-30 00:22:21 +01:00
|
|
|
|
2024-05-07 22:10:21 +02:00
|
|
|
pub(crate) fn get_path() -> PathBuf {
|
|
|
|
confy::get_configuration_file_path(env!("CARGO_PKG_NAME"), "config").expect("Application will not without confy")
|
|
|
|
}
|
|
|
|
|
2023-12-30 00:22:21 +01:00
|
|
|
pub(crate) fn get_username(&self) -> Sensitive<String> {
|
|
|
|
Sensitive::new(self.username.clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn get_password(&self) -> Sensitive<String> {
|
|
|
|
Sensitive::new(self.password.clone())
|
|
|
|
}
|
2023-06-19 00:26:50 +02:00
|
|
|
}
|
|
|
|
|
2023-12-17 01:42:41 +01:00
|
|
|
impl Default for Config {
|
|
|
|
fn default() -> Self {
|
|
|
|
Config {
|
2023-12-29 14:34:33 +01:00
|
|
|
instance: "".to_owned(),
|
2023-12-29 14:35:07 +01:00
|
|
|
username: "".to_owned(),
|
|
|
|
password: "".to_owned(),
|
2023-12-17 01:42:41 +01:00
|
|
|
status_post_url: None,
|
|
|
|
config_reload_seconds: 21600,
|
2023-12-18 11:23:47 +01:00
|
|
|
protected_communities: vec![],
|
2023-12-30 01:27:11 +01:00
|
|
|
series: vec![],
|
2023-06-19 00:26:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-17 01:42:41 +01:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
|
|
pub(crate) struct SeriesConfig {
|
2023-12-17 14:43:00 +01:00
|
|
|
pub(crate) slug: String,
|
|
|
|
pub(crate) parted: bool,
|
|
|
|
pub(crate) prepub_community: PostConfig,
|
|
|
|
pub(crate) volume_community: PostConfig,
|
2024-05-07 22:10:21 +02:00
|
|
|
pub(crate) fetcher: Fetcher
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SeriesConfig {
|
|
|
|
pub(crate) async fn update(&self, history: &mut SeriesHistory, lemmy: &Lemmy, config: &Arc<RwLock<Config>>) {
|
|
|
|
let info_msg = format!("Checking {} for Updates", self.slug);
|
|
|
|
info!(info_msg);
|
|
|
|
|
|
|
|
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().await {
|
|
|
|
Ok(data) => data,
|
|
|
|
Err(_) => {
|
|
|
|
let err_msg = format!("While checking feed for {}", self.slug);
|
|
|
|
error!(err_msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if post_list.is_empty() && Utc::now().minute() % 10 == 0 {
|
|
|
|
let info_msg = "No Updates found";
|
|
|
|
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()
|
|
|
|
);
|
|
|
|
info!(info);
|
|
|
|
|
|
|
|
let post_id = match lemmy.post(post_data).await {
|
2024-05-07 23:49:55 +02:00
|
|
|
Some(data) => data,
|
|
|
|
None=> {
|
2024-05-07 22:10:21 +02:00
|
|
|
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()
|
|
|
|
);
|
|
|
|
info!(info);
|
2024-05-07 23:49:55 +02:00
|
|
|
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![]
|
|
|
|
});
|
2024-05-07 22:10:21 +02:00
|
|
|
if !pinned_posts.is_empty() {
|
|
|
|
let community_pinned_post = &pinned_posts[0];
|
2024-05-07 23:49:55 +02:00
|
|
|
if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Community).await.is_none() {
|
|
|
|
error!("Error un-pinning post");
|
2024-05-07 22:10:21 +02:00
|
|
|
}
|
|
|
|
}
|
2024-05-07 23:49:55 +02:00
|
|
|
if lemmy.pin(post_id, PostFeatureType::Community).await.is_none() {
|
|
|
|
error!("Error pinning post");
|
2024-05-07 22:10:21 +02:00
|
|
|
}
|
|
|
|
} 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
|
|
|
|
);
|
|
|
|
warn!(message);
|
|
|
|
}
|
|
|
|
|
|
|
|
if post_info.get_post_config(self).pin_settings.pin_new_post_local {
|
|
|
|
let info = format!("Pinning '{}' to Instance", post_info.get_info().title);
|
|
|
|
info!(info);
|
|
|
|
let pinned_posts = match lemmy.get_local_pinned().await {
|
2024-05-07 23:49:55 +02:00
|
|
|
Some(data) => {data}
|
|
|
|
None => {
|
2024-05-07 22:10:21 +02:00
|
|
|
error!("Error fetching pinned posts");
|
2024-05-07 23:49:55 +02:00
|
|
|
vec![]
|
2024-05-07 22:10:21 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
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;
|
2024-05-07 23:49:55 +02:00
|
|
|
if lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Local).await.is_none() {
|
|
|
|
error!("Error pinning post");
|
|
|
|
continue;
|
2024-05-07 22:10:21 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-05-07 23:49:55 +02:00
|
|
|
if lemmy.pin(post_id, PostFeatureType::Local).await.is_none() {
|
|
|
|
error!("Error pinning post");
|
2024-05-07 22:10:21 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2024-05-07 23:49:55 +02:00
|
|
|
debug!("Saving History");
|
2024-05-07 22:10:21 +02:00
|
|
|
history.save_history();
|
|
|
|
}
|
|
|
|
}
|
2023-12-17 01:42:41 +01:00
|
|
|
}
|
2023-06-22 22:08:10 +02:00
|
|
|
|
2023-12-17 01:42:41 +01:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
|
|
pub(crate) struct PostConfig {
|
2023-12-17 20:17:00 +01:00
|
|
|
pub(crate) name: String,
|
2023-12-17 14:43:00 +01:00
|
|
|
pub(crate) pin_settings: PinConfig,
|
|
|
|
pub(crate) post_body: PostBody,
|
2023-12-17 01:42:41 +01:00
|
|
|
}
|
2023-06-22 22:08:10 +02:00
|
|
|
|
2023-12-17 01:42:41 +01:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
|
|
pub(crate) struct PinConfig {
|
2023-12-17 14:43:00 +01:00
|
|
|
pub(crate) pin_new_post_local: bool,
|
|
|
|
pub(crate) pin_new_post_community: bool,
|
2023-12-17 01:42:41 +01:00
|
|
|
}
|
2023-06-22 22:08:10 +02:00
|
|
|
|
2023-12-17 14:43:00 +01:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
2023-12-17 01:42:41 +01:00
|
|
|
#[serde(tag = "body_type", content = "body_content")]
|
|
|
|
pub(crate) enum PostBody {
|
|
|
|
None,
|
|
|
|
Description,
|
|
|
|
Custom(String),
|
2023-06-22 22:08:10 +02:00
|
|
|
}
|