(Re-)introduce Threading, Add support for different Config (Settings) Sources, Remove Async leftovers

This commit is contained in:
Neshura 2024-07-21 02:27:16 +02:00
parent 2ae6468ad8
commit 5976bd59a7
Signed by: Neshura
GPG key ID: 4E2D47B1374C297D
15 changed files with 634 additions and 329 deletions

View file

@ -1,5 +1,5 @@
use std::cmp::Ordering;
use crate::config::{Config, PostBody, PostConfig, SeriesConfig};
use crate::config::{PostConfig, SeriesConfig};
use crate::{HTTP_CLIENT};
use lemmy_api_common::community::{ListCommunities, ListCommunitiesResponse};
use lemmy_api_common::lemmy_db_views::structs::PostView;
@ -9,28 +9,10 @@ use lemmy_db_schema::newtypes::{CommunityId, LanguageId, PostId};
use lemmy_db_schema::{ListingType, PostFeatureType};
use reqwest::StatusCode;
use std::collections::HashMap;
use std::sync::{RwLock};
use lemmy_db_schema::sensitive::SensitiveString;
use serde::{Deserialize, Serialize};
use systemd_journal_logger::connected_to_journal;
macro_rules! debug {
($msg:tt) => {
match connected_to_journal() {
true => log::debug!("[DEBUG] {}", $msg),
false => println!("[DEBUG] {}", $msg),
}
};
}
macro_rules! error {
($msg:tt) => {
match connected_to_journal() {
true => log::error!("[ERROR] {}", $msg),
false => eprintln!("[ERROR] {}", $msg),
}
};
}
use crate::logging::Logging;
use crate::settings::{BotSettings, PostBody};
pub(crate) struct Lemmy {
jwt_token: SensitiveString,
@ -107,7 +89,7 @@ impl PartialOrd for PartInfo {
}
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum PostType {
Chapter,
Volume
@ -207,26 +189,24 @@ impl PartialOrd for PostInfo {
impl Lemmy {
pub(crate) fn get_community_id(&self, name: &str) -> CommunityId {
*self.communities.get(name).expect("Given community is invalid")
*self.communities.get(name).expect(format!("Community '{name}' is invalid").as_str())
}
pub(crate) async fn new(config: &RwLock<Config>) -> Result<Self, ()> {
let read_config = config.read().expect("Read Lock Failed").clone();
pub(crate) fn new(config: &BotSettings) -> Result<Self, ()> {
let login_params = Login {
username_or_email: read_config.get_username(),
password: read_config.get_password(),
username_or_email: config.username(),
password: config.password(),
totp_2fa_token: None,
};
let response = match HTTP_CLIENT
.post(read_config.instance.to_owned() + "/api/v3/user/login")
.post(config.instance().to_owned() + "/api/v3/user/login")
.json(&login_params)
.send()
.await
{
Ok(data) => data,
Err(e) => {
let err_msg = format!("{e}");
error!(err_msg);
Logging::error(err_msg.as_str());
return Err(());
}
};
@ -235,40 +215,41 @@ impl Lemmy {
StatusCode::OK => {
let data: LoginResponse = response
.json()
.await
.expect("Successful Login Request should return JSON");
match data.jwt {
Some(token) => Ok(Lemmy {
jwt_token: token.clone(),
instance: read_config.instance.to_owned(),
instance: config.instance().to_owned(),
communities: HashMap::new(),
}),
None => {
let err_msg = "Login did not return JWT token. Are the credentials valid?".to_owned();
error!(err_msg);
Logging::error("Login did not return JWT token. Are the credentials valid?");
Err(())
}
}
}
status => {
let err_msg = format!("Unexpected HTTP Status '{}' during Login", status);
error!(err_msg);
Logging::error(err_msg.as_str());
Err(())
}
}
}
pub(crate) async fn logout(&self) {
let _ = self.post_data_json("/api/v3/user/logout", &"").await;
pub fn logout(&self) {
let _ = self.post_data_json("/api/v3/user/logout", &"");
}
pub fn login(&self) {
}
pub(crate) async fn post(&self, post: CreatePost) -> Option<PostId> {
let response: String = match self.post_data_json("/api/v3/post", &post).await {
pub fn post(&self, post: CreatePost) -> Option<PostId> {
let response: String = match self.post_data_json("/api/v3/post", &post) {
Some(data) => data,
None => return None,
};
let json_data: PostView = match self.parse_json_map(&response).await {
let json_data: PostView = match self.parse_json_map(&response) {
Some(data) => data,
None => return None,
};
@ -276,12 +257,12 @@ impl Lemmy {
Some(json_data.post.id)
}
async fn feature(&self, params: FeaturePost) -> Option<PostView> {
let response: String = match self.post_data_json("/api/v3/post/feature", &params).await {
fn feature(&self, params: FeaturePost) -> Option<PostView> {
let response: String = match self.post_data_json("/api/v3/post/feature", &params) {
Some(data) => data,
None => return None,
};
let json_data: PostView = match self.parse_json_map(&response).await {
let json_data: PostView = match self.parse_json_map(&response) {
Some(data) => data,
None => return None,
};
@ -289,36 +270,36 @@ impl Lemmy {
Some(json_data)
}
pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> {
pub(crate) fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> {
let pin_params = FeaturePost {
post_id,
featured: false,
feature_type: location,
};
self.feature(pin_params).await
self.feature(pin_params)
}
pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> {
pub(crate) fn pin(&self, post_id: PostId, location: PostFeatureType) -> Option<PostView> {
let pin_params = FeaturePost {
post_id,
featured: true,
feature_type: location,
};
self.feature(pin_params).await
self.feature(pin_params)
}
pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Option<Vec<PostView>> {
pub(crate) fn get_community_pinned(&self, community: CommunityId) -> Option<Vec<PostView>> {
let list_params = GetPosts {
community_id: Some(community),
type_: Some(ListingType::Local),
..Default::default()
};
let response: String = match self.get_data_query("/api/v3/post/list", &list_params).await {
let response: String = match self.get_data_query("/api/v3/post/list", &list_params) {
Some(data) => data,
None => return None,
};
let json_data: GetPostsResponse = match self.parse_json(&response).await {
let json_data: GetPostsResponse = match self.parse_json(&response) {
Some(data) => data,
None => return None,
};
@ -331,17 +312,17 @@ impl Lemmy {
.collect())
}
pub(crate) async fn get_local_pinned(&self) -> Option<Vec<PostView>> {
pub(crate) fn get_local_pinned(&self) -> Option<Vec<PostView>> {
let list_params = GetPosts {
type_: Some(ListingType::Local),
..Default::default()
};
let response: String = match self.get_data_query("/api/v3/post/list", &list_params).await {
let response: String = match self.get_data_query("/api/v3/post/list", &list_params) {
Some(data) => data,
None => return None,
};
let json_data: GetPostsResponse = match self.parse_json(&response).await {
let json_data: GetPostsResponse = match self.parse_json(&response) {
Some(data) => data,
None => return None,
};
@ -354,17 +335,17 @@ impl Lemmy {
.collect())
}
pub(crate) async fn get_communities(&mut self) {
pub(crate) fn get_communities(&mut self) {
let list_params = ListCommunities {
type_: Some(ListingType::Local),
..Default::default()
};
let response: String = match self.get_data_query("/api/v3/community/list", &list_params).await {
let response: String = match self.get_data_query("/api/v3/community/list", &list_params) {
Some(data) => data,
None => return,
};
let json_data: ListCommunitiesResponse = match self.parse_json::<ListCommunitiesResponse>(&response).await {
let json_data: ListCommunitiesResponse = match self.parse_json::<ListCommunitiesResponse>(&response) {
Some(data) => data,
None => return,
};
@ -378,71 +359,71 @@ impl Lemmy {
self.communities = communities;
}
async fn post_data_json<T: Serialize>(&self, route: &str, json: &T ) -> Option<String> {
fn post_data_json<T: Serialize>(&self, route: &str, json: &T ) -> Option<String> {
let res = HTTP_CLIENT
.post(format!("{}{route}", &self.instance))
.bearer_auth(&self.jwt_token.to_string())
.json(&json)
.send()
.await;
self.extract_data(res).await
.send();
self.extract_data(res)
}
async fn get_data_query<T: Serialize>(&self, route: &str, param: &T ) -> Option<String> {
fn get_data_query<T: Serialize>(&self, route: &str, param: &T ) -> Option<String> {
let res = HTTP_CLIENT
.get(format!("{}{route}", &self.instance))
.bearer_auth(&self.jwt_token.to_string())
.query(&param)
.send()
.await;
self.extract_data(res).await
.send();
self.extract_data(res)
}
async fn extract_data(&self, response: Result<reqwest::Response, reqwest::Error>) -> Option<String> {
fn extract_data(&self, response: Result<reqwest::blocking::Response, reqwest::Error>) -> Option<String> {
match response {
Ok(data) => {
let msg = format!("Status Code: '{}'", data.status().clone());
Logging::debug(msg.as_str());
if data.status().is_success() {
match data.text().await {
match data.text() {
Ok(data) => Some(data),
Err(e) => {
let err_msg = format!("{e}");
error!(err_msg);
Logging::error(err_msg.as_str());
None
}
}
}
else {
let err_msg = format!("HTTP Request failed: {}", data.text().await.unwrap());
error!(err_msg);
let err_msg = format!("HTTP Request failed: {}", data.text().unwrap());
Logging::error(err_msg.as_str());
None
}
},
Err(e) => {
let err_msg = format!("{e}");
error!(err_msg);
Logging::error(err_msg.as_str());
None
}
}
}
async fn parse_json<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option<T> {
fn parse_json<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option<T> {
match serde_json::from_str::<T>(response) {
Ok(data) => Some(data),
Err(e) => {
let err_msg = format!("while parsing JSON: {e} ");
error!(err_msg);
Logging::error(err_msg.as_str());
None
}
}
}
async fn parse_json_map<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option<T> {
debug!(response);
fn parse_json_map<'a, T: Deserialize<'a>>(&self, response: &'a str) -> Option<T> {
Logging::debug(response);
match serde_json::from_str::<HashMap<&str, T>>(response) {
Ok(mut data) => Some(data.remove("post_view").expect("Element should be present")),
Err(e) => {
let err_msg = format!("while parsing JSON HashMap: {e}");
error!(err_msg);
Logging::error(err_msg.as_str());
None
}
}