diff --git a/src/api/channels/messages.rs b/src/api/channels/messages.rs index 60d6a97..053ee75 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, MessageAck, MessageModifySchema, MessageSearchEndpoint, + MessageSearchQuery, MessageSendSchema, Snowflake, }; impl Message { @@ -224,6 +225,223 @@ 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 + } + + /// 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 + } + + /// 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, + 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 + } + + /// 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 { 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..d80e487 100644 --- a/src/types/entities/emoji.rs +++ b/src/types/entities/emoji.rs @@ -26,3 +26,49 @@ 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.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 2522268..27a8857 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, @@ -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 e0ee804..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")] @@ -96,3 +96,30 @@ 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, +} + +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct MessageAck { + pub token: Option, + 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>, +}