aob-lemmy-bot/src/lemmy/mod.rs

387 lines
12 KiB
Rust
Raw Normal View History

use std::collections::HashMap;
use std::env;
use std::env::VarError;
2023-12-17 19:19:24 +00:00
use lemmy_api_common::community::{ListCommunities};
use lemmy_api_common::person::{Login, LoginResponse};
2023-12-17 19:19:24 +00:00
use lemmy_api_common::post::{GetPosts};
use lemmy_api_common::sensitive::Sensitive;
2023-12-17 19:19:24 +00:00
use lemmy_db_schema::newtypes::{CommunityId, InstanceId, LanguageId, PersonId, PostId};
use lemmy_db_schema::{ListingType, PostFeatureType, SubscribedType};
use reqwest::StatusCode;
use serde_derive::{Deserialize, Serialize};
use url::Url;
use crate::HTTP_CLIENT;
pub(crate) struct Credentials {
username: String,
password: String
}
impl Credentials {
pub(crate) fn get_username(&self) -> Sensitive<String> {
2023-12-17 19:35:37 +00:00
Sensitive::new(self.username.clone())
}
pub(crate) fn get_password(&self) -> Sensitive<String> {
2023-12-17 19:35:37 +00:00
Sensitive::new(self.password.clone())
}
pub(crate) fn set_credentials() -> Result<Self, VarError> {
let username = env::var("LEMMY_USERNAME")?;
let password = env::var("LEMMY_PASSWORD")?;
Ok(Credentials {
username,
password,
})
}
}
pub(crate) struct Lemmy {
jwt_token: Sensitive<String>,
instance: String,
}
2023-12-17 21:29:18 +00:00
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,
};
2023-12-17 21:29:18 +00:00
let response = match HTTP_CLIENT
.post(instance.to_string() + "/api/v3/user/login")
.json(&login_params)
.send()
2023-12-17 21:29:18 +00:00
.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_string(),
}),
2023-12-17 19:35:37 +00:00
None => panic!("Login did not return JWT token. Are the credentials valid?")
}
},
2023-12-17 19:35:37 +00:00
status => panic!("Unexpected HTTP Status '{}' during Login", status)
}
}
impl Lemmy {
2023-12-17 21:29:18 +00:00
pub(crate) async fn post(&self, post: CustomCreatePost) -> 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()
2023-12-17 21:29:18 +00:00
.await {
Ok(data) => {
match data.text().await {
Ok(data) => data,
Err(e) => return Err(format!("{}", e))
}
},
Err(e) => return Err(format!("{}", e))
};
2023-12-17 21:29:18 +00:00
let json_data = match serde_json::from_str::<HashMap<&str, CustomPostView>>(&response) {
Ok(mut data) => data.remove("post_view").expect("Element should be present"),
Err(e) => return Err(format!("{}", e))
};
Ok(json_data.post.id)
}
2023-12-17 21:29:18 +00:00
async fn feature(&self, params: CustomFeaturePost) -> Result<CustomPostView, String> {
let response = match HTTP_CLIENT
.post(format!("{}/api/v3/post/feature", &self.instance))
.bearer_auth(&self.jwt_token.to_string())
.json(&params)
.send()
2023-12-17 21:29:18 +00:00
.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, CustomPostView>>(&response) {
Ok(mut data) => data.remove("post_view").expect("Element should be present"),
Err(e) => return Err(format!("{}", e))
};
Ok(json_data)
}
2023-12-17 21:29:18 +00:00
pub(crate) async fn unpin(&self, post_id: PostId, location: PostFeatureType) -> Result<CustomPostView, String> {
let pin_params = CustomFeaturePost {
post_id,
featured: false,
feature_type: location,
};
self.feature(pin_params).await
}
2023-12-17 21:29:18 +00:00
pub(crate) async fn pin(&self, post_id: PostId, location: PostFeatureType) -> Result<CustomPostView, String> {
let pin_params = CustomFeaturePost {
post_id,
featured: true,
feature_type: location,
};
self.feature(pin_params).await
}
2023-12-17 21:29:18 +00:00
pub(crate) async fn get_community_pinned(&self, community: CommunityId) -> Result<Vec<CustomPostView>, String> {
let list_params = GetPosts {
community_id: Some(community),
type_: Some(ListingType::Local),
..Default::default()
};
2023-12-17 21:29:18 +00:00
let response = match HTTP_CLIENT
.get(format!("{}/api/v3/post/list", &self.instance))
.bearer_auth(&self.jwt_token.to_string())
.query(&list_params)
.send()
2023-12-17 21:29:18 +00:00
.await {
Ok(data) => {
match data.text().await {
Ok(data) => data,
Err(e) => return Err(format!("{}", e))
}
},
Err(e) => return Err(format!("{}", e))
};
2023-12-17 21:29:18 +00:00
let json_data: CustomGetPostsResponse = 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()
)
}
2023-12-17 21:29:18 +00:00
pub(crate) async fn get_local_pinned(&self) -> Result<Vec<CustomPostView>, String> {
let list_params = GetPosts {
type_: Some(ListingType::Local),
..Default::default()
};
2023-12-17 21:29:18 +00:00
let response = match HTTP_CLIENT
.get(format!("{}/api/v3/post/list", &self.instance))
.bearer_auth(&self.jwt_token.to_string())
.query(&list_params)
.send()
2023-12-17 21:29:18 +00:00
.await {
Ok(data) => {
match data.text().await {
Ok(data) => data,
Err(e) => return Err(format!("{}", e))
}
},
Err(e) => return Err(format!("{}", e))
};
2023-12-17 21:29:18 +00:00
let json_data: CustomGetPostsResponse = 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()
)
}
2023-12-17 21:29:18 +00:00
pub(crate) async fn get_communities(&self) -> Result<HashMap<String, CommunityId>, String> {
let list_params = ListCommunities {
type_: Some(ListingType::Local),
..Default::default()
};
2023-12-17 21:29:18 +00:00
let response = match HTTP_CLIENT
.get(format!("{}/api/v3/community/list", &self.instance))
.bearer_auth(&self.jwt_token.to_string())
.query(&list_params)
.send()
2023-12-17 21:29:18 +00:00
.await {
Ok(data) => {
match data.text().await {
Ok(data) => data,
Err(e) => return Err(format!("{}", e))
}
},
Err(e) => return Err(format!("{}", e))
};
2023-12-17 21:29:18 +00:00
let json_data: CustomListCommunitiesResponse = 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);
}
2023-12-17 19:35:37 +00:00
Ok(communities)
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct CustomCreatePost {
pub(crate) name: String,
pub(crate) community_id: CommunityId,
pub(crate) url: Option<Url>,
pub(crate) body: Option<String>,
pub(crate) honeypot: Option<String>,
pub(crate) nsfw: Option<bool>,
pub(crate) language_id: Option<LanguageId>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct CustomFeaturePost {
pub(crate) post_id: PostId,
pub(crate) featured: bool,
pub(crate) feature_type: PostFeatureType,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct CustomListCommunitiesResponse {
pub(crate) communities: Vec<CustomCommunityView>
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomCommunityView {
pub(crate) community: CustomCommunity,
pub(crate) subscribed: SubscribedType,
pub(crate) blocked: bool,
pub(crate) counts: CustomCommunityAggregates
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomCommunity {
pub(crate) actor_id: String,
pub(crate) banner: Option<String>,
pub(crate) deleted: bool,
pub(crate) description: Option<String>,
pub(crate) hidden: bool,
pub(crate) icon: Option<String>,
pub(crate) id: CommunityId,
pub(crate) instance_id: i32,
pub(crate) local: bool,
pub(crate) name: String,
pub(crate) nsfw: bool,
pub(crate) posting_restricted_to_mods: bool,
pub(crate) published: String,
pub(crate) removed: bool,
pub(crate) title: String,
pub(crate) updated: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomCommunityAggregates {
pub(crate) comments: i64,
pub(crate) community_id: i32,
pub(crate) posts: i64,
pub(crate) published: String,
pub(crate) subscribers: i64,
pub(crate) users_active_day: i64,
pub(crate) users_active_half_year: i64,
pub(crate) users_active_month: i64,
pub(crate) users_active_week: i64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomPostView {
pub(crate) community: CustomCommunity,
pub(crate) counts: CustomPostAggregates,
pub(crate) creator: CustomPerson,
pub(crate) creator_banned_from_community: bool,
pub(crate) creator_blocked: bool,
pub(crate) creator_is_admin: bool,
pub(crate) creator_is_moderator: bool,
pub(crate) my_vote: Option<i16>,
pub(crate) post: CustomPost,
pub(crate) read: bool,
pub(crate) saved: bool,
pub(crate) subscribed: SubscribedType,
pub(crate) unread_comments: i64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomPostAggregates {
pub(crate) comments: i64,
pub(crate) downvotes: i64,
pub(crate) post_id: PostId,
pub(crate) published: String,
pub(crate) score: i64,
pub(crate) upvotes: i64,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomPerson {
pub(crate) actor_id: String,
pub(crate) avatar: Option<String>,
pub(crate) ban_expires: Option<String>,
pub(crate) banned: bool,
pub(crate) banner: Option<String>,
pub(crate) bio: Option<String>,
pub(crate) bot_account: bool,
pub(crate) deleted: bool,
pub(crate) display_name: Option<String>,
pub(crate) id: PersonId,
pub(crate) instance_id : InstanceId,
pub(crate) local: bool,
pub(crate) matrix_user_id: Option<String>,
pub(crate) name: String,
pub(crate) published: String,
pub(crate) updated: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomPost {
pub(crate) id: PostId,
pub(crate) name: String,
pub(crate) url: Option<String>,
pub(crate) body: Option<String>,
pub(crate) creator_id: PersonId,
pub(crate) community_id: CommunityId,
pub(crate) removed: bool,
pub(crate) locked: bool,
pub(crate) published: String,
pub(crate) updated: Option<String>,
pub(crate) deleted: bool,
pub(crate) nsfw: bool,
pub(crate) embed_title: Option<String>,
pub(crate) embed_description: Option<String>,
pub(crate) thumbnail_url: Option<String>,
pub(crate) ap_id: String,
pub(crate) local: bool,
pub(crate) embed_video_url: Option<String>,
pub(crate) language_id: LanguageId,
pub(crate) featured_community: bool,
pub(crate) featured_local: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct CustomGetPostsResponse {
pub(crate) posts: Vec<CustomPostView>,
}