aboutsummaryrefslogtreecommitdiff
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
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
-rw-r--r--.gitignore10
-rw-r--r--CHANGELOG.md122
-rw-r--r--README.md7
-rw-r--r--examples/07_sample_bot_structure/.env.example10
-rw-r--r--examples/07_sample_bot_structure/Cargo.toml5
-rw-r--r--examples/07_sample_bot_structure/src/commands/mod.rs1
-rw-r--r--examples/07_sample_bot_structure/src/commands/owner.rs10
-rw-r--r--examples/07_sample_bot_structure/src/main.rs57
-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
-rw-r--r--tests/test_channels.rs2
-rw-r--r--tests/test_create_embed.rs2
29 files changed, 556 insertions, 104 deletions
diff --git a/.gitignore b/.gitignore
index 5f76dbe..5f4b3dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,13 @@
+# IDE directories and folders
+.vscode/
+.idea/
+
+# Target directory
target/
+# Lockfile
Cargo.lock
-.vscode/ \ No newline at end of file
+# Misc
+*.iml
+.env
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d934490..2a6fdd1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,90 @@
All notable changes to this project will be documented in this file.
This project mostly adheres to [Semantic Versioning][semver].
+## [0.4.3] - 2017-11-01
+
+This release contains bugfixes and marks the final release of the v0.4.x branch.
+Future development will continue on the v0.5.x branch.
+
+Thanks to the following for their contributions:
+
+- [@acdenisSK]
+- [@ThatsNoMoon]
+- [@zeyla]
+
+### Added
+
+- [model] Add `Guild::member_permissions` ([@zeyla]) [c:2ba4d03]
+
+### Changed
+
+- [model] Rename `Guild::permissions_for` to `Guild::permissions_in`, keep an
+ alias ([@zeyla]) [c:dcac271]
+
+### Fixed
+
+- [model] Make `Member::permissions` return guild-level permissions ([@zeyla])
+ [c:d3eddc6]
+
+### Misc.
+
+- [model] Add some docs to `BanOptions` ([@acdenisSK]) [c:c99091d]
+- [model] Have `Guild::has_perms` use `Guild::member_permissions` ([@zeyla])
+ [c:1b7101f]
+- [http] Slightly clarify ratelimiting documentation ([@zeyla]) [c:3be6e2e]
+- [docs] Fix ping bot example ([@ThatsNoMoon]) [c:800e58f]
+- [docs] Use consistent token names in examples ([@zeyla]) [c:e219a6a]
+
+## [0.4.2] - 2017-10-29
+
+This release contains the regular bugfixes, new features and slight behaviour
+changes.
+
+Thanks to the following people for their contributions:
+
+- [@acdenisSK]
+- [@efyang]
+- [@Caemor]
+- [@Flat]
+- [@hsiW]
+- [@Lakelezz]
+- [@UninterestinAcc]
+- [@zeyla]
+
+### Added
+
+- [general] Add a way to change a role's position ([@Flat]) [c:f47a0c8]
+- [general] Add logging and dotenv to example 07 ([@zeyla]) [c:d50b129]
+- [general] Add owner + quit function to example 07 ([@zeyla]) [c:41f26b3]
+- [framework] Add `PartialEq` impls and doc-tests to `Args` ([@acdenisSK]) [c:f9e5e76]
+- [framework] Add "zero-copy" parsing to `Args` ([@acdenisSK]) [c:9428787]
+- [framework] Add a debug impl to `DispatchError` ([@acdenisSK]) [c:a58de97]
+
+### Fixed
+
+- [general] Fix clippy warnings ([@hsiW]) [c:fbd6258]
+- [model] Fall back to `str::parse` if `utils::parse_username` fails ([@acdenisSK]) [c:292ceda]
+- [model] Fix `User::has_role` ([@zeyla]) [c:d3015a0ff]
+- [gateway] Fix shard connection ([@zeyla]) [c:585ac6e]
+- [gateway] Fix shard shutdown via `Context` ([@zeyla]) [c:3616585]
+- [framework] Fix `allow_whitespace` ([@UninterestinAcc]) [c:e694766]
+- [framework, gateway, cache] Properly update emojis in the cache, fix shard re-tries and do some cleanup to `help_commands.rs` ([@Lakelezz]) [c:e02d5fb]
+
+### Changed
+
+- [model] Do equality and hashing on just the user's id ([@acdenisSK]) [c:b7cdf15]
+- [model] defer to `delete_message` if there's just one message to delete ([@acdenisSK]) [c:c7aa27d]
+- [model] Use the underlaying integer value of `ChannelType` ([@acdenisSK]) [c:e57b510]
+
+### Misc.
+
+- [general] Update dependencies ([@zeyla]) [c:2219bb3]
+- [general] Re-export parking_lot's `Mutex` and `RwLock` from the prelude ([@zeyla]) [c:74ec713]
+- [general] Update the version in `Cargo.toml` to actually be `v0.4.2` ([@Caemor]) [c:5829c67]
+- [general] Cleanup gitignore to have comments ([@hsiW]) [c:ce4f8c2]
+- [gateway] Use update syntax for `Shard` ([@efyang]) [c:fcc4e2c]
+- [model] Deprecate some methods on `Channel` ([@zeyla]) [c:23ff6f]
+
## [0.4.1] - 2017-10-14
This release contains bugfixes and some newly added or newly exposed
@@ -1427,6 +1511,8 @@ rest::get_guilds(GuildPagination::After(GuildId(777)), 50);
Initial commit.
+[0.4.3]: https://github.com/zeyla/serenity/compare/v0.4.2...v0.4.3
+[0.4.2]: https://github.com/zeyla/serenity/compare/v0.4.1...v0.4.2
[0.4.1]: https://github.com/zeyla/serenity/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/zeyla/serenity/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/zeyla/serenity/compare/v0.2.0...v0.3.0
@@ -1450,9 +1536,11 @@ Initial commit.
[@barzamin]: https://github.com/barzamin
[@bippum]: https://github.com/bippum
[@blaenk]: https://github.com/blaenk
+[@Caemor]: https://github.com/Caemor
[@DeltaEvo]: https://github.com/DeltaEvo
[@eLunate]: https://github.com/eLunate
[@emoticon]: https://github.com/emoticon
+[@efyang]: https://github.com/efyang
[@Flat]: https://github.com/Flat
[@foxbot]: https://github.com/foxbot
[@ftriquet]: https://github.com/ftriquet
@@ -1469,9 +1557,20 @@ Initial commit.
[@Roughsketch]: https://github.com/Roughsketch
[@sschroe]: https://github.com/sschroe
[@SunDwarf]: https://github.com/SunDwarf
+[@ThatsNoMoon]: https://github.com/ThatsNoMoon
+[@UninterestinAcc]: https://github.com/UninterestinAcc
[@xentec]: https://github.com/xentec
[@zeyla]: https://github.com/zeyla
+[c:1b7101f]: https://github.com/zeyla/serenity/commit/1b7101fe71335c0e18bf855c0703acc23d87e427
+[c:2ba4d03]: https://github.com/zeyla/serenity/commit/2ba4d03f15d57d9f0fb1cc4d4f4355ebbc483d0a
+[c:3be6e2e]: https://github.com/zeyla/serenity/commit/3be6e2e28b0c3e9baaef19f405c463e3a41fed25
+[c:800e58f]: https://github.com/zeyla/serenity/commit/800e58f4603ce99ab69569b30cbec756301a6a63
+[c:c99091d]: https://github.com/zeyla/serenity/commit/c99091d241f240c6b76ac969655a8ec4423aaf80
+[c:d3eddc6]: https://github.com/zeyla/serenity/commit/d3eddc68e07bbc31e2043577cbf48741f0547ed3
+[c:dcac271]: https://github.com/zeyla/serenity/commit/dcac27168915b4f22745950ec0ef0c0af696774e
+[c:e219a6a]: https://github.com/zeyla/serenity/commit/e219a6a9d6a890b008fc390a909ae504a0c1a329
+
[c:002ce3a]: https://github.com/zeyla/serenity/commit/002ce3aa272fa51b84e820f12db39cb87a461a83
[c:022e35d]: https://github.com/zeyla/serenity/commit/022e35d5b12322bd77bbe74a1a3b2ad319977390
[c:05f158f]: https://github.com/zeyla/serenity/commit/05f158fc89f2adc82e31cf4b93706dc7d25e11d8
@@ -1848,3 +1947,26 @@ Initial commit.
[c:fb07751]: https://github.com/zeyla/serenity/commit/fb07751cfc1efb657cba7005c38ed5ec6b192b4f
[c:fb4d411]: https://github.com/zeyla/serenity/commit/fb4d411054fa44928b4fa052b19de19fce69d7cf
[c:ff4437a]: https://github.com/zeyla/serenity/commit/ff4437addb01e5c6c3ad8c5b1830db0d0a86396b
+
+[c:f47a0c8]: https://github.com/zeyla/serenity/commit/f47a0c831efe5842ca38cb1067de361ae42f6edc
+[c:d50b129]: https://github.com/zeyla/serenity/commit/d50b12931404946e219d3ff0878f0632445ef35f
+[c:41f26b3]: https://github.com/zeyla/serenity/commit/41f26b3757c7a5fba1f09f34e3192e2fd9702a4a
+[c:f9e5e76]: https://github.com/zeyla/serenity/commit/f9e5e76585a1f6317dadb67e440765b0070ca131
+[c:9428787]: https://github.com/zeyla/serenity/commit/9428787abb6126ba05bfef96cd2b8d2a217fdf5d
+[c:a58de97]: https://github.com/zeyla/serenity/commit/a58de97e6089aa98f04d2cdc7312ed38a9f72b22
+[c:fbd6258]: https://github.com/zeyla/serenity/commit/fbd625839e6a2e01b16e6c3814cb9b9f31dc7caa
+[c:292ceda]: https://github.com/zeyla/serenity/commit/292cedaa3462f7532efda98722354afa8e213b6a
+[c:d3015a0ff]: https://github.com/zeyla/serenity/commit/d3015a0ff0c0c87888437f991945453b92296875
+[c:585ac6e]: https://github.com/zeyla/serenity/commit/585ac6e6ca792facf29063776c83262fa849161b
+[c:3616585]: https://github.com/zeyla/serenity/commit/361658510f3e2eb9aefbe66232b9b1f1a1ebb80f
+[c:e694766]: https://github.com/zeyla/serenity/commit/e694766bb6c93d5f6a75ad9871cfdefbd0309a17
+[c:e02d5fb]: https://github.com/zeyla/serenity/commit/e02d5fb8171b11214e1502c6754fef1972bbf1b9
+[c:b7cdf15]: https://github.com/zeyla/serenity/commit/b7cdf1542cb9199c61c0b17bdd381d4f117f635e
+[c:c7aa27d]: https://github.com/zeyla/serenity/commit/c7aa27dbb64e64d70c7f13725c79017c4bba1c95
+[c:2219bb3]: https://github.com/zeyla/serenity/commit/2219bb37a80c4c2b4ff5a24d72b82737eb241195
+[c:74ec713]: https://github.com/zeyla/serenity/commit/74ec713825b2b4c55382fb76fa57bd967e66b3aa
+[c:5829c67]: https://github.com/zeyla/serenity/commit/5829c673c13655b86d317ab65d204067a2b1a7a4
+[c:ce4f8c2]: https://github.com/zeyla/serenity/commit/ce4f8c2ac8dd2c472ab537a60bf92579d078073b
+[c:fcc4e2c]: https://github.com/zeyla/serenity/commit/fcc4e2ce2e523248ed33c9f4853d3485cbc9b6e6
+[c:23ff6f]: https://github.com/zeyla/serenity/commit/23ff6f21019bc94f8dc32355fa34691b881bfb69
+[c:e57b510]: https://github.com/zeyla/serenity/commit/e57b510edd640abb243664337a1c163924313612
diff --git a/README.md b/README.md
index c687581..a75872d 100644
--- a/README.md
+++ b/README.md
@@ -42,12 +42,17 @@ A basic ping-pong bot looks like:
#[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/examples/07_sample_bot_structure/.env.example b/examples/07_sample_bot_structure/.env.example
new file mode 100644
index 0000000..3b3b4ac
--- /dev/null
+++ b/examples/07_sample_bot_structure/.env.example
@@ -0,0 +1,10 @@
+# This declares an environment variable named "DISCORD_TOKEN" with the given
+# value. When calling `kankyo::load()`, it will read the `.env` file and parse
+# these key-value pairs and insert them into the environment.
+#
+# Environment variables are separated by newlines and must not have space
+# around the equals sign (`=`).
+DISCORD_TOKEN=put your token here
+# Declares the level of logging to use. Read the documentation for the `log`
+# and `env_logger` crates for more information.
+RUST_LOG=debug
diff --git a/examples/07_sample_bot_structure/Cargo.toml b/examples/07_sample_bot_structure/Cargo.toml
index 0bebb46..b1dba53 100644
--- a/examples/07_sample_bot_structure/Cargo.toml
+++ b/examples/07_sample_bot_structure/Cargo.toml
@@ -3,6 +3,11 @@ name = "07_sample_bot_structure"
version = "0.1.0"
authors = ["my name <[email protected]>"]
+[dependencies]
+env_logger = "~0.4"
+kankyo = "~0.1"
+log = "~0.3"
+
[dependencies.serenity]
features = ["cache", "framework", "standard_framework"]
path = "../../"
diff --git a/examples/07_sample_bot_structure/src/commands/mod.rs b/examples/07_sample_bot_structure/src/commands/mod.rs
index bf58dba..9c5dfaa 100644
--- a/examples/07_sample_bot_structure/src/commands/mod.rs
+++ b/examples/07_sample_bot_structure/src/commands/mod.rs
@@ -1,2 +1,3 @@
pub mod math;
pub mod meta;
+pub mod owner;
diff --git a/examples/07_sample_bot_structure/src/commands/owner.rs b/examples/07_sample_bot_structure/src/commands/owner.rs
new file mode 100644
index 0000000..e80c19d
--- /dev/null
+++ b/examples/07_sample_bot_structure/src/commands/owner.rs
@@ -0,0 +1,10 @@
+command!(quit(ctx, msg, _args) {
+ match ctx.quit() {
+ Ok(()) => {
+ let _ = msg.reply("Shutting down!");
+ },
+ Err(why) => {
+ let _ = msg.reply(&format!("Failed to shutdown: {:?}", why));
+ },
+ }
+});
diff --git a/examples/07_sample_bot_structure/src/main.rs b/examples/07_sample_bot_structure/src/main.rs
index 7a0c216..4c0ff8b 100644
--- a/examples/07_sample_bot_structure/src/main.rs
+++ b/examples/07_sample_bot_structure/src/main.rs
@@ -9,29 +9,72 @@
//! features = ["framework", "standard_framework"]
//! ```
-#[macro_use]
-extern crate serenity;
+#[macro_use] extern crate log;
+#[macro_use] extern crate serenity;
+
+extern crate env_logger;
+extern crate kankyo;
mod commands;
-use serenity::prelude::*;
use serenity::framework::StandardFramework;
+use serenity::model::event::ResumedEvent;
+use serenity::model::Ready;
+use serenity::prelude::*;
+use serenity::http;
+use std::collections::HashSet;
use std::env;
-struct Handler; impl EventHandler for Handler {}
+struct Handler;
+
+impl EventHandler for Handler {
+ fn on_ready(&self, _: Context, ready: Ready) {
+ info!("Connected as {}", ready.user.name);
+ }
+
+ fn on_resume(&self, _: Context, _: ResumedEvent) {
+ info!("Resumed");
+ }
+}
fn main() {
+ // This will load the environment variables located at `./.env`, relative to
+ // the CWD. See `./.env.example` for an example on how to structure this.
+ kankyo::load().expect("Failed to load .env file");
+
+ // Initialize the logger to use environment variables.
+ //
+ // In this case, a good default is setting the environment variable
+ // `RUST_LOG` to debug`.
+ env_logger::init().expect("Failed to initialize env_logger");
+
let token = env::var("DISCORD_TOKEN")
.expect("Expected a token in the environment");
+
let mut client = Client::new(&token, Handler).expect("Err creating client");
+ let owners = match http::get_current_application_info() {
+ Ok(info) => {
+ let mut set = HashSet::new();
+ set.insert(info.owner.id);
+
+ set
+ },
+ Err(why) => panic!("Couldn't get application info: {:?}", why),
+ };
+
client.with_framework(StandardFramework::new()
- .configure(|c| c.prefix("~"))
+ .configure(|c| c
+ .owners(owners)
+ .prefix("~"))
.command("ping", |c| c.exec(commands::meta::ping))
.command("latency", |c| c.exec(commands::meta::latency))
- .command("multiply", |c| c.exec(commands::math::multiply)));
+ .command("multiply", |c| c.exec(commands::math::multiply))
+ .command("quit", |c| c
+ .exec(commands::owner::quit)
+ .owners_only(true)));
if let Err(why) = client.start() {
- println!("Client error: {:?}", why);
+ error!("Client error: {:?}", why);
}
}
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;
diff --git a/tests/test_channels.rs b/tests/test_channels.rs
index 51e8510..86523dc 100644
--- a/tests/test_channels.rs
+++ b/tests/test_channels.rs
@@ -1,3 +1,5 @@
+#![cfg(feature = "model")]
+
extern crate parking_lot;
extern crate serenity;
diff --git a/tests/test_create_embed.rs b/tests/test_create_embed.rs
index 01a7d3e..dd84154 100644
--- a/tests/test_create_embed.rs
+++ b/tests/test_create_embed.rs
@@ -1,5 +1,5 @@
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
-#![cfg(feature = "utils")]
+#![cfg(all(feature = "builder", feature = "utils"))]
#[macro_use]
extern crate serde_json;