diff options
| author | acdenisSK <[email protected]> | 2017-10-24 18:10:10 +0200 |
|---|---|---|
| committer | acdenisSK <[email protected]> | 2017-10-24 18:10:10 +0200 |
| commit | ef60c3cd5b93d61ff8200f5f6871b449bf7dccb5 (patch) | |
| tree | ff9ec9e09dc363c05c6b582380a120ca47290a9f /src | |
| parent | Remove `on_` prefix to EventHandler tymethods (diff) | |
| parent | Fall back to `str::parse` if `parse_username` fails (diff) | |
| download | serenity-ef60c3cd5b93d61ff8200f5f6871b449bf7dccb5.tar.xz serenity-ef60c3cd5b93d61ff8200f5f6871b449bf7dccb5.zip | |
Merge v0.4.2
Diffstat (limited to 'src')
| -rw-r--r-- | src/framework/standard/args.rs | 238 | ||||
| -rw-r--r-- | src/framework/standard/help_commands.rs | 33 | ||||
| -rw-r--r-- | src/framework/standard/mod.rs | 27 | ||||
| -rw-r--r-- | src/gateway/shard.rs | 26 | ||||
| -rw-r--r-- | src/http/mod.rs | 18 | ||||
| -rw-r--r-- | src/model/channel/channel_id.rs | 10 | ||||
| -rw-r--r-- | src/model/channel/guild_channel.rs | 2 | ||||
| -rw-r--r-- | src/model/channel/mod.rs | 11 | ||||
| -rw-r--r-- | src/model/event.rs | 4 | ||||
| -rw-r--r-- | src/model/guild/guild_id.rs | 23 | ||||
| -rw-r--r-- | src/model/guild/mod.rs | 20 | ||||
| -rw-r--r-- | src/model/misc.rs | 7 | ||||
| -rw-r--r-- | src/model/user.rs | 24 | ||||
| -rw-r--r-- | src/prelude.rs | 1 |
14 files changed, 409 insertions, 35 deletions
diff --git a/src/framework/standard/args.rs b/src/framework/standard/args.rs index f59908a..6186cd4 100644 --- a/src/framework/standard/args.rs +++ b/src/framework/standard/args.rs @@ -83,6 +83,17 @@ impl Args { } /// Removes the first element, parses it to a specific type if necessary, returns. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69", " "); + /// + /// assert_eq!(args.single::<i32>().unwrap(), 42); + /// assert_eq!(args, ["69"]); + /// ``` pub fn single<T: FromStr>(&mut self) -> Result<T, T::Err> where T::Err: StdError { if self.delimiter_split.is_empty() { @@ -95,8 +106,49 @@ impl Args { .parse::<T>()?) } + /// Like [`single`], but does "zero-copy" parsing. + /// + /// Refer to [`FromStrZc`]'s example on how to use this method. + /// + /// [`single`]: #method.single + /// [`FromStrZc`]: trait.FromStrZc.html + pub fn single_zc<'a, T: FromStrZc<'a> + 'a>(&'a mut self) -> Result<T, T::Err> + where T::Err: StdError { + + // This is a hack as to mitigate some nasty lifetime errors. + // + // (Culprit `Vec::remove`s return type) + fn get_and_remove(b: &mut Vec<String>) -> Option<&str> { + struct GetThenRemove<'a>(&'a mut Vec<String>); + + impl<'a> Drop for GetThenRemove<'a> { + fn drop(&mut self) { + if !self.0.is_empty() { + self.0.remove(0); + } + } + } + + GetThenRemove(b).0.get(0).map(|s| s.as_str()) + } + + let a = get_and_remove(&mut self.delimiter_split).ok_or(Error::Eos)?; + Ok(FromStrZc::from_str(a)?) + } + /// Like [`single`], but doesn't remove the element. /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let args = Args::new("42 69", " "); + /// + /// assert_eq!(args.single_n::<i32>().unwrap(), 42); + /// assert_eq!(args, ["42", "69"]); + /// ``` + /// /// [`single`]: #method.single pub fn single_n<T: FromStr>(&self) -> Result<T, T::Err> where T::Err: StdError { @@ -111,10 +163,31 @@ impl Args { } /// Skips if there's a first element, but also returns it. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69", " "); + /// + /// assert_eq!(args.skip().unwrap(), "42"); + /// assert_eq!(args, ["69"]); + /// ``` pub fn skip(&mut self) -> Option<String> { self.delimiter_split.shift() } /// Like [`skip`], but allows for multiple at once. /// + /// # Examples + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69 88 99", " "); + /// + /// assert_eq!(*args.skip_for(3).unwrap(), ["42".to_string(), "69".to_string(), "88".to_string()]); + /// assert_eq!(args, ["99"]); + /// ``` + /// /// [`skip`]: #method.skip pub fn skip_for(&mut self, i: u32) -> Option<Vec<String>> { let mut vec = Vec::with_capacity(i as usize); @@ -127,7 +200,18 @@ impl Args { } /// Like [`single`], but takes quotes into account. - /// + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new(r#""42"#, " "); + /// + /// assert_eq!(args.single_quoted::<i32>().unwrap(), 42); + /// assert!(args.is_empty()); + /// ``` + /// /// [`single`]: #method.single pub fn single_quoted<T: FromStr>(&mut self) -> Result<T, T::Err> where T::Err: StdError { @@ -162,20 +246,62 @@ impl Args { } /// Empty outs the internal vector while parsing (if necessary) and returning them + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69", " "); + /// + /// assert_eq!(*args.list::<i32>().unwrap(), [42, 69]); + /// ``` pub fn list<T: FromStr>(mut self) -> Result<Vec<T>, T::Err> where T::Err: StdError { Iter::<T>::new(&mut self).collect() } /// Provides an iterator of items: (`T: FromStr`) `Result<T, T::Err>`. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("3 4", " "); + /// + /// assert_eq!(*args.iter::<i32>().map(|num| num.unwrap().pow(2)).collect::<Vec<_>>(), [9, 16]); + /// assert!(args.is_empty()); + /// ``` pub fn iter<T: FromStr>(&mut self) -> Iter<T> where T::Err: StdError { Iter::new(self) } /// This method is just `internal_vector.join(delimiter)` + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("42 69", " "); + /// + /// assert_eq!(args.full(), "42 69"); + /// ``` pub fn full(&self) -> String { self.delimiter_split.join(&self.delimiter) } /// Returns the first argument that can be converted and removes it from the list. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let mut args = Args::new("c47 69", " "); + /// + /// assert_eq!(args.find::<i32>().unwrap(), 69); + /// assert_eq!(args, ["c47"]); + /// ``` pub fn find<T: FromStr>(&mut self) -> Result<T, T::Err> where T::Err: StdError { if self.delimiter_split.is_empty() { @@ -201,6 +327,17 @@ impl Args { } /// Returns the first argument that can be converted and does not remove it from the list. + /// + /// # Examples + /// + /// ```rust + /// use serenity::framework::standard::Args; + /// + /// let args = Args::new("c47 69", " "); + /// + /// assert_eq!(args.find_n::<i32>().unwrap(), 69); + /// assert_eq!(args, ["c47", "69"]); + /// ``` pub fn find_n<T: FromStr>(&self) -> Result<T, T::Err> where T::Err: StdError { if self.delimiter_split.is_empty() { @@ -215,12 +352,111 @@ impl Args { } } +/// A version of `FromStr` that allows for "zero-copy" parsing. +/// +/// # Examples +/// +/// ```rust,ignore +/// use serenity::framework::standard::{Args, FromStrZc}; +/// use std::fmt; +/// +/// struct NameDiscrim<'a>(&'a str, Option<&'a str>); +/// +/// #[derive(Debug)] +/// struct Error(&'static str); +/// +/// impl std::error::Error for Error { +/// fn description(&self) -> &str { self.0 } +/// } +/// +/// impl fmt::Display for Error { +/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } +/// } +/// +/// impl<'a> FromStrZc<'a> for NameDiscrim<'a> { +/// type Err = Error; +/// +/// fn from_str(s: &'a str) -> Result<NameDiscrim<'a>, Error> { +/// let mut it = s.split("#"); +/// let name = it.next().ok_or(Error("name must be specified"))?; +/// let discrim = it.next(); +/// Ok(NameDiscrim(name, discrim)) +/// } +/// } +/// +/// let mut args = Args::new("abc#1234", " "); +/// let NameDiscrim(name, discrim) = args.single_zc::<NameDiscrim>().unwrap(); +/// +/// assert_eq!(name, "abc"); +/// assert_eq!(discrim, Some("1234")); +/// ``` +pub trait FromStrZc<'a>: Sized { + type Err; + + fn from_str(s: &'a str) -> ::std::result::Result<Self, Self::Err>; +} + +impl<'a, T: FromStr> FromStrZc<'a> for T { + type Err = T::Err; + + fn from_str(s: &'a str) -> ::std::result::Result<Self, Self::Err> { + <T as FromStr>::from_str(s) + } +} + impl ::std::ops::Deref for Args { type Target = [String]; fn deref(&self) -> &Self::Target { &self.delimiter_split } } +impl<'a> PartialEq<[&'a str]> for Args { + fn eq(&self, other: &[&str]) -> bool { + let mut b = true; + + for (s, o) in self.delimiter_split.iter().zip(other.iter()) { + if s != o { + b = false; + break; + } + } + + b + } +} + +macro_rules! impl_slices { + ($($num:expr),*) => { + impl<'a> PartialEq<[&'a str; 0]> for Args { + fn eq(&self, _: &[&str; 0]) -> bool { + self.delimiter_split.is_empty() + } + } + + $( + impl<'a> PartialEq<[&'a str; $num]> for Args { + fn eq(&self, other: &[&str; $num]) -> bool { + <Args as PartialEq<[&str]>>::eq(self, &other[..]) + } + } + )* + } +} + +impl_slices! { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32 +} + +impl PartialEq for Args { + fn eq(&self, other: &Self) -> bool { + self.delimiter_split == other.delimiter_split + } +} + +impl Eq for Args {} + use std::marker::PhantomData; /// Provides `list`'s functionality, but as an iterator. diff --git a/src/framework/standard/help_commands.rs b/src/framework/standard/help_commands.rs index ce3e406..ef7732b 100644 --- a/src/framework/standard/help_commands.rs +++ b/src/framework/standard/help_commands.rs @@ -62,9 +62,9 @@ pub fn has_all_requirements(cmd: &Command, msg: &Message) -> bool { if let Ok(permissions) = member.permissions() { if cmd.allowed_roles.is_empty() { - return permissions.administrator() || has_correct_permissions(&cmd, &msg); + return permissions.administrator() || has_correct_permissions(cmd, msg); } else { - return permissions.administrator() || (has_correct_roles(&cmd, &guild, &member) && has_correct_permissions(cmd, msg)); + return permissions.administrator() || (has_correct_roles(cmd, &guild, member) && has_correct_permissions(cmd, msg)); } } } @@ -111,22 +111,20 @@ pub fn with_embeds(_: &mut Context, if name == with_prefix || name == *command_name { match *command { CommandOrAlias::Command(ref cmd) => { - if has_all_requirements(&cmd, &msg) { + if has_all_requirements(cmd, msg) { found = Some((command_name, cmd)); - } - else { + } else { break; } }, CommandOrAlias::Alias(ref name) => { - let actual_command = group.commands.get(name).unwrap(); + let actual_command = &group.commands[name]; match *actual_command { CommandOrAlias::Command(ref cmd) => { - if has_all_requirements(&cmd, &msg) { + if has_all_requirements(cmd, msg) { found = Some((name, cmd)); - } - else { + } else { break; } }, @@ -229,12 +227,9 @@ pub fn with_embeds(_: &mut Context, for name in command_names { let cmd = &commands[name]; - if cmd.help_available { - - if cmd.help_available && has_all_requirements(&cmd, &msg) { - let _ = write!(desc, "`{}`\n", name); - has_commands = true; - } + if cmd.help_available && has_all_requirements(cmd, msg) { + let _ = write!(desc, "`{}`\n", name); + has_commands = true; } } @@ -288,7 +283,7 @@ pub fn plain(_: &mut Context, if name == with_prefix || name == *command_name { match *command { CommandOrAlias::Command(ref cmd) => { - if has_all_requirements(&cmd, &msg) { + if has_all_requirements(cmd, msg) { found = Some((command_name, cmd)); } else { @@ -296,11 +291,11 @@ pub fn plain(_: &mut Context, } }, CommandOrAlias::Alias(ref name) => { - let actual_command = group.commands.get(name).unwrap(); + let actual_command = &group.commands[name]; match *actual_command { CommandOrAlias::Command(ref cmd) => { - if has_all_requirements(&cmd, &msg) { + if has_all_requirements(cmd, msg) { found = Some((name, cmd)); } else { @@ -389,7 +384,7 @@ pub fn plain(_: &mut Context, for name in command_names { let cmd = &commands[name]; - if cmd.help_available && has_all_requirements(&cmd, &msg) { + if cmd.help_available && has_all_requirements(cmd, msg) { let _ = write!(group_help, "`{}` ", name); } } diff --git a/src/framework/standard/mod.rs b/src/framework/standard/mod.rs index 6504c6a..36570af 100644 --- a/src/framework/standard/mod.rs +++ b/src/framework/standard/mod.rs @@ -14,7 +14,7 @@ pub use self::command::CommandOrAlias; pub use self::configuration::Configuration; pub use self::create_command::CreateCommand; pub use self::create_group::CreateGroup; -pub use self::args::{Args, Error as ArgError}; +pub use self::args::{Args, Iter, FromStrZc, Error as ArgError}; use self::command::{AfterHook, BeforeHook}; use std::collections::HashMap; @@ -138,6 +138,31 @@ pub enum DispatchError { WebhookAuthor, } +use std::fmt; + +impl fmt::Debug for DispatchError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::DispatchError::*; + + match *self { + CheckFailed(..) => write!(f, "DispatchError::CheckFailed"), + CommandDisabled(ref s) => f.debug_tuple("DispatchError::CommandDisabled").field(&s).finish(), + BlockedUser => write!(f, "DispatchError::BlockedUser"), + BlockedGuild => write!(f, "DispatchError::BlockedGuild"), + LackOfPermissions(ref perms) => f.debug_tuple("DispatchError::LackOfPermissions").field(&perms).finish(), + RateLimited(ref num) => f.debug_tuple("DispatchError::RateLimited").field(&num).finish(), + OnlyForDM => write!(f, "DispatchError::OnlyForDM"), + OnlyForOwners => write!(f, "DispatchError::OnlyForOwners"), + OnlyForGuilds => write!(f, "DispatchError::OnlyForGuilds"), + LackingRole => write!(f, "DispatchError::LackingRole"), + NotEnoughArguments { ref min, ref given } => f.debug_struct("DispatchError::NotEnoughArguments").field("min", &min).field("given", &given).finish(), + TooManyArguments { ref max, ref given } => f.debug_struct("DispatchError::TooManyArguments").field("max", &max).field("given", &given).finish(), + IgnoredBot => write!(f, "DispatchError::IgnoredBot"), + WebhookAuthor => write!(f, "DispatchError::WebhookAuthor"), + } + } +} + type DispatchErrorHook = Fn(Context, Message, DispatchError) + Send + Sync + 'static; /// A utility for easily managing dispatches to commands. diff --git a/src/gateway/shard.rs b/src/gateway/shard.rs index d7ffb59..3343adc 100644 --- a/src/gateway/shard.rs +++ b/src/gateway/shard.rs @@ -6,6 +6,7 @@ use std::io::Write; use std::net::Shutdown; use std::sync::Arc; use std::time::{Duration as StdDuration, Instant}; +use std::thread; use super::{ConnectionStage, GatewayError}; use websocket::client::Url; use websocket::message::{CloseData, OwnedMessage}; @@ -141,7 +142,7 @@ impl Shard { token: Arc<Mutex<String>>, shard_info: [u64; 2]) -> Result<Shard> { - let client = connect(&*ws_url.lock())?; + let client = connecting(&*ws_url.lock().unwrap()); let current_presence = (None, OnlineStatus::Online); let heartbeat_instants = (None, None); @@ -166,8 +167,8 @@ impl Shard { seq, stage, token, - session_id, shard_info, + session_id, ws_url, manager: VoiceManager::new(tx, user.id), manager_rx: rx, @@ -1131,5 +1132,24 @@ fn set_client_timeout(client: &mut WsClient) -> Result<()> { fn build_gateway_url(base: &str) -> Result<Url> { Url::parse(&format!("{}?v={}", base, constants::GATEWAY_VERSION)) - .map_err(|_| Error::Gateway(GatewayError::BuildingUrl)) + .map_err(|why| { + warn!("Error building gateway URL with base `{}`: {:?}", base, why); + + Error::Gateway(GatewayError::BuildingUrl) + }) +} + +/// Tries to connect and upon failure, retries. +fn connecting(uri: &str) -> WsClient { + let waiting_time = 30; + + loop { + match connect(&uri) { + Ok(client) => return client, + Err(why) => { + warn!("Connecting failed: {:?}\n Will retry in {} seconds.", why, waiting_time); + thread::sleep(StdDuration::from_secs(waiting_time)); + }, + }; + } } diff --git a/src/http/mod.rs b/src/http/mod.rs index d6b32f1..3fb5b7b 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -820,6 +820,24 @@ pub fn edit_role(guild_id: u64, role_id: u64, map: &JsonMap) -> Result<Role> { .map_err(From::from) } +/// Changes the position of a role in a guild. +pub fn edit_role_position(guild_id: u64, role_id: u64, position: u64) -> Result<Vec<Role>> { + let body = serde_json::to_string(&json!({ + "id": role_id, + "position": position, + }))?; + let response = request!( + Route::GuildsIdRolesId(guild_id), + patch(body), + "/guilds/{}/roles/{}", + guild_id, + role_id + ); + + serde_json::from_reader::<HyperResponse, Vec<Role>>(response) + .map_err(From::from) +} + /// Edits a the webhook with the given data. /// /// The Value is a map with optional values of: diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs index 3bed660..158ebcf 100644 --- a/src/model/channel/channel_id.rs +++ b/src/model/channel/channel_id.rs @@ -124,10 +124,14 @@ impl ChannelId { .into_iter() .map(|message_id| message_id.as_ref().0) .collect::<Vec<u64>>(); + + if ids.len() == 1 { + self.delete_message(ids[0]) + } else { + let map = json!({ "messages": ids }); - let map = json!({ "messages": ids }); - - http::delete_messages(self.0, &map) + http::delete_messages(self.0, &map) + } } /// Deletes all permission overrides in the channel from a member or role. diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index dacbfac..3f09bd7 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -260,7 +260,7 @@ impl GuildChannel { /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html #[inline] - pub fn delete_messages<'a, It: IntoIterator<Item=&'a MessageId>>(&self, message_ids: It) -> Result<()> { + pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>(&self, message_ids: It) -> Result<()> { self.id.delete_messages(message_ids) } diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index f7ecc0f..ae46805 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -67,6 +67,7 @@ impl Channel { /// [`Message::react`]: struct.Message.html#method.react /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { @@ -110,6 +111,7 @@ impl Channel { /// [`Message::delete`]: struct.Message.html#method.delete /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn delete_message<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { self.id().delete_message(message_id) @@ -123,6 +125,7 @@ impl Channel { /// [`Reaction`]: struct.Reaction.html /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn delete_reaction<M, R>(&self, message_id: M, @@ -154,6 +157,7 @@ impl Channel { /// [`Message`]: struct.Message.html /// [`the limit`]: ../builder/struct.CreateMessage.html#method.content #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message> where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> { @@ -181,6 +185,7 @@ impl Channel { /// /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> { self.id().message(message_id) @@ -203,6 +208,7 @@ impl Channel { /// /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn messages<F>(&self, f: F) -> Result<Vec<Message>> where F: FnOnce(GetMessages) -> GetMessages { @@ -226,6 +232,7 @@ impl Channel { /// [`User`]: struct.User.html /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn reaction_users<M, R, U>(&self, message_id: M, @@ -264,6 +271,7 @@ impl Channel { /// [`ChannelId`]: struct.ChannelId.html /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn say(&self, content: &str) -> Result<Message> { self.id().say(content) } @@ -286,6 +294,7 @@ impl Channel { /// [Attach Files]: permissions/constant.ATTACH_FILES.html /// [Send Messages]: permissions/constant.SEND_MESSAGES.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn send_files<'a, F, T, It: IntoIterator<Item=T>>(&self, files: It, f: F) -> Result<Message> where F: FnOnce(CreateMessage) -> CreateMessage, T: Into<AttachmentType<'a>> { @@ -312,6 +321,7 @@ impl Channel { /// [`CreateMessage`]: ../builder/struct.CreateMessage.html /// [Send Messages]: permissions/constant.SEND_MESSAGES.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn send_message<F>(&self, f: F) -> Result<Message> where F: FnOnce(CreateMessage) -> CreateMessage { @@ -325,6 +335,7 @@ impl Channel { /// [`Message`]: struct.Message.html /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html #[cfg(feature = "model")] + #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] #[inline] pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { self.id().unpin(message_id) diff --git a/src/model/event.rs b/src/model/event.rs index 90a6828..aea05b1 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -404,7 +404,9 @@ impl CacheUpdate for GuildEmojisUpdateEvent { fn update(&mut self, cache: &mut Cache) -> Option<()> { cache.guilds.get_mut(&self.guild_id).map(|guild| { - guild.with_mut(|g| g.emojis.extend(self.emojis.clone())) + guild.with_mut(|g| { + g.emojis.clone_from(&self.emojis) + }); }); None diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs index 2771fc8..7a851bb 100644 --- a/src/model/guild/guild_id.rs +++ b/src/model/guild/guild_id.rs @@ -110,7 +110,7 @@ impl GuildId { pub fn create_channel(&self, name: &str, kind: ChannelType) -> Result<GuildChannel> { let map = json!({ "name": name, - "type": kind.name(), + "type": kind as u8, }); http::create_channel(self.0, &map) @@ -311,6 +311,27 @@ impl GuildId { http::edit_role(self.0, role_id.into().0, &map) } + /// Edits the order of [`Role`]s + /// Requires the [Manage Roles] permission. + /// + /// # Examples + /// + /// Change the order of a role: + /// + /// ```rust,ignore + /// use serenity::model::{GuildId, RoleId}; + /// GuildId(7).edit_role_position(RoleId(8), 2); + /// ``` + /// + /// [`Role`]: struct.Role.html + /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html + #[inline] + pub fn edit_role_position<R>(&self, role_id: R, position: u64) -> Result<Vec<Role>> + where R: Into<RoleId> { + http::edit_role_position(self.0, role_id.into().0, position) + } + + /// Search the cache for the guild. #[cfg(feature = "cache")] pub fn find(&self) -> Option<Arc<RwLock<Guild>>> { CACHE.read().guild(*self) } diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index a90445a..7aeb98f 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -600,6 +600,26 @@ impl Guild { self.id.edit_role(role_id, f) } + /// Edits the order of [`Role`]s + /// Requires the [Manage Roles] permission. + /// + /// # Examples + /// + /// Change the order of a role: + /// + /// ```rust,ignore + /// use serenity::model::RoleId; + /// guild.edit_role_position(RoleId(8), 2); + /// ``` + /// + /// [`Role`]: struct.Role.html + /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html + #[inline] + pub fn edit_role_position<R>(&self, role_id: R, position: u64) -> Result<Vec<Role>> + where R: Into<RoleId> { + self.id.edit_role_position(role_id, position) + } + /// Gets a partial amount of guild data by its Id. /// /// Requires that the current user be in the guild. diff --git a/src/model/misc.rs b/src/model/misc.rs index af49a76..3398a14 100644 --- a/src/model/misc.rs +++ b/src/model/misc.rs @@ -125,9 +125,10 @@ impl FromStr for UserId { type Err = UserIdParseError; fn from_str(s: &str) -> StdResult<Self, Self::Err> { - utils::parse_username(s) - .ok_or_else(|| UserIdParseError::InvalidFormat) - .map(UserId) + Ok(match utils::parse_username(s) + Some(id) => UserId(id), + None => s.parse::<u64>().map(UserId).map_err(|_| UserIdParseError::InvalidFormat) + }) } } diff --git a/src/model/user.rs b/src/model/user.rs index 0d8388d..1bf69e4 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -349,7 +349,7 @@ impl Default for OnlineStatus { } /// Information about a user. -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, Deserialize)] pub struct User { /// The unique Id of the user. Can be used to calculate the account's /// cration date. @@ -371,6 +371,22 @@ pub struct User { pub name: String, } +use std::hash::{Hash, Hasher}; + +impl PartialEq for User { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for User {} + +impl Hash for User { + fn hash<H: Hasher>(&self, hasher: &mut H) { + self.id.hash(hasher); + } +} + #[cfg(feature = "model")] impl User { /// Returns the formatted URL of the user's icon, if one exists. @@ -584,7 +600,11 @@ impl User { CACHE.read() .guilds .get(&_guild_id) - .map(|g| g.read().roles.contains_key(&role_id)) + .map(|g| { + g.read().unwrap().members.get(&self.id) + .map(|m| m.roles.contains(&role_id)) + .unwrap_or(false) + }) .unwrap_or(false) } else { true diff --git a/src/prelude.rs b/src/prelude.rs index ff27bb6..435fb8a 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -16,6 +16,7 @@ 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}; |