aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoracdenisSK <[email protected]>2017-11-04 20:06:40 +0100
committeracdenisSK <[email protected]>2017-11-04 20:06:40 +0100
commit0bd519f4ef9784d0fb5663d74db0d567f0bb1ae5 (patch)
treecc2a6f44e97e42420507964dab4662fcccd9beb3 /src
parentFix Help-Commands to list all eligible commands in DMs. (#212) (diff)
parentBump to v0.4.3 (diff)
downloadserenity-0bd519f4ef9784d0fb5663d74db0d567f0bb1ae5.tar.xz
serenity-0bd519f4ef9784d0fb5663d74db0d567f0bb1ae5.zip
Merge v0.4.3
Diffstat (limited to 'src')
-rw-r--r--src/builder/create_embed.rs6
-rw-r--r--src/builder/create_invite.rs12
-rw-r--r--src/cache/mod.rs62
-rw-r--r--src/client/bridge/gateway/shard_runner.rs7
-rw-r--r--src/client/context.rs6
-rw-r--r--src/client/mod.rs2
-rw-r--r--src/framework/mod.rs2
-rw-r--r--src/framework/standard/command.rs37
-rw-r--r--src/framework/standard/mod.rs4
-rw-r--r--src/gateway/mod.rs2
-rw-r--r--src/gateway/shard.rs130
-rw-r--r--src/lib.rs7
-rw-r--r--src/model/channel/guild_channel.rs2
-rw-r--r--src/model/error.rs8
-rw-r--r--src/model/guild/member.rs19
-rw-r--r--src/model/guild/mod.rs108
-rw-r--r--src/model/user.rs12
-rw-r--r--src/model/utils.rs5
-rw-r--r--src/prelude.rs3
19 files changed, 340 insertions, 94 deletions
diff --git a/src/builder/create_embed.rs b/src/builder/create_embed.rs
index e22fc82..7c4d3d6 100644
--- a/src/builder/create_embed.rs
+++ b/src/builder/create_embed.rs
@@ -235,9 +235,15 @@ impl CreateEmbed {
/// struct Handler;
///
/// impl EventHandler for Handler {
+<<<<<<< HEAD
/// fn guild_member_addition(&self, _: Context, guild_id: GuildId, member: Member) {
/// use serenity::client::CACHE;
/// let cache = CACHE.read();
+=======
+ /// fn on_guild_member_addition(&self, _: Context, guild_id: GuildId, member: Member) {
+ /// use serenity::CACHE;
+ /// let cache = CACHE.read().unwrap();
+>>>>>>> v0.4.3
///
/// if let Some(guild) = cache.guild(guild_id) {
/// let guild = guild.read();
diff --git a/src/builder/create_invite.rs b/src/builder/create_invite.rs
index 138e5c7..0b05b66 100644
--- a/src/builder/create_invite.rs
+++ b/src/builder/create_invite.rs
@@ -19,8 +19,8 @@ use internal::prelude::*;
/// struct Handler;
///
/// impl EventHandler for Handler {
-/// fn message(&self, _: Context, msg: Message) {
-/// use serenity::client::CACHE;
+/// fn on_message(&self, _: Context, msg: Message) {
+/// use serenity::CACHE;
/// if msg.content == "!createinvite" {
/// let channel = match CACHE.read().guild_channel(msg.channel_id) {
/// Some(channel) => channel,
@@ -76,7 +76,7 @@ impl CreateInvite {
/// Create an invite with a max age of `3600` seconds, or 1 hour:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// # use serenity::model::ChannelId;
/// # use std::error::Error;
/// #
@@ -109,7 +109,7 @@ impl CreateInvite {
/// Create an invite with a max use limit of `5`:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// # use serenity::model::ChannelId;
/// # use std::error::Error;
/// #
@@ -140,7 +140,7 @@ impl CreateInvite {
/// Create an invite which is temporary:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// # use serenity::model::ChannelId;
/// # use std::error::Error;
/// #
@@ -171,7 +171,7 @@ impl CreateInvite {
/// Create an invite which is unique:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// # use serenity::model::ChannelId;
/// # use std::error::Error;
/// #
diff --git a/src/cache/mod.rs b/src/cache/mod.rs
index 730d169..e23e17e 100644
--- a/src/cache/mod.rs
+++ b/src/cache/mod.rs
@@ -171,7 +171,9 @@ impl Cache {
/// # use serenity::prelude::*;
/// # use serenity::model::*;
/// #
- /// use serenity::client::CACHE;
+ /// # #[cfg(feature = "client")]
+ /// # fn main() {
+ /// use serenity::CACHE;
/// use std::thread;
/// use std::time::Duration;
///
@@ -194,9 +196,13 @@ impl Cache {
/// }
/// }
///
- /// let mut client = Client::new("token", Handler).unwrap();
- ///
+ /// let mut client = Client::new("token", Handler).unwrap();
+ ///
/// client.start().unwrap();
+ /// # }
+ /// #
+ /// # #[cfg(not(feature = "client"))]
+ /// # fn main() { }
/// ```
///
/// [`Member`]: ../model/struct.Member.html
@@ -229,7 +235,7 @@ impl Cache {
/// Printing the count of all private channels and groups:
///
/// ```rust,no_run
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
///
/// let amount = CACHE.read().all_private_channels().len();
///
@@ -256,18 +262,26 @@ impl Cache {
/// Print all of the Ids of guilds in the Cache:
///
/// ```rust,no_run
+ /// # #[cfg(feature = "client")]
+ /// # fn main() {
/// # use serenity::prelude::*;
/// # use serenity::model::*;
/// #
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
///
/// struct Handler;
+ ///
/// impl EventHandler for Handler {
- /// fn ready(&self, _: Context, _: Ready) {
- /// println!("Guilds in the Cache: {:?}", CACHE.read().all_guilds());
+ /// fn on_ready(&self, _: Context, _: Ready) {
+ /// let guilds = CACHE.read().unwrap().guilds.len();
+ ///
+ /// println!("Guilds in the Cache: {}", guilds);
/// }
/// }
- /// let mut client = Client::new("token", Handler);
+ /// # }
+ /// #
+ /// # #[cfg(not(feature = "client"))]
+ /// # fn main() { }
/// ```
///
/// [`Context`]: ../client/struct.Context.html
@@ -334,7 +348,7 @@ impl Cache {
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
///
/// if let Some(guild) = CACHE.read().guild(7) {
/// println!("Guild name: {}", guild.read().name);
@@ -363,10 +377,12 @@ impl Cache {
/// [`Client::on_message`] event dispatch:
///
/// ```rust,no_run
+ /// # #[cfg(feature = "client")]
+ /// # fn main() {
/// # use serenity::prelude::*;
/// # use serenity::model::*;
/// #
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
///
/// struct Handler;
///
@@ -389,8 +405,12 @@ impl Cache {
/// }
///
/// let mut client = Client::new("token", Handler).unwrap();
- ///
+ ///
/// client.start().unwrap();
+ /// # }
+ /// #
+ /// # #[cfg(not(feature = "client"))]
+ /// # fn main() { }
/// ```
///
/// [`ChannelId`]: ../model/struct.ChannelId.html
@@ -419,7 +439,7 @@ impl Cache {
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
///
/// if let Some(group) = CACHE.read().group(7) {
/// println!("Owner Id: {}", group.read().owner_id);
@@ -496,16 +516,22 @@ impl Cache {
///
/// # Examples
///
- /// Retrieve a private channel from the cache and send a message:
+ /// Retrieve a private channel from the cache and print its recipient's
+ /// name:
///
/// ```rust,no_run
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
+ ///
+ /// let cache = CACHE.read()?;
///
- /// if let Some(channel) = CACHE.read().private_channel(7) {
- /// channel.read().say("Hello there!");
+ /// if let Some(channel) = cache.private_channel(7) {
+ /// let channel_reader = channel.read().unwrap();
+ /// let user_reader = channel_reader.recipient.read().unwrap();
+ ///
+ /// println!("The recipient is {}", user_reader.name);
/// }
/// # Ok(())
/// # }
@@ -537,7 +563,7 @@ impl Cache {
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
///
/// if let Some(role) = CACHE.read().role(7, 77) {
/// println!("Role with Id 77 is called {}", role.name);
@@ -572,7 +598,7 @@ impl Cache {
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
- /// use serenity::client::CACHE;
+ /// use serenity::CACHE;
///
/// if let Some(user) = CACHE.read().user(7) {
/// println!("User with Id 7 is currently named {}", user.read().name);
diff --git a/src/client/bridge/gateway/shard_runner.rs b/src/client/bridge/gateway/shard_runner.rs
index a5fde3d..0ba83ba 100644
--- a/src/client/bridge/gateway/shard_runner.rs
+++ b/src/client/bridge/gateway/shard_runner.rs
@@ -422,4 +422,11 @@ impl<H: EventHandler + Send + Sync + 'static> ShardRunner<H> {
Ok(())
}
+
+ fn request_shutdown(&self) -> Result<()> {
+ debug!("[ShardRunner {:?}] Requesting shutdown", self.shard_info);
+ let _ = self.manager_tx.send(ShardManagerMessage::ShutdownAll);
+
+ Ok(())
+ }
}
diff --git a/src/client/context.rs b/src/client/context.rs
index 9b89cde..cc7ee63 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -281,6 +281,8 @@ impl Context {
/// playing:
///
/// ```rust,no_run
+ /// # #[cfg(feature = "model")]
+ /// # fn main() {
/// # use serenity::prelude::*;
/// # use serenity::model::*;
/// #
@@ -303,6 +305,10 @@ impl Context {
/// let mut client = Client::new("token", Handler).unwrap();
///
/// client.start().unwrap();
+ /// # }
+ ///
+ /// # #[cfg(not(feature = "model"))]
+ /// # fn main() {}
/// ```
///
/// [`Online`]: ../model/enum.OnlineStatus.html#variant.Online
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 16e61fa..a227920 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -720,7 +720,7 @@ impl Client {
/// use serenity::Client;
/// use std::env;
///
- /// let token = env::var("DISCORD_BOT_TOKEN").unwrap();
+ /// let token = env::var("DISCORD_TOKEN").unwrap();
/// let mut client = Client::new(&token, Handler);
///
/// let _ = client.start_shard_range([4, 7], 10);
diff --git a/src/framework/mod.rs b/src/framework/mod.rs
index a5f458d..85ae6f4 100644
--- a/src/framework/mod.rs
+++ b/src/framework/mod.rs
@@ -36,7 +36,7 @@
//! use serenity::model::Message;
//! use std::env;
//!
-//! let mut client = Client::new(&env::var("DISCORD_BOT_TOKEN").unwrap());
+//! let mut client = Client::new(&env::var("DISCORD_TOKEN").unwrap());
//!
//! client.with_framework(|f| f
//! .configure(|c| c.prefix("~"))
diff --git a/src/framework/standard/command.rs b/src/framework/standard/command.rs
index 797bed5..be7ec2a 100644
--- a/src/framework/standard/command.rs
+++ b/src/framework/standard/command.rs
@@ -128,6 +128,7 @@ pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Opti
if let Some(mention_end) = find_mention_end(&msg.content, conf) {
positions.push(mention_end);
+ return Some(positions);
} else if let Some(ref func) = conf.dynamic_prefix {
if let Some(x) = func(ctx, msg) {
if msg.content.starts_with(&x) {
@@ -152,22 +153,18 @@ pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Opti
return None;
}
- if conf.allow_whitespace {
- let pos = *unsafe { positions.get_unchecked(0) };
+ let pos = *unsafe { positions.get_unchecked(0) };
- positions.insert(0, pos + 1);
+ if conf.allow_whitespace {
+ positions.insert(0, find_end_of_prefix_with_whitespace(&msg.content, pos).unwrap_or(pos));
+ } else if find_end_of_prefix_with_whitespace(&msg.content, pos).is_some() {
+ return None;
}
Some(positions)
} else if conf.on_mention.is_some() {
find_mention_end(&msg.content, conf).map(|mention_end| {
- let mut positions = vec![mention_end];
-
- if conf.allow_whitespace {
- positions.insert(0, mention_end + 1);
- }
-
- positions
+ vec![mention_end] // This can simply be returned without trying to find the end whitespaces as trim will remove it later
})
} else {
None
@@ -182,3 +179,23 @@ fn find_mention_end(content: &str, conf: &Configuration) -> Option<usize> {
.map(|m| m.len())
})
}
+
+// Finds the end of the first continuous block of whitespace after the prefix
+fn find_end_of_prefix_with_whitespace(content: &str, position: usize) -> Option<usize> {
+ let mut ws_split = content.split_whitespace();
+ if let Some(cmd) = ws_split.nth(1) {
+ if let Some(index_of_cmd) = content.find(cmd) {
+ if index_of_cmd > position && index_of_cmd <= content.len() {
+ let slice = unsafe { content.slice_unchecked(position, index_of_cmd) }.as_bytes();
+ for byte in slice.iter() {
+ // 0x20 is ASCII for space
+ if *byte != 0x20u8 {
+ return None;
+ }
+ }
+ return Some(index_of_cmd);
+ }
+ }
+ }
+ None
+}
diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs
index 1681bd7..fdab3a8 100644
--- a/src/framework/standard/mod.rs
+++ b/src/framework/standard/mod.rs
@@ -859,7 +859,7 @@ impl Framework for StandardFramework {
'outer: for position in positions {
let mut built = String::new();
let round = message.content.chars().skip(position).collect::<String>();
- let round = round.trim().split_whitespace().collect::<Vec<&str>>();
+ let round = round.trim().split_whitespace().collect::<Vec<&str>>(); // Call to `trim` causes the related bug under the main bug #206 - where the whitespace settings are ignored. The fix is implemented as an additional check inside command::positions
for i in 0..self.configuration.depth {
if i != 0 {
@@ -965,7 +965,7 @@ pub fn has_correct_permissions(command: &Command, message: &Message) -> bool {
if !command.required_permissions.is_empty() {
if let Some(guild) = message.guild() {
let perms = guild
- .with(|g| g.permissions_for(message.channel_id, message.author.id));
+ .with(|g| g.permissions_in(message.channel_id, message.author.id));
return perms.contains(command.required_permissions);
}
diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs
index 147808e..c2acaac 100644
--- a/src/gateway/mod.rs
+++ b/src/gateway/mod.rs
@@ -141,7 +141,7 @@ impl ConnectionStage {
match *self {
Connecting | Handshake | Identifying | Resuming => true,
- _ => false,
+ Connected | Disconnected => false,
}
}
}
diff --git a/src/gateway/shard.rs b/src/gateway/shard.rs
index ef05cf4..340e2e8 100644
--- a/src/gateway/shard.rs
+++ b/src/gateway/shard.rs
@@ -82,6 +82,8 @@ pub struct Shard {
seq: u64,
session_id: Option<String>,
shard_info: [u64; 2],
+ /// Whether the shard has permanently shutdown.
+ shutdown: bool,
stage: ConnectionStage,
pub token: Arc<Mutex<String>>,
ws_url: Arc<Mutex<String>>,
@@ -154,8 +156,12 @@ impl Shard {
let user = http::get_current_user()?;
Shard {
+<<<<<<< HEAD
manager: VoiceManager::new(tx, user.id),
manager_rx: rx,
+=======
+ shutdown: false,
+>>>>>>> v0.4.3
client,
current_presence,
heartbeat_instants,
@@ -170,6 +176,7 @@ impl Shard {
}
} else {
Shard {
+ shutdown: false,
client,
current_presence,
heartbeat_instants,
@@ -186,11 +193,49 @@ impl Shard {
})
}
+<<<<<<< HEAD
/// Retrieves the current presence of the shard.
#[inline]
pub fn current_presence(&self) -> &CurrentPresence {
&self.current_presence
}
+=======
+ /// Whether the shard has permanently shutdown.
+ ///
+ /// This should normally happen due to manual calling of [`shutdown`] or
+ /// [`shutdown_clean`].
+ ///
+ /// [`shutdown`]: #method.shutdown
+ /// [`shutdown_clean`]: #method.shutdown_clean
+ #[inline]
+ pub fn is_shutdown(&self) -> bool {
+ self.shutdown
+ }
+
+ /// Retrieves a copy of the current shard information.
+ ///
+ /// The first element is the _current_ shard - 0-indexed - while the second
+ /// element is the _total number_ of shards -- 1-indexed.
+ ///
+ /// For example, if using 3 shards in total, and if this is shard 1, then it
+ /// can be read as "the second of three shards".
+ ///
+ /// # Examples
+ ///
+ /// Retrieving the shard info for the second shard, out of two shards total:
+ ///
+ /// ```rust,no_run
+ /// # use serenity::client::gateway::Shard;
+ /// # use std::sync::{Arc, Mutex};
+ /// #
+ /// # let mutex = Arc::new(Mutex::new("".to_string()));
+ /// #
+ /// # let shard = Shard::new(mutex.clone(), mutex, [1, 2]).unwrap();
+ /// #
+ /// assert_eq!(shard.shard_info(), [1, 2]);
+ /// ```
+ pub fn shard_info(&self) -> [u64; 2] { self.shard_info }
+>>>>>>> v0.4.3
/// Retrieves the heartbeat instants of the shard.
///
@@ -220,6 +265,7 @@ impl Shard {
///
/// # Errors
///
+<<<<<<< HEAD
/// Returns [`GatewayError::HeartbeatFailed`] if there was an error sending
/// a heartbeat.
///
@@ -272,6 +318,26 @@ impl Shard {
}
#[inline]
+=======
+ /// ```rust,no_run
+ /// # #[cfg(feature = "model")]
+ /// # fn main() {
+ /// # use serenity::client::gateway::Shard;
+ /// # use std::sync::{Arc, Mutex};
+ /// #
+ /// # let mutex = Arc::new(Mutex::new("".to_string()));
+ /// #
+ /// # let mut shard = Shard::new(mutex.clone(), mutex, [0, 1]).unwrap();
+ /// #
+ /// use serenity::model::Game;
+ ///
+ /// shard.set_game(Some(Game::playing("Heroes of the Storm")));
+ /// # }
+ /// #
+ /// # #[cfg(not(feature = "model"))]
+ /// # fn main() { }
+ /// ```
+>>>>>>> v0.4.3
pub fn set_game(&mut self, game: Option<Game>) {
self.current_presence.0 = game;
}
@@ -304,10 +370,15 @@ impl Shard {
/// Retrieving the shard info for the second shard, out of two shards total:
///
/// ```rust,no_run
+<<<<<<< HEAD
/// # extern crate parking_lot;
/// # extern crate serenity;
/// #
/// # use parking_lot::Mutex;
+=======
+ /// # #[cfg(feature = "model")]
+ /// # fn main() {
+>>>>>>> v0.4.3
/// # use serenity::client::gateway::Shard;
/// # use std::error::Error;
/// # use std::sync::Arc;
@@ -317,6 +388,7 @@ impl Shard {
/// #
/// # let shard = Shard::new(mutex.clone(), mutex, [1, 2]).unwrap();
/// #
+<<<<<<< HEAD
/// assert_eq!(shard.shard_info(), [1, 2]);
/// # Ok(())
/// # }
@@ -324,6 +396,15 @@ impl Shard {
/// # fn main() {
/// # try_main().unwrap();
/// # }
+=======
+ /// use serenity::model::{Game, OnlineStatus};
+ ///
+ /// shard.set_presence(Some(Game::playing("Heroes of the Storm")), OnlineStatus::Online, false);
+ /// # }
+ /// #
+ /// # #[cfg(not(feature = "model"))]
+ /// # fn main() { }
+>>>>>>> v0.4.3
/// ```
pub fn shard_info(&self) -> [u64; 2] { self.shard_info }
@@ -614,6 +695,44 @@ impl Shard {
}
/// Calculates the heartbeat latency between the shard and the gateway.
+<<<<<<< HEAD
+=======
+ ///
+ /// # Examples
+ ///
+ /// When using the [`Client`], output the latency in response to a `"~ping"`
+ /// message handled through [`Client::on_message`].
+ ///
+ /// ```rust,no_run
+ /// # #[cfg(feature = "model")]
+ /// # fn main() {
+ /// # use serenity::prelude::*;
+ /// # use serenity::model::*;
+ /// struct Handler;
+ ///
+ /// impl EventHandler for Handler {
+ /// fn on_message(&self, ctx: Context, msg: Message) {
+ /// if msg.content == "~ping" {
+ /// if let Some(latency) = ctx.shard.lock().latency() {
+ /// let s = format!("{}.{}s", latency.as_secs(), latency.subsec_nanos());
+ ///
+ /// let _ = msg.channel_id.say(&s);
+ /// } else {
+ /// let _ = msg.channel_id.say("N/A");
+ /// }
+ /// }
+ /// }
+ /// }
+ /// let mut client = Client::new("token", Handler); client.start().unwrap();
+ /// # }
+ /// #
+ /// # #[cfg(not(feature = "model"))]
+ /// # fn main() { }
+ /// ```
+ ///
+ /// [`Client`]: ../struct.Client.html
+ /// [`EventHandler::on_message`]: ../event_handler/trait.EventHandler.html#method.on_message
+>>>>>>> v0.4.3
// Shamelessly stolen from brayzure's commit in eris:
// <https://github.com/abalabahaha/eris/commit/0ce296ae9a542bcec0edf1c999ee2d9986bed5a6>
pub fn latency(&self) -> Option<StdDuration> {
@@ -647,9 +766,14 @@ impl Shard {
pub(crate) fn cycle_voice_recv(&mut self) -> Vec<Value> {
let mut messages = vec![];
+<<<<<<< HEAD
while let Ok(v) = self.manager_rx.try_recv() {
messages.push(v);
}
+=======
+ self.shutdown = true;
+ debug!("[Shard {:?}] Cleanly shutdown shard", self.shard_info);
+>>>>>>> v0.4.3
messages
}
@@ -684,8 +808,14 @@ impl Shard {
self.shard_info,
);
+<<<<<<< HEAD
self.reconnect()
}
+=======
+ self.shutdown = true;
+
+ Ok(())
+>>>>>>> v0.4.3
}
/// Requests that one or multiple [`Guild`]s be chunked.
diff --git a/src/lib.rs b/src/lib.rs
index 0564a3d..c207e44 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -33,12 +33,17 @@
//! #[macro_use] extern crate serenity;
//!
//! use serenity::client::Client;
+//! use serenity::prelude::EventHandler;
//! use serenity::framework::standard::StandardFramework;
//! use std::env;
//!
+//! struct Handler;
+//!
+//! impl EventHandler for Handler {}
+//!
//! fn main() {
//! // Login with a bot token from the environment
-//! let mut client = Client::new(&env::var("DISCORD_TOKEN").expect("token"));
+//! let mut client = Client::new(&env::var("DISCORD_TOKEN").expect("token"), Handler);
//! client.with_framework(StandardFramework::new()
//! .configure(|c| c.prefix("~")) // set the bot's prefix to "~"
//! .on("ping", ping));
diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs
index 8fc9abb..76cea47 100644
--- a/src/model/channel/guild_channel.rs
+++ b/src/model/channel/guild_channel.rs
@@ -512,7 +512,7 @@ impl GuildChannel {
pub fn permissions_for<U: Into<UserId>>(&self, user_id: U) -> Result<Permissions> {
self.guild()
.ok_or_else(|| Error::Model(ModelError::GuildNotFound))
- .map(|g| g.read().permissions_for(self.id, user_id))
+ .map(|g| g.read().permissions_in(self.id, user_id))
}
/// Pins a [`Message`] to the channel.
diff --git a/src/model/error.rs b/src/model/error.rs
index 835678a..c2f568b 100644
--- a/src/model/error.rs
+++ b/src/model/error.rs
@@ -13,10 +13,10 @@ use super::Permissions;
/// re-ban all members with an odd discriminator:
///
/// ```rust,no_run
-/// # #[cfg(feature="client")]
+/// # #[cfg(all(feature = "client", feature = "model"))]
/// # use std::error::Error;
/// #
-/// # #[cfg(feature="client")]
+/// # #[cfg(all(feature = "client", feature = "model"))]
/// # fn try_main() -> Result<(), Box<Error>> {
/// use serenity::prelude::*;
/// use serenity::model::*;
@@ -53,12 +53,12 @@ use super::Permissions;
/// # Ok(())
/// # }
/// #
-/// # #[cfg(feature="client")]
+/// # #[cfg(all(feature = "client", feature = "model"))]
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// #
-/// # #[cfg(not(feature="client"))]
+/// # #[cfg(not(all(feature="client", feature = "model")))]
/// # fn main() { }
/// ```
///
diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs
index 7420faa..e5feee7 100644
--- a/src/model/guild/member.rs
+++ b/src/model/guild/member.rs
@@ -14,6 +14,7 @@ use utils::Colour;
#[cfg(all(feature = "cache", feature = "model"))]
use {CACHE, http, utils};
+/// A trait for allowing both u8 or &str or (u8, &str) to be passed into the `ban` methods in `Guild` and `Member`.
pub trait BanOptions {
fn dmd(&self) -> u8 { 0 }
fn reason(&self) -> &str { "" }
@@ -275,7 +276,7 @@ impl Member {
.get(&self.guild_id)
.map(|guild| guild.read().has_perms(req));
- if let Some(Ok(false)) = has_perms {
+ if let Some(false) = has_perms {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -283,7 +284,7 @@ impl Member {
self.guild_id.kick(self.user.read().id)
}
- /// Returns the permissions for the member.
+ /// Returns the guild-level permissions for the member.
///
/// # Examples
///
@@ -310,19 +311,9 @@ impl Member {
None => return Err(From::from(ModelError::GuildNotFound)),
};
- let guild = guild.read();
+ let reader = guild.read().unwrap();
- let default_channel = match guild.default_channel(self.user.read().id) {
- Some(dc) => dc,
- None => return Err(From::from(ModelError::ItemMissing)),
- };
-
- let default_channel_reader = default_channel.read();
-
- Ok(
- guild
- .permissions_for(default_channel_reader.id, self.user.read().id),
- )
+ Ok(reader.member_permissions(self.user.read().unwrap().id))
}
/// Removes a [`Role`] from the member, editing its roles in-place if the
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
index 8483e75..ab74109 100644
--- a/src/model/guild/mod.rs
+++ b/src/model/guild/mod.rs
@@ -135,8 +135,8 @@ impl Guild {
/// returns `None`)
pub fn default_channel(&self, uid: UserId) -> Option<Arc<RwLock<GuildChannel>>> {
for (cid, channel) in &self.channels {
- if self.permissions_for(*cid, uid).read_messages() {
- return Some(channel.clone());
+ if self.permissions_in(*cid, uid).read_messages() {
+ return Some(channel.read().unwrap().clone());
}
}
@@ -151,8 +151,8 @@ impl Guild {
pub fn default_channel_guaranteed(&self) -> Option<Arc<RwLock<GuildChannel>>> {
for (cid, channel) in &self.channels {
for memid in self.members.keys() {
- if self.permissions_for(*cid, *memid).read_messages() {
- return Some(channel.clone());
+ if self.permissions_in(*cid, *memid).read_messages() {
+ return Some(channel.read().unwrap().clone());
}
}
}
@@ -161,21 +161,13 @@ impl Guild {
}
#[cfg(feature = "cache")]
- fn has_perms(&self, mut permissions: Permissions) -> Result<bool> {
- let member = match self.members.get(&CACHE.read().user.id) {
- Some(member) => member,
- None => return Err(Error::Model(ModelError::ItemMissing)),
- };
-
- let default_channel = match self.default_channel(member.user.read().id) {
- Some(dc) => dc,
- None => return Err(Error::Model(ModelError::ItemMissing)),
- };
+ fn has_perms(&self, mut permissions: Permissions) -> bool {
+ let user_id = CACHE.read().unwrap().user.id;
- let perms = self.permissions_for(default_channel.read().id, member.user.read().id);
+ let perms = self.member_permissions(user_id);
permissions.remove(perms);
- Ok(permissions.is_empty())
+ permissions.is_empty()
}
/// Ban a [`User`] from the guild. All messages by the
@@ -213,7 +205,7 @@ impl Guild {
{
let req = Permissions::BAN_MEMBERS;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -238,7 +230,7 @@ impl Guild {
{
let req = Permissions::BAN_MEMBERS;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -325,7 +317,7 @@ impl Guild {
{
let req = Permissions::MANAGE_CHANNELS;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -396,7 +388,7 @@ impl Guild {
{
let req = Permissions::MANAGE_ROLES;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -498,7 +490,7 @@ impl Guild {
{
let req = Permissions::MANAGE_GUILD;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -578,7 +570,7 @@ impl Guild {
{
let req = Permissions::CHANGE_NICKNAME;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -660,7 +652,7 @@ impl Guild {
{
let req = Permissions::MANAGE_GUILD;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -1007,6 +999,57 @@ impl Guild {
}
}
+ /// Calculate a [`Member`]'s permissions in the guild.
+ ///
+ /// [`Member`]: struct.Member.html
+ pub fn member_permissions<U>(&self, user_id: U) -> Permissions
+ where U: Into<UserId> {
+ let user_id = user_id.into();
+
+ if user_id == self.owner_id {
+ return Permissions::all();
+ }
+
+ let everyone = match self.roles.get(&RoleId(self.id.0)) {
+ Some(everyone) => everyone,
+ None => {
+ error!(
+ "(╯°□°)╯︵ ┻━┻ @everyone role ({}) missing in '{}'",
+ self.id,
+ self.name,
+ );
+
+ return Permissions::empty();
+ },
+ };
+
+ let member = match self.members.get(&user_id) {
+ Some(member) => member,
+ None => return everyone.permissions,
+ };
+
+ let mut permissions = everyone.permissions;
+
+ for role in &member.roles {
+ if let Some(role) = self.roles.get(&role) {
+ if role.permissions.contains(Permissions::ADMINISTRATOR) {
+ return Permissions::all();
+ }
+
+ permissions |= role.permissions;
+ } else {
+ warn!(
+ "(╯°□°)╯︵ ┻━┻ {} on {} has non-existent role {:?}",
+ member.user.read().unwrap().id,
+ self.id,
+ role,
+ );
+ }
+ }
+
+ permissions
+ }
+
/// Moves a member to a specific voice channel.
///
/// Requires the [Move Members] permission.
@@ -1018,10 +1061,21 @@ impl Guild {
self.id.move_member(user_id, channel_id)
}
+ /// Alias for [`permissions_in`].
+ ///
+ /// [`permissions_in`]: #method.permissions_in
+ #[deprecated(since = "0.4.3",
+ note = "This will serve a different purpose in 0.5")]
+ #[inline]
+ pub fn permissions_for<C, U>(&self, channel_id: C, user_id: U)
+ -> Permissions where C: Into<ChannelId>, U: Into<UserId> {
+ self.permissions_in(channel_id, user_id)
+ }
+
/// Calculate a [`User`]'s permissions in a given channel in the guild.
///
/// [`User`]: struct.User.html
- pub fn permissions_for<C, U>(&self, channel_id: C, user_id: U) -> Permissions
+ pub fn permissions_in<C, U>(&self, channel_id: C, user_id: U) -> Permissions
where C: Into<ChannelId>, U: Into<UserId> {
let user_id = user_id.into();
@@ -1169,7 +1223,7 @@ impl Guild {
{
let req = Permissions::KICK_MEMBERS;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -1252,7 +1306,7 @@ impl Guild {
{
let req = Permissions::KICK_MEMBERS;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
@@ -1277,7 +1331,7 @@ impl Guild {
{
let req = Permissions::BAN_MEMBERS;
- if !self.has_perms(req)? {
+ if !self.has_perms(req) {
return Err(Error::Model(ModelError::InvalidPermissions(req)));
}
}
diff --git a/src/model/user.rs b/src/model/user.rs
index 7eea83e..9f36d0f 100644
--- a/src/model/user.rs
+++ b/src/model/user.rs
@@ -48,7 +48,7 @@ impl CurrentUser {
/// Print out the current user's avatar url if one is set:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// #
/// # let cache = CACHE.read();
/// #
@@ -127,7 +127,7 @@ impl CurrentUser {
/// Print out the names of all guilds the current user is in:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// #
/// # let cache = CACHE.read();
/// #
@@ -155,7 +155,7 @@ impl CurrentUser {
/// Get the invite url with no permissions set:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// #
/// # let mut cache = CACHE.write();
///
@@ -178,7 +178,7 @@ impl CurrentUser {
/// Get the invite url with some basic permissions set:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// #
/// # let mut cache = CACHE.write();
///
@@ -234,7 +234,7 @@ impl CurrentUser {
/// Print out the current user's static avatar url if one is set:
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// #
/// # let cache = CACHE.read();
/// #
@@ -258,7 +258,7 @@ impl CurrentUser {
/// Print out the current user's distinct identifier (e.g., Username#1234):
///
/// ```rust,no_run
- /// # use serenity::client::CACHE;
+ /// # use serenity::CACHE;
/// #
/// # let cache = CACHE.read();
/// #
diff --git a/src/model/utils.rs b/src/model/utils.rs
index 62ecc4e..d366e5f 100644
--- a/src/model/utils.rs
+++ b/src/model/utils.rs
@@ -178,7 +178,10 @@ pub fn user_has_perms(channel_id: ChannelId, mut permissions: Permissions) -> Re
None => return Err(Error::Model(ModelError::ItemMissing)),
};
- let perms = guild.read().permissions_for(channel_id, current_user.id);
+ let perms = guild
+ .read()
+ .unwrap()
+ .permissions_in(channel_id, current_user.id);
permissions.remove(perms);
diff --git a/src/prelude.rs b/src/prelude.rs
index 435fb8a..8a361e9 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -16,7 +16,6 @@
pub use error::Error as SerenityError;
pub use model::Mentionable;
-pub use parking_lot::{Mutex, RwLock};
#[cfg(feature = "client")]
pub use client::{Client, ClientError as ClientError, Context, EventHandler};
@@ -26,5 +25,7 @@ pub use gateway::GatewayError;
pub use http::HttpError;
#[cfg(feature = "model")]
pub use model::ModelError;
+#[cfg(feature = "parking_lot")]
+pub use parking_lot::{Mutex, RwLock};
#[cfg(feature = "voice")]
pub use voice::VoiceError;