use std::collections::HashMap; 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::{ListingType, PostFeatureType}; use reqwest::StatusCode; use crate::config::Config; use crate::HTTP_CLIENT; pub(crate) struct Credentials { username: String, password: String } impl Credentials { pub(crate) fn get_username(&self) -> Sensitive<String> { Sensitive::new(self.username.clone()) } pub(crate) fn get_password(&self) -> Sensitive<String> { Sensitive::new(self.password.clone()) } pub(crate) fn set_credentials(config: &Config) -> Self { Self { username: config.username.clone(), password: config.password.clone(), } } } pub(crate) struct Lemmy { jwt_token: Sensitive<String>, instance: String, } pub(crate) async fn login(credentials: &Credentials, instance: &str) -> Result<Lemmy, String> { let login_params = Login { username_or_email: credentials.get_username(), password: credentials.get_password(), totp_2fa_token: None, }; let response = match HTTP_CLIENT .post(instance.to_owned() + "/api/v3/user/login") .json(&login_params) .send() .await { Ok(data) => data, Err(e) => return Err(format!("{}", e)) }; 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: instance.to_owned(), }), None => panic!("Login did not return JWT token. Are the credentials valid?") } }, status => panic!("Unexpected HTTP Status '{}' during Login", status) } } impl Lemmy { pub(crate) async fn post(&self, post: CreatePost) -> Result<PostId, String> { let response = match HTTP_CLIENT .post(format!("{}/api/v3/post", &self.instance)) .bearer_auth(&self.jwt_token.to_string()) .json(&post) .send() .await { Ok(data) => { match data.text().await { Ok(data) => data, Err(e) => return Err(format!("{}", e)) } }, Err(e) => return Err(format!("{}", e)) }; let json_data = match serde_json::from_str::<HashMap<&str, PostView>>(&response) { Ok(mut data) => data.remove("post_view").expect("Element should be present"), Err(e) => return Err(format!("{}", e)) }; Ok(json_data.post.id) } async fn feature(&self, params: FeaturePost) -> Result<PostView, String> { let response = match HTTP_CLIENT .post(format!("{}/api/v3/post/feature", &self.instance)) .bearer_auth(&self.jwt_token.to_string()) .json(¶ms) .send() .await { Ok(data) => { match data.text().await { Ok(data) => data, Err(e) => return Err(format!("{}", e)) } }, Err(e) => return Err(format!("{}", e)) }; let json_data = match serde_json::from_str::<HashMap<&str, PostView>>(&response) { Ok(mut data) => data.remove("post_view").expect("Element should be present"), Err(e) => return Err(format!("{}", e)) }; Ok(json_data) } pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Result<PostView, String> { let pin_params = FeaturePost { post_id, featured: false, feature_type: location, }; self.feature(pin_params).await } pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Result<PostView, String> { let pin_params = FeaturePost { post_id, featured: true, feature_type: location, }; self.feature(pin_params).await } pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Result<Vec<PostView>, String> { let list_params = GetPosts { community_id: Some(community), type_: Some(ListingType::Local), ..Default::default() }; let response = match HTTP_CLIENT .get(format!("{}/api/v3/post/list", &self.instance)) .bearer_auth(&self.jwt_token.to_string()) .query(&list_params) .send() .await { Ok(data) => { match data.text().await { Ok(data) => data, Err(e) => return Err(format!("{}", e)) } }, Err(e) => return Err(format!("{}", e)) }; let json_data: GetPostsResponse = match serde_json::from_str(&response) { Ok(data) => data, Err(e) => return Err(format!("{}", e)) }; Ok(json_data.posts.iter().filter(|post| { post.post.featured_community }) .cloned() .collect() ) } pub(crate) async fn get_local_pinned(&self) -> Result<Vec<PostView>, String> { let list_params = GetPosts { type_: Some(ListingType::Local), ..Default::default() }; let response = match HTTP_CLIENT .get(format!("{}/api/v3/post/list", &self.instance)) .bearer_auth(&self.jwt_token.to_string()) .query(&list_params) .send() .await { Ok(data) => { match data.text().await { Ok(data) => data, Err(e) => return Err(format!("{}", e)) } }, Err(e) => return Err(format!("{}", e)) }; let json_data: GetPostsResponse = match serde_json::from_str(&response) { Ok(data) => data, Err(e) => return Err(format!("{}", e)) }; Ok(json_data.posts.iter().filter(|post| { post.post.featured_local }) .cloned() .collect() ) } pub(crate) async fn get_communities(&self) -> Result<HashMap<String, CommunityId>, String> { let list_params = ListCommunities { type_: Some(ListingType::Local), ..Default::default() }; let response = match HTTP_CLIENT .get(format!("{}/api/v3/community/list", &self.instance)) .bearer_auth(&self.jwt_token.to_string()) .query(&list_params) .send() .await { Ok(data) => { match data.text().await { Ok(data) => data, Err(e) => return Err(format!("{}", e)) } }, Err(e) => return Err(format!("{}", e)) }; let json_data: ListCommunitiesResponse = match serde_json::from_str(&response) { Ok(data) => data, Err(e) => return Err(format!("{}", e)) }; let mut communities: HashMap<String, CommunityId> = HashMap::new(); for community_view in json_data.communities { let community = community_view.community; communities.insert(community.name, community.id); } Ok(communities) } }