From b94b0bfcc683633c75ae3399ffe4b5a5ac66b622 Mon Sep 17 00:00:00 2001 From: Chronophylos Date: Tue, 22 Nov 2022 13:10:35 +0100 Subject: [PATCH] add missing fields to guild channel Added `flags`, `total_messages_sent`, `available_tags`, `applied_tags`, `default_reaction_emoji`, `default_thread_rate_limit_per_user` and `default_sort_order` fields to `GuildChannel`. Added `ForumTag`, `ForumTagId`, `DefaultReaction` and `SortOrder` objects. Box the `Channel::Guild` variant to reduce enum size. --- src/cache/event.rs | 6 +- src/cache/mod.rs | 17 ++++-- src/model/channel/channel_id.rs | 2 +- src/model/channel/guild_channel.rs | 30 ++++++++- src/model/channel/mod.rs | 87 +++++++++++++++++++++++++-- src/model/guild/member.rs | 2 +- src/model/id.rs | 7 +++ src/model/mention.rs | 11 +++- src/utils/argument_convert/channel.rs | 6 +- src/utils/content_safe.rs | 7 +++ 10 files changed, 155 insertions(+), 20 deletions(-) diff --git a/src/cache/event.rs b/src/cache/event.rs index 44d23b5dedf..3be88d678c9 100644 --- a/src/cache/event.rs +++ b/src/cache/event.rs @@ -48,7 +48,7 @@ impl CacheUpdate for ChannelCreateEvent { .get_mut(&guild_id) .and_then(|mut g| g.channels.insert(channel_id, self.channel.clone())); - cache.channels.insert(channel_id, channel.clone()); + cache.channels.insert(channel_id, *channel.clone()); old_channel }, @@ -130,7 +130,7 @@ impl CacheUpdate for ChannelUpdateEvent { Channel::Guild(ref channel) => { let (guild_id, channel_id) = (channel.guild_id, channel.id); - cache.channels.insert(channel_id, channel.clone()); + cache.channels.insert(channel_id, *channel.clone()); cache .guilds @@ -194,7 +194,7 @@ impl CacheUpdate for GuildCreateEvent { for pair in guild.channels.clone() { if let Channel::Guild(channel) = pair.1 { - cache.channels.insert(pair.0, channel); + cache.channels.insert(pair.0, *channel); } } diff --git a/src/cache/mod.rs b/src/cache/mod.rs index 6039da88b6f..d873141f7e1 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -361,13 +361,13 @@ impl Cache { fn _channel(&self, id: ChannelId) -> Option { if let Some(channel) = self.channels.get(&id) { let channel = channel.clone(); - return Some(Channel::Guild(channel)); + return Some(Channel::Guild(Box::new(channel))); } #[cfg(feature = "temp_cache")] { if let Some(channel) = self.temp_channels.get(&id) { - return Some(Channel::Guild(channel)); + return Some(Channel::Guild(Box::new(channel))); } } @@ -699,7 +699,7 @@ impl Cache { g.channels .iter() .filter_map(|c| match c.1 { - Channel::Guild(channel) => Some((channel.id, channel.clone())), + Channel::Guild(channel) => Some((channel.id, *channel.clone())), _ => None, }) .collect() @@ -1120,7 +1120,7 @@ mod test { assert!(!channel.contains_key(&MessageId(3))); } - let channel = Channel::Guild(GuildChannel { + let channel = Channel::Guild(Box::new(GuildChannel { id: event.message.channel_id, bitrate: None, parent_id: None, @@ -1142,7 +1142,14 @@ mod test { thread_metadata: None, member: None, default_auto_archive_duration: None, - }); + flags: ChannelFlags::empty(), + total_message_sent: None, + available_tags: Vec::new(), + applied_tags: Vec::new(), + default_reaction_emoji: None, + default_thread_rate_limit_per_user: None, + default_sort_order: SortOrder::Unknown, + })); // Add a channel delete event to the cache, the cached messages for that // channel should now be gone. diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs index d3e7f9189fc..ccf7809be39 100644 --- a/src/model/channel/channel_id.rs +++ b/src/model/channel/channel_id.rs @@ -434,7 +434,7 @@ impl ChannelId { { if let Some(cache) = cache_http.cache() { if let Channel::Guild(guild_channel) = &channel { - cache.temp_channels.insert(guild_channel.id, guild_channel.clone()); + cache.temp_channels.insert(guild_channel.id, *guild_channel.clone()); } } } diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index 47f6f229689..12ad3728af8 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -100,7 +100,7 @@ pub struct GuildChannel { pub position: i64, /// The topic of the channel. /// - /// **Note**: This is only available for text and stage channels. + /// **Note**: This is only available for text, forum and stage channels. pub topic: Option, /// The maximum number of members allowed in the channel. /// @@ -149,6 +149,34 @@ pub struct GuildChannel { /// /// **Note**: It can currently only be set to 60, 1440, 4320, 10080. pub default_auto_archive_duration: Option, + /// Extra information about the channel + /// + /// **Note**: This is only available in forum channels. + pub flags: ChannelFlags, + /// The number of messages ever sent in a thread, it's similar to `message_count` + /// on message creation, but will not decrement the number when a message is deleted. + pub total_message_sent: Option, + /// The set of available tags. + /// + /// **Note**: This is only available in forum channels. + pub available_tags: Vec, + /// The set of applied tags. + /// + /// **Note**: This is only available in a thread in a forum. + pub applied_tags: Vec, + /// The emoji to show in the add reaction button + /// + /// **Note**: This is only available in a forum. + pub default_reaction_emoji: Option, + /// The initial `rate_limit_per_user` to set on newly created threads in a channel. + /// This field is copied to the thread at creation time and does not live update. + /// + /// **Note**: This is only available in a forum or text channel. + pub default_thread_rate_limit_per_user: Option, + /// The default sort order type used to order posts + /// + /// **Note**: This is only available in a forum. + pub default_sort_order: SortOrder, } #[cfg(feature = "model")] diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 3a5d5458a73..135a0176850 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -51,7 +51,7 @@ pub enum Channel { /// [voice]: ChannelType::Voice /// [stage]: ChannelType::Stage /// [directory]: ChannelType::Directory - Guild(GuildChannel), + Guild(Box), /// A private channel to another [`User`]. No other users may access the /// channel. For multi-user "private channels", use a group. Private(PrivateChannel), @@ -87,7 +87,7 @@ impl Channel { #[must_use] pub fn guild(self) -> Option { match self { - Self::Guild(lock) => Some(lock), + Self::Guild(lock) => Some(*lock), _ => None, } } @@ -240,6 +240,7 @@ impl<'de> Deserialize<'de> for Channel { match kind { 0 | 2 | 5 | 10 | 11 | 12 | 13 | 14 | 15 => from_value::(Value::from(v)) + .map(Box::new) .map(Channel::Guild) .map_err(DeError::custom), 1 => from_value::(Value::from(v)) @@ -449,7 +450,7 @@ pub enum VideoQualityMode { } enum_number!(VideoQualityMode { - Auto, + Auto Full }); @@ -539,6 +540,13 @@ mod test { thread_metadata: None, member: None, default_auto_archive_duration: None, + applied_tags: Vec::new(), + flags: ChannelFlags::empty(), + total_message_sent: None, + available_tags: Vec::new(), + default_reaction_emoji: None, + default_thread_rate_limit_per_user: None, + default_sort_order: SortOrder::Unknown, } } @@ -585,7 +593,7 @@ mod test { channel.nsfw = false; assert!(!channel.is_nsfw()); - let channel = Channel::Guild(channel); + let channel = Channel::Guild(Box::new(channel)); assert!(!channel.is_nsfw()); let private_channel = private_channel(); @@ -631,3 +639,74 @@ impl FromStrAndCache for Channel { } } } + +/// An object that represents a tag that is able to be applied to a thread in a `GUILD_FORUM` channel. +/// +/// See [Discord docs](https://discord.com/developers/docs/resources/channel#forum-tag-object) +#[derive(Debug, Clone, Serialize, Deserialize)] +#[non_exhaustive] +pub struct ForumTag { + /// The id of the tag. + pub id: ForumTagId, + /// The name of the tag (0-20 characters). + pub name: String, + /// Whether this tag can only be added to or removed from threads by a member with the MANAGE_THREADS permission. + pub moderated: bool, + /// The id of a guild's custom emoji. + /// + /// **Note**: At most one of `emoji_id` and `emoji_name` may be set. + pub emoji_id: Option, + /// The unicode character of the emoji. + /// + /// **Note**: At most one of `emoji_id` and `emoji_name` may be set. + pub emoji_name: Option, +} + +/// An object that specifies the emoji to use as the default way to react to a forum post. +/// +/// See [Discord docs](https://discord.com/developers/docs/resources/channel#forum-tag-object) +#[derive(Debug, Clone, Serialize, Deserialize)] +#[non_exhaustive] +pub struct DefaultReaction { + /// The id of a guild's custom emoji. + /// + /// **Note**: At most one of `emoji_id` and `emoji_name` may be set. + pub emoji_id: Option, + /// The unicode character of the emoji. + /// + /// **Note**: At most one of `emoji_id` and `emoji_name` may be set. + pub emoji_name: Option, +} + +/// The sort order for threads in a forum. +/// +/// [Discord docs](https://discord.com/developers/docs/resources/channel#channel-object-sort-order-types). +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[non_exhaustive] +pub enum SortOrder { + /// Sort forum posts by activity. + LatestActivity = 0, + /// Sort forum posts by creation time (from most recent to oldest). + CreationDate = 2, + /// No sort order has been set. + Unknown = !0, +} + +enum_number!(SortOrder { + LatestActivity + CreationDate +}); + +bitflags! { + /// Describes extra features of the channel. + /// + /// [Discord docs](https://discord.com/developers/docs/resources/channel#channel-object-channel-flags). + #[derive(Default)] + pub struct ChannelFlags: u64 { + /// This thread is pinned to the top of its parent GUILD_FORUM channel + const PINNED = 1 << 1; + /// Whether a tag is required to be specified when creating a + /// thread in a GUILD_FORUM channel. Tags are specified in the applied_tags field. + const REQUIRE_TAG = 1 << 4; + } +} diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index 3c986041ae5..c7441bb410e 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -234,7 +234,7 @@ impl Member { for channel in guild.channels.values() { if let Channel::Guild(channel) = channel { if guild.user_permissions_in(channel, member).ok()?.view_channel() { - return Some(channel.clone()); + return Some(*channel.clone()); } } } diff --git a/src/model/id.rs b/src/model/id.rs index 5f9a910e390..a5727b8042d 100644 --- a/src/model/id.rs +++ b/src/model/id.rs @@ -224,6 +224,12 @@ pub struct TargetId(#[serde(with = "snowflake")] pub u64); )] pub struct StageInstanceId(#[serde(with = "snowflake")] pub u64); +/// An identifier for a forum tag. +#[derive( + Copy, Clone, Default, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize, +)] +pub struct ForumTagId(#[serde(with = "snowflake")] pub u64); + id_u64! { AttachmentId; ApplicationId; @@ -249,6 +255,7 @@ id_u64! { TargetId; StageInstanceId; RuleId; + ForumTagId; } /// Used with `#[serde(with|deserialize_with|serialize_with)]` diff --git a/src/model/mention.rs b/src/model/mention.rs index b24a69513bc..79f45b27707 100644 --- a/src/model/mention.rs +++ b/src/model/mention.rs @@ -209,7 +209,7 @@ mod test { #[test] fn test_mention() { - let channel = Channel::Guild(GuildChannel { + let channel = Channel::Guild(Box::new(GuildChannel { bitrate: None, parent_id: None, guild_id: GuildId(1), @@ -231,7 +231,14 @@ mod test { thread_metadata: None, member: None, default_auto_archive_duration: None, - }); + flags: ChannelFlags::empty(), + total_message_sent: None, + available_tags: Vec::new(), + applied_tags: Vec::new(), + default_reaction_emoji: None, + default_thread_rate_limit_per_user: None, + default_sort_order: SortOrder::Unknown, + })); let emoji = Emoji { animated: false, available: true, diff --git a/src/utils/argument_convert/channel.rs b/src/utils/argument_convert/channel.rs index 5a8f40559cc..9035cfd0bb1 100644 --- a/src/utils/argument_convert/channel.rs +++ b/src/utils/argument_convert/channel.rs @@ -59,7 +59,7 @@ async fn lookup_channel_global( None } }) { - return Ok(Channel::Guild(channel)); + return Ok(Channel::Guild(Box::new(channel))); } if let Some(guild_id) = guild_id { @@ -67,7 +67,7 @@ async fn lookup_channel_global( if let Some(channel) = channels.into_iter().find(|channel| channel.name.eq_ignore_ascii_case(s)) { - return Ok(Channel::Guild(channel)); + return Ok(Channel::Guild(Box::new(channel))); } } @@ -155,7 +155,7 @@ impl ArgumentConvert for GuildChannel { s: &str, ) -> Result { match Channel::convert(ctx, guild_id, channel_id, s).await { - Ok(Channel::Guild(channel)) => Ok(channel), + Ok(Channel::Guild(channel)) => Ok(*channel), Ok(_) => Err(GuildChannelParseError::NotAGuildChannel), Err(ChannelParseError::Http(e)) => Err(GuildChannelParseError::Http(e)), Err(ChannelParseError::NotFoundOrMalformed) => { diff --git a/src/utils/content_safe.rs b/src/utils/content_safe.rs index 8f1fee053dd..8a988904bec 100644 --- a/src/utils/content_safe.rs +++ b/src/utils/content_safe.rs @@ -407,6 +407,13 @@ mod tests { thread_metadata: None, member: None, default_auto_archive_duration: None, + flags: ChannelFlags::empty(), + total_message_sent: None, + available_tags: Vec::new(), + applied_tags: Vec::new(), + default_reaction_emoji: None, + default_thread_rate_limit_per_user: None, + default_sort_order: SortOrder::Unknown, }; let cache = Arc::new(Cache::default());