aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoracdenisSK <[email protected]>2017-10-24 18:10:10 +0200
committeracdenisSK <[email protected]>2017-10-24 18:10:10 +0200
commitef60c3cd5b93d61ff8200f5f6871b449bf7dccb5 (patch)
treeff9ec9e09dc363c05c6b582380a120ca47290a9f /src
parentRemove `on_` prefix to EventHandler tymethods (diff)
parentFall back to `str::parse` if `parse_username` fails (diff)
downloadserenity-ef60c3cd5b93d61ff8200f5f6871b449bf7dccb5.tar.xz
serenity-ef60c3cd5b93d61ff8200f5f6871b449bf7dccb5.zip
Merge v0.4.2
Diffstat (limited to 'src')
-rw-r--r--src/framework/standard/args.rs238
-rw-r--r--src/framework/standard/help_commands.rs33
-rw-r--r--src/framework/standard/mod.rs27
-rw-r--r--src/gateway/shard.rs26
-rw-r--r--src/http/mod.rs18
-rw-r--r--src/model/channel/channel_id.rs10
-rw-r--r--src/model/channel/guild_channel.rs2
-rw-r--r--src/model/channel/mod.rs11
-rw-r--r--src/model/event.rs4
-rw-r--r--src/model/guild/guild_id.rs23
-rw-r--r--src/model/guild/mod.rs20
-rw-r--r--src/model/misc.rs7
-rw-r--r--src/model/user.rs24
-rw-r--r--src/prelude.rs1
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};