diff options
| author | acdenisSK <[email protected]> | 2017-09-09 10:53:44 +0200 |
|---|---|---|
| committer | acdenisSK <[email protected]> | 2017-09-09 10:56:29 +0200 |
| commit | 4be6b9d5008ff8bb3d1fdddff5647a6bb307513c (patch) | |
| tree | 516b811875ae4cb2b98c308aabb69bc6e7a94fd2 /src | |
| parent | Change order to avoid subtraction overflow error (#160) (diff) | |
| download | serenity-4be6b9d5008ff8bb3d1fdddff5647a6bb307513c.tar.xz serenity-4be6b9d5008ff8bb3d1fdddff5647a6bb307513c.zip | |
Implement categories
Diffstat (limited to 'src')
| -rw-r--r-- | src/cache/cache_events_impl.rs | 10 | ||||
| -rw-r--r-- | src/cache/mod.rs | 8 | ||||
| -rw-r--r-- | src/client/dispatch.rs | 13 | ||||
| -rw-r--r-- | src/client/event_handler.rs | 2 | ||||
| -rw-r--r-- | src/model/channel/channel_category.rs | 121 | ||||
| -rw-r--r-- | src/model/channel/channel_id.rs | 4 | ||||
| -rw-r--r-- | src/model/channel/guild_channel.rs | 2 | ||||
| -rw-r--r-- | src/model/channel/mod.rs | 22 | ||||
| -rw-r--r-- | src/model/misc.rs | 1 | ||||
| -rw-r--r-- | src/model/utils.rs | 6 |
10 files changed, 187 insertions, 2 deletions
diff --git a/src/cache/cache_events_impl.rs b/src/cache/cache_events_impl.rs index 23fa314..64dbfde 100644 --- a/src/cache/cache_events_impl.rs +++ b/src/cache/cache_events_impl.rs @@ -112,6 +112,9 @@ impl CacheEventsImpl for super::Cache { let ch = self.private_channels.insert(id, channel.clone()); ch.map(Channel::Private) }, + Channel::Category(ref category) => { + self.categories.insert(category.read().unwrap().id, category.clone()).map(Channel::Category) + }, } } @@ -123,6 +126,8 @@ impl CacheEventsImpl for super::Cache { // anymore. Channel::Private(_) | Channel::Group(_) => unreachable!(), + // TODO: Fix this later. + Channel::Category(_) => unreachable!(), }; let (guild_id, channel_id) = channel.with(|channel| (channel.guild_id, channel.id)); @@ -219,6 +224,11 @@ impl CacheEventsImpl for super::Cache { .get_mut(&channel.read().unwrap().id) .map(|private| private.clone_from(channel)); }, + Channel::Category(ref category) => { + self.categories + .get_mut(&category.read().unwrap().id) + .map(|c| c.clone_from(category)); + }, } } diff --git a/src/cache/mod.rs b/src/cache/mod.rs index b92e72f..3a53262 100644 --- a/src/cache/mod.rs +++ b/src/cache/mod.rs @@ -76,6 +76,8 @@ pub struct Cache { /// [`Event::GuildUnavailable`]: ../model/event/struct.GuildUnavailableEvent.html /// [`Guild`]: ../model/struct.Guild.html pub channels: HashMap<ChannelId, Arc<RwLock<GuildChannel>>>, + /// A map of channel categories. + pub categories: HashMap<ChannelId, Arc<RwLock<ChannelCategory>>>, /// A map of the groups that the current user is in. /// /// For bot users this will always be empty, except for in [special cases]. @@ -593,6 +595,11 @@ impl Cache { self.users.get(&user_id.into()).cloned() } + #[inline] + pub fn categories<C: Into<ChannelId>>(&self, channel_id: C) -> Option<Arc<RwLock<ChannelCategory>>> { + self.categories.get(&channel_id.into()).cloned() + } + fn update_user_entry(&mut self, user: &User) { match self.users.entry(user.id) { Entry::Vacant(e) => { @@ -609,6 +616,7 @@ impl Default for Cache { fn default() -> Cache { Cache { channels: HashMap::default(), + categories: HashMap::default(), groups: HashMap::with_capacity(128), guilds: HashMap::default(), notes: HashMap::default(), diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index d5509b5..428c11e 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -164,6 +164,12 @@ fn handle_event<H: EventHandler + 'static>(event: Event, Ok(()) }); }, + Channel::Category(channel) => { + tokio_handle.spawn_fn(move || { + h.on_category_create(context, channel); + Ok(()) + }); + }, } }, Event::ChannelDelete(event) => { @@ -181,6 +187,13 @@ fn handle_event<H: EventHandler + 'static>(event: Event, Ok(()) }); }, + Channel::Category(channel) => { + let h = event_handler.clone(); + tokio_handle.spawn_fn(move || { + h.on_category_delete(context, channel); + Ok(()) + }); + }, } }, Event::ChannelPinsUpdate(event) => { diff --git a/src/client/event_handler.rs b/src/client/event_handler.rs index 03a2f2e..6e3c78e 100644 --- a/src/client/event_handler.rs +++ b/src/client/event_handler.rs @@ -11,6 +11,8 @@ pub trait EventHandler { #[cfg(feature = "cache")] fn on_cached(&self, _: Context, _: Vec<GuildId>) {} fn on_channel_create(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {} + fn on_category_create(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {} + fn on_category_delete(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {} fn on_private_channel_create(&self, _: Context, _: Arc<RwLock<PrivateChannel>>) {} fn on_channel_delete(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {} fn on_channel_pins_update(&self, _: Context, _: ChannelPinsUpdateEvent) {} diff --git a/src/model/channel/channel_category.rs b/src/model/channel/channel_category.rs new file mode 100644 index 0000000..f8374fc --- /dev/null +++ b/src/model/channel/channel_category.rs @@ -0,0 +1,121 @@ +use model::*; +use builder::EditChannel; +use http; +use utils as serenity_utils; + +/// A category of [`GuildChannel`]s. +/// +/// [`GuildChannel`]: struct.GuildChannel.html +#[derive(Clone, Debug, Deserialize)] +pub struct ChannelCategory { + /// Id of this category. + pub id: ChannelId, + /// If this category belongs to another category. + pub parent_id: Option<ChannelId>, + /// The position of this category. + pub position: i64, + /// Indicator of the type of channel this is. + /// + /// This should always be [`ChannelType::Category`]. + /// + /// [`ChannelType::Category`]: enum.ChannelType.html#variant.Category + pub kind: ChannelType, + /// The name of the category. + pub name: String, + /// Whether this category is nsfw. (This'll be inherited by all channels in this category) + #[serde(default)] + pub nsfw: bool, + /// Permission overwrites for the [`GuildChannel`]s. + /// + /// [`GuildChannel`]: struct.GuildChannel.html + pub permission_overwrites: Vec<PermissionOverwrite>, +} + +impl ChannelCategory { + /// Adds a permission overwrite to the category's channels. + #[inline] + pub fn create_permission(&self, target: &PermissionOverwrite) -> Result<()> { + self.id.create_permission(target) + } + + /// Deletes all permission overrides in the category from the channels. + /// + /// **Note**: Requires the [Manage Channel] permission. + /// + /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html + #[inline] + pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> { + self.id.delete_permission(permission_type) + } + + /// Deletes this category. + #[inline] + pub fn delete(&self) -> Result<()> { + #[cfg(feature = "cache")] + { + let req = permissions::MANAGE_CHANNELS; + + if !utils::user_has_perms(self.id, req)? { + return Err(Error::Model(ModelError::InvalidPermissions(req))); + } + } + + self.id.delete().map(|_| ()) + } + + /// Modifies the category's settings, such as its position or name. + /// + /// Refer to `EditChannel`s documentation for a full list of methods. + /// + /// # Examples + /// + /// Change a voice channels name and bitrate: + /// + /// ```rust,ignore + /// category.edit(|c| c.name("test").bitrate(86400)); + /// ``` + #[cfg(feature = "model")] + pub fn edit<F>(&mut self, f: F) -> Result<()> + where F: FnOnce(EditChannel) -> EditChannel { + #[cfg(feature = "cache")] + { + let req = permissions::MANAGE_CHANNELS; + + if !utils::user_has_perms(self.id, req)? { + return Err(Error::Model(ModelError::InvalidPermissions(req))); + } + } + + let mut map = Map::new(); + map.insert( + "name".to_owned(), + Value::String(self.name.clone()), + ); + map.insert( + "position".to_owned(), + Value::Number(Number::from(self.position)), + ); + map.insert( + "type".to_owned(), + Value::String(self.kind.name().to_owned()), + ); + + let edited = f(EditChannel(map)).0; + + http::edit_channel(self.id.0, &edited).map(|channel| { + let GuildChannel { id, parent_id, permission_overwrites, nsfw, name, position, kind, .. } = channel; + + *self = ChannelCategory { id, parent_id, permission_overwrites, nsfw, name, position, kind }; + () + }) + } + + #[cfg(feature = "utils")] + #[inline] + pub fn is_nsfw(&self) -> bool { + self.kind == ChannelType::Text && (self.nsfw || serenity_utils::is_nsfw(&self.name)) + } + + /// Returns the name of the category. + pub fn name(&self) -> &str { &self.name } +}
\ No newline at end of file diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs index 122b5bc..aec6cf0 100644 --- a/src/model/channel/channel_id.rs +++ b/src/model/channel/channel_id.rs @@ -302,6 +302,7 @@ impl ChannelId { pub fn name(&self) -> Option<String> { use self::Channel::*; + // TODO: Replace this second match with `?`. Some(match match self.find() { Some(c) => c, None => return None, @@ -313,6 +314,7 @@ impl ChannelId { Cow::Owned(name) => name, } }, + Category(category) => category.read().unwrap().name().to_string(), Private(channel) => channel.read().unwrap().name(), }) } @@ -516,6 +518,7 @@ impl From<Channel> for ChannelId { Channel::Group(group) => group.with(|g| g.channel_id), Channel::Guild(ch) => ch.with(|c| c.id), Channel::Private(ch) => ch.with(|c| c.id), + Channel::Category(ch) => ch.with(|c| c.id), } } } @@ -527,6 +530,7 @@ impl<'a> From<&'a Channel> for ChannelId { Channel::Group(ref group) => group.with(|g| g.channel_id), Channel::Guild(ref ch) => ch.with(|c| c.id), Channel::Private(ref ch) => ch.with(|c| c.id), + Channel::Category(ref ch) => ch.with(|c| c.id), } } } diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index d4e93f0..b713e18 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -24,6 +24,8 @@ pub struct GuildChannel { /// /// The default channel Id shares the Id of the guild and the default role. pub id: ChannelId, + /// Whether this guild channel belongs in a category. + pub parent_id: Option<ChannelId>, /// The bitrate of the channel. /// /// **Note**: This is only available for voice channels. diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index ddaa183..c1e22f3 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -6,6 +6,7 @@ mod guild_channel; mod message; mod private_channel; mod reaction; +mod channel_category; pub use self::attachment::*; pub use self::channel_id::*; @@ -15,6 +16,7 @@ pub use self::guild_channel::*; pub use self::message::*; pub use self::private_channel::*; pub use self::reaction::*; +pub use self::channel_category::*; use serde::de::Error as DeError; use serde_json; @@ -45,6 +47,10 @@ pub enum Channel { /// /// [`User`]: struct.User.html Private(Arc<RwLock<PrivateChannel>>), + /// A category of [`GuildChannel`]s + /// + /// [`GuildChannel`]: struct.GuildChannel.html + Category(Arc<RwLock<ChannelCategory>>), } #[cfg(feature = "model")] @@ -84,6 +90,9 @@ impl Channel { Channel::Private(ref private_channel) => { let _ = private_channel.read().unwrap().delete()?; }, + Channel::Category(ref category) => { + let _ = category.read().unwrap().delete()?; + }, } Ok(()) @@ -160,6 +169,7 @@ impl Channel { pub fn is_nsfw(&self) -> bool { match *self { Channel::Guild(ref channel) => channel.with(|c| c.is_nsfw()), + Channel::Category(ref category) => category.with(|c| c.is_nsfw()), Channel::Group(_) | Channel::Private(_) => false, } @@ -240,6 +250,7 @@ impl Channel { Channel::Group(ref group) => group.with(|g| g.channel_id), Channel::Guild(ref ch) => ch.with(|c| c.id), Channel::Private(ref ch) => ch.with(|c| c.id), + Channel::Category(ref category) => category.with(|c| c.id), } } @@ -342,6 +353,11 @@ impl<'de> Deserialize<'de> for Channel { .map(|x| Channel::Group(Arc::new(RwLock::new(x)))) .map_err(DeError::custom) }, + 4 => { + serde_json::from_value::<ChannelCategory>(Value::Object(v)) + .map(|x| Channel::Category(Arc::new(RwLock::new(x)))) + .map_err(DeError::custom) + }, _ => Err(DeError::custom("Unknown channel type")), } } @@ -372,6 +388,7 @@ impl Display for Channel { Display::fmt(&recipient.name, f) }, + Channel::Category(ref category) => Display::fmt(&category.read().unwrap().name, f), } } } @@ -395,6 +412,10 @@ enum_number!( [`Group`]: struct.Group.html"] Group = 3, + #[doc="An indicator that the channel is the channel of a [`ChannelCategory`]. + +[`ChannelCategory`]: struct.ChannelCategory.html"] + Category = 4, } ); @@ -405,6 +426,7 @@ impl ChannelType { ChannelType::Private => "private", ChannelType::Text => "text", ChannelType::Voice => "voice", + ChannelType::Category => "category", } } } diff --git a/src/model/misc.rs b/src/model/misc.rs index dce8491..d54a046 100644 --- a/src/model/misc.rs +++ b/src/model/misc.rs @@ -31,6 +31,7 @@ impl Mentionable for Channel { Channel::Guild(ref x) => format!("<#{}>", x.with(|x| x.id.0)), Channel::Private(ref x) => format!("<#{}>", x.with(|x| x.id.0)), Channel::Group(ref x) => format!("<#{}>", x.with(|x| x.channel_id.0)), + Channel::Category(_) => panic!("Categories can't be mentioned"), } } } diff --git a/src/model/utils.rs b/src/model/utils.rs index 9fed660..123f683 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -76,6 +76,7 @@ pub fn deserialize_private_channels<'de, D: Deserializer<'de>>( Channel::Group(ref group) => group.read().unwrap().channel_id, Channel::Private(ref channel) => channel.read().unwrap().id, Channel::Guild(_) => unreachable!("Guild private channel decode"), + Channel::Category(_) => unreachable!("Channel category private channel decode"), }; private_channels.insert(id, private_channel); @@ -157,8 +158,9 @@ pub fn user_has_perms(channel_id: ChannelId, mut permissions: Permissions) -> Re let guild_id = match channel { Channel::Guild(channel) => channel.read().unwrap().guild_id, Channel::Group(_) | - Channel::Private(_) => { - // Both users in DMs, and all users in groups, will have the same + Channel::Private(_) | + Channel::Category(_) => { + // Both users in DMs, and all users in groups and maybe all channels in categories will have the same // permissions. // // The only exception to this is when the current user is blocked by |