2023-12-17 19:17:14 +00:00
use std ::collections ::HashMap ;
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 21:29:18 +00:00
use tokio ::sync ::{ RwLock } ;
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 ;
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 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: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-17 21:29:18 +00:00
write . config = match Config ::load ( ) {
2023-12-17 19:17:14 +00:00
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {} " , e ) ,
} ;
last_reload = Utc ::now ( ) ;
2023-12-17 21:29:18 +00:00
}
2023-12-17 19:17:14 +00:00
2023-12-17 21:29:18 +00:00
{
let read = data . read ( ) . await ;
lemmy = match lemmy ::login ( & credentials , read . config . instance . as_str ( ) ) . await {
2023-12-17 19:17:14 +00:00
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {} " , e ) ,
} ;
login_error = false ;
2023-12-17 19:57:03 +00:00
communities = match lemmy . get_communities ( ) . await {
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {} " , e ) ,
} ;
2023-12-17 19:17:14 +00:00
}
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 ;
}
2023-12-17 21:29:18 +00:00
{
let mut write = data . write ( ) . await ;
write . start = Utc ::now ( ) ;
}
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 21:29:18 +00:00
if write . start - last_reload > Duration ::seconds ( write . config . config_reload_seconds as i64 ) {
write . config = match Config ::load ( ) {
Ok ( data ) = > data ,
Err ( e ) = > panic! ( " {} " , e ) ,
} ;
2023-12-17 21:48:53 +00:00
write . messages . push ( Message ::Info ( " Config reloaded " . to_string ( ) ) ) ;
2023-12-17 21:29:18 +00:00
}
2023-12-17 19:17:14 +00:00
}
2023-12-17 21:29:18 +00:00
{
let read = data . read ( ) . await ;
if login_error {
lemmy = match lemmy ::login ( & credentials , read . config . instance . as_str ( ) ) . await {
Ok ( data ) = > data ,
Err ( e ) = > {
drop ( read ) ;
let mut write = data . write ( ) . await ;
write . messages . push ( Message ::Error ( e . to_string ( ) ) ) ;
continue
}
} ;
login_error = false ;
}
}
{
let read = data . read ( ) . await ;
if read . start - last_reload > Duration ::seconds ( read . config . config_reload_seconds as i64 ) {
communities = match lemmy . get_communities ( ) . await {
Ok ( data ) = > data ,
Err ( e ) = > {
login_error = true ;
drop ( read ) ;
let mut write = data . write ( ) . await ;
write . messages . push ( Message ::Error ( e . to_string ( ) ) ) ;
continue
}
} ;
2023-12-17 21:48:53 +00:00
let mut write = data . write ( ) . await ;
write . messages . push ( Message ::Info ( " Communities reloaded " . to_string ( ) ) ) ;
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 ;
write . post_history = match SeriesHistory ::load_history ( ) {
2023-12-17 19:17:14 +00:00
Ok ( data ) = > data ,
Err ( e ) = > {
2023-12-17 21:29:18 +00:00
write . messages . push ( Message ::Warning ( e . to_string ( ) ) ) ;
2023-12-17 19:17:14 +00:00
continue
}
} ;
}
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 {
match handle_series ( & series , & communities , & lemmy , & data ) . await {
Ok ( data ) = > data ,
Err ( e ) = > {
login_error = true ;
let mut write = data . write ( ) . await ;
write . messages . push ( Message ::Warning ( e . to_string ( ) ) ) ;
continue
}
} ;
2023-12-17 19:17:14 +00:00
}
2023-12-17 21:29:18 +00:00
}
2023-12-17 19:17:14 +00:00
2023-12-17 21:29:18 +00:00
let read = data . read ( ) . await ;
if read . messages . len ( ) > 15 {
let mut list = read . messages . clone ( ) ;
drop ( read ) ;
list . reverse ( ) ;
while list . len ( ) > 15 {
list . pop ( ) ;
}
list . reverse ( ) ;
let mut write = data . write ( ) . await ;
write . messages = list
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 ;
2023-12-17 21:29:18 +00:00
write . messages . push ( Message ::Error ( e . to_string ( ) ) )
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 > > ,
) -> Result < ( ) , String > {
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-17 21:29:18 +00:00
Err ( e ) = > {
return Err ( e . to_string ( ) ) ;
} ,
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 ( ) ) {
drop ( read ) ;
let mut write = data . write ( ) . await ;
write . messages . push ( Message ::Info ( format! ( " Skipping ' {} ' since already posted " , post_lemmy_info . title ) ) ) ;
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 ( ) ) ,
} ;
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 ? ;
}
2023-12-17 21:29:18 +00:00
let read = data . read ( ) . await ;
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 ) ;
let _ = match write . post_history . save_history ( ) {
Ok ( data ) = > data ,
Err ( e ) = > return Err ( format! ( " {} " , e ) )
} ;
2023-12-17 19:17:14 +00:00
}
Ok ( ( ) )
}