aob-lemmy-bot/src/main.rs

183 lines
5.6 KiB
Rust
Raw Normal View History

use chrono::Utc;
use config::{Config, PrevPost, Secrets};
use lemmy_api_common::{
person::{Login, LoginResponse},
post::{CreatePost, GetPosts, GetPostsResponse},
sensitive::Sensitive,
};
use lemmy_db_schema::{
newtypes::{CommunityId, LanguageId},
ListingType, SortType,
};
use once_cell::sync::Lazy;
use reqwest::{blocking::Client, StatusCode};
use std::{thread::sleep, time};
use url::Url;
use crate::config::FeedData;
mod config;
pub static CLIENT: Lazy<Client> = Lazy::new(|| {
let client = Client::builder()
.timeout(time::Duration::from_secs(30))
.connect_timeout(time::Duration::from_secs(30))
.build()
.expect("build client");
client
});
struct Bot {
secrets: Secrets,
config: Config,
post_history: Vec<PrevPost>,
auth: Sensitive<String>,
}
impl Bot {
pub(crate) fn new() -> Bot {
Bot {
secrets: Secrets::load(),
config: Config::load(),
post_history: PrevPost::load(),
auth: Sensitive::new("".to_string()),
}
}
pub(crate) fn login(&mut self) {
let login_params = Login {
username_or_email: self.secrets.lemmy.get_username(),
password: self.secrets.lemmy.get_password(),
};
let res = CLIENT
.post("https://lemmy.neshweb.net/api/v3/user/login")
.json(&login_params)
.send()
.unwrap();
if res.status() == StatusCode::OK {
let data: &LoginResponse = &res.json().unwrap();
let jwt = data.jwt.clone().expect("JWT Token could not be acquired");
self.auth = jwt;
} else {
println!("Error Code: {:?}", res.status());
panic!("JWT Token could not be acquired");
}
}
pub(crate) fn post(&mut self, post_data: CreatePost) {
let res = CLIENT
.post("https://lemmy.neshweb.net/api/v3/post")
.json(&post_data)
.send()
.unwrap();
}
}
fn list_posts(auth: &Sensitive<String>) -> GetPostsResponse {
let params = GetPosts {
type_: Some(ListingType::Local),
sort: Some(SortType::New),
auth: Some(auth.clone()),
..Default::default()
};
let res = CLIENT
.get("https://lemmy.neshweb.net/api/v3/post/list")
.query(&params)
.send()
.unwrap()
.text()
.unwrap();
return serde_json::from_str(&res).unwrap();
}
fn main() {
// Get all needed auth tokens at the start
let mut old = Utc::now().time();
let mut this = Bot::new();
println!("{}", this.secrets.lemmy.username);
this.login();
// Create empty eTag list
println!("TODO: Etag list");
// Enter a loop (not for debugging)
loop {
let start = Utc::now();
print!("\x1B[2J\x1B[1;1H");
println!("Started loop at {} {}", start.format("%H:%M:%S"), start.timezone());
if start.time() - old > chrono::Duration::seconds(6) {
old = start.time();
this.config = Config::load();
}
// Start the polling process
// Get all feed URLs (use cache)
let mut post_queue: Vec<CreatePost> = vec![];
this.config.feeds.iter().for_each(|feed| {
let res = CLIENT
.get(feed.feed_url.clone())
.send()
.unwrap()
.text()
.unwrap();
let data: FeedData = serde_json::from_str(&res).unwrap();
let mut prev_post_idx: Option<usize> = None;
let mut do_post = true;
this.post_history
.iter()
.enumerate()
.for_each(|(idx, post)| {
if &post.last_post_url == &data.items[0].url {
do_post = false;
} else if &post.title == &data.title {
prev_post_idx = Some(idx);
}
});
if do_post {
let item = &data.items[0];
let new_post = CreatePost {
name: item.title.clone(),
community_id: CommunityId(3), // TODO get community id by using community name at the start, save it in a list, planned refresh once a day
url: Some(Url::parse(&item.url).unwrap()),
body: Some(
"[Reddit](https://reddit.com)\n\n[Discord](https://discord.com)".into(),
),
honeypot: None,
nsfw: Some(false),
language_id: Some(LanguageId(0)), // TODO get English language id by api (at the start)
auth: this.auth.clone(),
};
post_queue.push(new_post);
match prev_post_idx {
Some(idx) => {
this.post_history[idx].title = data.title;
this.post_history[idx].last_post_url = item.url.clone();
}
None => this.post_history.push(PrevPost {
title: data.title,
last_post_url: item.url.clone(),
}),
}
}
sleep(time::Duration::from_millis(100)); // Should prevent dos-ing J-Novel servers
});
PrevPost::save(&this.post_history);
post_queue.iter().for_each(|post| {
println!("Posted: {}", post.name);
this.post(post.clone());
});
while Utc::now().time() - start.time() < chrono::Duration::seconds(60) {
sleep(time::Duration::from_secs(10));
}
}
}