Automatic Post Pinning and Unpinning, Closes #2
This commit is contained in:
parent
7f717d30b7
commit
8c58088321
2 changed files with 142 additions and 32 deletions
|
@ -1,7 +1,7 @@
|
|||
use std::{fs::{self, OpenOptions}, path::Path, io::Write, thread::sleep, time};
|
||||
|
||||
use lemmy_api_common::{sensitive::Sensitive, post::CreatePost, community::{ListCommunities, ListCommunitiesResponse}};
|
||||
use lemmy_db_schema::{newtypes::{LanguageId, CommunityId}, ListingType};
|
||||
use lemmy_db_schema::{newtypes::{LanguageId, CommunityId, PostId}, ListingType};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
|
||||
|
@ -98,8 +98,8 @@ impl Config {
|
|||
}
|
||||
|
||||
pub(crate) fn check_feeds(&mut self, post_history: &mut Vec<PrevPost>
|
||||
, community_ids: &CommunitiesVector, auth: &Sensitive<String>) -> Result<Vec<CreatePost>, reqwest::Error> {
|
||||
let mut post_queue: Vec<CreatePost> = vec![];
|
||||
, community_ids: &CommunitiesVector, auth: &Sensitive<String>) -> Result<Vec<(CreatePost, (Option<usize>, usize, String))>, reqwest::Error> {
|
||||
let mut post_queue: Vec<(CreatePost, (Option<usize>, usize, String))> = vec![];
|
||||
|
||||
match self.feeds.iter().map(|feed| {
|
||||
let res = CLIENT
|
||||
|
@ -135,18 +135,14 @@ impl Config {
|
|||
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
|
||||
auth: auth.clone(),
|
||||
};
|
||||
post_queue.push(new_post);
|
||||
match prev_post_idx {
|
||||
Some(idx) => {
|
||||
post_history[idx].title = data.title;
|
||||
post_history[idx].last_post_url = item.url.clone();
|
||||
}
|
||||
None => post_history.push(PrevPost {
|
||||
id: feed.id,
|
||||
title: data.title,
|
||||
last_post_url: item.url.clone(),
|
||||
}),
|
||||
}
|
||||
|
||||
let prev_data = (
|
||||
prev_post_idx,
|
||||
feed.id,
|
||||
data.title
|
||||
);
|
||||
|
||||
post_queue.push((new_post, prev_data));
|
||||
}
|
||||
sleep(time::Duration::from_millis(100)); // Should prevent dos-ing J-Novel servers
|
||||
return Ok(());
|
||||
|
@ -155,7 +151,6 @@ impl Config {
|
|||
Err(e) => return Err(e)
|
||||
}
|
||||
|
||||
PrevPost::save(&post_history);
|
||||
return Ok(post_queue);
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +179,7 @@ pub(crate) enum LemmyCommunities {
|
|||
aobprepub,
|
||||
aoblightnovel,
|
||||
aobmanga,
|
||||
aobanime
|
||||
metadiscussions
|
||||
}
|
||||
|
||||
pub_struct!(FeedRedditSettings {
|
||||
|
@ -195,6 +190,7 @@ pub_struct!(FeedRedditSettings {
|
|||
// Posts structs
|
||||
pub_struct!(PrevPost {
|
||||
id: usize,
|
||||
post_id: PostId,
|
||||
title: String,
|
||||
last_post_url: String,
|
||||
});
|
||||
|
@ -206,13 +202,13 @@ impl PrevPost {
|
|||
if Path::new("posts.json").exists() {
|
||||
let file_contents = match fs::read_to_string("posts.json") {
|
||||
Ok(data) => data,
|
||||
Err(e) => panic!("ERROR: secrets.json could not be read:\n\n{:#?}", e),
|
||||
Err(e) => panic!("ERROR: posts.json could not be read:\n\n{:#?}", e),
|
||||
};
|
||||
|
||||
if file_contents.len() > 0 {
|
||||
let history_parse: Vec<PrevPost> = match serde_json::from_str(&file_contents) {
|
||||
Ok(data) => data,
|
||||
Err(e) => panic!("ERROR: secrets.json could not be parsed:\n\n{:#?}", e),
|
||||
Err(e) => panic!("ERROR: posts.json could not be parsed:\n\n{:#?}", e),
|
||||
};
|
||||
history = history_parse;
|
||||
}
|
||||
|
|
140
src/main.rs
140
src/main.rs
|
@ -1,16 +1,16 @@
|
|||
use chrono::{Utc, DateTime, NaiveTime, NaiveDateTime, Timelike, NaiveDate};
|
||||
use config::{Config, PrevPost, Secrets, CommunitiesVector};
|
||||
use chrono::{Utc, NaiveDateTime};
|
||||
use config::{Config, PrevPost, Secrets, CommunitiesVector, LemmyCommunities};
|
||||
use lemmy_api_common::{
|
||||
person::{Login, LoginResponse},
|
||||
post::{CreatePost, GetPosts, GetPostsResponse},
|
||||
sensitive::Sensitive,
|
||||
post::{CreatePost, GetPosts, GetPostsResponse, FeaturePost},
|
||||
sensitive::Sensitive, lemmy_db_views::structs::PostView,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
ListingType, SortType,
|
||||
ListingType, SortType, PostFeatureType, newtypes::CommunityId,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use reqwest::{blocking::Client, StatusCode};
|
||||
use std::{thread::sleep, time, io, error::Error};
|
||||
use std::{thread::sleep, time, collections::HashMap};
|
||||
|
||||
mod config;
|
||||
|
||||
|
@ -80,17 +80,102 @@ impl Bot {
|
|||
/// * `post_data` : Object of type [CreatePost] containing post info
|
||||
/// * `return` : Returns true if Post was succesful, false otherwise
|
||||
#[warn(unused_results)]
|
||||
pub(crate) fn post(&mut self, post_data: CreatePost) -> Result<(), reqwest::Error> {
|
||||
pub(crate) fn post(&mut self, post_data: CreatePost) -> Result<PostView, reqwest::Error> {
|
||||
let res = CLIENT
|
||||
.post(self.config.instance.clone() + "/api/v3/post")
|
||||
.json(&post_data)
|
||||
.send()?;
|
||||
|
||||
// TODO: process res to get info about if post was successfuly (mostly if jwt token was valid)
|
||||
// TODO: process res to get info about if post was successfuly (mostly if jwt token was valid)
|
||||
let ret: PostView = serde_json::from_str::<HashMap<&str, PostView>>(res.text()?.as_str()).unwrap().remove("post_view").unwrap();
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
#[warn(unused_results)]
|
||||
pub(crate) fn pin_new(&mut self, old_post: &Option<usize>, new_post: &PostView) -> Result<(), reqwest::Error> {
|
||||
match old_post {
|
||||
Some(id) => {
|
||||
let remove_community_pin = FeaturePost {
|
||||
post_id: self.post_history[*id].post_id,
|
||||
featured: false,
|
||||
feature_type: PostFeatureType::Community,
|
||||
auth: self.auth.clone()
|
||||
};
|
||||
|
||||
let _ = self.pin(remove_community_pin);
|
||||
}
|
||||
None => {
|
||||
println!("Unable to unpin old post, please do so manually");
|
||||
}
|
||||
}
|
||||
|
||||
// Unpin the other chapter post on local
|
||||
// Get all local pinned posts first
|
||||
// Filter out any post made in the meta community (Community ID)
|
||||
let get_params = GetPosts {
|
||||
auth: Some(self.auth.clone()),
|
||||
..Default::default()
|
||||
};
|
||||
let post_list_json = CLIENT
|
||||
.get(self.config.instance.clone() + "/api/v3/post/list")
|
||||
.query(&get_params)
|
||||
.send()?
|
||||
.text()?;
|
||||
|
||||
let post_list: GetPostsResponse = serde_json::from_str(post_list_json.as_str()).unwrap();
|
||||
|
||||
let mut meta_community: CommunityId = CommunityId(15);
|
||||
|
||||
self.community_ids.ids.iter().for_each(|(id, name)| {
|
||||
if name == &LemmyCommunities::metadiscussions.to_string() {
|
||||
meta_community = id.clone();
|
||||
}
|
||||
});
|
||||
|
||||
for post_view in post_list.posts {
|
||||
if post_view.community.id != meta_community && post_view.post.featured_local {
|
||||
let remove_local_pin = FeaturePost {
|
||||
post_id: post_view.post.id,
|
||||
featured: false,
|
||||
feature_type: PostFeatureType::Local,
|
||||
auth: self.auth.clone()
|
||||
};
|
||||
|
||||
let _ = self.pin(remove_local_pin);
|
||||
}
|
||||
}
|
||||
|
||||
let pin_new_community = FeaturePost {
|
||||
post_id: new_post.post.id,
|
||||
featured: true,
|
||||
feature_type: PostFeatureType::Community,
|
||||
auth: self.auth.clone(),
|
||||
};
|
||||
|
||||
let _ = self.pin(pin_new_community);
|
||||
|
||||
let pin_new_local = FeaturePost {
|
||||
post_id: new_post.post.id,
|
||||
featured: true,
|
||||
feature_type: PostFeatureType::Local,
|
||||
auth: self.auth.clone(),
|
||||
};
|
||||
|
||||
let _ = self.pin(pin_new_local);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn pin (&mut self, pin_data: FeaturePost) -> Result<bool, reqwest::Error> {
|
||||
let res = CLIENT
|
||||
.post(self.config.instance.clone() + "/api/v3/post/feature")
|
||||
.json(&pin_data)
|
||||
.send()?;
|
||||
|
||||
let ret: PostView = serde_json::from_str::<HashMap<&str, PostView>>(res.text()?.as_str()).unwrap().remove("post_view").unwrap();
|
||||
return Ok(ret.post.featured_local);
|
||||
}
|
||||
|
||||
#[warn(unused_results)]
|
||||
pub(crate) fn run_once(&mut self, prev_time: &mut NaiveDateTime) -> Result<(), reqwest::Error> {
|
||||
println!("{:#<1$}", "", 30);
|
||||
|
@ -107,17 +192,46 @@ impl Bot {
|
|||
// Start the polling process
|
||||
// Get all feed URLs (use cache)
|
||||
println!("Checking Feeds");
|
||||
let post_queue: Vec<CreatePost> = self.config.check_feeds(&mut self.post_history, &self.community_ids, &self.auth)?;
|
||||
let post_queue: Vec<(CreatePost, (Option<usize>, usize, String))> = self.config.check_feeds(&mut self.post_history, &self.community_ids, &self.auth)?;
|
||||
println!("Done!");
|
||||
|
||||
post_queue.iter().for_each(|post| {
|
||||
post_queue.iter().for_each(|(post, (prev_idx, feed_id, feed_title))| {
|
||||
println!("Posting: {}", post.name);
|
||||
|
||||
let post_data: PostView;
|
||||
|
||||
loop {
|
||||
if self.post(post.clone()).is_ok() {break};
|
||||
let post_res = self.post(post.clone());
|
||||
if post_res.is_ok() {
|
||||
post_data = post_res.unwrap();
|
||||
break
|
||||
};
|
||||
println!("Post attempt failed, retrying");
|
||||
}
|
||||
|
||||
loop {
|
||||
if self.pin_new(prev_idx, &post_data).is_ok() {break};
|
||||
println!("Pin attempt failed, retrying");
|
||||
}
|
||||
|
||||
// Move current post to old post list
|
||||
match prev_idx {
|
||||
Some(idx) => {
|
||||
self.post_history[*idx].title = feed_title.clone();
|
||||
self.post_history[*idx].post_id = post_data.post.id;
|
||||
self.post_history[*idx].last_post_url = post.url.clone().unwrap().to_string();
|
||||
}
|
||||
None => self.post_history.push(PrevPost {
|
||||
id: feed_id.clone(),
|
||||
post_id: post_data.post.id,
|
||||
title: feed_title.clone(),
|
||||
last_post_url: post.url.clone().unwrap().to_string(),
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
PrevPost::save(&self.post_history);
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -176,7 +290,7 @@ fn run_bot() {
|
|||
let mut this = Bot::new();
|
||||
match this.login() {
|
||||
Ok(_) => {
|
||||
this.community_ids.load(&this.auth, &this.config.instance);
|
||||
let _ = this.community_ids.load(&this.auth, &this.config.instance);
|
||||
|
||||
// Enter a loop (not for debugging)
|
||||
loop {
|
||||
|
@ -186,7 +300,7 @@ fn run_bot() {
|
|||
while !this.run_once(&mut old).is_ok() && loop_breaker <= 3 {
|
||||
println!("Unable to complete Bot cycle, retrying with fresh login credentials");
|
||||
if this.login().is_ok() {
|
||||
this.community_ids.load(&this.auth, &this.config.instance);
|
||||
let _ = this.community_ids.load(&this.auth, &this.config.instance);
|
||||
}
|
||||
sleep(time::Duration::from_secs(10));
|
||||
loop_breaker += 1;
|
||||
|
|
Loading…
Reference in a new issue