diff options
| author | acdenisSK <[email protected]> | 2017-11-04 20:06:40 +0100 |
|---|---|---|
| committer | acdenisSK <[email protected]> | 2017-11-04 20:06:40 +0100 |
| commit | 0bd519f4ef9784d0fb5663d74db0d567f0bb1ae5 (patch) | |
| tree | cc2a6f44e97e42420507964dab4662fcccd9beb3 | |
| parent | Fix Help-Commands to list all eligible commands in DMs. (#212) (diff) | |
| parent | Bump to v0.4.3 (diff) | |
| download | serenity-0bd519f4ef9784d0fb5663d74db0d567f0bb1ae5.tar.xz serenity-0bd519f4ef9784d0fb5663d74db0d567f0bb1ae5.zip | |
Merge v0.4.3
29 files changed, 556 insertions, 104 deletions
@@ -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 @@ -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. @@ -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; |