2023-12-17 19:17:14 +00:00
use std ::collections ::HashMap ;
use std ::sync ::{ Arc } ;
2023-12-30 00:22:04 +00:00
use chrono ::{ DateTime , Duration , Utc } ;
2023-12-19 21:08:23 +00:00
use lemmy_api_common ::post ::CreatePost ;
2023-12-17 19:17:14 +00:00
use lemmy_db_schema ::newtypes ::{ CommunityId , LanguageId } ;
use lemmy_db_schema ::PostFeatureType ;
2023-12-17 21:29:18 +00:00
use tokio ::sync ::{ RwLock } ;
2023-12-29 23:40:12 +00:00
use crate ::{ jnovel , lemmy , SharedData , write_error , write_info , write_warn } ;
2023-12-17 19:17:14 +00:00
use crate ::config ::{ Config , PostBody , SeriesConfig } ;
use crate ::jnovel ::PostInfo ;
2023-12-19 21:08:23 +00:00
use crate ::lemmy ::{ Lemmy } ;
2023-12-17 19:17:14 +00:00
use crate ::post_history ::SeriesHistory ;
use tokio ::time ::sleep ;
2023-12-17 21:29:18 +00:00
pub ( crate ) async fn run ( data : Arc < RwLock < SharedData > > ) {
2023-12-17 19:17:14 +00:00
let mut last_reload : DateTime < Utc > ;
let mut lemmy : Lemmy ;
let mut login_error : bool ;
2023-12-17 19:57:03 +00:00
let mut communities ;
2023-12-17 19:17:14 +00:00
{
2023-12-17 21:29:18 +00:00
let mut write = data . write ( ) . await ;
2023-12-17 19:17:14 +00:00
// 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
2023-12-29 23:40:12 +00:00
write . config = Config ::load ( ) ;
2023-12-17 19:17:14 +00:00
last_reload = Utc ::now ( ) ;
2023-12-17 21:29:18 +00:00
}
2023-12-17 19:17:14 +00:00
2023-12-29 13:35:07 +00:00
{
let read = data . read ( ) . await ;
2023-12-29 23:40:12 +00:00
lemmy = match lemmy ::login ( & read . config ) . await {
2023-12-17 19:17:14 +00:00
Ok ( data ) = > data ,
2023-12-29 23:40:12 +00:00
Err ( _ ) = > panic! ( ) ,
2023-12-17 19:17:14 +00:00
} ;
login_error = false ;
2023-12-17 19:57:03 +00:00
communities = match lemmy . get_communities ( ) . await {
Ok ( data ) = > data ,
2023-12-29 23:40:12 +00:00
Err ( _ ) = > panic! ( ) ,
2023-12-17 19:57:03 +00:00
} ;
2023-12-17 19:17:14 +00:00
}
2023-12-17 21:29:18 +00:00
{
let mut write = data . write ( ) . await ;
write . start = Utc ::now ( ) ;
}
2023-12-29 23:53:00 +00:00
let info_msg = " Bot init successful, starting normal operations " . to_owned ( ) ;
write_info ( info_msg ) ;
2023-12-17 19:17:14 +00:00
loop {
idle ( & data ) . await ;
2023-12-17 21:29:18 +00:00
{
let mut write = data . write ( ) . await ;
2023-12-17 19:17:14 +00:00
2023-12-17 21:29:18 +00:00
write . start = Utc ::now ( ) ;
2023-12-17 19:17:14 +00:00
2023-12-17 22:00:37 +00:00
if write . start - last_reload > = Duration ::seconds ( write . config . config_reload_seconds as i64 ) {
2023-12-29 23:40:12 +00:00
write . config = Config ::load ( ) ;
let message = " Config reloaded " . to_owned ( ) ;
write_info ( message ) ;
2023-12-29 13:35:07 +00:00
}
}
2023-12-17 21:29:18 +00:00
{
let read = data . read ( ) . await ;
if login_error {
2023-12-29 23:40:12 +00:00
lemmy = match lemmy ::login ( & read . config ) . await {
2023-12-17 21:29:18 +00:00
Ok ( data ) = > data ,
2023-12-29 23:40:12 +00:00
Err ( _ ) = > continue ,
2023-12-17 21:29:18 +00:00
} ;
login_error = false ;
}
}
{
let read = data . read ( ) . await ;
2023-12-17 22:00:37 +00:00
if read . start - last_reload > = Duration ::seconds ( read . config . config_reload_seconds as i64 ) {
2023-12-17 21:29:18 +00:00
communities = match lemmy . get_communities ( ) . await {
Ok ( data ) = > data ,
2023-12-29 23:40:12 +00:00
Err ( _ ) = > {
2023-12-17 21:29:18 +00:00
login_error = true ;
continue
}
} ;
2023-12-29 23:40:12 +00:00
let message = " Communities reloaded " . to_owned ( ) ;
write_info ( message ) ;
2023-12-17 21:29:18 +00:00
last_reload = Utc ::now ( ) ;
}
2023-12-17 19:17:14 +00:00
}
2023-12-17 21:29:18 +00:00
{
let mut write = data . write ( ) . await ;
2023-12-30 00:02:20 +00:00
write . post_history = SeriesHistory ::load_history ( ) ;
2023-12-17 19:17:14 +00:00
}
2023-12-17 21:29:18 +00:00
{
let read = data . read ( ) . await ;
let series = read . config . series . clone ( ) ;
drop ( read ) ;
for series in series {
2023-12-29 23:40:12 +00:00
if handle_series ( & series , & communities , & lemmy , & data ) . await . is_err ( ) {
login_error = true ;
continue
2023-12-17 21:29:18 +00:00
} ;
2023-12-17 19:17:14 +00:00
}
2023-12-17 21:29:18 +00:00
}
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 ) = > {
2023-12-29 23:40:12 +00:00
let err_msg = format! ( " {e} " ) ;
write_error ( err_msg ) ;
2023-12-17 19:17:14 +00:00
} ,
}
} ;
while Utc ::now ( ) - read . start < sleep_duration {
sleep ( Duration ::milliseconds ( 100 ) . to_std ( ) . unwrap ( ) ) . await ;
}
}
2023-12-17 21:29:18 +00:00
async fn handle_series (
2023-12-17 19:17:14 +00:00
series : & SeriesConfig ,
communities : & HashMap < String , CommunityId > ,
lemmy : & Lemmy ,
2023-12-17 21:29:18 +00:00
data : & Arc < RwLock < SharedData > > ,
2023-12-29 23:40:12 +00:00
) -> Result < ( ) , ( ) > {
2023-12-17 21:29:18 +00:00
2023-12-17 19:17:14 +00:00
let mut post_list = match jnovel ::check_feed ( series . slug . as_str ( ) , series . parted ) . await {
Ok ( data ) = > data ,
2023-12-29 23:40:12 +00:00
Err ( _ ) = > return Err ( ( ) ) ,
2023-12-17 19:17:14 +00:00
} ;
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 ( ) ;
2023-12-17 21:29:18 +00:00
{
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 ( ) ) {
2023-12-29 23:40:12 +00:00
let message = format! ( " Skipping ' {} ' since already posted " , post_lemmy_info . title ) ;
write_info ( message ) ;
2023-12-17 19:17:14 +00:00
post_list . remove ( index ) ;
continue
}
2023-12-17 21:29:18 +00:00
}
2023-12-17 19:17:14 +00:00
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 ( ) ) ,
} ;
2023-12-19 21:08:23 +00:00
let post_data = CreatePost {
2023-12-17 19:17:14 +00:00
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 ? ;
2023-12-18 22:33:14 +00:00
{
let read = data . read ( ) . await ;
if post_series_config . pin_settings . pin_new_post_community & & ! read . config . protected_communities . contains ( & post_series_config . name ) {
let pinned_posts = lemmy . get_community_pinned ( community_id ) . await ? ;
if ! pinned_posts . is_empty ( ) {
let community_pinned_post = & pinned_posts [ 0 ] ;
lemmy . unpin ( community_pinned_post . post . id , PostFeatureType ::Community ) . await ? ;
}
lemmy . pin ( post_id , PostFeatureType ::Community ) . await ? ;
} else if read . config . protected_communities . contains ( & post_series_config . name ) {
2023-12-29 23:40:12 +00:00
let message = format! ( " Community ' {} ' for Series ' {} ' is protected. Is this intended? " , & post_series_config . name , series . slug ) ;
write_warn ( message ) ;
2023-12-17 19:17:14 +00:00
}
2023-12-18 10:23:47 +00:00
}
2023-12-17 19:17:14 +00:00
2023-12-18 10:23:47 +00:00
let read = data . read ( ) . await ;
2023-12-17 19:17:14 +00:00
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-18 10:23:47 +00:00
for pinned_post in pinned_posts {
if read . config . protected_communities . contains ( & pinned_post . community . name ) {
continue
}
else {
let community_pinned_post = & pinned_post ;
lemmy . unpin ( community_pinned_post . post . id , PostFeatureType ::Local ) . await ? ;
break
}
}
2023-12-17 19:17:14 +00:00
}
lemmy . pin ( post_id , PostFeatureType ::Local ) . await ? ;
}
2023-12-17 21:29:18 +00:00
let mut series_history = read . post_history . get_series ( series . slug . as_str ( ) ) ;
2023-12-17 19:17:14 +00:00
let mut part_history = series_history . get_part ( post_part_info . as_string ( ) . as_str ( ) ) ;
2023-12-17 21:29:18 +00:00
drop ( read ) ;
2023-12-17 19:17:14 +00:00
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 ) ;
2023-12-17 21:29:18 +00:00
let mut write = data . write ( ) . await ;
write . post_history . set_series ( series . slug . as_str ( ) , series_history ) ;
2023-12-30 00:02:20 +00:00
write . post_history . save_history ( ) ;
2023-12-17 19:17:14 +00:00
}
Ok ( ( ) )
}