Skip to content

Commit

Permalink
Add Methods to get Permissions of Roles in GuildChannels (#689)
Browse files Browse the repository at this point in the history
Add `RoleNotFound`-variant to `ModelError`-enum.
Add methods to return a `RoleId`'s permissions.
It takes a given `ChannelId` into consideration.
  • Loading branch information
Lakelezz committed Aug 22, 2019
1 parent ccbba0a commit 09c1e01
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 70 deletions.
8 changes: 4 additions & 4 deletions src/framework/standard/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,10 +379,10 @@ impl StandardFramework {
/// Adds a group to be used by the framework. Primary use-case is runtime modification
/// of groups in the framework; will _not_ mark the framework as initialized. Refer to
/// [`group`] for adding groups in initial configuration.
///
///
/// Note: does _not_ return `Self` like many other commands. This is because
/// it's not intended to be chained as the other commands are.
///
///
/// [`group`]: #method.group
pub fn group_add(&mut self, group: &'static CommandGroup) {
let map = if group.options.prefixes.is_empty() {
Expand All @@ -396,7 +396,7 @@ impl StandardFramework {

/// Removes a group from being used in the framework. Primary use-case is runtime modification
/// of groups in the framework.
///
///
/// Note: does _not_ return `Self` like many other commands. This is because
/// it's not intended to be chained as the other commands are.
pub fn group_remove(&mut self, group: &'static CommandGroup) {
Expand Down Expand Up @@ -865,7 +865,7 @@ pub(crate) fn has_correct_permissions(
if options.required_permissions().is_empty() {
true
} else if let Some(guild) = message.guild(&cache) {
let perms = guild.with(|g| g.permissions_in(message.channel_id, message.author.id));
let perms = guild.with(|g| g.user_permissions_in(message.channel_id, message.author.id));

perms.contains(*options.required_permissions())
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/framework/standard/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ fn check_discrepancy(

let guild = guild.read();

let perms = guild.permissions_in(msg.channel_id, msg.author.id);
let perms = guild.user_permissions_in(msg.channel_id, msg.author.id);

if !perms.contains(*options.required_permissions())
&& !(options.owner_privilege() && config.owners.contains(&msg.author.id))
Expand Down
141 changes: 137 additions & 4 deletions src/model/channel/guild_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,15 +567,148 @@ impl GuildChannel {
/// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "cache")]
#[inline]
#[deprecated(since="0.6.4", note="Please use `permissions_for_user` instead.")]
pub fn permissions_for<U: Into<UserId>>(&self, cache: impl AsRef<CacheRwLock>, user_id: U) -> Result<Permissions> {
self._permissions_for(&cache, user_id.into())
self.permissions_for_user(&cache, user_id.into())
}

/// Calculates the permissions of a role.
///
/// The Id of the argument must be a [`Role`] of the [`Guild`] that the
/// channel is in.
///
/// # Errors
///
/// Returns a [`ModelError::GuildNotFound`] if the channel's guild could
/// not be found in the [`Cache`].
///
/// Returns a [`ModelError::RoleNotFound`] if the given role could not
/// be found in the [`Cache`].
///
/// [`Cache`]: ../../cache/struct.Cache.html
/// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
/// [`ModelError::RoleNotFound`]: ../error/enum.Error.html#variant.RoleNotFound
/// [`Guild`]: ../guild/struct.Guild.html
/// [`Role`]: ../guild/struct.Role.html
#[cfg(feature = "cache")]
#[inline]
pub fn permissions_for_user<U: Into<UserId>>(&self, cache: impl AsRef<CacheRwLock>, user_id: U) -> Result<Permissions> {
self._permissions_for_user(&cache, user_id.into())
}

#[cfg(feature = "cache")]
fn _permissions_for(&self, cache: impl AsRef<CacheRwLock>, user_id: UserId) -> Result<Permissions> {
fn _permissions_for_user(&self, cache: impl AsRef<CacheRwLock>, user_id: UserId) -> Result<Permissions> {
self.guild(&cache)
.ok_or_else(|| Error::Model(ModelError::GuildNotFound))
.map(|g| g.read().permissions_in(self.id, user_id))
.map(|g| g.read().user_permissions_in(self.id, user_id))
}

/// Calculates the permissions of a member.
///
/// The Id of the argument must be a [`Member`] of the [`Guild`] that the
/// channel is in.
///
/// # Examples
///
/// Calculate the permissions of a [`User`] who posted a [`Message`] in a
/// channel:
///
/// ```rust,no_run
/// use serenity::prelude::*;
/// use serenity::model::prelude::*;
/// struct Handler;
///
/// impl EventHandler for Handler {
/// fn message(&self, context: Context, msg: Message) {
/// let channel = match context.cache.read().guild_channel(msg.channel_id) {
/// Some(channel) => channel,
/// None => return,
/// };
///
/// let permissions = channel.read().permissions_for(&context.cache, &msg.author).unwrap();
///
/// println!("The user's permissions: {:?}", permissions);
/// }
/// }
/// let mut client = Client::new("token", Handler).unwrap();
///
/// client.start().unwrap();
/// ```
///
/// Check if the current user has the [Attach Files] and [Send Messages]
/// permissions (note: serenity will automatically check this for; this is
/// for demonstrative purposes):
///
/// ```rust,no_run
/// use serenity::prelude::*;
/// use serenity::model::prelude::*;
/// use serenity::model::channel::Channel;
/// use std::fs::File;
///
/// struct Handler;
///
/// impl EventHandler for Handler {
/// fn message(&self, context: Context, mut msg: Message) {
/// let channel = match context.cache.read().guild_channel(msg.channel_id) {
/// Some(channel) => channel,
/// None => return,
/// };
///
/// let current_user_id = context.cache.read().user.id;
/// let permissions =
/// channel.read().permissions_for(&context.cache, current_user_id).unwrap();
///
/// if !permissions.contains(Permissions::ATTACH_FILES | Permissions::SEND_MESSAGES) {
/// return;
/// }
///
/// let file = match File::open("./cat.png") {
/// Ok(file) => file,
/// Err(why) => {
/// println!("Err opening file: {:?}", why);
///
/// return;
/// },
/// };
///
/// let _ = msg.channel_id.send_files(&context.http, vec![(&file, "cat.png")], |mut m| {
/// m.content("here's a cat");
///
/// m
/// });
/// }
/// }
///
/// let mut client = Client::new("token", Handler).unwrap();
///
/// client.start().unwrap();
/// ```
///
/// # Errors
///
/// Returns a [`ModelError::GuildNotFound`] if the channel's guild could
/// not be found in the [`Cache`].
///
/// [`Cache`]: ../../cache/struct.Cache.html
/// [`ModelError::GuildNotFound`]: ../error/enum.Error.html#variant.GuildNotFound
/// [`Guild`]: ../guild/struct.Guild.html
/// [`Member`]: ../guild/struct.Member.html
/// [`Message`]: struct.Message.html
/// [`User`]: ../user/struct.User.html
/// [Attach Files]: ../permissions/struct.Permissions.html#associatedconstant.ATTACH_FILES
/// [Send Messages]: ../permissions/struct.Permissions.html#associatedconstant.SEND_MESSAGES
#[cfg(feature = "cache")]
#[inline]
pub fn permissions_for_role<R: Into<RoleId>>(&self, cache: impl AsRef<CacheRwLock>, role_id: R) -> Result<Permissions> {
self._permissions_for_role(&cache, role_id.into())
}

#[cfg(feature = "cache")]
fn _permissions_for_role(&self, cache: impl AsRef<CacheRwLock>, role_id: RoleId) -> Result<Permissions> {
self.guild(&cache)
.ok_or(Error::Model(ModelError::GuildNotFound))?
.read().role_permissions_in(self.id, role_id)
.ok_or(Error::Model(ModelError::GuildNotFound))
}

/// Pins a [`Message`] to the channel.
Expand Down Expand Up @@ -756,7 +889,7 @@ impl GuildChannel {
.members
.iter()
.filter_map(|e|
if self.permissions_for(&cache, e.0).map(|p| p.contains(Permissions::READ_MESSAGES)).unwrap_or(false) {
if self.permissions_for_user(&cache, e.0).map(|p| p.contains(Permissions::READ_MESSAGES)).unwrap_or(false) {
Some(e.1.clone())
} else {
None
Expand Down
28 changes: 18 additions & 10 deletions src/model/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ pub enum Error {
/// [`GuildId`]: ../id/struct.GuildId.html
/// [`Cache`]: ../../cache/struct.Cache.html
GuildNotFound,
/// An indication that a [role][`Role`] could not be found by
/// [Id][`RoleId`] in the [`Cache`].
///
/// [`Role`]: ../guild/struct.Role.html
/// [`RoleId`]: ../id/struct.GuildId.html
/// [`Cache`]: ../../cache/struct.Cache.html
RoleNotFound,
/// Indicates that there are hierarchy problems restricting an action.
///
/// For example, when banning a user, if the other user has a role with an
Expand Down Expand Up @@ -142,17 +149,18 @@ impl Display for Error {
impl StdError for Error {
fn description(&self) -> &str {
match *self {
Error::BulkDeleteAmount => "Too few/many messages to bulk delete",
Error::DeleteMessageDaysAmount(_) => "Invalid delete message days",
Error::EmbedTooLarge(_) => "Embed too large",
Error::GuildNotFound => "Guild not found in the cache",
Error::Hierarchy => "Role hierarchy prevents this action",
Error::BulkDeleteAmount => "Too few/many messages to bulk delete.",
Error::DeleteMessageDaysAmount(_) => "Invalid delete message days.",
Error::EmbedTooLarge(_) => "Embed too large.",
Error::GuildNotFound => "Guild not found in the cache.",
Error::RoleNotFound => "Role not found in the cache.",
Error::Hierarchy => "Role hierarchy prevents this action.",
Error::InvalidChannelType => "The channel cannot perform the action.",
Error::InvalidPermissions(_) => "Invalid permissions",
Error::InvalidUser => "The current user cannot perform the action",
Error::ItemMissing => "The required item is missing from the cache",
Error::MessageTooLong(_) => "Message too large",
Error::MessagingBot => "Attempted to message another bot user",
Error::InvalidPermissions(_) => "Invalid permissions.",
Error::InvalidUser => "The current user cannot perform the action.",
Error::ItemMissing => "The required item is missing from the cache.",
Error::MessageTooLong(_) => "Message too large.",
Error::MessagingBot => "Attempted to message another bot user.",
Error::__Nonexhaustive => unreachable!(),
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/model/guild/member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl Member {
let reader = guild.read();

for (cid, channel) in &reader.channels {
if reader.permissions_in(*cid, self.user.read().id).read_messages() {
if reader.user_permissions_in(*cid, self.user.read().id).read_messages() {
return Some(Arc::clone(channel));
}
}
Expand Down

0 comments on commit 09c1e01

Please sign in to comment.