aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoracdenisSK <[email protected]>2017-09-09 10:53:44 +0200
committeracdenisSK <[email protected]>2017-09-09 10:56:29 +0200
commit4be6b9d5008ff8bb3d1fdddff5647a6bb307513c (patch)
tree516b811875ae4cb2b98c308aabb69bc6e7a94fd2 /src
parentChange order to avoid subtraction overflow error (#160) (diff)
downloadserenity-4be6b9d5008ff8bb3d1fdddff5647a6bb307513c.tar.xz
serenity-4be6b9d5008ff8bb3d1fdddff5647a6bb307513c.zip
Implement categories
Diffstat (limited to 'src')
-rw-r--r--src/cache/cache_events_impl.rs10
-rw-r--r--src/cache/mod.rs8
-rw-r--r--src/client/dispatch.rs13
-rw-r--r--src/client/event_handler.rs2
-rw-r--r--src/model/channel/channel_category.rs121
-rw-r--r--src/model/channel/channel_id.rs4
-rw-r--r--src/model/channel/guild_channel.rs2
-rw-r--r--src/model/channel/mod.rs22
-rw-r--r--src/model/misc.rs1
-rw-r--r--src/model/utils.rs6
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