2023-12-17 19:17:14 +00:00
use std ::collections ::HashMap ;
use std ::error ::Error ;
use std ::sync ::{ Arc } ;
2023-12-17 19:19:24 +00:00
use chrono ::{ DateTime , Duration , Timelike , Utc } ;
2023-12-17 19:17:14 +00:00
use lemmy_db_schema ::newtypes ::{ CommunityId , LanguageId } ;
use lemmy_db_schema ::PostFeatureType ;
2023-12-17 19:19:24 +00:00
use tokio ::sync ::{ RwLock , RwLockWriteGuard } ;
2023-12-17 19:17:14 +00:00
use crate ::{ jnovel , lemmy , Message , SharedData } ;
use crate ::config ::{ Config , PostBody , SeriesConfig } ;
use crate ::jnovel ::PostInfo ;
use crate ::lemmy ::{ CustomCreatePost , Lemmy } ;
use crate ::post_history ::SeriesHistory ;
use tokio ::time ::sleep ;
pub ( crate ) async fn run ( data : Arc < RwLock < SharedData > > ) {
let credentials = match lemmy ::Credentials ::set_credentials ( ) {
Ok ( creds ) = > creds ,
Err ( e ) = > panic! ( " {} " , e . to_string ( ) ) ,
} ;
let mut last_reload : DateTime < Utc > ;
let mut lemmy : Lemmy ;
let mut login_error : bool ;
2023-12-17 19:35:37 +00:00
let mut communities = HashMap ::new ( ) ;
2023-12-17 19:17:14 +00:00
{
let mut shared_data = data . write ( ) . await ;
// Errors during bot init are likely unrecoverable and therefore should panic the bot
// Does not really matter since the bot will get restarted anyway but this way the uptime url logs a downtime
shared_data . config = match Config ::load ( ) {
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {} " , e ) ,
} ;
last_reload = Utc ::now ( ) ;
lemmy = match lemmy ::login ( & credentials , shared_data . config . instance . as_str ( ) ) . await {
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {} " , e ) ,
} ;
login_error = false ;
}
2023-12-17 19:35:37 +00:00
while Utc ::now ( ) . naive_local ( ) . second ( ) ! = 30 {
2023-12-17 19:17:14 +00:00
sleep ( Duration ::milliseconds ( 100 ) . to_std ( ) . unwrap ( ) ) . await ;
}
loop {
idle ( & data ) . await ;
let mut shared_data = data . write ( ) . await ;
shared_data . start = Utc ::now ( ) ;
if shared_data . start - last_reload > Duration ::seconds ( shared_data . config . config_reload_seconds as i64 ) {
shared_data . config = match Config ::load ( ) {
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {} " , e ) ,
} ;
}
if login_error {
lemmy = match lemmy ::login ( & credentials , shared_data . config . instance . as_str ( ) ) . await {
Ok ( data ) = > data ,
Err ( e ) = > {
shared_data . messages . push ( Message ::Error ( format! ( " {} " , e ) ) ) ;
continue
}
} ;
login_error = false ;
}
if shared_data . start - last_reload > Duration ::seconds ( shared_data . config . config_reload_seconds as i64 ) {
2023-12-17 19:35:37 +00:00
communities = match lemmy . get_communities ( ) . await {
2023-12-17 19:17:14 +00:00
Ok ( data ) = > data ,
Err ( e ) = > {
login_error = true ;
shared_data . messages . push ( Message ::Error ( format! ( " {} " , e ) ) ) ;
continue
}
} ;
last_reload = Utc ::now ( ) ;
}
shared_data . post_history = match SeriesHistory ::load_history ( ) {
Ok ( data ) = > data ,
Err ( e ) = > {
login_error = true ;
shared_data . messages . push ( Message ::Warning ( format! ( " {} " , e ) ) ) ;
continue
}
} ;
for series in shared_data . config . series . clone ( ) {
match handle_series ( & series , & communities , & lemmy , & mut shared_data ) . await {
Ok ( data ) = > data ,
Err ( e ) = > {
login_error = true ;
shared_data . messages . push ( Message ::Warning ( format! ( " {} " , e ) ) ) ;
continue
}
} ;
}
2023-12-17 19:35:37 +00:00
let _ = shared_data . downgrade ( ) ;
2023-12-17 19:17:14 +00:00
idle ( & data ) . await ;
}
}
async fn idle ( data : & Arc < RwLock < SharedData > > ) {
let read = data . read ( ) . await ;
let mut sleep_duration = Duration ::seconds ( 30 ) ;
if Utc ::now ( ) - read . start > sleep_duration {
sleep_duration = Duration ::seconds ( 60 ) ;
}
if let Some ( status_url ) = read . config . status_post_url . clone ( ) {
match reqwest ::get ( status_url ) . await {
Ok ( _ ) = > { }
Err ( e ) = > {
let mut write = data . write ( ) . await ;
write . messages . push ( Message ::Error ( format! ( " {} " , e ) ) )
} ,
}
} ;
while Utc ::now ( ) - read . start < sleep_duration {
sleep ( Duration ::milliseconds ( 100 ) . to_std ( ) . unwrap ( ) ) . await ;
}
}
async fn handle_series < ' a > (
series : & SeriesConfig ,
communities : & HashMap < String , CommunityId > ,
lemmy : & Lemmy ,
data : & mut RwLockWriteGuard < ' a , SharedData > ,
) -> Result < ( ) , Box < dyn Error > > {
let mut post_list = match jnovel ::check_feed ( series . slug . as_str ( ) , series . parted ) . await {
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {:#?} " , e ) ,
} ;
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_lemmy_info = post_info . get_lemmy_info ( ) ;
if data . post_history . check_for_post ( series . slug . as_str ( ) , post_part_info . as_string ( ) . as_str ( ) , post_lemmy_info . title . as_str ( ) ) {
data . messages . push ( Message ::Info ( format! ( " Skipping ' {} ' since already posted " , post_lemmy_info . title ) ) ) ;
post_list . remove ( index ) ;
continue
}
let post_series_config = match post_info {
PostInfo ::Chapter { .. } = > { & series . prepub_community } ,
PostInfo ::Volume { .. } = > { & series . volume_community }
} ;
let community_id = * communities
. get ( post_series_config . name . as_str ( ) )
. expect ( " Given community is invalid " ) ;
let post_body = match & post_series_config . post_body {
PostBody ::None = > None ,
PostBody ::Description = > post_info . get_description ( ) ,
PostBody ::Custom ( text ) = > Some ( text . clone ( ) ) ,
} ;
let post_data = CustomCreatePost {
name : post_lemmy_info . title ,
2023-12-17 19:35:37 +00:00
community_id ,
2023-12-17 19:17:14 +00:00
url : Some ( post_lemmy_info . url ) ,
body : post_body ,
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
} ;
let post_id = lemmy . post ( post_data ) . await ? ;
if post_series_config . pin_settings . pin_new_post_community {
let pinned_posts = lemmy . get_community_pinned ( community_id ) . await ? ;
2023-12-17 19:35:37 +00:00
if ! pinned_posts . is_empty ( ) {
2023-12-17 19:17:14 +00:00
let community_pinned_post = & pinned_posts [ 0 ] ;
lemmy . unpin ( community_pinned_post . post . id , PostFeatureType ::Community ) . await ? ;
}
lemmy . pin ( post_id , PostFeatureType ::Community ) . await ? ;
}
if post_series_config . pin_settings . pin_new_post_local {
let pinned_posts = lemmy . get_local_pinned ( ) . await ? ;
2023-12-17 19:35:37 +00:00
if ! pinned_posts . is_empty ( ) {
2023-12-17 19:17:14 +00:00
let community_pinned_post = & pinned_posts [ 0 ] ;
lemmy . unpin ( community_pinned_post . post . id , PostFeatureType ::Local ) . await ? ;
}
lemmy . pin ( post_id , PostFeatureType ::Local ) . await ? ;
}
let mut series_history = data . post_history . get_series ( series . slug . as_str ( ) ) ;
let mut part_history = series_history . get_part ( post_part_info . as_string ( ) . as_str ( ) ) ;
match post_info {
PostInfo ::Chapter { .. } = > {
part_history . chapter = 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 ) ;
data . post_history . set_series ( series . slug . as_str ( ) , series_history ) ;
2023-12-17 19:35:37 +00:00
let _ = data . post_history . save_history ( ) ;
2023-12-17 19:17:14 +00:00
}
Ok ( ( ) )
}