From 9db9d2a51238db26b3685efd1246ab1fc89b60b3 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Wed, 23 Aug 2023 00:08:43 +0200 Subject: [PATCH 1/5] Add get user guilds endpoint --- src/api/users/guilds.rs | 27 ++++++++++++++++++++++++++- src/types/schema/guild.rs | 21 +++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/api/users/guilds.rs b/src/api/users/guilds.rs index 355f38c..e4ffbe0 100644 --- a/src/api/users/guilds.rs +++ b/src/api/users/guilds.rs @@ -1,10 +1,11 @@ use reqwest::Client; use serde_json::to_string; +use crate::api::LimitType; use crate::errors::ChorusResult; use crate::instance::UserMeta; use crate::ratelimiter::ChorusRequest; -use crate::types::Snowflake; +use crate::types::{GetUserGuildSchema, Guild, Snowflake}; impl UserMeta { /// Leaves a given guild. @@ -30,4 +31,28 @@ impl UserMeta { .handle_request_as_result(self) .await } + + /// Returns a list of user guild objects representing the guilds the current user is a member of. + /// This endpoint returns 200 guilds by default + /// + /// # Reference: + /// See: + pub async fn get_guilds( + &mut self, + query: Option, + ) -> ChorusResult> { + let url = format!("{}/users/@me/guilds", self.belongs_to.borrow().urls.api,); + let chorus_request = ChorusRequest { + request: Client::new() + .get(url) + .header("Authorization", self.token()) + .header("Content-Type", "application/json") + .body(to_string(&query).unwrap()), + + limit_type: LimitType::Global, + }; + chorus_request + .deserialize_response::>(self) + .await + } } diff --git a/src/types/schema/guild.rs b/src/types/schema/guild.rs index 937c439..75452df 100644 --- a/src/types/schema/guild.rs +++ b/src/types/schema/guild.rs @@ -1,3 +1,5 @@ +use std::default; + use serde::{Deserialize, Serialize}; use crate::types::entities::Channel; @@ -56,3 +58,22 @@ pub struct GuildModifySchema { pub preferred_locale: Option, pub premium_progress_bar_enabled: Option, } + +#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub struct GetUserGuildSchema { + pub before: Option, + pub after: Option, + pub limit: Option, + pub with_counts: Option, +} + +impl std::default::Default for GetUserGuildSchema { + fn default() -> Self { + Self { + before: Default::default(), + after: Default::default(), + limit: Some(200), + with_counts: Some(false), + } + } +} From 5ed2f0f2c315c2ff378dfc29376cb0dd67dfd870 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Thu, 24 Aug 2023 21:05:39 +0200 Subject: [PATCH 2/5] add get guild preview route --- src/api/guilds/guilds.rs | 29 +++++++++++++++- src/lib.rs | 9 +++++ src/types/entities/sticker.rs | 64 ++++++++++++++++++++++++++++++++++- src/types/schema/guild.rs | 20 +++++++++-- 4 files changed, 118 insertions(+), 4 deletions(-) diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 85a18b7..389c08d 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -8,7 +8,8 @@ use crate::errors::ChorusResult; use crate::instance::UserMeta; use crate::ratelimiter::ChorusRequest; use crate::types::{ - Channel, ChannelCreateSchema, Guild, GuildBanCreateSchema, GuildCreateSchema, GuildModifySchema, + Channel, ChannelCreateSchema, Guild, GuildBanCreateSchema, GuildCreateSchema, + GuildModifySchema, GuildPreview, MFALevel, }; use crate::types::{GuildBan, Snowflake}; @@ -179,12 +180,38 @@ impl Guild { guild_id, )) .header("Authorization", user.token()) + .header("Content-Type", "application/json") .body(to_string(&schema).unwrap()), limit_type: LimitType::Guild(guild_id), }; let response = chorus_request.deserialize_response::(user).await?; Ok(response) } + + /// Returns a guild preview object for the given guild ID. If the user is not in the guild, the guild must be discoverable. + /// # Reference: + /// + /// See + pub async fn get_preview( + guild_id: Snowflake, + user: &mut UserMeta, + ) -> ChorusResult { + let chorus_request = ChorusRequest { + request: Client::new() + .patch(format!( + "{}/guilds/{}/preview", + user.belongs_to.borrow().urls.api, + guild_id, + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json"), + limit_type: LimitType::Guild(guild_id), + }; + let response = chorus_request + .deserialize_response::(user) + .await?; + Ok(response) + } } impl Channel { diff --git a/src/lib.rs b/src/lib.rs index 6563bed..7a957dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,6 +74,15 @@ impl UrlBundle { } } +/// Unwraps an `Option`. Returns an empty string if the String is `None`, or the String contents +/// if it is `Some`. +pub(crate) fn unwrap_empty_if_none(string: Option) -> String { + match string { + Some(str) => str, + None => "".to_string(), + } +} + #[cfg(test)] mod lib { use super::*; diff --git a/src/types/entities/sticker.rs b/src/types/entities/sticker.rs index 5413112..c2cdb46 100644 --- a/src/types/entities/sticker.rs +++ b/src/types/entities/sticker.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use crate::types::{entities::User, utils::Snowflake}; -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, Default)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] /// Represents a sticker that can be sent in messages. /// @@ -28,6 +28,68 @@ pub struct Sticker { pub sort_value: Option, } +impl PartialEq for Sticker { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + && self.pack_id == other.pack_id + && self.name == other.name + && self.description == other.description + && self.tags == other.tags + && self.asset == other.asset + && self.sticker_type == other.sticker_type + && self.format_type == other.format_type + && self.available == other.available + && self.guild_id == other.guild_id + && self.sort_value == other.sort_value + } +} + +impl PartialOrd for Sticker { + fn partial_cmp(&self, other: &Self) -> Option { + match self.id.partial_cmp(&other.id) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.pack_id.partial_cmp(&other.pack_id) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.name.partial_cmp(&other.name) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.description.partial_cmp(&other.description) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.tags.partial_cmp(&other.tags) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.asset.partial_cmp(&other.asset) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.sticker_type.partial_cmp(&other.sticker_type) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.format_type.partial_cmp(&other.format_type) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.available.partial_cmp(&other.available) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.guild_id.partial_cmp(&other.guild_id) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + self.sort_value.partial_cmp(&other.sort_value) + } +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] /// A partial sticker object. /// diff --git a/src/types/schema/guild.rs b/src/types/schema/guild.rs index 75452df..2622796 100644 --- a/src/types/schema/guild.rs +++ b/src/types/schema/guild.rs @@ -5,8 +5,8 @@ use serde::{Deserialize, Serialize}; use crate::types::entities::Channel; use crate::types::types::guild_configuration::GuildFeatures; use crate::types::{ - ExplicitContentFilterLevel, MessageNotificationLevel, Snowflake, SystemChannelFlags, - VerificationLevel, + Emoji, ExplicitContentFilterLevel, MessageNotificationLevel, Snowflake, Sticker, + SystemChannelFlags, VerificationLevel, }; #[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] @@ -77,3 +77,19 @@ impl std::default::Default for GetUserGuildSchema { } } } + +#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, PartialOrd)] +pub struct GuildPreview { + pub id: Snowflake, + pub name: String, + pub icon: Option, + pub description: Option, + pub splash: Option, + pub discovery_splash: Option, + pub home_header: Option, + pub features: Vec, + pub emojis: Vec, + pub stickers: Vec, + pub approximate_member_count: u32, + pub approximate_presence_count: u32, +} From eb163d278a4e903e2e365f29b4bd30aed0c47aab Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Thu, 24 Aug 2023 21:46:02 +0200 Subject: [PATCH 3/5] Change UserMeta to ChorusUser --- src/api/guilds/guilds.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 48bbf7a..370922a 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -194,7 +194,7 @@ impl Guild { /// See pub async fn get_preview( guild_id: Snowflake, - user: &mut UserMeta, + user: &mut ChorusUser, ) -> ChorusResult { let chorus_request = ChorusRequest { request: Client::new() From 0d93ad8578590b0b5174382ef72da21cbf5284fd Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Fri, 25 Aug 2023 01:35:49 +0200 Subject: [PATCH 4/5] Remove unneeded function --- src/lib.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7a957dd..6563bed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,15 +74,6 @@ impl UrlBundle { } } -/// Unwraps an `Option`. Returns an empty string if the String is `None`, or the String contents -/// if it is `Some`. -pub(crate) fn unwrap_empty_if_none(string: Option) -> String { - match string { - Some(str) => str, - None => "".to_string(), - } -} - #[cfg(test)] mod lib { use super::*; From 516a1467fa4e37335c459fcf284e75d418517d17 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Fri, 25 Aug 2023 01:35:57 +0200 Subject: [PATCH 5/5] Add some more routes --- src/api/guilds/guilds.rs | 86 ++++++++++++++++++++++++++++++++++++++- src/types/schema/guild.rs | 17 +++++++- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/api/guilds/guilds.rs b/src/api/guilds/guilds.rs index 09afe79..e59f3bc 100644 --- a/src/api/guilds/guilds.rs +++ b/src/api/guilds/guilds.rs @@ -8,8 +8,8 @@ use crate::errors::ChorusResult; use crate::instance::ChorusUser; use crate::ratelimiter::ChorusRequest; use crate::types::{ - Channel, ChannelCreateSchema, Guild, GuildBanCreateSchema, GuildCreateSchema, - GuildModifySchema, GuildPreview, MFALevel, + Channel, ChannelCreateSchema, Guild, GuildBanCreateSchema, GuildCreateSchema, GuildMember, + GuildMemberSearchSchema, GuildModifySchema, GuildPreview, }; use crate::types::{GuildBan, Snowflake}; @@ -213,6 +213,88 @@ impl Guild { .await?; Ok(response) } + + /// Returns a list of guild member objects that are members of the guild. + /// + /// # Reference + /// See + pub async fn get_members( + guild_id: Snowflake, + user: &mut ChorusUser, + ) -> ChorusResult> { + let request = ChorusRequest::new( + http::Method::GET, + format!( + "{}/guilds/{}/members", + user.belongs_to.borrow().urls.api, + guild_id, + ) + .as_str(), + None, + None, + None, + Some(user), + LimitType::Guild(guild_id), + ); + request.deserialize_response::>(user).await + } + + /// Returns a list of guild member objects whose username or nickname starts with a provided string. + /// + /// # Reference: + /// See + pub async fn search_members( + guild_id: Snowflake, + query: GuildMemberSearchSchema, + user: &mut ChorusUser, + ) -> ChorusResult> { + let mut request = ChorusRequest::new( + http::Method::GET, + format!( + "{}/guilds/{}/members/search", + user.belongs_to.borrow().urls.api, + guild_id, + ) + .as_str(), + None, + None, + None, + Some(user), + LimitType::Guild(guild_id), + ); + request.request = request + .request + .query(&[("query", to_string(&query).unwrap())]); + request.deserialize_response::>(user).await + } + + /// Removes a member from a guild. Requires the KICK_MEMBERS permission. Returns a 204 empty response on success. + /// + /// # Reference + /// See + pub async fn remove_member( + guild_id: Snowflake, + member_id: Snowflake, + audit_log_reason: Option, + user: &mut ChorusUser, + ) -> ChorusResult<()> { + let request = ChorusRequest::new( + http::Method::DELETE, + format!( + "{}/guilds/{}/members/{}", + user.belongs_to.borrow().urls.api, + guild_id, + member_id, + ) + .as_str(), + None, + audit_log_reason.as_deref(), + None, + Some(user), + LimitType::Guild(guild_id), + ); + request.handle_request_as_result(user).await + } } impl Channel { diff --git a/src/types/schema/guild.rs b/src/types/schema/guild.rs index 2622796..34ced20 100644 --- a/src/types/schema/guild.rs +++ b/src/types/schema/guild.rs @@ -1,5 +1,3 @@ -use std::default; - use serde::{Deserialize, Serialize}; use crate::types::entities::Channel; @@ -93,3 +91,18 @@ pub struct GuildPreview { pub approximate_member_count: u32, pub approximate_presence_count: u32, } + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd, Eq, Ord)] +pub struct GuildMemberSearchSchema { + pub query: String, + pub limit: Option, +} + +impl Default for GuildMemberSearchSchema { + fn default() -> Self { + Self { + query: Default::default(), + limit: Some(1), + } + } +}