rustfmt
All checks were successful
Run Tests on Code / run-tests (push) Successful in 14s
Build binary file and bundle packages / test (pull_request) Successful in 13s
Build binary file and bundle packages / build (pull_request) Successful in 34s

This commit is contained in:
Neshura 2023-12-30 01:27:11 +01:00
parent 8be74585a0
commit 8c1da63e0c
Signed by: Neshura
GPG key ID: B6983AAA6B9A7A6C
6 changed files with 267 additions and 220 deletions

View file

@ -1,15 +1,15 @@
use std::collections::HashMap; use crate::config::{Config, PostBody, SeriesConfig};
use std::sync::{Arc}; use crate::jnovel::PostInfo;
use crate::lemmy::Lemmy;
use crate::post_history::SeriesHistory;
use crate::{jnovel, lemmy, write_error, write_info, write_warn, SharedData};
use chrono::{DateTime, Duration, Utc}; use chrono::{DateTime, Duration, Utc};
use lemmy_api_common::post::CreatePost; use lemmy_api_common::post::CreatePost;
use lemmy_db_schema::newtypes::{CommunityId, LanguageId}; use lemmy_db_schema::newtypes::{CommunityId, LanguageId};
use lemmy_db_schema::PostFeatureType; use lemmy_db_schema::PostFeatureType;
use tokio::sync::{RwLock}; use std::collections::HashMap;
use crate::{jnovel, lemmy, SharedData, write_error, write_info, write_warn}; use std::sync::Arc;
use crate::config::{Config, PostBody, SeriesConfig}; use tokio::sync::RwLock;
use crate::jnovel::PostInfo;
use crate::lemmy::{Lemmy};
use crate::post_history::SeriesHistory;
use tokio::time::sleep; use tokio::time::sleep;
pub(crate) async fn run(data: Arc<RwLock<SharedData>>) { pub(crate) async fn run(data: Arc<RwLock<SharedData>>) {
@ -81,7 +81,7 @@ pub(crate) async fn run(data: Arc<RwLock<SharedData>>) {
Ok(data) => data, Ok(data) => data,
Err(_) => { Err(_) => {
login_error = true; login_error = true;
continue continue;
} }
}; };
let message = "Communities reloaded".to_owned(); let message = "Communities reloaded".to_owned();
@ -100,9 +100,12 @@ pub(crate) async fn run(data: Arc<RwLock<SharedData>>) {
let series = read.config.series.clone(); let series = read.config.series.clone();
drop(read); drop(read);
for series in series { for series in series {
if handle_series(&series, &communities, &lemmy, &data).await.is_err() { if handle_series(&series, &communities, &lemmy, &data)
.await
.is_err()
{
login_error = true; login_error = true;
continue continue;
}; };
} }
} }
@ -124,7 +127,7 @@ async fn idle(data: &Arc<RwLock<SharedData>>) {
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
}, }
} }
}; };
@ -133,36 +136,34 @@ async fn idle(data: &Arc<RwLock<SharedData>>) {
} }
} }
async fn handle_series( async fn handle_series(series: &SeriesConfig, communities: &HashMap<String, CommunityId>, lemmy: &Lemmy, data: &Arc<RwLock<SharedData>>) -> Result<(), ()> {
series: &SeriesConfig,
communities: &HashMap<String, CommunityId>,
lemmy: &Lemmy,
data: &Arc<RwLock<SharedData>>,
) -> Result<(), ()> {
let mut post_list = match jnovel::check_feed(series.slug.as_str(), series.parted).await { let mut post_list = match jnovel::check_feed(series.slug.as_str(), series.parted).await {
Ok(data) => data, Ok(data) => data,
Err(_) => return Err(()), Err(_) => return Err(()),
}; };
for (index, post_info) in post_list.clone().iter().enumerate() { // todo .clone() likely not needed for (index, post_info) in post_list.clone().iter().enumerate() {
// todo .clone() likely not needed
let post_part_info = post_info.get_part_info(); let post_part_info = post_info.get_part_info();
let post_lemmy_info = post_info.get_lemmy_info(); let post_lemmy_info = post_info.get_lemmy_info();
{ {
let read = data.read().await; let read = data.read().await;
if read.post_history.check_for_post(series.slug.as_str(), post_part_info.as_string().as_str(), post_lemmy_info.title.as_str()) { if read.post_history.check_for_post(
series.slug.as_str(),
post_part_info.as_string().as_str(),
post_lemmy_info.title.as_str(),
) {
let message = format!("Skipping '{}' since already posted", post_lemmy_info.title); let message = format!("Skipping '{}' since already posted", post_lemmy_info.title);
write_info(message); write_info(message);
post_list.remove(index); post_list.remove(index);
continue continue;
} }
} }
let post_series_config = match post_info { let post_series_config = match post_info {
PostInfo::Chapter {..} => {&series.prepub_community}, PostInfo::Chapter { .. } => &series.prepub_community,
PostInfo::Volume {..} => {&series.volume_community} PostInfo::Volume { .. } => &series.volume_community,
}; };
let community_id = *communities let community_id = *communities
@ -185,23 +186,45 @@ async fn handle_series(
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 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
}; };
let info = format!("Posting '{}' to {}", post_lemmy_info.title.as_str(), post_series_config.name.as_str()); let info = format!(
"Posting '{}' to {}",
post_lemmy_info.title.as_str(),
post_series_config.name.as_str()
);
write_info(info); write_info(info);
let post_id = lemmy.post(post_data).await?; let post_id = lemmy.post(post_data).await?;
{ {
let read = data.read().await; let read = data.read().await;
if post_series_config.pin_settings.pin_new_post_community && !read.config.protected_communities.contains(&post_series_config.name) { if post_series_config.pin_settings.pin_new_post_community
let info = format!("Pinning '{}' to {}", post_lemmy_info.title, post_series_config.name.as_str()); && !read
.config
.protected_communities
.contains(&post_series_config.name)
{
let info = format!(
"Pinning '{}' to {}",
post_lemmy_info.title,
post_series_config.name.as_str()
);
write_info(info); write_info(info);
let pinned_posts = lemmy.get_community_pinned(community_id).await?; let pinned_posts = lemmy.get_community_pinned(community_id).await?;
if !pinned_posts.is_empty() { if !pinned_posts.is_empty() {
let community_pinned_post = &pinned_posts[0]; let community_pinned_post = &pinned_posts[0];
lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Community).await?; lemmy
.unpin(community_pinned_post.post.id, PostFeatureType::Community)
.await?;
} }
lemmy.pin(post_id, PostFeatureType::Community).await?; lemmy.pin(post_id, PostFeatureType::Community).await?;
} else if read.config.protected_communities.contains(&post_series_config.name) { } else if read
let message = format!("Community '{}' for Series '{}' is protected. Is this intended?", &post_series_config.name, series.slug); .config
.protected_communities
.contains(&post_series_config.name)
{
let message = format!(
"Community '{}' for Series '{}' is protected. Is this intended?",
&post_series_config.name, series.slug
);
write_warn(message); write_warn(message);
} }
} }
@ -213,13 +236,18 @@ async fn handle_series(
let pinned_posts = lemmy.get_local_pinned().await?; let pinned_posts = lemmy.get_local_pinned().await?;
if !pinned_posts.is_empty() { if !pinned_posts.is_empty() {
for pinned_post in pinned_posts { for pinned_post in pinned_posts {
if read.config.protected_communities.contains(&pinned_post.community.name) { if read
continue .config
} .protected_communities
else { .contains(&pinned_post.community.name)
{
continue;
} else {
let community_pinned_post = &pinned_post; let community_pinned_post = &pinned_post;
lemmy.unpin(community_pinned_post.post.id, PostFeatureType::Local).await?; lemmy
break .unpin(community_pinned_post.post.id, PostFeatureType::Local)
.await?;
break;
} }
} }
} }
@ -231,21 +259,16 @@ async fn handle_series(
drop(read); drop(read);
match post_info { match post_info {
PostInfo::Chapter {..} => { PostInfo::Chapter { .. } => part_history.chapter = post_info.get_lemmy_info().title,
part_history.chapter = post_info.get_lemmy_info().title PostInfo::Volume { .. } => part_history.volume = post_info.get_lemmy_info().title,
},
PostInfo::Volume {..} => {
part_history.volume = post_info.get_lemmy_info().title
}
} }
series_history.set_part(post_part_info.as_string().as_str(), part_history); series_history.set_part(post_part_info.as_string().as_str(), part_history);
let mut write = data.write().await; let mut write = data.write().await;
write.post_history.set_series(series.slug.as_str(), series_history); write
.post_history
.set_series(series.slug.as_str(), series_history);
write.post_history.save_history(); write.post_history.save_history();
} }
Ok(()) Ok(())
} }

View file

@ -1,6 +1,6 @@
use crate::config::PostBody::Description;
use lemmy_api_common::sensitive::Sensitive; use lemmy_api_common::sensitive::Sensitive;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use crate::config::PostBody::Description;
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub(crate) struct Config { pub(crate) struct Config {
@ -58,7 +58,7 @@ impl Default for Config {
status_post_url: None, status_post_url: None,
config_reload_seconds: 21600, config_reload_seconds: 21600,
protected_communities: vec![], protected_communities: vec![],
series: vec![] series: vec![],
} }
} }
} }

View file

@ -1,12 +1,12 @@
use crate::jnovel::PartInfo::{NoParts, Part};
use crate::jnovel::PostInfo::{Chapter, Volume};
use crate::{write_error, HTTP_CLIENT};
use chrono::{DateTime, Duration, Utc};
use serde_derive::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Sub; use std::ops::Sub;
use chrono::{DateTime, Duration, Utc};
use serde_derive::{Deserialize, Serialize};
use url::Url; use url::Url;
use crate::{HTTP_CLIENT, write_error};
use crate::jnovel::PartInfo::{NoParts, Part};
use crate::jnovel::PostInfo::{Chapter, Volume};
static PAST_DAYS_ELIGIBLE: u8 = 4; static PAST_DAYS_ELIGIBLE: u8 = 4;
@ -61,7 +61,6 @@ pub(crate) struct VolumeDetail {
pub(crate) cover: Cover, pub(crate) cover: Cover,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub(crate) struct ChapterDetail { pub(crate) struct ChapterDetail {
pub(crate) title: String, pub(crate) title: String,
@ -105,9 +104,13 @@ impl PartialEq for PartInfo {
impl PartialOrd for PartInfo { impl PartialOrd for PartInfo {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.gt(other) { Some(Ordering::Greater) } if self.gt(other) {
else if self.eq(other) { Some(Ordering::Equal) } Some(Ordering::Greater)
else { Some(Ordering::Less) } } else if self.eq(other) {
Some(Ordering::Equal)
} else {
Some(Ordering::Less)
}
} }
fn lt(&self, other: &Self) -> bool { fn lt(&self, other: &Self) -> bool {
@ -135,29 +138,40 @@ impl PartialOrd for PartInfo {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum PostInfo { pub(crate) enum PostInfo {
Chapter { part: PartInfo, lemmy_info: LemmyPostInfo }, Chapter {
Volume { part: PartInfo, description: String, lemmy_info: LemmyPostInfo }, part: PartInfo,
lemmy_info: LemmyPostInfo,
},
Volume {
part: PartInfo,
description: String,
lemmy_info: LemmyPostInfo,
},
} }
impl PostInfo { impl PostInfo {
pub(crate) fn get_part_info(&self) -> PartInfo { pub(crate) fn get_part_info(&self) -> PartInfo {
match self { match self {
Chapter {part: part_info, ..} => *part_info, Chapter {
Volume {part: part_info, ..} => *part_info part: part_info, ..
} => *part_info,
Volume {
part: part_info, ..
} => *part_info,
} }
} }
pub(crate) fn get_lemmy_info(&self) -> LemmyPostInfo { pub(crate) fn get_lemmy_info(&self) -> LemmyPostInfo {
match self { match self {
Chapter {lemmy_info, ..} => lemmy_info.clone(), Chapter { lemmy_info, .. } => lemmy_info.clone(),
Volume {lemmy_info, ..} => lemmy_info.clone() Volume { lemmy_info, .. } => lemmy_info.clone(),
} }
} }
pub(crate) fn get_description(&self) -> Option<String> { pub(crate) fn get_description(&self) -> Option<String> {
match self { match self {
Chapter {..} => None, Chapter { .. } => None,
Volume {description, ..} => Some(description.clone()), Volume { description, .. } => Some(description.clone()),
} }
} }
} }
@ -165,13 +179,13 @@ impl PostInfo {
impl PartialEq for PostInfo { impl PartialEq for PostInfo {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
let self_part = match self { let self_part = match self {
Chapter {part, ..} => part, Chapter { part, .. } => part,
Volume {part, ..} => part, Volume { part, .. } => part,
}; };
let other_part = match other { let other_part = match other {
Chapter {part, ..} => part, Chapter { part, .. } => part,
Volume {part, ..} => part, Volume { part, .. } => part,
}; };
self_part.eq(other_part) self_part.eq(other_part)
@ -180,20 +194,24 @@ impl PartialEq for PostInfo {
impl PartialOrd for PostInfo { impl PartialOrd for PostInfo {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.gt(other) { Some(Ordering::Greater) } if self.gt(other) {
else if self.eq(other) { Some(Ordering::Equal) } Some(Ordering::Greater)
else { Some(Ordering::Less) } } else if self.eq(other) {
Some(Ordering::Equal)
} else {
Some(Ordering::Less)
}
} }
fn lt(&self, other: &Self) -> bool { fn lt(&self, other: &Self) -> bool {
let self_part = match self { let self_part = match self {
Chapter {part, ..} => part, Chapter { part, .. } => part,
Volume {part, ..} => part, Volume { part, .. } => part,
}; };
let other_part = match other { let other_part = match other {
Chapter {part, ..} => part, Chapter { part, .. } => part,
Volume {part, ..} => part, Volume { part, .. } => part,
}; };
self_part < other_part self_part < other_part
@ -205,13 +223,13 @@ impl PartialOrd for PostInfo {
fn gt(&self, other: &Self) -> bool { fn gt(&self, other: &Self) -> bool {
let self_part = match self { let self_part = match self {
Chapter {part, ..} => part, Chapter { part, .. } => part,
Volume {part, ..} => part, Volume { part, .. } => part,
}; };
let other_part = match other { let other_part = match other {
Chapter {part, ..} => part, Chapter { part, .. } => part,
Volume {part, ..} => part, Volume { part, .. } => part,
}; };
self_part > other_part self_part > other_part
@ -226,21 +244,20 @@ pub(crate) async fn check_feed(series_slug: &str, series_has_parts: bool) -> Res
let response = match HTTP_CLIENT let response = match HTTP_CLIENT
.get(api_url!() + "/series/" + series_slug + "/volumes?format=json") .get(api_url!() + "/series/" + series_slug + "/volumes?format=json")
.send() .send()
.await { .await
Ok(data) => { {
match data.text().await { Ok(data) => match data.text().await {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
}
} }
}, },
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -249,7 +266,7 @@ pub(crate) async fn check_feed(series_slug: &str, series_has_parts: bool) -> Res
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
volume_brief_data.volumes.reverse(); // Makes breaking out of the volume loop easier volume_brief_data.volumes.reverse(); // Makes breaking out of the volume loop easier
@ -272,11 +289,12 @@ pub(crate) async fn check_feed(series_slug: &str, series_has_parts: bool) -> Res
let mut part_number: Option<u8> = None; let mut part_number: Option<u8> = None;
let splits: Vec<&str> = volume.slug.split('-').collect(); let splits: Vec<&str> = volume.slug.split('-').collect();
for (index, split) in splits.clone().into_iter().enumerate() { for (index, split) in splits.clone().into_iter().enumerate() {
if split == "part" { if split == "part" {
part_number = Some(splits[index +1] part_number = Some(
splits[index + 1]
.parse::<u8>() .parse::<u8>()
.expect("Split Element after 'Part' should always be a number")); .expect("Split Element after 'Part' should always be a number"),
);
break; break;
} }
} }
@ -286,14 +304,17 @@ pub(crate) async fn check_feed(series_slug: &str, series_has_parts: bool) -> Res
None => { None => {
println!("No Part found, assuming 1"); println!("No Part found, assuming 1");
new_part_info = Part(1); new_part_info = Part(1);
},
} }
} }
else { } else {
new_part_info = NoParts; new_part_info = NoParts;
} }
let post_url = format!("{}/series/{series_slug}#volume-{}", jnc_base_url!(), volume.number); let post_url = format!(
"{}/series/{series_slug}#volume-{}",
jnc_base_url!(),
volume.number
);
let post_details = LemmyPostInfo { let post_details = LemmyPostInfo {
title: volume.title.clone(), title: volume.title.clone(),
url: Url::parse(&post_url).unwrap(), url: Url::parse(&post_url).unwrap(),
@ -321,7 +342,7 @@ pub(crate) async fn check_feed(series_slug: &str, series_has_parts: bool) -> Res
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 = Chapter { let prepub_post_info = Chapter {
part: new_part_info, part: new_part_info,
lemmy_info: prepub_info lemmy_info: prepub_info,
}; };
prepub_map prepub_map
@ -346,31 +367,29 @@ async fn get_latest_prepub(volume_slug: &str) -> Result<Option<LemmyPostInfo>, (
let response = match HTTP_CLIENT let response = match HTTP_CLIENT
.get(api_url!() + "/volumes/" + volume_slug + "/parts?format=json") .get(api_url!() + "/volumes/" + volume_slug + "/parts?format=json")
.send() .send()
.await { .await
Ok(data) => { {
match data.text().await { Ok(data) => match data.text().await {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
}
} }
}, },
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
let mut volume_prepub_parts_data: ChapterWrapper = match serde_json::from_str(&response) { let mut volume_prepub_parts_data: ChapterWrapper = match serde_json::from_str(&response) {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
volume_prepub_parts_data.parts.reverse(); // Makes breaking out of the parts loop easier volume_prepub_parts_data.parts.reverse(); // Makes breaking out of the parts loop easier
@ -380,10 +399,9 @@ async fn get_latest_prepub(volume_slug: &str) -> Result<Option<LemmyPostInfo>, (
for prepub_part in volume_prepub_parts_data.parts.iter() { for prepub_part in volume_prepub_parts_data.parts.iter() {
let publishing_date = DateTime::parse_from_rfc3339(&prepub_part.launch).unwrap(); let publishing_date = DateTime::parse_from_rfc3339(&prepub_part.launch).unwrap();
if publishing_date > Utc::now() { if publishing_date > Utc::now() {
break break;
} } else if publishing_date < Utc::now().sub(Duration::days(PAST_DAYS_ELIGIBLE as i64)) {
else if publishing_date < Utc::now().sub(Duration::days(PAST_DAYS_ELIGIBLE as i64)) { continue;
continue
} }
let post_url = format!("{}/read/{}", jnc_base_url!(), prepub_part.slug); let post_url = format!("{}/read/{}", jnc_base_url!(), prepub_part.slug);
@ -391,7 +409,6 @@ async fn get_latest_prepub(volume_slug: &str) -> Result<Option<LemmyPostInfo>, (
title: prepub_part.title.clone(), title: prepub_part.title.clone(),
url: Url::parse(&post_url).unwrap(), url: Url::parse(&post_url).unwrap(),
}); });
} }
Ok(post_details) Ok(post_details)

View file

@ -1,4 +1,5 @@
use std::collections::HashMap; use crate::config::Config;
use crate::{write_error, HTTP_CLIENT};
use lemmy_api_common::community::{ListCommunities, ListCommunitiesResponse}; use lemmy_api_common::community::{ListCommunities, ListCommunitiesResponse};
use lemmy_api_common::lemmy_db_views::structs::PostView; use lemmy_api_common::lemmy_db_views::structs::PostView;
use lemmy_api_common::person::{Login, LoginResponse}; use lemmy_api_common::person::{Login, LoginResponse};
@ -7,8 +8,7 @@ use lemmy_api_common::sensitive::Sensitive;
use lemmy_db_schema::newtypes::{CommunityId, PostId}; use lemmy_db_schema::newtypes::{CommunityId, PostId};
use lemmy_db_schema::{ListingType, PostFeatureType}; use lemmy_db_schema::{ListingType, PostFeatureType};
use reqwest::StatusCode; use reqwest::StatusCode;
use crate::config::Config; use std::collections::HashMap;
use crate::{HTTP_CLIENT, write_error};
pub(crate) struct Lemmy { pub(crate) struct Lemmy {
jwt_token: Sensitive<String>, jwt_token: Sensitive<String>,
@ -26,18 +26,22 @@ pub(crate) async fn login(config: &Config) -> Result<Lemmy, ()> {
.post(config.instance.to_owned() + "/api/v3/user/login") .post(config.instance.to_owned() + "/api/v3/user/login")
.json(&login_params) .json(&login_params)
.send() .send()
.await { .await
{
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
match response.status() { match response.status() {
StatusCode::OK => { StatusCode::OK => {
let data: LoginResponse = response.json().await.expect("Successful Login Request should return JSON"); let data: LoginResponse = response
.json()
.await
.expect("Successful Login Request should return JSON");
match data.jwt { match data.jwt {
Some(token) => Ok(Lemmy { Some(token) => Ok(Lemmy {
jwt_token: token.clone(), jwt_token: token.clone(),
@ -49,7 +53,7 @@ pub(crate) async fn login(config: &Config) -> Result<Lemmy, ()> {
Err(()) Err(())
} }
} }
}, }
status => { status => {
let err_msg = format!("Unexpected HTTP Status '{}' during Login", status); let err_msg = format!("Unexpected HTTP Status '{}' during Login", status);
write_error(err_msg); write_error(err_msg);
@ -65,21 +69,20 @@ impl Lemmy {
.bearer_auth(&self.jwt_token.to_string()) .bearer_auth(&self.jwt_token.to_string())
.json(&post) .json(&post)
.send() .send()
.await { .await
Ok(data) => { {
match data.text().await { Ok(data) => match data.text().await {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
}
} }
}, },
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -88,7 +91,7 @@ impl Lemmy {
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -101,21 +104,20 @@ impl Lemmy {
.bearer_auth(&self.jwt_token.to_string()) .bearer_auth(&self.jwt_token.to_string())
.json(&params) .json(&params)
.send() .send()
.await { .await
Ok(data) => { {
match data.text().await { Ok(data) => match data.text().await {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
}
} }
}, },
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -124,7 +126,7 @@ impl Lemmy {
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -161,21 +163,20 @@ impl Lemmy {
.bearer_auth(&self.jwt_token.to_string()) .bearer_auth(&self.jwt_token.to_string())
.query(&list_params) .query(&list_params)
.send() .send()
.await { .await
Ok(data) => { {
match data.text().await { Ok(data) => match data.text().await {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
}
} }
}, },
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -184,16 +185,16 @@ impl Lemmy {
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
Ok(json_data.posts.iter().filter(|post| { Ok(json_data
post.post.featured_community .posts
}) .iter()
.filter(|post| post.post.featured_community)
.cloned() .cloned()
.collect() .collect())
)
} }
pub(crate) async fn get_local_pinned(&self) -> Result<Vec<PostView>, ()> { pub(crate) async fn get_local_pinned(&self) -> Result<Vec<PostView>, ()> {
@ -207,21 +208,20 @@ impl Lemmy {
.bearer_auth(&self.jwt_token.to_string()) .bearer_auth(&self.jwt_token.to_string())
.query(&list_params) .query(&list_params)
.send() .send()
.await { .await
Ok(data) => { {
match data.text().await { Ok(data) => match data.text().await {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
}
} }
}, },
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -230,16 +230,16 @@ impl Lemmy {
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
Ok(json_data.posts.iter().filter(|post| { Ok(json_data
post.post.featured_local .posts
}) .iter()
.filter(|post| post.post.featured_local)
.cloned() .cloned()
.collect() .collect())
)
} }
pub(crate) async fn get_communities(&self) -> Result<HashMap<String, CommunityId>, ()> { pub(crate) async fn get_communities(&self) -> Result<HashMap<String, CommunityId>, ()> {
@ -253,21 +253,20 @@ impl Lemmy {
.bearer_auth(&self.jwt_token.to_string()) .bearer_auth(&self.jwt_token.to_string())
.query(&list_params) .query(&list_params)
.send() .send()
.await { .await
Ok(data) => { {
match data.text().await { Ok(data) => match data.text().await {
Ok(data) => data, Ok(data) => data,
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
}
} }
}, },
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };
@ -276,7 +275,7 @@ impl Lemmy {
Err(e) => { Err(e) => {
let err_msg = format!("{e}"); let err_msg = format!("{e}");
write_error(err_msg); write_error(err_msg);
return Err(()) return Err(());
} }
}; };

View file

@ -1,37 +1,37 @@
use chrono::{DateTime, Duration, Utc};
use once_cell::sync::Lazy;
use reqwest::{Client};
use std::{collections::HashMap};
use std::fmt::Debug;
use std::sync::{Arc};
use log::{error, warn, info, LevelFilter};
use tokio::sync::{RwLock};
use systemd_journal_logger::{connected_to_journal, JournalLog};
use tokio::time::sleep;
use crate::config::Config; use crate::config::Config;
use crate::post_history::{SeriesHistory}; use crate::post_history::SeriesHistory;
use chrono::{DateTime, Duration, Utc};
use log::{error, info, warn, LevelFilter};
use once_cell::sync::Lazy;
use reqwest::Client;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::Arc;
use systemd_journal_logger::{connected_to_journal, JournalLog};
use tokio::sync::RwLock;
use tokio::time::sleep;
mod bot;
mod config; mod config;
mod jnovel; mod jnovel;
mod bot;
mod lemmy; mod lemmy;
mod post_history; mod post_history;
pub (crate) fn write_error(err_msg: String) { pub(crate) fn write_error(err_msg: String) {
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => println!("[ERROR] {err_msg}"), false => println!("[ERROR] {err_msg}"),
} }
} }
pub (crate) fn write_warn(warn_msg: String) { pub(crate) fn write_warn(warn_msg: String) {
match connected_to_journal() { match connected_to_journal() {
true => warn!("[WARN] {warn_msg}"), true => warn!("[WARN] {warn_msg}"),
false => println!("[WARN] {warn_msg}"), false => println!("[WARN] {warn_msg}"),
} }
} }
pub (crate) fn write_info(info_msg: String) { pub(crate) fn write_info(info_msg: String) {
match connected_to_journal() { match connected_to_journal() {
true => info!("[INFO] {info_msg}"), true => info!("[INFO] {info_msg}"),
false => println!("[INFO] {info_msg}"), false => println!("[INFO] {info_msg}"),
@ -67,7 +67,10 @@ impl SharedData {
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
JournalLog::new().expect("Systemd-Logger crate error").install().expect("Systemd-Logger crate error"); JournalLog::new()
.expect("Systemd-Logger crate error")
.install()
.expect("Systemd-Logger crate error");
log::set_max_level(LevelFilter::Info); log::set_max_level(LevelFilter::Info);
let mut data = SharedData::new(); let mut data = SharedData::new();
@ -87,10 +90,15 @@ async fn main() {
let err_msg = "Bot crashed due to unknown Error, restarting thread after wait..."; let err_msg = "Bot crashed due to unknown Error, restarting thread after wait...";
match connected_to_journal() { match connected_to_journal() {
true => error!("[ERROR] {err_msg}"), true => error!("[ERROR] {err_msg}"),
false => println!("[ERROR] {err_msg}") false => println!("[ERROR] {err_msg}"),
} }
} }
sleep(Duration::seconds(5).to_std().expect("Conversion should always work since static")).await; sleep(
Duration::seconds(5)
.to_std()
.expect("Conversion should always work since static"),
)
.await;
} }
} }

View file

@ -1,6 +1,6 @@
use std::collections::HashMap;
use serde_derive::{Deserialize, Serialize};
use crate::write_error; use crate::write_error;
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Default, Clone, Debug)] #[derive(Serialize, Deserialize, Default, Clone, Debug)]
pub(crate) struct SeriesHistory { pub(crate) struct SeriesHistory {
@ -25,7 +25,7 @@ impl SeriesHistory {
pub(crate) fn check_for_post(&self, series: &str, part: &str, title: &str) -> bool { pub(crate) fn check_for_post(&self, series: &str, part: &str, title: &str) -> bool {
if let Some(series_map) = self.series.get(series) { if let Some(series_map) = self.series.get(series) {
if let Some(part_info) = series_map.parts.get(part) { if let Some(part_info) = series_map.parts.get(part) {
return part_info.volume == title || part_info.chapter == title return part_info.volume == title || part_info.chapter == title;
} }
} }
false false
@ -35,15 +35,15 @@ impl SeriesHistory {
match self.series.get(series) { match self.series.get(series) {
Some(history) => history.clone(), Some(history) => history.clone(),
None => PostHistory { None => PostHistory {
parts: HashMap::new() parts: HashMap::new(),
} },
} }
} }
pub(crate) fn set_series(&mut self, series: &str, data: PostHistory) { pub(crate) fn set_series(&mut self, series: &str, data: PostHistory) {
self.series.entry(series.to_owned()).and_modify(|val| { self.series
*val = data.clone() .entry(series.to_owned())
}) .and_modify(|val| *val = data.clone())
.or_insert(data); .or_insert(data);
} }
} }
@ -60,14 +60,14 @@ impl PostHistory {
None => PostHistoryInner { None => PostHistoryInner {
volume: "".to_owned(), volume: "".to_owned(),
chapter: "".to_owned(), chapter: "".to_owned(),
} },
} }
} }
pub(crate) fn set_part(&mut self, part: &str, data: PostHistoryInner) { pub(crate) fn set_part(&mut self, part: &str, data: PostHistoryInner) {
self.parts.entry(part.to_owned()).and_modify(|val| { self.parts
*val = data.clone() .entry(part.to_owned())
}) .and_modify(|val| *val = data.clone())
.or_insert(data); .or_insert(data);
} }
} }