parent
aefceda628
commit
6520cc65a3
8 changed files with 715 additions and 481 deletions
src
272
src/lemmy.rs
272
src/lemmy.rs
|
@ -1,14 +1,16 @@
|
|||
use crate::config::Config;
|
||||
use std::cmp::Ordering;
|
||||
use crate::config::{Config, PostBody, PostConfig, SeriesConfig};
|
||||
use crate::{HTTP_CLIENT};
|
||||
use lemmy_api_common::community::{ListCommunities, ListCommunitiesResponse};
|
||||
use lemmy_api_common::lemmy_db_views::structs::PostView;
|
||||
use lemmy_api_common::person::{Login, LoginResponse};
|
||||
use lemmy_api_common::post::{CreatePost, FeaturePost, GetPosts, GetPostsResponse};
|
||||
use lemmy_api_common::sensitive::Sensitive;
|
||||
use lemmy_db_schema::newtypes::{CommunityId, PostId};
|
||||
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 serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
use systemd_journal_logger::connected_to_journal;
|
||||
|
@ -25,6 +27,7 @@ macro_rules! error {
|
|||
pub(crate) struct Lemmy {
|
||||
jwt_token: Sensitive<String>,
|
||||
instance: String,
|
||||
communities: HashMap<String, CommunityId>,
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,60 +37,221 @@ pub(crate) struct PostInfoInner {
|
|||
pub(crate) url: Url,
|
||||
}
|
||||
|
||||
pub(crate) trait PostInfo {
|
||||
fn get_info(&self) -> PostInfoInner;
|
||||
|
||||
fn get_description(&self) -> Option<String>;
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum PartInfo {
|
||||
NoParts,
|
||||
Part(u8),
|
||||
}
|
||||
|
||||
pub(crate) async fn login(config: &Config) -> Result<Lemmy, ()> {
|
||||
let login_params = Login {
|
||||
username_or_email: config.get_username(),
|
||||
password: config.get_password(),
|
||||
totp_2fa_token: None,
|
||||
};
|
||||
|
||||
let response = match HTTP_CLIENT
|
||||
.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);
|
||||
return Err(());
|
||||
impl PartInfo {
|
||||
pub(crate) fn as_u8(&self) -> u8 {
|
||||
match self {
|
||||
PartInfo::Part(number) => *number,
|
||||
PartInfo::NoParts => 0,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match response.status() {
|
||||
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: config.instance.to_owned(),
|
||||
}),
|
||||
None => {
|
||||
let err_msg = "Login did not return JWT token. Are the credentials valid?".to_owned();
|
||||
error!(err_msg);
|
||||
Err(())
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.as_u8().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PartInfo {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let self_numeric = self.as_u8();
|
||||
let other_numeric = other.as_u8();
|
||||
self_numeric == other_numeric
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PartInfo {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
if self.gt(other) {
|
||||
Some(Ordering::Greater)
|
||||
} else if self.eq(other) {
|
||||
Some(Ordering::Equal)
|
||||
} else {
|
||||
Some(Ordering::Less)
|
||||
}
|
||||
}
|
||||
|
||||
fn lt(&self, other: &Self) -> bool {
|
||||
let self_numeric = self.as_u8();
|
||||
let other_numeric = other.as_u8();
|
||||
|
||||
self_numeric < other_numeric
|
||||
}
|
||||
|
||||
fn le(&self, other: &Self) -> bool {
|
||||
!self.gt(other)
|
||||
}
|
||||
|
||||
fn gt(&self, other: &Self) -> bool {
|
||||
let self_numeric = self.as_u8();
|
||||
let other_numeric = other.as_u8();
|
||||
|
||||
self_numeric > other_numeric
|
||||
}
|
||||
|
||||
fn ge(&self, other: &Self) -> bool {
|
||||
!self.lt(other)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) enum PostType {
|
||||
Chapter,
|
||||
Volume
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct PostInfo {
|
||||
pub(crate) part: Option<PartInfo>,
|
||||
pub(crate) lemmy_info: PostInfoInner,
|
||||
pub(crate) description: Option<String>,
|
||||
pub(crate) post_type: Option<PostType>
|
||||
}
|
||||
|
||||
impl PostInfo {
|
||||
pub(crate)fn get_info(&self) -> PostInfoInner {
|
||||
self.lemmy_info.clone()
|
||||
}
|
||||
|
||||
pub(crate)fn get_description(&self) -> Option<String> {
|
||||
self.description.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn get_part_info(&self) -> Option<PartInfo> {
|
||||
self.part
|
||||
}
|
||||
|
||||
pub(crate) fn get_post_config(&self, series: &SeriesConfig) -> PostConfig {
|
||||
match self.post_type {
|
||||
Some(post_type) => {
|
||||
match post_type {
|
||||
PostType::Chapter => series.prepub_community.clone(),
|
||||
PostType::Volume => series.volume_community.clone(),
|
||||
}
|
||||
}
|
||||
None => series.prepub_community.clone(),
|
||||
}
|
||||
status => {
|
||||
let err_msg = format!("Unexpected HTTP Status '{}' during Login", status);
|
||||
error!(err_msg);
|
||||
Err(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_post_data(&self, series: &SeriesConfig, lemmy: &Lemmy) -> CreatePost {
|
||||
let post_config = self.get_post_config(series);
|
||||
|
||||
let post_body = match &post_config.post_body {
|
||||
PostBody::None => None,
|
||||
PostBody::Description => self.get_description(),
|
||||
PostBody::Custom(text) => Some(text.clone()),
|
||||
};
|
||||
|
||||
let community_id: CommunityId = lemmy.get_community_id(&post_config.name);
|
||||
|
||||
CreatePost {
|
||||
name: self.get_info().title.clone(),
|
||||
community_id,
|
||||
url: Some(self.get_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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PostInfo {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.part.eq(&other.part)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PostInfo {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
if self.gt(other) {
|
||||
Some(Ordering::Greater)
|
||||
} else if self.eq(other) {
|
||||
Some(Ordering::Equal)
|
||||
} else {
|
||||
Some(Ordering::Less)
|
||||
}
|
||||
}
|
||||
|
||||
fn lt(&self, other: &Self) -> bool {
|
||||
self.part < other.part
|
||||
}
|
||||
|
||||
fn le(&self, other: &Self) -> bool {
|
||||
!self.gt(other)
|
||||
}
|
||||
|
||||
fn gt(&self, other: &Self) -> bool {
|
||||
self.part > other.part
|
||||
}
|
||||
|
||||
fn ge(&self, other: &Self) -> bool {
|
||||
!self.lt(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Lemmy {
|
||||
pub(crate) fn get_community_id(&self, name: &str) -> CommunityId {
|
||||
*self.communities.get(name).expect("Given community is invalid")
|
||||
}
|
||||
pub(crate) async fn new(config: &RwLock<Config>) -> Result<Self, ()> {
|
||||
let read_config = config.read().expect("Read Lock Failed").clone();
|
||||
let login_params = Login {
|
||||
username_or_email: read_config.get_username(),
|
||||
password: read_config.get_password(),
|
||||
totp_2fa_token: None,
|
||||
};
|
||||
|
||||
let response = match HTTP_CLIENT
|
||||
.post(read_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);
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
match response.status() {
|
||||
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(),
|
||||
communities: HashMap::new(),
|
||||
}),
|
||||
None => {
|
||||
let err_msg = "Login did not return JWT token. Are the credentials valid?".to_owned();
|
||||
error!(err_msg);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
status => {
|
||||
let err_msg = format!("Unexpected HTTP Status '{}' during Login", status);
|
||||
error!(err_msg);
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn logout(&self) {
|
||||
let _ = self.post_data_json("/api/v3/user/logout", &"").await;
|
||||
}
|
||||
|
||||
|
||||
pub(crate) async fn post(&self, post: CreatePost) -> Result<PostId, ()> {
|
||||
let response: String = self.post_data_json("/api/v3/post", &post).await?;
|
||||
let json_data: PostView = self.parse_json_map(&response).await?;
|
||||
|
@ -155,14 +319,26 @@ impl Lemmy {
|
|||
.collect())
|
||||
}
|
||||
|
||||
pub(crate) async fn get_communities(&self) -> Result<HashMap<String, CommunityId>, ()> {
|
||||
pub(crate) async fn get_communities(&mut self) {
|
||||
let list_params = ListCommunities {
|
||||
type_: Some(ListingType::Local),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let response: String = self.get_data_query("/api/v3/community/list", &list_params).await?;
|
||||
let json_data: ListCommunitiesResponse = self.parse_json(&response).await?;
|
||||
let response: String = match self.get_data_query("/api/v3/community/list", &list_params).await {
|
||||
Ok(data) => data,
|
||||
Err(_) => {
|
||||
error!("Unable to extract data from request");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let json_data: ListCommunitiesResponse = match self.parse_json::<ListCommunitiesResponse>(&response).await {
|
||||
Ok(data) => data,
|
||||
Err(_) => {
|
||||
error!("Unable to parse data from json");
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
let mut communities: HashMap<String, CommunityId> = HashMap::new();
|
||||
for community_view in json_data.communities {
|
||||
|
@ -170,7 +346,7 @@ impl Lemmy {
|
|||
communities.insert(community.name, community.id);
|
||||
}
|
||||
|
||||
Ok(communities)
|
||||
self.communities = communities;
|
||||
}
|
||||
|
||||
async fn post_data_json<T: Serialize>(&self, route: &str, json: &T ) -> Result<String,()> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue