From 2352034b82f3fea3560e0d73d5f9bf4b391964dc Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 21:55:24 +0200 Subject: [PATCH 1/8] Create Greet Message --- src/api/channels/messages.rs | 26 +++++++++++++++++++++++++- src/types/entities/message.rs | 6 +++--- src/types/schema/message.rs | 7 +++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 60d6a97..538cae6 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -8,7 +8,8 @@ use crate::errors::{ChorusError, ChorusResult}; use crate::instance::UserMeta; use crate::ratelimiter::ChorusRequest; use crate::types::{ - Channel, Message, MessageSearchEndpoint, MessageSearchQuery, MessageSendSchema, Snowflake, + Channel, CreateGreetMessage, Message, MessageSearchEndpoint, MessageSearchQuery, + MessageSendSchema, Snowflake, }; impl Message { @@ -224,6 +225,29 @@ impl Message { }; chorus_request.deserialize_response::(user).await } + + /// Posts a greet message to a channel. This endpoint requires the channel is a DM channel or you reply to a system message. + /// # Reference: + /// See: + pub async fn create_greet( + channel_id: Snowflake, + schema: CreateGreetMessage, + user: &mut UserMeta, + ) -> ChorusResult { + let chorus_request = ChorusRequest { + request: Client::new() + .post(format!( + "{}/channels/{}/messages/greet", + user.belongs_to.borrow().urls.api, + channel_id, + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json") + .body(to_string(&schema).unwrap()), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request.deserialize_response::(user).await + } } fn search_error(result_text: String) -> ChorusError { diff --git a/src/types/entities/message.rs b/src/types/entities/message.rs index 2522268..f59cdc5 100644 --- a/src/types/entities/message.rs +++ b/src/types/entities/message.rs @@ -104,7 +104,7 @@ impl PartialEq for Message { } } -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, Ord, PartialOrd)] /// # Reference /// See pub struct MessageReference { @@ -124,7 +124,7 @@ pub struct MessageInteraction { pub member: Option>>, } -#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Eq, PartialOrd, Ord)] pub struct AllowedMention { parse: Vec, roles: Vec, @@ -132,7 +132,7 @@ pub struct AllowedMention { replied_user: bool, } -#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)] #[serde(rename_all = "snake_case")] pub enum AllowedMentionType { Roles, diff --git a/src/types/schema/message.rs b/src/types/schema/message.rs index e0ee804..bf9cddc 100644 --- a/src/types/schema/message.rs +++ b/src/types/schema/message.rs @@ -96,3 +96,10 @@ impl std::default::Default for MessageSearchQuery { } } } + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct CreateGreetMessage { + pub sticker_ids: Vec, + pub allowed_mentions: Option, + pub message_reference: Option, +} From c11ab6f72744fe5ae8eda29abfdcd733a4bf7787 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 22:06:19 +0200 Subject: [PATCH 2/8] Add message acknowledge endpoint --- src/api/channels/messages.rs | 36 +++++++++++++++++++++++++++++++++++- src/types/schema/message.rs | 7 +++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 538cae6..36545ac 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -8,7 +8,7 @@ use crate::errors::{ChorusError, ChorusResult}; use crate::instance::UserMeta; use crate::ratelimiter::ChorusRequest; use crate::types::{ - Channel, CreateGreetMessage, Message, MessageSearchEndpoint, MessageSearchQuery, + Channel, CreateGreetMessage, Message, MessageAck, MessageSearchEndpoint, MessageSearchQuery, MessageSendSchema, Snowflake, }; @@ -248,6 +248,40 @@ impl Message { }; chorus_request.deserialize_response::(user).await } + + /// Sets the channel's latest acknowledged message (marks a message as read) for the current user. + /// The message ID parameter does not need to be a valid message ID, but it must be a valid snowflake. + /// If the message ID is being set to a message sent prior to the latest acknowledged one, + /// manual should be true or the resulting read state update should be ignored by clients (but is still saved), resulting in undefined behavior. + /// In this case, mention_count should also be set to the amount of mentions unacknowledged as it is not automatically calculated by Discord. + /// + /// Returns an optional token, which can be used as the new `ack` token for following `ack`s. + /// + /// # Reference: + /// See: + pub async fn acknowledge( + channel_id: Snowflake, + message_id: Snowflake, + schema: MessageAck, + user: &mut UserMeta, + ) -> ChorusResult> { + let chorus_request = ChorusRequest { + request: Client::new() + .post(format!( + "{}/channels/{}/messages/{}/ack", + user.belongs_to.borrow().urls.api, + channel_id, + message_id + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json") + .body(to_string(&schema).unwrap()), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request + .deserialize_response::>(user) + .await + } } fn search_error(result_text: String) -> ChorusError { diff --git a/src/types/schema/message.rs b/src/types/schema/message.rs index bf9cddc..7603555 100644 --- a/src/types/schema/message.rs +++ b/src/types/schema/message.rs @@ -103,3 +103,10 @@ pub struct CreateGreetMessage { pub allowed_mentions: Option, pub message_reference: Option, } + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct MessageAck { + pub token: Option, + pub manual: Option, + pub mention_count: Option, +} From 41bac67d67904dc794c4e8f39ce0eb87a29d53e6 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 22:10:12 +0200 Subject: [PATCH 3/8] Add crosspost endpoint --- src/api/channels/messages.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 36545ac..9a6687d 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -282,6 +282,29 @@ impl Message { .deserialize_response::>(user) .await } + + /// Crossposts a message in a News Channel to following channels. + /// This endpoint requires the `SEND_MESSAGES` permission, if the current user sent the message, + /// or additionally the `MANAGE_MESSAGES` permission, for all other messages, to be present for the current user. + pub async fn crosspost( + channel_id: Snowflake, + message_id: Snowflake, + user: &mut UserMeta, + ) -> ChorusResult { + let chorus_request = ChorusRequest { + request: Client::new() + .post(format!( + "{}/channels/{}/messages/{}/crosspost", + user.belongs_to.borrow().urls.api, + channel_id, + message_id + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json"), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request.deserialize_response::(user).await + } } fn search_error(result_text: String) -> ChorusError { From c9562457d2d90279fba4a735912ba58ee1856a1b Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 22:16:07 +0200 Subject: [PATCH 4/8] Add Hide from guild feed endpoint --- src/api/channels/messages.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 9a6687d..1eec276 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -286,6 +286,9 @@ impl Message { /// Crossposts a message in a News Channel to following channels. /// This endpoint requires the `SEND_MESSAGES` permission, if the current user sent the message, /// or additionally the `MANAGE_MESSAGES` permission, for all other messages, to be present for the current user. + /// + /// # Reference: + /// See pub async fn crosspost( channel_id: Snowflake, message_id: Snowflake, @@ -305,6 +308,30 @@ impl Message { }; chorus_request.deserialize_response::(user).await } + + /// Hides a message from the feed of the guild the channel belongs to. Returns a 204 empty response on success. + /// + /// # Reference: + /// See + pub async fn hide_from_guild_feed( + channel_id: Snowflake, + message_id: Snowflake, + user: &mut UserMeta, + ) -> ChorusResult<()> { + let chorus_request = ChorusRequest { + request: Client::new() + .delete(format!( + "{}/channels/{}/messages/{}/hide-guild-feed", + user.belongs_to.borrow().urls.api, + channel_id, + message_id + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json"), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request.handle_request_as_result(user).await + } } fn search_error(result_text: String) -> ChorusError { From e8a02e57f8dae6ff9ab4ae03c2009235c0f42fbf Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 22:27:09 +0200 Subject: [PATCH 5/8] Add Modify Message endpoint --- src/api/channels/messages.rs | 36 ++++++++++++++++++++++-- src/types/entities/attachment.rs | 2 +- src/types/entities/emoji.rs | 47 ++++++++++++++++++++++++++++++++ src/types/entities/message.rs | 20 +++++++------- src/types/schema/message.rs | 15 +++++++++- 5 files changed, 106 insertions(+), 14 deletions(-) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 1eec276..05680b9 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -8,8 +8,8 @@ use crate::errors::{ChorusError, ChorusResult}; use crate::instance::UserMeta; use crate::ratelimiter::ChorusRequest; use crate::types::{ - Channel, CreateGreetMessage, Message, MessageAck, MessageSearchEndpoint, MessageSearchQuery, - MessageSendSchema, Snowflake, + Channel, CreateGreetMessage, Message, MessageAck, MessageModifySchema, MessageSearchEndpoint, + MessageSearchQuery, MessageSendSchema, Snowflake, }; impl Message { @@ -332,6 +332,38 @@ impl Message { }; chorus_request.handle_request_as_result(user).await } + + /// Edits a previously sent message. All fields can be edited by the original message author. + /// Other users can only edit flags and only if they have the MANAGE_MESSAGES permission in the corresponding channel. + /// When specifying flags, ensure to include all previously set flags/bits in addition to ones that you are modifying. + /// When the content field is edited, the mentions array in the message object will be reconstructed from scratch based on the new content. + /// The allowed_mentions field of the edit request controls how this happens. + /// If there is no explicit allowed_mentions in the edit request, the content will be parsed with default allowances, that is, + /// without regard to whether or not an allowed_mentions was present in the request that originally created the message. + /// + /// # Reference: + /// See: + pub async fn modify( + channel_id: Snowflake, + message_id: Snowflake, + schema: MessageModifySchema, + user: &mut UserMeta, + ) -> ChorusResult { + let chorus_request = ChorusRequest { + request: Client::new() + .patch(format!( + "{}/channels/{}/messages/{}", + user.belongs_to.borrow().urls.api, + channel_id, + message_id + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json") + .body(to_string(&schema).unwrap()), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request.deserialize_response::(user).await + } } fn search_error(result_text: String) -> ChorusError { diff --git a/src/types/entities/attachment.rs b/src/types/entities/attachment.rs index 59ad53d..ffbc520 100644 --- a/src/types/entities/attachment.rs +++ b/src/types/entities/attachment.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::types::utils::Snowflake; -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, PartialOrd)] #[cfg_attr(feature = "sqlx", derive(sqlx::FromRow))] /// # Reference /// See diff --git a/src/types/entities/emoji.rs b/src/types/entities/emoji.rs index 68700e6..fddef8c 100644 --- a/src/types/entities/emoji.rs +++ b/src/types/entities/emoji.rs @@ -26,3 +26,50 @@ pub struct Emoji { pub animated: Option, pub available: Option, } + +impl PartialEq for Emoji { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + && self.name == other.name + && self.roles == other.roles + && self.roles == other.roles + && self.require_colons == other.require_colons + && self.managed == other.managed + && self.animated == other.animated + && self.available == other.available + } +} + +impl PartialOrd for Emoji { + fn partial_cmp(&self, other: &Self) -> Option { + match self.id.partial_cmp(&other.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.roles.partial_cmp(&other.roles) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.roles.partial_cmp(&other.roles) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.require_colons.partial_cmp(&other.require_colons) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.managed.partial_cmp(&other.managed) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + match self.animated.partial_cmp(&other.animated) { + Some(core::cmp::Ordering::Equal) => {} + ord => return ord, + } + self.available.partial_cmp(&other.available) + } +} diff --git a/src/types/entities/message.rs b/src/types/entities/message.rs index f59cdc5..27a8857 100644 --- a/src/types/entities/message.rs +++ b/src/types/entities/message.rs @@ -149,7 +149,7 @@ pub struct ChannelMention { name: String, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd)] pub struct Embed { title: Option, #[serde(rename = "type")] @@ -167,14 +167,14 @@ pub struct Embed { fields: Option>, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct EmbedFooter { text: String, icon_url: Option, proxy_icon_url: Option, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)] pub struct EmbedImage { url: String, proxy_url: String, @@ -182,7 +182,7 @@ pub struct EmbedImage { width: Option, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)] pub struct EmbedThumbnail { url: String, proxy_url: Option, @@ -190,7 +190,7 @@ pub struct EmbedThumbnail { width: Option, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)] struct EmbedVideo { url: Option, proxy_url: Option, @@ -198,13 +198,13 @@ struct EmbedVideo { width: Option, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)] pub struct EmbedProvider { name: Option, url: Option, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)] pub struct EmbedAuthor { name: String, url: Option, @@ -212,14 +212,14 @@ pub struct EmbedAuthor { proxy_icon_url: Option, } -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, PartialOrd, Ord)] pub struct EmbedField { name: String, value: String, inline: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialOrd, PartialEq)] pub struct Reaction { pub count: u32, pub burst_count: u32, @@ -229,7 +229,7 @@ pub struct Reaction { pub emoji: Emoji, } -#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize, Eq, PartialOrd, Ord)] pub enum Component { ActionRow = 1, Button = 2, diff --git a/src/types/schema/message.rs b/src/types/schema/message.rs index 7603555..d430e2a 100644 --- a/src/types/schema/message.rs +++ b/src/types/schema/message.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::types::entities::{ AllowedMention, Component, Embed, MessageReference, PartialDiscordFileAttachment, }; -use crate::types::Snowflake; +use crate::types::{Attachment, Snowflake}; #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)] #[serde(rename_all = "snake_case")] @@ -110,3 +110,16 @@ pub struct MessageAck { pub manual: Option, pub mention_count: Option, } + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, PartialOrd)] +pub struct MessageModifySchema { + content: Option, + embeds: Option>, + embed: Option, + allowed_mentions: Option, + components: Option>, + flags: Option, + files: Option>, + payload_json: Option, + attachments: Option>, +} From 10109211a602bec9ad7e4ff0862e0784ffa1a179 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 22:32:48 +0200 Subject: [PATCH 6/8] add delete and bulk_delete endpoints --- src/api/channels/messages.rs | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 05680b9..c0f6d7e 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -364,6 +364,62 @@ impl Message { }; chorus_request.deserialize_response::(user).await } + + /// Deletes a message. If operating on a guild channel and trying to delete a message that was not sent by the current user, + /// this endpoint requires the `MANAGE_MESSAGES` permission. Returns a 204 empty response on success. + pub async fn delete( + channel_id: Snowflake, + message_id: Snowflake, + user: &mut UserMeta, + ) -> ChorusResult<()> { + let chorus_request = ChorusRequest { + request: Client::new() + .delete(format!( + "{}/channels/{}/messages/{}", + user.belongs_to.borrow().urls.api, + channel_id, + message_id + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json"), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request.handle_request_as_result(user).await + } + + /// Deletes multiple messages in a single request. This endpoint can only be used on guild channels and requires the MANAGE_MESSAGES permission. + /// Returns a 204 empty response on success. + /// + /// **This endpoint will not delete messages older than 2 weeks, and will fail if any message provided is older than that or if any duplicate message IDs are provided.** + /// + /// **This endpoint is not usable by user accounts.** (At least according to Discord.com. Spacebar behaviour may differ.) + /// + /// # Reference: + /// See: + pub async fn bulk_delete( + channel_id: Snowflake, + messages: Vec, + user: &mut UserMeta, + ) -> ChorusResult<()> { + if messages.len() < 2 { + return Err(ChorusError::InvalidArguments { + error: "`messages` must contain at least 2 entries.".to_string(), + }); + } + let chorus_request = ChorusRequest { + request: Client::new() + .post(format!( + "{}/channels/{}/messages/bulk-delete", + user.belongs_to.borrow().urls.api, + channel_id, + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json") + .body(to_string(&messages).unwrap()), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request.handle_request_as_result(user).await + } } fn search_error(result_text: String) -> ChorusError { From d6de9812546245524e5739a91b28b72e60b63633 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 22:34:37 +0200 Subject: [PATCH 7/8] Add acknowledge_pinned endpoint --- src/api/channels/messages.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index c0f6d7e..053ee75 100644 --- a/src/api/channels/messages.rs +++ b/src/api/channels/messages.rs @@ -420,6 +420,28 @@ impl Message { }; chorus_request.handle_request_as_result(user).await } + + /// Acknowledges the currently pinned messages in a channel. Returns a 204 empty response on success. + /// + /// # Reference: + /// See: + pub async fn acknowledge_pinned( + channel_id: Snowflake, + user: &mut UserMeta, + ) -> ChorusResult<()> { + let chorus_request = ChorusRequest { + request: Client::new() + .post(format!( + "{}/channels/{}/pins/ack", + user.belongs_to.borrow().urls.api, + channel_id, + )) + .header("Authorization", user.token()) + .header("Content-Type", "application/json"), + limit_type: LimitType::Channel(channel_id), + }; + chorus_request.handle_request_as_result(user).await + } } fn search_error(result_text: String) -> ChorusError { From 3006a6d860b857f055ae2542d51f464ab2293329 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Tue, 22 Aug 2023 22:40:36 +0200 Subject: [PATCH 8/8] Fix clippy lint --- src/types/entities/emoji.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/types/entities/emoji.rs b/src/types/entities/emoji.rs index fddef8c..d80e487 100644 --- a/src/types/entities/emoji.rs +++ b/src/types/entities/emoji.rs @@ -29,14 +29,13 @@ pub struct Emoji { impl PartialEq for Emoji { fn eq(&self, other: &Self) -> bool { - self.id == other.id - && self.name == other.name - && self.roles == other.roles - && self.roles == other.roles - && self.require_colons == other.require_colons - && self.managed == other.managed - && self.animated == other.animated - && self.available == other.available + !(self.id != other.id + || self.name != other.name + || self.roles != other.roles + || self.require_colons != other.require_colons + || self.managed != other.managed + || self.animated != other.animated + || self.available != other.available) } }