diff options
| author | Zeyla Hellyer <[email protected]> | 2018-03-25 19:24:19 -0700 |
|---|---|---|
| committer | Zeyla Hellyer <[email protected]> | 2018-03-25 19:24:19 -0700 |
| commit | 461ebe03d58d1ec78ed5addb65e688d63ecfcf45 (patch) | |
| tree | 84475e0717d089551b7e615cdf7d2cfcbff32d57 /src/model | |
| parent | Rewrite the library to use Futures (diff) | |
| download | serenity-461ebe03d58d1ec78ed5addb65e688d63ecfcf45.tar.xz serenity-461ebe03d58d1ec78ed5addb65e688d63ecfcf45.zip | |
Remove cache/http methods on structs
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/channel/attachment.rs | 1 | ||||
| -rw-r--r-- | src/model/channel/channel_category.rs | 87 | ||||
| -rw-r--r-- | src/model/channel/embed.rs | 7 | ||||
| -rw-r--r-- | src/model/channel/group.rs | 319 | ||||
| -rw-r--r-- | src/model/channel/guild_channel.rs | 599 | ||||
| -rw-r--r-- | src/model/channel/message.rs | 471 | ||||
| -rw-r--r-- | src/model/channel/mod.rs | 285 | ||||
| -rw-r--r-- | src/model/channel/private_channel.rs | 295 | ||||
| -rw-r--r-- | src/model/channel/reaction.rs | 148 | ||||
| -rw-r--r-- | src/model/gateway.rs | 1 | ||||
| -rw-r--r-- | src/model/guild/emoji.rs | 139 | ||||
| -rw-r--r-- | src/model/guild/guild_id.rs | 1 | ||||
| -rw-r--r-- | src/model/guild/member.rs | 384 | ||||
| -rw-r--r-- | src/model/guild/mod.rs | 819 | ||||
| -rw-r--r-- | src/model/guild/partial_guild.rs | 406 | ||||
| -rw-r--r-- | src/model/guild/role.rs | 74 | ||||
| -rw-r--r-- | src/model/invite.rs | 80 | ||||
| -rw-r--r-- | src/model/mod.rs | 3 | ||||
| -rw-r--r-- | src/model/permissions.rs | 1 | ||||
| -rw-r--r-- | src/model/user.rs | 385 | ||||
| -rw-r--r-- | src/model/utils.rs | 11 | ||||
| -rw-r--r-- | src/model/webhook.rs | 145 |
22 files changed, 14 insertions, 4647 deletions
diff --git a/src/model/channel/attachment.rs b/src/model/channel/attachment.rs index 65df67e..8e3f149 100644 --- a/src/model/channel/attachment.rs +++ b/src/model/channel/attachment.rs @@ -20,7 +20,6 @@ pub struct Attachment { pub width: Option<u64>, } -#[cfg(feature = "model")] impl Attachment { /// If this attachment is an image, then a tuple of the width and height /// in pixels is returned. diff --git a/src/model/channel/channel_category.rs b/src/model/channel/channel_category.rs index 451d550..0fd1a69 100644 --- a/src/model/channel/channel_category.rs +++ b/src/model/channel/channel_category.rs @@ -1,11 +1,5 @@ -use client::Client; -use futures::{Future, future}; use model::prelude::*; -use std::rc::Rc; -use ::FutureResult; -#[cfg(all(feature = "builder", feature = "model"))] -use builder::EditChannel; #[cfg(feature = "utils")] use utils as serenity_utils; @@ -37,90 +31,9 @@ pub struct ChannelCategory { /// /// [`GuildChannel`]: struct.GuildChannel.html pub permission_overwrites: Vec<PermissionOverwrite>, - #[serde(skip)] - pub(crate) client: Option<Rc<Client>>, } impl ChannelCategory { - /// Adds a permission overwrite to the category's channels. - #[inline] - pub fn create_permission(&self, target: &PermissionOverwrite) -> FutureResult<()> { - ftryopt!(self.client) - .http - .create_permission(self.id.0, target) - } - - /// Deletes all permission overrides in the category from the channels. - /// - /// **Note**: Requires the [Manage Channel] permission. - /// - /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html - #[inline] - pub fn delete_permission(&self, permission_type: PermissionOverwriteType) - -> FutureResult<()> { - let id = match permission_type { - PermissionOverwriteType::Member(id) => id.0, - PermissionOverwriteType::Role(id) => id.0, - }; - - let done = ftryopt!(self.client) - .http - .delete_permission(self.id.0, id); - - Box::new(done) - } - - /// Deletes this category. - #[inline] - pub fn delete(&self) -> FutureResult<()> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_CHANNELS; - - if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - let done = client.http.delete_channel(self.id.0).map(|_| ()); - - Box::new(done) - } - - /// Modifies the category's settings, such as its position or name. - /// - /// Refer to `EditChannel`s documentation for a full list of methods. - /// - /// # Examples - /// - /// Change a voice channels name and bitrate: - /// - /// ```rust,ignore - /// category.edit(|c| c.name("test").bitrate(86400)); - /// ``` - #[cfg(all(feature = "builder", feature = "model", feature = "utils"))] - pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F) - -> FutureResult<GuildChannel> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_CHANNELS; - - if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.edit_channel(self.id.0, f) - } - #[cfg(feature = "utils")] #[inline] pub fn is_nsfw(&self) -> bool { diff --git a/src/model/channel/embed.rs b/src/model/channel/embed.rs index 8837a8d..79dee11 100644 --- a/src/model/channel/embed.rs +++ b/src/model/channel/embed.rs @@ -1,11 +1,9 @@ -#[cfg(feature = "model")] use builder::CreateEmbed; -#[cfg(feature = "model")] use internal::prelude::*; +use utils; + #[cfg(feature = "utils")] use utils::Colour; -#[cfg(feature = "model")] -use utils; /// Represents a rich embed which allows using richer markdown, multiple fields /// and more. This was heavily inspired by [slack's attachments]. @@ -65,7 +63,6 @@ pub struct Embed { pub video: Option<EmbedVideo>, } -#[cfg(feature = "model")] impl Embed { /// Creates a fake Embed, giving back a `serde_json` map. /// diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs index f55a9dd..6573052 100644 --- a/src/model/channel/group.rs +++ b/src/model/channel/group.rs @@ -1,17 +1,9 @@ use chrono::{DateTime, FixedOffset}; -use client::Client; -use futures::{Future, future}; use model::prelude::*; -use std::cell::RefCell; -use std::rc::Rc; -use ::FutureResult; - -// #[cfg(feature = "model")] -use builder::{CreateMessage, EditMessage, GetMessages}; -#[cfg(feature = "model")] use std::borrow::Cow; -#[cfg(feature = "model")] +use std::cell::RefCell; use std::fmt::Write as FmtWrite; +use std::rc::Rc; /// A group channel - potentially including other [`User`]s - separate from a /// [`Guild`]. @@ -37,162 +29,9 @@ pub struct Group { #[serde(deserialize_with = "deserialize_users", serialize_with = "serialize_users")] pub recipients: HashMap<UserId, Rc<RefCell<User>>>, - #[serde(skip)] - pub(crate) client: Option<Rc<Client>>, } impl Group { - /// Adds the given user to the group. If the user is already in the group, - /// then nothing is done. - /// - /// Refer to [`http::add_group_recipient`] for more information. - /// - /// **Note**: Groups have a limit of 10 recipients, including the current - /// user. - /// - /// [`http::add_group_recipient`]: ../http/fn.add_group_recipient.html - pub fn add_recipient<U: Into<UserId>>(&self, user: U) -> FutureResult<()> { - let user = user.into(); - - // If the group already contains the recipient, do nothing. - if self.recipients.contains_key(&user) { - return Box::new(future::ok(())); - } - - ftryopt!(self.client) - .http - .add_group_recipient(self.channel_id.0, user.0) - } - - /// Broadcasts that the current user is typing in the group. - #[inline] - pub fn broadcast_typing(&self) -> FutureResult<()> { - ftryopt!(self.client).http.broadcast_typing(self.channel_id.0) - } - - /// React to a [`Message`] with a custom [`Emoji`] or unicode character. - /// - /// [`Message::react`] may be a more suited method of reacting in most - /// cases. - /// - /// Requires the [Add Reactions] permission, _if_ the current user is the - /// first user to perform a react with a certain emoji. - /// - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`Message::react`]: struct.Message.html#method.react - /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html - #[inline] - pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) - -> FutureResult<()> - where M: Into<MessageId>, R: Into<ReactionType> { - ftryopt!(self.client).http.create_reaction( - self.channel_id.0, - message_id.into().0, - &reaction_type.into(), - ) - } - - /// Deletes all messages by Ids from the given vector in the channel. - /// - /// Refer to [`Channel::delete_messages`] for more information. - /// - /// Requires the [Manage Messages] permission. - /// - /// **Note**: Messages that are older than 2 weeks can't be deleted using - /// this method. - /// - /// # Errors - /// - /// Returns [`ModelError::BulkDeleteAmount`] if an attempt was made to - /// delete either 0 or more than 100 messages. - /// - /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages - /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>( - &self, - message_ids: It, - ) -> FutureResult<()> { - ftryopt!(self.client).http.delete_messages( - self.channel_id.0, - message_ids, - ) - } - - /// Deletes all permission overrides in the channel from a member - /// or role. - /// - /// **Note**: Requires the [Manage Channel] permission. - /// - /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html - #[inline] - pub fn delete_permission(&self, permission_type: PermissionOverwriteType) - -> FutureResult<()> { - let overwrite_id = match permission_type { - PermissionOverwriteType::Member(id) => id.0, - PermissionOverwriteType::Role(id) => id.0, - }; - - ftryopt!(self.client).http.delete_permission( - self.channel_id.0, - overwrite_id, - ) - } - - /// Deletes the given [`Reaction`] from the channel. - /// - /// **Note**: Requires the [Manage Messages] permission, _if_ the current - /// user did not perform the reaction. - /// - /// [`Reaction`]: struct.Reaction.html - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn delete_reaction<M, R>( - &self, - message_id: M, - user_id: Option<UserId>, - reaction_type: R, - ) -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> { - ftryopt!(self.client).http.delete_reaction( - self.channel_id.0, - message_id.into().0, - user_id.map(|x| x.0), - &reaction_type.into(), - ) - } - - /// Edits a [`Message`] in the channel given its Id. - /// - /// Message editing preserves all unchanged message data. - /// - /// Refer to the documentation for [`EditMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// **Note**: Requires that the current user be the author of the message. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the [`the limit`], containing the number of unicode code points - /// over the limit. - /// - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`EditMessage`]: ../builder/struct.EditMessage.html - /// [`Message`]: struct.Message.html - /// [`the limit`]: ../builder/struct.EditMessage.html#method.content - #[inline] - pub fn edit_message<F, M>(&self, message_id: M, f: F) - -> FutureResult<Message> - where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> { - ftryopt!(self.client).http.edit_message( - self.channel_id.0, - message_id.into().0, - f, - ) - } - /// Returns the formatted URI of the group's icon if one exists. pub fn icon_url(&self) -> Option<String> { self.icon.as_ref().map(|icon| { @@ -211,33 +50,6 @@ impl Group { #[inline] pub fn is_nsfw(&self) -> bool { false } - /// Leaves the group. - #[inline] - pub fn leave(&self) -> FutureResult<()> { - ftryopt!(self.client).http.leave_group(self.channel_id.0) - } - - /// Gets a message from the channel. - /// - /// Requires the [Read Message History] permission. - /// - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn message<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<Message> { - ftryopt!(self.client).http.get_message(self.channel_id.0, message_id.into().0) - } - - /// Gets messages from the channel. - /// - /// Requires the [Read Message History] permission. - /// - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn messages<'a, F: FnOnce(GetMessages) -> GetMessages>(&'a self, f: F) - -> Box<Future<Item = Vec<Message>, Error = Error> + 'a> { - ftryopt!(self.client).http.get_messages(self.channel_id.0, f) - } - /// Generates a name for the group. /// /// If there are no recipients in the group, the name will be "Empty Group". @@ -262,131 +74,4 @@ impl Group { }, } } - - /// Retrieves the list of messages that have been pinned in the group. - #[inline] - pub fn pins(&self) -> FutureResult<Vec<Message>> { - ftryopt!(self.client).http.get_pins(self.channel_id.0) - } - - /// Gets the list of [`User`]s who have reacted to a [`Message`] with a - /// certain [`Emoji`]. - /// - /// Refer to [`Channel::reaction_users`] for more information. - /// - /// **Note**: Requires the [Read Message History] permission. - /// - /// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`User`]: struct.User.html - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn reaction_users<M, R, U>( - &self, - message_id: M, - reaction_type: R, - limit: Option<u8>, - after: U, - ) -> FutureResult<Vec<User>> - where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> { - ftryopt!(self.client).http.get_reaction_users( - self.channel_id.0, - message_id.into().0, - &reaction_type.into(), - limit, - after.into().map(|x| x.0), - ) - } - - /// Removes a recipient from the group. If the recipient is already not in - /// the group, then nothing is done. - /// - /// **Note**: This is only available to the group owner. - pub fn remove_recipient<U>(&self, user: U) -> FutureResult<()> - where U: Into<UserId> { - let user = user.into(); - - // If the group does not contain the recipient already, do nothing. - if !self.recipients.contains_key(&user) { - return Box::new(future::ok(())); - } - - ftryopt!(self.client).http.remove_group_recipient( - self.channel_id.0, - user.0, - ) - } - - /// Sends a message with just the given message content in the channel. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// [`ChannelId`]: ../model/id/struct.ChannelId.html - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - #[inline] - pub fn say(&self, content: &str) -> FutureResult<Message> { - ftryopt!(self.client).http.send_message(self.channel_id.0, |f| f - .content(content)) - } - - /// Sends (a) file(s) along with optional message contents. - /// - /// Refer to [`ChannelId::send_files`] for examples and more information. - /// - /// The [Attach Files] and [Send Messages] permissions are required. - /// - /// **Note**: Message contents must be under 2000 unicode code points. - /// - /// # Errors - /// - /// If the content of the message is over the above limit, then a - /// [`ClientError::MessageTooLong`] will be returned, containing the number - /// of unicode code points over the limit. - /// - /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files - /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong - /// [Attach Files]: permissions/constant.ATTACH_FILES.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - // todo - // #[inline] - // pub fn send_files<'a, F, T, It>(&self, files: It, f: F) - // -> FutureResult<Message> - // where F: FnOnce(CreateMessage) -> CreateMessage, - // T: Into<AttachmentType<'a>>, - // It: IntoIterator<Item=T> { - // ftryopt!(self.client).http.send_files(self.channel_id.0, files, f) - // } - - /// Sends a message to the group with the given content. - /// - /// Refer to the documentation for [`CreateMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// **Note**: Requires the [Send Messages] permission. - /// - /// [`CreateMessage`]: ../builder/struct.CreateMessage.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - #[inline] - pub fn send_message<F>(&self, f: F) -> FutureResult<Message> - where F: FnOnce(CreateMessage) -> CreateMessage { - ftryopt!(self.client).http.send_message(self.channel_id.0, f) - } - - /// Unpins a [`Message`] in the channel given by its Id. - /// - /// Requires the [Manage Messages] permission. - /// - /// [`Message`]: struct.Message.html - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> { - ftryopt!(self.client) - .http - .unpin_message(self.channel_id.0, message_id.into().0) - } } diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index ea99377..0ec65eb 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -1,17 +1,8 @@ use chrono::{DateTime, FixedOffset}; -use futures::{Future, future}; use model::prelude::*; -use std::cell::RefCell; -use super::super::WrappedClient; -use ::FutureResult; -#[cfg(feature = "model")] -use builder::{CreateInvite, CreateMessage, EditChannel, EditMessage, GetMessages}; -#[cfg(all(feature = "cache", feature = "model"))] -use internal::prelude::*; -#[cfg(feature = "model")] use std::fmt::{Display, Formatter, Result as FmtResult}; -#[cfg(all(feature = "model", feature = "utils"))] +#[cfg(feature = "utils")] use utils as serenity_utils; /// Represents a guild's text or voice channel. Some methods are available only @@ -75,326 +66,9 @@ pub struct GuildChannel { // default to `false`. #[serde(default)] pub nsfw: bool, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl GuildChannel { - /// Broadcasts to the channel that the current user is typing. - /// - /// For bots, this is a good indicator for long-running commands. - /// - /// **Note**: Requires the [Send Messages] permission. - /// - /// # Errors - /// - /// Returns a [`ModelError::InvalidPermissions`] if the current user does - /// not have the required permissions. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - pub fn broadcast_typing(&self) -> FutureResult<()> { - ftryopt!(self.client).http.broadcast_typing(self.id.0) - } - - /// Creates an invite leading to the given channel. - /// - /// # Examples - /// - /// Create an invite that can only be used 5 times: - /// - /// ```rust,ignore - /// let invite = channel.create_invite(|i| i.max_uses(5)); - /// ``` - #[cfg(feature = "utils")] - pub fn create_invite<F>(&self, f: F) -> FutureResult<RichInvite> - where F: FnOnce(CreateInvite) -> CreateInvite { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::CREATE_INVITE; - - if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.create_invite(self.id.0, f) - } - - /// Creates a [permission overwrite][`PermissionOverwrite`] for either a - /// single [`Member`] or [`Role`] within a [`Channel`]. - /// - /// Refer to the documentation for [`PermissionOverwrite`]s for more - /// information. - /// - /// Requires the [Manage Channels] permission. - /// - /// # Examples - /// - /// Creating a permission overwrite for a member by specifying the - /// [`PermissionOverwrite::Member`] variant, allowing it the [Send Messages] - /// permission, but denying the [Send TTS Messages] and [Attach Files] - /// permissions: - /// - /// ```rust,no_run - /// # use serenity::model::id::{ChannelId, UserId}; - /// # use std::error::Error; - /// # - /// # fn try_main() -> Result<(), Box<Error>> { - /// # let (channel_id, user_id) = (ChannelId(0), UserId(0)); - /// # - /// use serenity::model::channel::{ - /// PermissionOverwrite, - /// PermissionOverwriteType, - /// }; - /// use serenity::model::{ModelError, Permissions}; - /// use serenity::CACHE; - /// - /// let allow = Permissions::SEND_MESSAGES; - /// let deny = Permissions::SEND_TTS_MESSAGES | Permissions::ATTACH_FILES; - /// let overwrite = PermissionOverwrite { - /// allow: allow, - /// deny: deny, - /// kind: PermissionOverwriteType::Member(user_id), - /// }; - /// - /// let cache = CACHE.read(); - /// let channel = cache - /// .guild_channel(channel_id) - /// .ok_or(ModelError::ItemMissing)?; - /// - /// channel.read().create_permission(&overwrite)?; - /// # Ok(()) - /// # } - /// # - /// # fn main() { - /// # try_main().unwrap(); - /// # } - /// ``` - /// - /// Creating a permission overwrite for a role by specifying the - /// [`PermissionOverwrite::Role`] variant, allowing it the [Manage Webhooks] - /// permission, but denying the [Send TTS Messages] and [Attach Files] - /// permissions: - /// - /// ```rust,no_run - /// # use serenity::model::id::{ChannelId, UserId}; - /// # use std::error::Error; - /// # - /// # fn try_main() -> Result<(), Box<Error>> { - /// # let (channel_id, user_id) = (ChannelId(0), UserId(0)); - /// # - /// use serenity::model::channel::{ - /// PermissionOverwrite, - /// PermissionOverwriteType, - /// }; - /// use serenity::model::{ModelError, Permissions}; - /// use serenity::CACHE; - /// - /// let allow = Permissions::SEND_MESSAGES; - /// let deny = Permissions::SEND_TTS_MESSAGES | Permissions::ATTACH_FILES; - /// let overwrite = PermissionOverwrite { - /// allow: allow, - /// deny: deny, - /// kind: PermissionOverwriteType::Member(user_id), - /// }; - /// - /// let cache = CACHE.read(); - /// let channel = cache - /// .guild_channel(channel_id) - /// .ok_or(ModelError::ItemMissing)?; - /// - /// channel.read().create_permission(&overwrite)?; - /// # Ok(()) - /// # } - /// # - /// # fn main() { - /// # try_main().unwrap(); - /// # } - /// ``` - /// - /// [`Channel`]: enum.Channel.html - /// [`Member`]: struct.Member.html - /// [`PermissionOverwrite`]: struct.PermissionOverwrite.html - /// [`PermissionOverwrite::Member`]: struct.PermissionOverwrite.html#variant.Member - /// [`PermissionOverwrite::Role`]: struct.PermissionOverwrite.html#variant.Role - /// [`Role`]: struct.Role.html - /// [Attach Files]: permissions/constant.ATTACH_FILES.html - /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html - /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - /// [Send TTS Messages]: permissions/constant.SEND_TTS_MESSAGES.html - #[inline] - pub fn create_permission(&self, target: &PermissionOverwrite) - -> FutureResult<()> { - ftryopt!(self.client).http.create_permission(self.id.0, target) - } - - /// Deletes this channel, returning the channel on a successful deletion. - pub fn delete(&self) -> FutureResult<Channel> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_CHANNELS; - - if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.delete_channel(self.id.0) - } - - /// Deletes all messages by Ids from the given vector in the channel. - /// - /// Refer to [`Channel::delete_messages`] for more information. - /// - /// Requires the [Manage Messages] permission. - /// - /// **Note**: Messages that are older than 2 weeks can't be deleted using - /// this method. - /// - /// # Errors - /// - /// Returns [`ModelError::BulkDeleteAmount`] if an attempt was made to - /// delete either 0 or more than 100 messages. - /// - /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages - /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn delete_messages<T, It>(&self, message_ids: It) -> FutureResult<()> - where T: AsRef<MessageId>, It: IntoIterator<Item=T> { - ftryopt!(self.client).http.delete_messages(self.id.0, message_ids) - } - - /// Deletes all permission overrides in the channel from a member - /// or role. - /// - /// **Note**: Requires the [Manage Channel] permission. - /// - /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html - #[inline] - pub fn delete_permission(&self, permission_type: PermissionOverwriteType) - -> FutureResult<()> { - let overwrite_id = match permission_type { - PermissionOverwriteType::Member(id) => id.0, - PermissionOverwriteType::Role(id) => id.0, - }; - - ftryopt!(self.client).http.delete_permission(self.id.0, overwrite_id) - } - - /// Deletes the given [`Reaction`] from the channel. - /// - /// **Note**: Requires the [Manage Messages] permission, _if_ the current - /// user did not perform the reaction. - /// - /// [`Reaction`]: struct.Reaction.html - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn delete_reaction<M, R>( - &self, - message_id: M, - user_id: Option<UserId>, - reaction_type: R, - ) -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> { - ftryopt!(self.client).http.delete_reaction( - self.id.0, - message_id.into().0, - user_id.map(|x| x.0), - &reaction_type.into(), - ) - } - - /// Modifies a channel's settings, such as its position or name. - /// - /// Refer to `EditChannel`s documentation for a full list of methods. - /// - /// # Examples - /// - /// Change a voice channels name and bitrate: - /// - /// ```rust,ignore - /// channel.edit(|c| c.name("test").bitrate(86400)); - /// ``` - #[cfg(feature = "utils")] - pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F) - -> FutureResult<GuildChannel> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_CHANNELS; - - if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.edit_channel(self.id.0, f) - } - - /// Edits a [`Message`] in the channel given its Id. - /// - /// Message editing preserves all unchanged message data. - /// - /// Refer to the documentation for [`EditMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// **Note**: Requires that the current user be the author of the message. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the [`the limit`], containing the number of unicode code points - /// over the limit. - /// - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`EditMessage`]: ../builder/struct.EditMessage.html - /// [`Message`]: struct.Message.html - /// [`the limit`]: ../builder/struct.EditMessage.html#method.content - #[inline] - pub fn edit_message<F, M>(&self, message_id: M, f: F) - -> FutureResult<Message> - where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> { - ftryopt!(self.client).http.edit_message( - self.id.0, - message_id.into().0, - f, - ) - } - - /// Attempts to find this channel's guild in the Cache. - /// - /// **Note**: Right now this performs a clone of the guild. This will be - /// optimized in the future. - #[cfg(feature = "cache")] - pub fn guild(&self) -> Option<Rc<RefCell<Guild>>> { - self.client.as_ref()?.cache.try_borrow().ok().and_then(|cache| { - cache.guild(self.guild_id) - }) - } - - /// Gets all of the channel's invites. - /// - /// Requires the [Manage Channels] permission. - /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html - #[inline] - pub fn invites(&self) -> FutureResult<Vec<RichInvite>> { - ftryopt!(self.client).http.get_channel_invites(self.id.0) - } - /// Determines if the channel is NSFW. /// /// Refer to [`utils::is_nsfw`] for more details. @@ -411,281 +85,10 @@ impl GuildChannel { self.kind == ChannelType::Text && (self.nsfw || serenity_utils::is_nsfw(&self.name)) } - /// Gets a message from the channel. - /// - /// Requires the [Read Message History] permission. - /// - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn message<M: Into<MessageId>>(&self, message_id: M) - -> FutureResult<Message> { - ftryopt!(self.client).http.get_message(self.id.0, message_id.into().0) - } - - /// Gets messages from the channel. - /// - /// Refer to [`Channel::messages`] for more information. - /// - /// Requires the [Read Message History] permission. - /// - /// [`Channel::messages`]: enum.Channel.html#method.messages - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn messages<'a, F: FnOnce(GetMessages) -> GetMessages>(&'a self, f: F) - -> Box<Future<Item = Vec<Message>, Error = Error> + 'a> { - ftryopt!(self.client).http.get_messages(self.id.0, f) - } - /// Returns the name of the guild channel. pub fn name(&self) -> &str { &self.name } - - /// Calculates the permissions of a member. - /// - /// The Id of the argument must be a [`Member`] of the [`Guild`] that the - /// channel is in. - /// - /// # Examples - /// - /// Calculate the permissions of a [`User`] who posted a [`Message`] in a - /// channel: - /// - /// ```rust,no_run - /// use serenity::prelude::*; - /// use serenity::model::prelude::*; - /// struct Handler; - /// - /// use serenity::CACHE; - /// - /// impl EventHandler for Handler { - /// fn message(&self, _: Context, msg: Message) { - /// let channel = match CACHE.read().guild_channel(msg.channel_id) { - /// Some(channel) => channel, - /// None => return, - /// }; - /// - /// let permissions = channel.read().permissions_for(&msg.author).unwrap(); - /// - /// println!("The user's permissions: {:?}", permissions); - /// } - /// } - /// let mut client = Client::new("token", Handler).unwrap(); - /// - /// client.start().unwrap(); - /// ``` - /// - /// Check if the current user has the [Attach Files] and [Send Messages] - /// permissions (note: serenity will automatically check this for; this is - /// for demonstrative purposes): - /// - /// ```rust,no_run - /// use serenity::CACHE; - /// use serenity::prelude::*; - /// use serenity::model::prelude::*; - /// use std::fs::File; - /// - /// struct Handler; - /// - /// impl EventHandler for Handler { - /// fn message(&self, _: Context, msg: Message) { - /// let channel = match CACHE.read().guild_channel(msg.channel_id) { - /// Some(channel) => channel, - /// None => return, - /// }; - /// - /// let current_user_id = CACHE.read().user.id; - /// let permissions = - /// channel.read().permissions_for(current_user_id).unwrap(); - /// - /// if !permissions.contains(Permissions::ATTACH_FILES | Permissions::SEND_MESSAGES) { - /// return; - /// } - /// - /// let file = match File::open("./cat.png") { - /// Ok(file) => file, - /// Err(why) => { - /// println!("Err opening file: {:?}", why); - /// - /// return; - /// }, - /// }; - /// - /// let _ = msg.channel_id.send_files(vec![(&file, "cat.png")], |m| - /// m.content("here's a cat")); - /// } - /// } - /// - /// let mut client = Client::new("token", Handler).unwrap(); - /// - /// client.start().unwrap(); - /// ``` - /// - /// # Errors - /// - /// Returns a [`ModelError::GuildNotFound`] if the channel's guild could - /// not be found in the [`Cache`]. - /// - /// [`Cache`]: ../cache/struct.Cache.html - /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound - /// [`Guild`]: struct.Guild.html - /// [`Member`]: struct.Member.html - /// [`Message`]: struct.Message.html - /// [`User`]: struct.User.html - /// [Attach Files]: permissions/constant.ATTACH_FILES.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - #[cfg(feature = "cache")] - 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.borrow().permissions_in(self.id, user_id)) - } - - /// Pins a [`Message`] to the channel. - #[inline] - pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> { - ftryopt!(self.client).http.pin_message(self.id.0, message_id.into().0) - } - - /// Gets all channel's pins. - #[inline] - pub fn pins(&self) -> FutureResult<Vec<Message>> { - ftryopt!(self.client).http.get_pins(self.id.0) - } - - /// Gets the list of [`User`]s who have reacted to a [`Message`] with a - /// certain [`Emoji`]. - /// - /// Refer to [`Channel::reaction_users`] for more information. - /// - /// **Note**: Requires the [Read Message History] permission. - /// - /// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`User`]: struct.User.html - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - pub fn reaction_users<M, R, U>( - &self, - message_id: M, - reaction_type: R, - limit: Option<u8>, - after: U, - ) -> FutureResult<Vec<User>> - where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> { - ftryopt!(self.client).http.get_reaction_users( - self.id.0, - message_id.into().0, - &reaction_type.into(), - limit, - after.into().map(|x| x.0), - ) - } - - /// Sends a message with just the given message content in the channel. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// [`ChannelId`]: struct.ChannelId.html - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - #[inline] - pub fn say(&self, content: &str) -> FutureResult<Message> { - ftryopt!(self.client) - .http - .send_message(self.id.0, |m| m.content(content)) - } - - /// Sends (a) file(s) along with optional message contents. - /// - /// Refer to [`ChannelId::send_files`] for examples and more information. - /// - /// The [Attach Files] and [Send Messages] permissions are required. - /// - /// **Note**: Message contents must be under 2000 unicode code points. - /// - /// # Errors - /// - /// If the content of the message is over the above limit, then a - /// [`ClientError::MessageTooLong`] will be returned, containing the number - /// of unicode code points over the limit. - /// - /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files - /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong - /// [Attach Files]: permissions/constant.ATTACH_FILES.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - // todo - // #[inline] - // pub fn send_files<'a, F, T, It>(&self, files: It, f: F) - // -> FutureResult<Message> - // where F: FnOnce(CreateMessage) -> CreateMessage, - // T: Into<AttachmentType<'a>>, - // It: IntoIterator<Item=T> { - // ftryopt!(self.client).http.send_files(self.id.0, files, f) - // } - - /// Sends a message to the channel with the given content. - /// - /// **Note**: This will only work when a [`Message`] is received. - /// - /// **Note**: Requires the [Send Messages] permission. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// Returns a [`ModelError::InvalidPermissions`] if the current user does - /// not have the required permissions. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`Message`]: struct.Message.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) - -> FutureResult<Message> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::SEND_MESSAGES; - - if !ftry!(client.cache.borrow().user_has_perms(self.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.send_message(self.id.0, f) - } - - /// Unpins a [`Message`] in the channel given by its Id. - /// - /// Requires the [Manage Messages] permission. - /// - /// [`Message`]: struct.Message.html - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> { - ftryopt!(self.client).http.unpin_message(self.id.0, message_id.into().0) - } - - /// Retrieves the channel's webhooks. - /// - /// **Note**: Requires the [Manage Webhooks] permission. - /// - /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html - #[inline] - pub fn webhooks(&self) -> FutureResult<Vec<Webhook>> { - ftryopt!(self.client).http.get_channel_webhooks(self.id.0) - } } -#[cfg(feature = "model")] impl Display for GuildChannel { /// Formats the channel, creating a mention of it. fn fmt(&self, f: &mut Formatter) -> FmtResult { Display::fmt(&self.id.mention(), f) } diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs index 97c0ab2..fc336df 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message.rs @@ -1,20 +1,9 @@ //! Models relating to Discord channels. use chrono::{DateTime, FixedOffset}; -use futures::future; +use constants; use model::prelude::*; use serde_json::Value; -use std::cell::RefCell; -use std::rc::Rc; -use super::super::WrappedClient; -use ::FutureResult; - -#[cfg(feature = "model")] -use builder::EditMessage; -#[cfg(all(feature = "cache", feature = "model"))] -use std::fmt::Write; -#[cfg(feature = "model")] -use constants; /// A representation of a message over a guild's text channel, a group, or a /// private channel. @@ -66,183 +55,10 @@ pub struct Message { pub tts: bool, /// The Id of the webhook that sent this message, if one did. pub webhook_id: Option<WebhookId>, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl Message { - /// Retrieves the related channel located in the cache. - /// - /// Returns `None` if the channel is not in the cache. - /// - /// # Examples - /// - /// On command, print the name of the channel that a message took place in: - /// - /// ```rust,no_run - /// # #[macro_use] extern crate serenity; - /// # - /// # fn main() { - /// # use serenity::prelude::*; - /// # struct Handler; - /// # - /// # impl EventHandler for Handler {} - /// # let mut client = Client::new("token", Handler).unwrap(); - /// # - /// use serenity::model::channel::Channel; - /// use serenity::framework::StandardFramework; - /// - /// client.with_framework(StandardFramework::new() - /// .configure(|c| c.prefix("~")) - /// .cmd("channelname", channel_name)); - /// - /// command!(channel_name(_ctx, msg) { - /// let _ = match msg.channel() { - /// Some(Channel::Category(c)) => msg.reply(&c.read().name), - /// Some(Channel::Group(c)) => msg.reply(&c.read().name()), - /// Some(Channel::Guild(c)) => msg.reply(&c.read().name), - /// Some(Channel::Private(c)) => { - /// let channel = c.read(); - /// let user = channel.recipient.read(); - /// - /// msg.reply(&format!("DM with {}", user.name.clone())) - /// }, - /// None => msg.reply("Unknown"), - /// }; - /// }); - /// # } - /// ``` - #[cfg(feature = "cache")] - pub fn channel(&self) -> Option<Channel> { - self.client.as_ref()?.cache.try_borrow().ok()?.channel(self.channel_id) - } - - /// A util function for determining whether this message was sent by someone else, or the - /// bot. - #[cfg(all(feature = "cache", feature = "utils"))] - pub fn is_own(&self) -> Result<bool> { - let client = self.client.as_ref().ok_or_else(|| { - Error::Model(ModelError::ClientNotPresent) - })?; - - Ok(self.author.id == client.cache.try_borrow()?.user.id) - } - - /// Deletes the message. - /// - /// **Note**: The logged in user must either be the author of the message or - /// have the [Manage Messages] permission. - /// - /// # Errors - /// - /// If the `cache` feature is enabled, then returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have - /// the required permissions. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - pub fn delete(&self) -> FutureResult<()> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let cache = ftry!(client.cache.try_borrow()); - let req = Permissions::MANAGE_MESSAGES; - let is_author = self.author.id == cache.user.id; - let has_perms = ftry!(cache.user_has_perms(self.channel_id, req)); - - if !is_author && !has_perms { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.delete_message(self.channel_id.0, self.id.0) - } - - /// Deletes all of the [`Reaction`]s associated with the message. - /// - /// **Note**: Requires the [Manage Messages] permission. - /// - /// # Errors - /// - /// If the `cache` feature is enabled, then returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have - /// the required permissions. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`Reaction`]: struct.Reaction.html - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - pub fn delete_reactions(&self) -> FutureResult<()> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_MESSAGES; - - if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.delete_message_reactions( - self.channel_id.0, - self.id.0, - ) - } - - /// Edits this message, replacing the original content with new content. - /// - /// Message editing preserves all unchanged message data. - /// - /// Refer to the documentation for [`EditMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// **Note**: Requires that the current user be the author of the message. - /// - /// # Examples - /// - /// Edit a message with new content: - /// - /// ```rust,ignore - /// // assuming a `message` has already been bound - /// - /// message.edit(|m| m.content("new content")); - /// ``` - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidUser`] if the - /// current user is not the author. - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over [`the limit`], containing the number of unicode code points - /// over the limit. - /// - /// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`EditMessage`]: ../builder/struct.EditMessage.html - /// [`the limit`]: ../builder/struct.EditMessage.html#method.content - pub fn edit<F: FnOnce(EditMessage) -> EditMessage>(&self, f: F) - -> FutureResult<Message> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - if self.author.id != ftry!(client.cache.try_borrow()).user.id { - return Box::new(future::err(Error::Model(ModelError::InvalidUser))); - } - } - - ftryopt!(self.client).http.edit_message(self.channel_id.0, self.id.0, f) - } - - pub(crate) fn transform_content(&mut self) { + pub fn transform_content(&mut self) { match self.kind { MessageType::PinsAdd => { self.content = format!( @@ -264,137 +80,6 @@ impl Message { } } - /// Returns message content, but with user and role mentions replaced with - /// names and everyone/here mentions cancelled. - #[cfg(feature = "cache")] - pub fn content_safe(&self) -> String { - let mut result = self.content.clone(); - - // First replace all user mentions. - for u in &self.mentions { - let mut at_distinct = String::with_capacity(38); - at_distinct.push('@'); - at_distinct.push_str(&u.name); - at_distinct.push('#'); - let _ = write!(at_distinct, "{}", u.discriminator); - result = result.replace(&u.mention(), &at_distinct); - } - - if let Some(guild) = self.guild() { - if let Some(guild) = guild.try_borrow().ok() { - // Then replace all role mentions. - for id in &self.mention_roles { - let mention = id.mention(); - - if let Some(role) = guild.roles.get(id).and_then(|r| r.try_borrow().ok()) { - result = result.replace( - &mention, - &format!("@{}", role.name), - ); - } else { - result = result.replace(&mention, "@deleted-role"); - } - } - } - } - - // And finally replace everyone and here mentions. - result - .replace("@everyone", "@\u{200B}everyone") - .replace("@here", "@\u{200B}here") - } - - /// Gets the list of [`User`]s who have reacted to a [`Message`] with a - /// certain [`Emoji`]. - /// - /// The default `limit` is `50` - specify otherwise to receive a different - /// maximum number of users. The maximum that may be retrieve at a time is - /// `100`, if a greater number is provided then it is automatically reduced. - /// - /// The optional `after` attribute is to retrieve the users after a certain - /// user. This is useful for pagination. - /// - /// **Note**: Requires the [Read Message History] permission. - /// - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`User`]: struct.User.html - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn reaction_users<R, U>( - &self, - reaction_type: R, - limit: Option<u8>, - after: U, - ) -> FutureResult<Vec<User>> - where R: Into<ReactionType>, U: Into<Option<UserId>> { - ftryopt!(self.client).http.get_reaction_users( - self.channel_id.0, - self.id.0, - &reaction_type.into(), - limit, - after.into().map(|x| x.0), - ) - } - - /// Returns the associated `Guild` for the message if one is in the cache. - /// - /// Returns `None` if the guild's Id could not be found via [`guild_id`] or - /// if the Guild itself is not cached. - /// - /// Requires the `cache` feature be enabled. - /// - /// [`guild_id`]: #method.guild_id - #[cfg(feature = "cache")] - pub fn guild(&self) -> Option<Rc<RefCell<Guild>>> { - self.guild_id().and_then(|guild_id| { - self.client.as_ref()?.cache.try_borrow().ok()?.guild(guild_id) - }) - } - - /// Retrieves the Id of the guild that the message was sent in, if sent in - /// one. - /// - /// Returns `None` if the channel data or guild data does not exist in the - /// cache. - #[cfg(feature = "cache")] - pub fn guild_id(&self) -> Option<GuildId> { - match self.client.as_ref()?.cache.try_borrow().ok()?.channel(self.channel_id) { - Some(Channel::Guild(ch)) => Some(ch.borrow().guild_id), - _ => None, - } - } - - /// True if message was sent using direct messages. - /// - /// Returns `false` if a client is not present on the model. - #[cfg(feature = "cache")] - pub fn is_private(&self) -> bool { - self.client.as_ref().map(|client| { - let cache = client.cache.borrow(); - - match cache.channel(self.channel_id) { - Some(Channel::Group(_)) | Some(Channel::Private(_)) => true, - _ => false, - } - }).unwrap_or(false) - } - - /// Retrieves a clone of the author's Member instance, if this message was - /// sent in a guild. - /// - /// Note that since this clones, it is preferable performance-wise to - /// manually retrieve the guild from the cache and access - /// [`Guild::members`]. - /// - /// [`Guild::members`]: ../guild/struct.Guild.html#structfield.members - #[cfg(feature = "cache")] - pub fn member(&self) -> Option<Rc<RefCell<Member>>> { - self.guild().and_then(|guild| { - guild.try_borrow().ok()?.members.get(&self.author.id).cloned() - }) - } - /// Checks the length of a string to ensure that it is within Discord's /// maximum message length limit. /// @@ -414,155 +99,7 @@ impl Message { } } - /// Pins this message to its channel. - /// - /// **Note**: Requires the [Manage Messages] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have - /// the required permissions. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - pub fn pin(&self) -> FutureResult<()> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_MESSAGES; - - if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.pin_message(self.channel_id.0, self.id.0) - } - - /// React to the message with a custom [`Emoji`] or unicode character. - /// - /// **Note**: Requires the [Add Reactions] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have the - /// required [permissions]. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`Emoji`]: struct.Emoji.html - /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html - /// [permissions]: permissions - pub fn react<R: Into<ReactionType>>(&self, reaction_type: R) - -> FutureResult<()> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::ADD_REACTIONS; - - if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.create_reaction( - self.channel_id.0, - self.id.0, - &reaction_type.into(), - ) - } - - /// Replies to the user, mentioning them prior to the content in the form - /// of: `@<USER_ID>: YOUR_CONTENT`. - /// - /// User mentions are generally around 20 or 21 characters long. - /// - /// **Note**: Requires the [Send Messages] permission. - /// - /// **Note**: Message contents must be under 2000 unicode code points. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have - /// the required permissions. - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - pub fn reply(&self, content: &str) -> FutureResult<Message> { - if let Some(length_over) = Message::overflow_length(content) { - return Box::new(future::err(Error::Model( - ModelError::MessageTooLong(length_over), - ))); - } - - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::SEND_MESSAGES; - - if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - let mut gen = self.author.mention(); - gen.push_str(": "); - gen.push_str(content); - - let done = client.http.send_message( - self.channel_id.0, - |f| f.content(gen).tts(false), - ); - - Box::new(done) - } - - /// Unpins the message from its channel. - /// - /// **Note**: Requires the [Manage Messages] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have - /// the required permissions. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - pub fn unpin(&self) -> FutureResult<()> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_MESSAGES; - - if !ftry!(client.cache.borrow().user_has_perms(self.channel_id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - client.http.unpin_message(self.channel_id.0, self.id.0) - } - - pub(crate) fn check_content_length(map: &JsonMap) -> Result<()> { + pub fn check_content_length(map: &JsonMap) -> Result<()> { if let Some(content) = map.get("content") { if let Value::String(ref content) = *content { if let Some(length_over) = Message::overflow_length(content) { @@ -574,7 +111,7 @@ impl Message { Ok(()) } - pub(crate) fn check_embed_length(map: &JsonMap) -> Result<()> { + pub fn check_embed_length(map: &JsonMap) -> Result<()> { let embed = match map.get("embed") { Some(&Value::Object(ref value)) => value, _ => return Ok(()), diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs index 9177064..a527173 100644 --- a/src/model/channel/mod.rs +++ b/src/model/channel/mod.rs @@ -20,20 +20,14 @@ pub use self::private_channel::*; pub use self::reaction::*; pub use self::channel_category::*; -use futures::Future; use model::prelude::*; use serde::de::Error as DeError; use serde::ser::{SerializeStruct, Serialize, Serializer}; use serde_json; use std::cell::RefCell; +use std::fmt::{Display, Formatter, Result as FmtResult}; use std::rc::Rc; use super::utils::deserialize_u64; -use ::FutureResult; - -#[cfg(feature = "model")] -use builder::{CreateMessage, EditMessage}; -#[cfg(feature = "model")] -use std::fmt::{Display, Formatter, Result as FmtResult}; /// A container for any channel. #[derive(Clone, Debug)] @@ -181,131 +175,6 @@ impl Channel { } } - /// React to a [`Message`] with a custom [`Emoji`] or unicode character. - /// - /// [`Message::react`] may be a more suited method of reacting in most - /// cases. - /// - /// Requires the [Add Reactions] permission, _if_ the current user is the - /// first user to perform a react with a certain emoji. - /// - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`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) - -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> { - ftryopt!(ftry!(self.client())).http.create_reaction( - self.id().0, - message_id.into().0, - &reaction_type.into(), - ) - } - - /// Deletes the inner channel. - /// - /// **Note**: There is no real function as _deleting_ a [`Group`]. The - /// closest functionality is leaving it. - /// - /// [`Group`]: struct.Group.html - #[cfg(feature = "model")] - pub fn delete(&self) -> FutureResult<()> { - match *self { - Channel::Group(ref group) => { - Box::new(group.borrow().leave()) - }, - Channel::Guild(ref public_channel) => { - Box::new(public_channel.borrow().delete().map(|_| ())) - }, - Channel::Private(ref private_channel) => { - Box::new(private_channel.borrow().delete().map(|_| ())) - }, - Channel::Category(ref category) => { - Box::new(category.borrow().delete()) - }, - } - } - - /// Deletes a [`Message`] given its Id. - /// - /// Refer to [`Message::delete`] for more information. - /// - /// Requires the [Manage Messages] permission, if the current user is not - /// the author of the message. - /// - /// [`Message`]: struct.Message.html - /// [`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>(&self, message_id: M) -> FutureResult<()> - where M: Into<MessageId> { - ftryopt!(ftry!(self.client())).http.delete_message( - self.id().0, - message_id.into().0, - ) - } - - /// Deletes the given [`Reaction`] from the channel. - /// - /// **Note**: Requires the [Manage Messages] permission, _if_ the current - /// user did not perform the reaction. - /// - /// [`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, - user_id: Option<UserId>, - reaction_type: R, - ) -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> { - ftryopt!(ftry!(self.client())).http.delete_reaction( - self.id().0, - message_id.into().0, - user_id.map(|x| x.0), - &reaction_type.into(), - ) - } - - /// Edits a [`Message`] in the channel given its Id. - /// - /// Message editing preserves all unchanged message data. - /// - /// Refer to the documentation for [`EditMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// **Note**: Requires that the current user be the author of the message. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the [`the limit`], containing the number of unicode code points - /// over the limit. - /// - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`EditMessage`]: ../builder/struct.EditMessage.html - /// [`Message`]: struct.Message.html - /// [`the limit`]: ../builder/struct.EditMessage.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) - -> FutureResult<Message> - where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> { - ftryopt!(ftry!(self.client())).http.edit_message( - self.id().0, - message_id.into().0, - f, - ) - } - /// Determines if the channel is NSFW. /// /// Refer to [`utils::is_nsfw`] for more details. @@ -321,57 +190,6 @@ impl Channel { } } - /// Gets a message from the channel. - /// - /// Requires the [Read Message History] permission. - /// - /// [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>(&self, message_id: M) -> FutureResult<Message> - where M: Into<MessageId> { - ftryopt!(ftry!(self.client())).http.get_message( - self.id().0, - message_id.into().0, - ) - } - - /// Gets the list of [`User`]s who have reacted to a [`Message`] with a - /// certain [`Emoji`]. - /// - /// The default `limit` is `50` - specify otherwise to receive a different - /// maximum number of users. The maximum that may be retrieve at a time is - /// `100`, if a greater number is provided then it is automatically reduced. - /// - /// The optional `after` attribute is to retrieve the users after a certain - /// user. This is useful for pagination. - /// - /// **Note**: Requires the [Read Message History] permission. - /// - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`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, - reaction_type: R, - limit: Option<u8>, - after: U, - ) -> FutureResult<Vec<User>> - where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> { - ftryopt!(ftry!(self.client())).http.get_reaction_users( - self.id().0, - message_id.into().0, - &reaction_type.into(), - limit, - after.into().map(|x| x.0), - ) - } - /// Retrieves the Id of the inner [`Group`], [`GuildChannel`], or /// [`PrivateChannel`]. /// @@ -386,106 +204,6 @@ impl Channel { Channel::Category(ref category) => category.borrow().id, } } - - /// Sends a message with just the given message content in the channel. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// [`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) -> FutureResult<Message> { - ftryopt!(ftry!(self.client())).http.send_message(self.id().0, |f| - f.content(content)) - } - - /// Sends (a) file(s) along with optional message contents. - /// - /// Refer to [`ChannelId::send_files`] for examples and more information. - /// - /// The [Attach Files] and [Send Messages] permissions are required. - /// - /// **Note**: Message contents must be under 2000 unicode code points. - /// - /// # Errors - /// - /// If the content of the message is over the above limit, then a - /// [`ClientError::MessageTooLong`] will be returned, containing the number - /// of unicode code points over the limit. - /// - /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files - /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong - /// [Attach Files]: permissions/constant.ATTACH_FILES.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - // todo - // #[cfg(feature = "model")] - // #[deprecated(since = "0.4.2", note = "Use the inner channel's method")] - // #[inline] - // pub fn send_files<'a, F, T, It>(&self, files: It, f: F) - // -> FutureResult<Message> - // where F: FnOnce(CreateMessage) -> CreateMessage, - // T: Into<AttachmentType<'a>>, - // It: IntoIterator<Item = T> { - // ftryopt!(ftry!(self.client())).http.send_files(self.id(), files, f) - // } - - /// Sends a message to the channel. - /// - /// Refer to the documentation for [`CreateMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// The [Send Messages] permission is required. - /// - /// **Note**: Message contents must be under 2000 unicode code points. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// [`Channel`]: enum.Channel.html - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`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) -> FutureResult<Message> - where F: FnOnce(CreateMessage) -> CreateMessage { - ftryopt!(ftry!(self.client())).http.send_message(self.id().0, f) - } - - /// Unpins a [`Message`] in the channel given by its Id. - /// - /// Requires the [Manage Messages] permission. - /// - /// [`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) -> FutureResult<()> { - let retrieve = ftry!(self.client()); - let obtained = ftryopt!(retrieve); - - obtained.http.unpin_message(self.id().0, message_id.into().0) - } - - fn client(&self) -> Result<WrappedClient> { - Ok(match *self { - Channel::Category(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone), - Channel::Group(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone), - Channel::Guild(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone), - Channel::Private(ref c) => c.try_borrow()?.client.as_ref().map(Rc::clone), - }) - } } impl<'de> Deserialize<'de> for Channel { @@ -535,7 +253,6 @@ impl Serialize for Channel { } } -#[cfg(feature = "model")] impl Display for Channel { /// Formats the channel into a "mentioned" string. /// diff --git a/src/model/channel/private_channel.rs b/src/model/channel/private_channel.rs index 48d887b..74f6785 100644 --- a/src/model/channel/private_channel.rs +++ b/src/model/channel/private_channel.rs @@ -1,15 +1,9 @@ use chrono::{DateTime, FixedOffset}; -use futures::Future; use model::prelude::*; use std::cell::RefCell; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::rc::Rc; -use super::super::WrappedClient; use super::deserialize_single_recipient; -use ::FutureResult; - -#[cfg(feature = "model")] -use builder::{CreateMessage, EditMessage, GetMessages}; /// A Direct Message text channel with another user. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -36,302 +30,13 @@ pub struct PrivateChannel { rename = "recipients", serialize_with = "serialize_user")] pub recipient: Rc<RefCell<User>>, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl PrivateChannel { - /// Broadcasts that the current user is typing to the recipient. - pub fn broadcast_typing(&self) -> FutureResult<()> { - ftryopt!(self.client).http.broadcast_typing(self.id.0) - } - - /// React to a [`Message`] with a custom [`Emoji`] or unicode character. - /// - /// [`Message::react`] may be a more suited method of reacting in most - /// cases. - /// - /// Requires the [Add Reactions] permission, _if_ the current user is the - /// first user to perform a react with a certain emoji. - /// - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`Message::react`]: struct.Message.html#method.react - /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html - pub fn create_reaction<M, R>(&self, message_id: M, reaction_type: R) - -> FutureResult<()> where M: Into<MessageId>, R: Into<ReactionType> { - ftryopt!(self.client).http.create_reaction( - self.id.0, - message_id.into().0, - &reaction_type.into(), - ) - } - - /// Deletes the channel. This does not delete the contents of the channel, - /// and is equivalent to closing a private channel on the client, which can - /// be re-opened. - #[inline] - pub fn delete(&self) -> FutureResult<Channel> { - ftryopt!(self.client).http.delete_channel(self.id.0) - } - - /// Deletes all messages by Ids from the given vector in the channel. - /// - /// Refer to [`Channel::delete_messages`] for more information. - /// - /// Requires the [Manage Messages] permission. - /// - /// **Note**: Messages that are older than 2 weeks can't be deleted using - /// this method. - /// - /// # Errors - /// - /// Returns [`ModelError::BulkDeleteAmount`] if an attempt was made to - /// delete either 0 or more than 100 messages. - /// - /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages - /// [`ModelError::BulkDeleteAmount`]: ../enum.ModelError.html#variant.BulkDeleteAmount - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn delete_messages<T: AsRef<MessageId>, It: IntoIterator<Item=T>>( - &self, - message_ids: It, - ) -> FutureResult<()> { - ftryopt!(self.client).http.delete_messages(self.id.0, message_ids) - } - - /// Deletes all permission overrides in the channel from a member - /// or role. - /// - /// **Note**: Requires the [Manage Channel] permission. - /// - /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html - #[inline] - pub fn delete_permission(&self, permission_type: PermissionOverwriteType) - -> FutureResult<()> { - let overwrite_id = match permission_type { - PermissionOverwriteType::Member(id) => id.0, - PermissionOverwriteType::Role(id) => id.0, - }; - - ftryopt!(self.client).http.delete_permission(self.id.0, overwrite_id) - } - - /// Deletes the given [`Reaction`] from the channel. - /// - /// **Note**: Requires the [Manage Messages] permission, _if_ the current - /// user did not perform the reaction. - /// - /// [`Reaction`]: struct.Reaction.html - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn delete_reaction<M: Into<MessageId>, R: Into<ReactionType>>( - &self, - message_id: M, - user_id: Option<UserId>, - reaction_type: R, - ) -> FutureResult<()> { - ftryopt!(self.client).http.delete_reaction( - self.id.0, - message_id.into().0, - user_id.map(|x| x.0), - &reaction_type.into(), - ) - } - - /// Edits a [`Message`] in the channel given its Id. - /// - /// Message editing preserves all unchanged message data. - /// - /// Refer to the documentation for [`EditMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// **Note**: Requires that the current user be the author of the message. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the [`the limit`], containing the number of unicode code points - /// over the limit. - /// - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`EditMessage`]: ../builder/struct.EditMessage.html - /// [`Message`]: struct.Message.html - /// [`the limit`]: ../builder/struct.EditMessage.html#method.content - #[inline] - pub fn edit_message<F, M>(&self, message_id: M, f: F) - -> FutureResult<Message> - where F: FnOnce(EditMessage) -> EditMessage, M: Into<MessageId> { - ftryopt!(self.client).http.edit_message( - self.id.0, - message_id.into().0, - f, - ) - } - - /// Determines if the channel is NSFW. - /// - /// Refer to [`utils::is_nsfw`] for more details. - /// - /// **Note**: This method is for consistency. This will always return - /// `false`, due to DMs not being considered NSFW. - /// - /// [`utils::is_nsfw`]: ../utils/fn.is_nsfw.html - #[inline] - pub fn is_nsfw(&self) -> bool { false } - - /// Gets a message from the channel. - /// - /// Requires the [Read Message History] permission. - /// - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn message<M: Into<MessageId>>(&self, message_id: M) - -> FutureResult<Message> { - ftryopt!(self.client).http.get_message(self.id.0, message_id.into().0) - } - - /// Gets messages from the channel. - /// - /// Refer to [`Channel::messages`] for more information. - /// - /// Requires the [Read Message History] permission. - /// - /// [`Channel::messages`]: enum.Channel.html#method.messages - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn messages<'a, F: FnOnce(GetMessages) -> GetMessages>(&'a self, f: F) - -> Box<Future<Item = Vec<Message>, Error = Error> + 'a> { - ftryopt!(self.client).http.get_messages(self.id.0, f) - } - /// Returns "DM with $username#discriminator". pub fn name(&self) -> String { format!("DM with {}", self.recipient.borrow().tag()) } - - /// Gets the list of [`User`]s who have reacted to a [`Message`] with a - /// certain [`Emoji`]. - /// - /// Refer to [`Channel::reaction_users`] for more information. - /// - /// **Note**: Requires the [Read Message History] permission. - /// - /// [`Channel::reaction_users`]: enum.Channel.html#method.reaction_users - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`User`]: struct.User.html - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn reaction_users<M, R, U>( - &self, - message_id: M, - reaction_type: R, - limit: Option<u8>, - after: U, - ) -> FutureResult<Vec<User>> - where M: Into<MessageId>, R: Into<ReactionType>, U: Into<Option<UserId>> { - ftryopt!(self.client).http.get_reaction_users( - self.id.0, - message_id.into().0, - &reaction_type.into(), - limit, - after.into().map(|x| x.0), - ) - } - - /// Pins a [`Message`] to the channel. - /// - /// [`Message`]: struct.Message.html - #[inline] - pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> { - ftryopt!(self.client).http.pin_message(self.id.0, message_id.into().0) - } - - /// Retrieves the list of messages that have been pinned in the private - /// channel. - #[inline] - pub fn pins(&self) -> FutureResult<Vec<Message>> { - ftryopt!(self.client).http.get_pins(self.id.0) - } - - /// Sends a message with just the given message content in the channel. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// [`ChannelId`]: ../model/id/struct.ChannelId.html - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - #[inline] - pub fn say<D: ::std::fmt::Display>(&self, content: D) - -> FutureResult<Message> { - ftryopt!(self.client) - .http - .send_message(self.id.0, |m| m.content(content)) - } - - /// Sends (a) file(s) along with optional message contents. - /// - /// Refer to [`ChannelId::send_files`] for examples and more information. - /// - /// The [Attach Files] and [Send Messages] permissions are required. - /// - /// **Note**: Message contents must be under 2000 unicode code points. - /// - /// # Errors - /// - /// If the content of the message is over the above limit, then a - /// [`ClientError::MessageTooLong`] will be returned, containing the number - /// of unicode code points over the limit. - /// - /// [`ChannelId::send_files`]: struct.ChannelId.html#method.send_files - /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong - /// [Attach Files]: permissions/constant.ATTACH_FILES.html - /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - // todo - // #[inline] - // pub fn send_files<'a, F, T, It>(&self, files: It, f: F) - // -> FutureResult<Message> - // where F: FnOnce(CreateMessage) -> CreateMessage, - // T: Into<AttachmentType<'a>>, - // It: IntoIterator<Item=T> { - // ftryopt!(self.client).http.send_files(self.id.0, files, f) - // } - - /// Sends a message to the channel with the given content. - /// - /// Refer to the documentation for [`CreateMessage`] for more information - /// regarding message restrictions and requirements. - /// - /// # Errors - /// - /// Returns a [`ModelError::MessageTooLong`] if the content of the message - /// is over the above limit, containing the number of unicode code points - /// over the limit. - /// - /// [`ModelError::MessageTooLong`]: enum.ModelError.html#variant.MessageTooLong - /// [`CreateMessage`]: ../builder/struct.CreateMessage.html - /// [`Message`]: struct.Message.html - #[inline] - pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) - -> FutureResult<Message> { - ftryopt!(self.client).http.send_message(self.id.0, f) - } - - /// Unpins a [`Message`] in the channel given by its Id. - /// - /// Requires the [Manage Messages] permission. - /// - /// [`Message`]: struct.Message.html - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[inline] - pub fn unpin<M: Into<MessageId>>(&self, message_id: M) -> FutureResult<()> { - ftryopt!(self.client).http.unpin_message(self.id.0, message_id.into().0) - } } impl Display for PrivateChannel { diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs index 28ddb8d..1111a55 100644 --- a/src/model/channel/reaction.rs +++ b/src/model/channel/reaction.rs @@ -1,12 +1,9 @@ -use futures::future; use model::prelude::*; use serde::de::{Deserialize, Error as DeError, MapAccess, Visitor}; use serde::ser::{SerializeMap, Serialize, Serializer}; use std::error::Error as StdError; use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite}; use std::str::FromStr; -use super::super::WrappedClient; -use ::FutureResult; use internal::prelude::*; /// An emoji reaction to a message. @@ -27,150 +24,6 @@ pub struct Reaction { /// /// [`User`]: struct.User.html pub user_id: UserId, - #[serde(skip)] - pub(crate) client: WrappedClient, -} - -#[cfg(feature = "model")] -impl Reaction { - /// Retrieves the associated the reaction was made in. - /// - /// If the cache is enabled, this will search for the already-cached - /// channel. If not - or the channel was not found - this will perform a - /// request over the REST API for the channel. - /// - /// Requires the [Read Message History] permission. - /// - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - #[inline] - pub fn channel(&self) -> FutureResult<Channel> { - ftryopt!(self.client).http.get_channel(self.channel_id.0) - } - - /// Deletes the reaction, but only if the current user is the user who made - /// the reaction or has permission to. - /// - /// Requires the [Manage Messages] permission, _if_ the current - /// user did not perform the reaction. - /// - /// # Errors - /// - /// If the `cache` is enabled, then returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have - /// the required [permissions]. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - /// [permissions]: permissions - pub fn delete(&self) -> FutureResult<()> { - let client = ftryopt!(self.client); - - let user_id = feature_cache! {{ - let cache = ftry!(client.cache.try_borrow()); - - let user = if self.user_id == cache.user.id { - None - } else { - Some(self.user_id.0) - }; - - // If the reaction is one _not_ made by the current user, then ensure - // that the current user has permission* to delete the reaction. - // - // Normally, users can only delete their own reactions. - // - // * The `Manage Messages` permission. - if user.is_some() { - let req = Permissions::MANAGE_MESSAGES; - - if !cache.user_has_perms(self.channel_id, req).unwrap_or(true) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - user - } else { - Some(self.user_id.0) - }}; - - ftryopt!(self.client).http.delete_reaction( - self.channel_id.0, - self.message_id.0, - user_id, - &self.emoji, - ) - } - - /// Retrieves the [`Message`] associated with this reaction. - /// - /// Requires the [Read Message History] permission. - /// - /// **Note**: This will send a request to the REST API. Prefer maintaining - /// your own message cache or otherwise having the message available if - /// possible. - /// - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - /// [`Message`]: struct.Message.html - #[inline] - pub fn message(&self) -> FutureResult<Message> { - ftryopt!(self.client).http.get_message( - self.channel_id.0, - self.message_id.0, - ) - } - - /// Retrieves the user that made the reaction. - /// - /// If the cache is enabled, this will search for the already-cached user. - /// If not - or the user was not found - this will perform a request over - /// the REST API for the user. - #[inline] - pub fn user(&self) -> FutureResult<User> { - ftryopt!(self.client).http.get_user(self.user_id.0) - } - - /// Retrieves the list of [`User`]s who have reacted to a [`Message`] with a - /// certain [`Emoji`]. - /// - /// The default `limit` is `50` - specify otherwise to receive a different - /// maximum number of users. The maximum that may be retrieve at a time is - /// `100`, if a greater number is provided then it is automatically reduced. - /// - /// The optional `after` attribute is to retrieve the users after a certain - /// user. This is useful for pagination. - /// - /// Requires the [Read Message History] permission. - /// - /// **Note**: This will send a request to the REST API. - /// - /// # Errors - /// - /// Returns a [`ModelError::InvalidPermissions`] if the current user does - /// not have the required [permissions]. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`Emoji`]: struct.Emoji.html - /// [`Message`]: struct.Message.html - /// [`User`]: struct.User.html - /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html - /// [permissions]: permissions - pub fn users<R, U>( - &self, - reaction_type: R, - limit: Option<u8>, - after: Option<U>, - ) -> FutureResult<Vec<User>> - where R: Into<ReactionType>, U: Into<UserId> { - ftryopt!(self.client).http.get_reaction_users( - self.channel_id.0, - self.message_id.0, - &reaction_type.into(), - limit, - after.map(|u| u.into().0), - ) - } } /// The type of a [`Reaction`] sent. @@ -313,7 +166,6 @@ impl ReactionType { } } -#[cfg(feature = "model")] impl From<char> for ReactionType { /// Creates a `ReactionType` from a `char`. /// diff --git a/src/model/gateway.rs b/src/model/gateway.rs index ad30bab..b5fa84b 100644 --- a/src/model/gateway.rs +++ b/src/model/gateway.rs @@ -39,7 +39,6 @@ pub struct Game { pub url: Option<String>, } -#[cfg(feature = "model")] impl Game { /// Creates a `Game` struct that appears as a `Playing <name>` status. /// diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs index 48d9b7d..08f63ef 100644 --- a/src/model/guild/emoji.rs +++ b/src/model/guild/emoji.rs @@ -1,17 +1,5 @@ use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite}; use super::super::id::{EmojiId, RoleId}; -use super::super::WrappedClient; - -#[cfg(feature = "cache")] -use futures::{Future, future}; -#[cfg(all(feature = "cache", feature = "model"))] -use internal::prelude::*; -#[cfg(all(feature = "cache", feature = "model"))] -use super::super::ModelError; -#[cfg(all(feature = "cache", feature = "model"))] -use super::super::id::GuildId; -#[cfg(feature = "cache")] -use ::FutureResult; /// Represents a custom guild emoji, which can either be created using the API, /// or via an integration. Emojis created using the API only work within the @@ -38,136 +26,9 @@ pub struct Emoji { /// /// [`Role`]: struct.Role.html pub roles: Vec<RoleId>, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl Emoji { - /// Deletes the emoji. - /// - /// **Note**: The [Manage Emojis] permission is required. - /// - /// **Note**: Only user accounts may use this method. - /// - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - /// - /// # Examples - /// - /// Delete a given emoji: - /// - /// ```rust,no_run - /// # use serenity::model::guild::Emoji; - /// # use serenity::model::id::EmojiId; - /// # - /// # let mut emoji = Emoji { - /// # animated: false, - /// # id: EmojiId(7), - /// # name: String::from("blobface"), - /// # managed: false, - /// # require_colons: false, - /// # roles: vec![], - /// # }; - /// # - /// // assuming emoji has been set already - /// match emoji.delete() { - /// Ok(()) => println!("Emoji deleted."), - /// Err(_) => println!("Could not delete emoji.") - /// } - /// ``` - #[cfg(feature = "cache")] - pub fn delete(&self) -> FutureResult<()> { - let guild_id = match self.find_guild_id() { - Some(guild_id) => guild_id, - None => return Box::new(future::err(Error::Model( - ModelError::ItemMissing, - ))), - }; - - ftryopt!(self.client).http.delete_emoji(guild_id.0, self.id.0) - } - - /// Edits the emoji by updating it with a new name. - /// - /// **Note**: The [Manage Emojis] permission is required. - /// - /// **Note**: Only user accounts may use this method. - /// - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - /// - /// # Examples - /// - /// Change the name of an emoji: - /// - /// ```rust,no_run - /// # use serenity::model::guild::Emoji; - /// # use serenity::model::id::EmojiId; - /// # - /// # let mut emoji = Emoji { - /// # animated: false, - /// # id: EmojiId(7), - /// # name: String::from("blobface"), - /// # managed: false, - /// # require_colons: false, - /// # roles: vec![], - /// # }; - /// # - /// // assuming emoji has been set already - /// let _ = emoji.edit("blobuwu"); - /// assert_eq!(emoji.name, "blobuwu"); - /// ``` - #[cfg(feature = "cache")] - pub fn edit<'a>(&'a mut self, name: &'a str) - -> Box<Future<Item = Emoji, Error = Error> + 'a> { - let guild_id = match self.find_guild_id() { - Some(guild_id) => guild_id, - None => return Box::new(future::err(Error::Model( - ModelError::ItemMissing, - ))), - }; - - ftryopt!(self.client).http.edit_emoji(guild_id.0, self.id.0, name) - } - - /// Finds the [`Guild`] that owns the emoji by looking through the Cache. - /// - /// [`Guild`]: struct.Guild.html - /// - /// # Examples - /// - /// Print the guild id that owns this emoji: - /// - /// ```rust,no_run - /// # use serenity::model::guild::Emoji; - /// # use serenity::model::id::EmojiId; - /// # - /// # let mut emoji = Emoji { - /// # animated: false, - /// # id: EmojiId(7), - /// # name: String::from("blobface"), - /// # managed: false, - /// # require_colons: false, - /// # roles: vec![], - /// # }; - /// # - /// // assuming emoji has been set already - /// if let Some(guild_id) = emoji.find_guild_id() { - /// println!("{} is owned by {}", emoji.name, guild_id); - /// } - /// ``` - #[cfg(feature = "cache")] - pub fn find_guild_id(&self) -> Option<GuildId> { - for guild in self.client.as_ref()?.cache.borrow().guilds.values() { - let guild = guild.borrow(); - - if guild.emojis.contains_key(&self.id) { - return Some(guild.id); - } - } - - None - } - /// Generates a URL to the emoji's image. /// /// # Examples diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs index ea095a0..eacf07e 100644 --- a/src/model/guild/guild_id.rs +++ b/src/model/guild/guild_id.rs @@ -1,6 +1,5 @@ use model::prelude::*; -#[cfg(feature = "model")] impl GuildId { /// Converts the guild Id into the default channel's Id. #[inline] diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs index 0c22597..bc8e27e 100644 --- a/src/model/guild/member.rs +++ b/src/model/guild/member.rs @@ -1,19 +1,8 @@ use chrono::{DateTime, FixedOffset}; -use futures::future; use model::prelude::*; +use std::borrow::Cow; use std::cell::RefCell; -use super::super::WrappedClient; use super::deserialize_user; -use ::FutureResult; - -#[cfg(all(feature = "builder", feature = "cache", feature = "model"))] -use builder::EditMember; -#[cfg(all(feature = "cache", feature = "model"))] -use internal::prelude::*; -#[cfg(feature = "model")] -use std::borrow::Cow; -#[cfg(all(feature = "cache", feature = "model", feature = "utils"))] -use utils::Colour; /// 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 { @@ -70,132 +59,9 @@ pub struct Member { #[serde(deserialize_with = "deserialize_user", serialize_with = "serialize_user")] pub user: Rc<RefCell<User>>, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl Member { - /// Adds a [`Role`] to the member. - /// - /// **Note**: Requires the [Manage Roles] permission. - /// - /// [`Role`]: struct.Role.html - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[cfg(feature = "cache")] - pub fn add_role<R: Into<RoleId>>(&mut self, role_id: R) - -> FutureResult<()> { - let role_id = role_id.into(); - - if self.roles.contains(&role_id) { - return Box::new(future::ok(())); - } - - ftryopt!(self.client).http.add_member_role( - self.guild_id.0, - self.user.borrow().id.0, - role_id.0, - ) - } - - /// Adds one or multiple [`Role`]s to the member. - /// - /// **Note**: Requires the [Manage Roles] permission. - /// - /// [`Role`]: struct.Role.html - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[cfg(feature = "cache")] - pub fn add_roles(&mut self, role_ids: &[RoleId]) -> FutureResult<()> { - let mut roles = self.roles.clone(); - roles.extend(role_ids); - - ftryopt!(self.client).http.edit_member( - self.guild_id.0, - self.user.borrow().id.0, - |f| f.roles(roles), - ) - } - - /// Ban the member from its guild, deleting the last X number of - /// days' worth of messages. - /// - /// **Note**: Requires the [Ban Members] permission. - /// - /// # Errors - /// - /// Returns a [`ModelError::GuildNotFound`] if the guild could not be - /// found. - /// - /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound - /// - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - #[cfg(feature = "cache")] - pub fn ban<BO: BanOptions>(&self, ban_options: &BO) -> FutureResult<()> { - let dmd = ban_options.dmd(); - - if dmd > 7 { - return Box::new(future::err(Error::Model( - ModelError::DeleteMessageDaysAmount(dmd), - ))); - } - - let reason = ban_options.reason(); - - if reason.len() > 512 { - return Box::new(future::err(Error::ExceededLimit( - reason.to_string(), - 512, - ))); - } - - ftryopt!(self.client).http.ban_user( - self.guild_id.0, - self.user.borrow().id.0, - dmd, - &*reason, - ) - } - - /// Determines the member's colour. - #[cfg(all(feature = "cache", feature = "utils"))] - pub fn colour(&self) -> Option<Colour> { - let client = self.client.as_ref()?; - let cache = client.cache.try_borrow().ok()?; - let guild = cache.guild(self.guild_id)?; - let guild = guild.borrow(); - - let mut roles = self.roles - .iter() - .filter_map(|role_id| guild.roles.get(role_id)) - .collect::<Vec<&Rc<RefCell<Role>>>>(); - roles.sort_by(|a, b| b.cmp(a)); - - let default = Colour::default(); - - roles - .iter() - .find(|r| r.borrow().colour.0 != default.0) - .map(|r| r.borrow().colour) - } - - /// Returns the "default channel" of the guild for the member. - /// (This returns the first channel that can be read by the member, if there isn't - /// one returns `None`) - #[cfg(feature = "cache")] - pub fn default_channel(&self) -> Option<Rc<RefCell<GuildChannel>>> { - let cache = self.client.as_ref()?.cache.borrow(); - let guild = cache.guild(self.guild_id)?; - let reader = guild.borrow(); - - for (cid, channel) in &reader.channels { - if reader.permissions_in(*cid, self.user.borrow().id).read_messages() { - return Some(Rc::clone(channel)); - } - } - - None - } - /// Calculates the member's display name. /// /// The nickname takes priority over the member's username if it exists. @@ -222,252 +88,4 @@ impl Member { ) } } - - /// Edits the member with the given data. See [`Guild::edit_member`] for - /// more information. - /// - /// See [`EditMember`] for the permission(s) required for separate builder - /// methods, as well as usage of this. - /// - /// [`Guild::edit_member`]: ../model/guild/struct.Guild.html#method.edit_member - /// [`EditMember`]: ../builder/struct.EditMember.html - #[cfg(feature = "cache")] - pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) - -> FutureResult<()> { - ftryopt!(self.client).http.edit_member( - self.guild_id.0, - self.user.borrow().id.0, - f, - ) - } - - /// Retrieves the ID and position of the member's highest role in the - /// hierarchy, if they have one. - /// - /// This _may_ return `None` if the user has roles, but they are not present - /// in the cache for cache inconsistency reasons. - /// - /// The "highest role in hierarchy" is defined as the role with the highest - /// position. If two or more roles have the same highest position, then the - /// role with the lowest ID is the highest. - /// - /// # Deadlocking - /// - /// This function will deadlock if you have a write lock to the member's - /// guild. - #[cfg(feature = "cache")] - pub fn highest_role_info(&self) -> Option<(RoleId, i64)> { - let cache = self.client.as_ref()?.cache.try_borrow().ok()?; - let guild = cache.guild(self.guild_id)?; - let reader = guild.borrow(); - - let mut highest = None; - - for role_id in &self.roles { - let role = reader.roles - .get(&role_id) - .and_then(|x| x.try_borrow().ok()); - - if let Some(role) = role { - // Skip this role if this role in iteration has: - // - // - a position less than the recorded highest - // - a position equal to the recorded, but a higher ID - if let Some((id, pos)) = highest { - if role.position < pos || (role.position == pos && role.id > id) { - continue; - } - } - - highest = Some((role.id, role.position)); - } - } - - highest - } - - /// Kick the member from the guild. - /// - /// **Note**: Requires the [Kick Members] permission. - /// - /// # Examples - /// - /// Kick a member from its guild: - /// - /// ```rust,ignore - /// // assuming a `member` has already been bound - /// match member.kick() { - /// Ok(()) => println!("Successfully kicked member"), - /// Err(Error::Model(ModelError::GuildNotFound)) => { - /// println!("Couldn't determine guild of member"); - /// }, - /// Err(Error::Model(ModelError::InvalidPermissions(missing_perms))) => { - /// println!("Didn't have permissions; missing: {:?}", missing_perms); - /// }, - /// _ => {}, - /// } - /// ``` - /// - /// # Errors - /// - /// Returns a [`ModelError::GuildNotFound`] if the Id of the member's guild - /// could not be determined. - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform the kick. - /// - /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Kick Members]: permissions/constant.KICK_MEMBERS.html - pub fn kick(&self) -> FutureResult<()> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let cache = ftry!(client.cache.try_borrow()); - - if let Some(guild) = cache.guilds.get(&self.guild_id) { - let req = Permissions::KICK_MEMBERS; - let reader = ftry!(guild.try_borrow()); - - if !reader.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - - ftry!(reader.check_hierarchy(ftry!(self.user.try_borrow()).id)); - } - } - - let user_id = ftry!(self.user.try_borrow()).id.0; - - ftryopt!(self.client).http.kick_member(self.guild_id.0, user_id) - } - - /// Returns the guild-level permissions for the member. - /// - /// # Examples - /// - /// ```rust,ignore - /// // assuming there's a `member` variable gotten from anything. - /// println!("The permission bits for the member are: {}", - /// member.permissions().expect("permissions").bits); - /// ``` - /// - /// # Errors - /// - /// Returns a [`ModelError::GuildNotFound`] if the guild the member's in could not be - /// found in the cache. - /// - /// And/or returns [`ModelError::ItemMissing`] if the "default channel" of the guild is not - /// found. - /// - /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound - /// [`ModelError::ItemMissing`]: enum.ModelError.html#variant.ItemMissing - #[cfg(feature = "cache")] - pub fn permissions(&self) -> Result<Permissions> { - let client = self.client.as_ref().ok_or_else(|| { - Error::Model(ModelError::ClientNotPresent) - })?; - let cache = client.cache.try_borrow()?; - let guild = match cache.guilds.get(&self.guild_id) { - Some(guild) => guild, - None => return Err(From::from(ModelError::GuildNotFound)), - }; - - let guild = guild.try_borrow()?; - - Ok(guild.member_permissions(self.user.try_borrow()?.id)) - } - - /// Removes a [`Role`] from the member. - /// - /// **Note**: Requires the [Manage Roles] permission. - /// - /// [`Role`]: struct.Role.html - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[cfg(feature = "cache")] - pub fn remove_role<R: Into<RoleId>>(&mut self, role_id: R) -> FutureResult<()> { - let role_id = role_id.into(); - - if !self.roles.contains(&role_id) { - return Box::new(future::ok(())); - } - - ftryopt!(self.client).http.remove_member_role( - self.guild_id.0, - self.user.borrow().id.0, - role_id.0, - ) - } - - /// Removes one or multiple [`Role`]s from the member. - /// - /// **Note**: Requires the [Manage Roles] permission. - /// - /// [`Role`]: struct.Role.html - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[cfg(feature = "cache")] - pub fn remove_roles(&mut self, role_ids: &[RoleId]) -> FutureResult<()> { - let mut roles = self.roles.clone(); - roles.retain(|r| !role_ids.contains(r)); - - ftryopt!(self.client).http.edit_member( - self.guild_id.0, - self.user.borrow().id.0, - |f| f.roles(roles), - ) - } - - /// Retrieves the full role data for the user's roles. - /// - /// This is shorthand for manually searching through the CACHE. - /// - /// If role data can not be found for the member, then `None` is returned. - #[cfg(feature = "cache")] - pub fn roles(&self) -> Option<Vec<Rc<RefCell<Role>>>> { - let client = self.client.as_ref()?; - let cache = client.cache.try_borrow().ok()?; - - cache.guilds.get(&self.guild_id).and_then(|guild| { - let guild = guild.try_borrow().ok()?; - - let roles = guild - .roles - .values() - .filter(|role| { - let role = match role.try_borrow() { - Ok(role) => role, - Err(_) => return false, - }; - - self.roles.contains(&role.id) - }) - .cloned() - .collect(); - - Some(roles) - }) - } - - /// Unbans the [`User`] from the guild. - /// - /// **Note**: Requires the [Ban Members] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`User`]: struct.User.html - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - #[cfg(feature = "cache")] - pub fn unban(&self) -> FutureResult<()> { - ftryopt!(self.client).http.remove_ban( - self.guild_id.0, - self.user.borrow().id.0, - ) - } } diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs index 6c0fa82..d2281e9 100644 --- a/src/model/guild/mod.rs +++ b/src/model/guild/mod.rs @@ -17,24 +17,15 @@ pub use self::role::*; pub use self::audit_log::*; use chrono::{DateTime, FixedOffset}; -use futures::{Future, future}; +use constants::LARGE_THRESHOLD; use model::prelude::*; use serde::de::Error as DeError; use serde_json; +use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; -use super::utils::*; -use super::WrappedClient; -use ::FutureResult; - -#[cfg(feature = "model")] -use builder::{EditGuild, EditMember, EditRole}; -#[cfg(feature = "model")] -use constants::LARGE_THRESHOLD; -#[cfg(feature = "model")] use std; -#[cfg(feature = "model")] -use std::borrow::Cow; +use super::utils::*; /// A representation of a banning of a user. #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)] @@ -143,29 +134,9 @@ pub struct Guild { /// [`User`]: struct.User.html #[serde(serialize_with = "serialize_gen_map")] pub voice_states: HashMap<UserId, VoiceState>, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl Guild { - #[cfg(feature = "cache")] - fn check_hierarchy(&self, other_user: UserId) -> Result<()> { - let client = self.client.as_ref().ok_or_else(|| { - Error::Model(ModelError::ClientNotPresent) - })?; - - let current_id = client.cache.try_borrow()?.user.id; - - if let Some(higher) = self.greater_member_hierarchy(other_user, current_id) { - if higher != current_id { - return Err(Error::Model(ModelError::Hierarchy)); - } - } - - Ok(()) - } - /// Returns the "default" channel of the guild for the passed user id. /// (This returns the first channel that can be read by the user, if there isn't one, /// returns `None`) @@ -196,569 +167,6 @@ impl Guild { None } - #[cfg(feature = "cache")] - fn has_perms(&self, mut permissions: Permissions) -> bool { - let client = match self.client.as_ref() { - Some(client) => client, - None => return true, - }; - let cache = match client.cache.try_borrow() { - Ok(cache) => cache, - Err(_) => return true, - }; - - let user_id = cache.user.id; - - let perms = self.member_permissions(user_id); - permissions.remove(perms); - - permissions.is_empty() - } - - /// Ban a [`User`] from the guild. All messages by the - /// user within the last given number of days given will be deleted. - /// - /// Refer to the documentation for [`Guild::ban`] for more information. - /// - /// **Note**: Requires the [Ban Members] permission. - /// - /// # Examples - /// - /// Ban a member and remove all messages they've sent in the last 4 days: - /// - /// ```rust,ignore - /// // assumes a `user` and `guild` have already been bound - /// let _ = guild.ban(user, 4); - /// ``` - /// - /// # Errors - /// - /// Returns a [`ModelError::InvalidPermissions`] if the current user does - /// not have permission to perform bans. - /// - /// Returns a [`ModelError::DeleteMessageDaysAmount`] if the number of - /// days' worth of messages to delete is over the maximum. - /// - /// [`ModelError::DeleteMessageDaysAmount`]: - /// enum.ModelError.html#variant.DeleteMessageDaysAmount - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`Guild::ban`]: struct.Guild.html#method.ban - /// [`User`]: struct.User.html - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - pub fn ban<U: Into<UserId>, BO: BanOptions>(&self, user: U, options: &BO) - -> FutureResult<()> { - let user = user.into(); - - #[cfg(feature = "cache")] - { - let req = Permissions::BAN_MEMBERS; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - - ftry!(self.check_hierarchy(user)); - } - - let dmd = options.dmd(); - if dmd > 7 { - return Box::new(future::err(Error::Model( - ModelError::DeleteMessageDaysAmount(dmd), - ))); - } - - let reason = options.reason(); - - if reason.len() > 512 { - return Box::new(future::err(Error::ExceededLimit( - reason.to_string(), - 512, - ))); - } - - ftryopt!(self.client) - .http - .ban_user(self.id.0, user.0, dmd, reason) - } - - /// Retrieves a list of [`Ban`]s for the guild. - /// - /// **Note**: Requires the [Ban Members] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`Ban`]: struct.Ban.html - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - pub fn bans(&self) -> FutureResult<Vec<Ban>> { - #[cfg(feature = "cache")] - { - let req = Permissions::BAN_MEMBERS; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.get_bans(self.id.0) - } - - /// Retrieves a list of [`AuditLogs`] for the guild. - /// - /// [`AuditLogs`]: audit_log/struct.AuditLogs.html - #[inline] - pub fn audit_logs( - &self, - action_type: Option<u8>, - user_id: Option<UserId>, - before: Option<AuditLogEntryId>, - limit: Option<u8>, - ) -> FutureResult<AuditLogs> { - ftryopt!(self.client).http.get_audit_logs( - self.id.0, - action_type, - user_id.map(|x| x.0), - before.map(|x| x.0), - limit, - ) - } - - /// Gets all of the guild's channels over the REST API. - /// - /// [`Guild`]: struct.Guild.html - #[inline] - pub fn channels(&self) -> FutureResult<HashMap<ChannelId, GuildChannel>> { - let done = ftryopt!(self.client) - .http - .get_channels(self.id.0) - .map(|channels| { - let mut map = HashMap::with_capacity(channels.len()); - - for channel in channels { - map.insert(channel.id, channel); - } - - map - }); - - Box::new(done) - } - - /// Creates a new [`Channel`] in the guild. - /// - /// **Note**: Requires the [Manage Channels] permission. - /// - /// # Examples - /// - /// ```rust,ignore - /// use serenity::model::ChannelType; - /// - /// // assuming a `guild` has already been bound - /// - /// let _ = guild.create_channel("my-test-channel", ChannelType::Text, None); - /// ``` - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`Channel`]: struct.Channel.html - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html - pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) - -> FutureResult<GuildChannel> where C: Into<Option<ChannelId>> { - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_CHANNELS; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.create_channel( - self.id.0, - name, - kind, - category.into().map(|x| x.0), - ) - } - - /// Creates an emoji in the guild with a name and base64-encoded image. The - /// [`utils::read_image`] function is provided for you as a simple method to - /// read an image and encode it into base64, if you are reading from the - /// filesystem. - /// - /// The name of the emoji must be at least 2 characters long and can only - /// contain alphanumeric characters and underscores. - /// - /// Requires the [Manage Emojis] permission. - /// - /// # Examples - /// - /// See the [`EditProfile::avatar`] example for an in-depth example as to - /// how to read an image from the filesystem and encode it as base64. Most - /// of the example can be applied similarly for this method. - /// - /// [`EditProfile::avatar`]: ../builder/struct.EditProfile.html#method.avatar - /// [`utils::read_image`]: ../fn.read_image.html - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - #[inline] - pub fn create_emoji(&self, name: &str, image: &str) -> FutureResult<Emoji> { - ftryopt!(self.client).http.create_emoji(self.id.0, name, image) - } - - /// Creates an integration for the guild. - /// - /// Requires the [Manage Guild] permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - #[inline] - pub fn create_integration<I>(&self, integration_id: I, kind: &str) - -> FutureResult<()> where I: Into<IntegrationId> { - ftryopt!(self.client).http.create_guild_integration( - self.id.0, - integration_id.into().0, - kind, - ) - } - - /// Creates a new role in the guild with the data set, if any. - /// - /// **Note**: Requires the [Manage Roles] permission. - /// - /// # Examples - /// - /// Create a role which can be mentioned, with the name 'test': - /// - /// ```rust,ignore - /// // assuming a `guild` has been bound - /// - /// let role = guild.create_role(|r| r.hoist(true).name("role")); - /// ``` - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`Role`]: struct.Role.html - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - pub fn create_role<F>(&self, f: F) -> FutureResult<Role> - where F: FnOnce(EditRole) -> EditRole { - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_ROLES; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.create_role(self.id.0, f) - } - - /// Deletes the current guild if the current user is the owner of the - /// guild. - /// - /// **Note**: Requires the current user to be the owner of the guild. - /// - /// # Errors - /// - /// If the `cache` is enabled, then returns a [`ModelError::InvalidUser`] - /// if the current user is not the guild owner. - /// - /// [`ModelError::InvalidUser`]: enum.ModelError.html#variant.InvalidUser - pub fn delete(&self) -> FutureResult<PartialGuild> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let cache = ftry!(client.cache.try_borrow()); - - if self.owner_id != cache.user.id { - let req = Permissions::MANAGE_GUILD; - - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.delete_guild(self.id.0) - } - - /// Deletes an [`Emoji`] from the guild. - /// - /// Requires the [Manage Emojis] permission. - /// - /// [`Emoji`]: struct.Emoji.html - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - #[inline] - pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) - -> FutureResult<()> { - ftryopt!(self.client).http.delete_emoji(self.id.0, emoji_id.into().0) - } - - /// Deletes an integration by Id from the guild. - /// - /// Requires the [Manage Guild] permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - #[inline] - pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) - -> FutureResult<()> { - ftryopt!(self.client).http.delete_guild_integration( - self.id.0, - integration_id.into().0, - ) - } - - /// Deletes a [`Role`] by Id from the guild. - /// - /// Also see [`Role::delete`] if you have the `cache` and `methods` features - /// enabled. - /// - /// Requires the [Manage Roles] permission. - /// - /// [`Role`]: struct.Role.html - /// [`Role::delete`]: struct.Role.html#method.delete - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[inline] - pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> FutureResult<()> { - ftryopt!(self.client).http.delete_role(self.id.0, role_id.into().0) - } - - /// Edits the current guild with new data where specified. - /// - /// Refer to `EditGuild`'s documentation for a full list of methods. - /// - /// **Note**: Requires the current user to have the [Manage Guild] - /// permission. - /// - /// # Examples - /// - /// Change a guild's icon using a file name "icon.png": - /// - /// ```rust,ignore - /// use serenity::utils; - /// - /// // We are using read_image helper function from utils. - /// let base64_icon = utils::read_image("./icon.png") - /// .expect("Failed to read image"); - /// - /// guild.edit(|g| g.icon(base64_icon)); - /// ``` - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&self, f: F) - -> FutureResult<PartialGuild> { - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_GUILD; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.edit_guild(self.id.0, f) - } - - /// Edits an [`Emoji`]'s name in the guild. - /// - /// Also see [`Emoji::edit`] if you have the `cache` and `methods` features - /// enabled. - /// - /// Requires the [Manage Emojis] permission. - /// - /// [`Emoji`]: struct.Emoji.html - /// [`Emoji::edit`]: struct.Emoji.html#method.edit - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - #[inline] - pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) - -> FutureResult<Emoji> { - let emoji_id = emoji_id.into().0; - - ftryopt!(self.client).http.edit_emoji(self.id.0, emoji_id, name) - } - - /// Edits the properties of member of the guild, such as muting or - /// nicknaming them. - /// - /// Refer to `EditMember`'s documentation for a full list of methods and - /// permission restrictions. - /// - /// # Examples - /// - /// Mute a member and set their roles to just one role with a predefined Id: - /// - /// ```rust,ignore - /// guild.edit_member(user_id, |m| m.mute(true).roles(&vec![role_id])); - /// ``` - #[inline] - pub fn edit_member<F, U>(&self, user_id: U, f: F) -> FutureResult<()> - where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> { - ftryopt!(self.client).http.edit_member(self.id.0, user_id.into().0, f) - } - - /// Edits the current user's nickname for the guild. - /// - /// Pass `None` to reset the nickname. - /// - /// **Note**: Requires the [Change Nickname] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to change their own - /// nickname. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html - pub fn edit_nickname(&self, new_nickname: Option<&str>) - -> FutureResult<()> { - #[cfg(feature = "cache")] - { - let req = Permissions::CHANGE_NICKNAME; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.edit_nickname(self.id.0, new_nickname) - } - - /// Edits a role, optionally setting its fields. - /// - /// Requires the [Manage Roles] permission. - /// - /// # Examples - /// - /// Make a role hoisted: - /// - /// ```rust,ignore - /// guild.edit_role(RoleId(7), |r| r.hoist(true)); - /// ``` - /// - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[inline] - pub fn edit_role<F, R>(&self, role_id: R, f: F) -> FutureResult<Role> - where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> { - ftryopt!(self.client).http.edit_role(self.id.0, role_id.into().0, 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) - -> FutureResult<Vec<Role>> where R: Into<RoleId> { - ftryopt!(self.client) - .http - .edit_role_position(self.id.0, role_id.into().0, position) - } - - /// Returns which of two [`User`]s has a higher [`Member`] hierarchy. - /// - /// Hierarchy is essentially who has the [`Role`] with the highest - /// [`position`]. - /// - /// Returns [`None`] if at least one of the given users' member instances - /// is not present. Returns `None` if the users have the same hierarchy, as - /// neither are greater than the other. - /// - /// If both user IDs are the same, `None` is returned. If one of the users - /// is the guild owner, their ID is returned. - #[cfg(feature = "cache")] - pub fn greater_member_hierarchy<T, U>(&self, lhs_id: T, rhs_id: U) - -> Option<UserId> where T: Into<UserId>, U: Into<UserId> { - let lhs_id = lhs_id.into(); - let rhs_id = rhs_id.into(); - - // Check that the IDs are the same. If they are, neither is greater. - if lhs_id == rhs_id { - return None; - } - - // Check if either user is the guild owner. - if lhs_id == self.owner_id { - return Some(lhs_id); - } else if rhs_id == self.owner_id { - return Some(rhs_id); - } - - let lhs = self.members.get(&lhs_id)? - .borrow() - .highest_role_info() - .unwrap_or((RoleId(0), 0)); - let rhs = self.members.get(&rhs_id)? - .borrow() - .highest_role_info() - .unwrap_or((RoleId(0), 0)); - - // If LHS and RHS both have no top position or have the same role ID, - // then no one wins. - if (lhs.1 == 0 && rhs.1 == 0) || (lhs.0 == rhs.0) { - return None; - } - - // If LHS's top position is higher than RHS, then LHS wins. - if lhs.1 > rhs.1 { - return Some(lhs_id) - } - - // If RHS's top position is higher than LHS, then RHS wins. - if rhs.1 > lhs.1 { - return Some(rhs_id); - } - - // If LHS and RHS both have the same position, but LHS has the lower - // role ID, then LHS wins. - // - // If RHS has the higher role ID, then RHS wins. - if lhs.1 == rhs.1 && lhs.0 < rhs.0 { - Some(lhs_id) - } else { - Some(rhs_id) - } - } - /// Returns the formatted URL of the guild's icon, if one exists. pub fn icon_url(&self) -> Option<String> { self.icon @@ -766,40 +174,6 @@ impl Guild { .map(|icon| format!(cdn!("/icons/{}/{}.webp"), self.id, icon)) } - /// Gets all integration of the guild. - /// - /// This performs a request over the REST API. - #[inline] - pub fn integrations(&self) -> FutureResult<Vec<Integration>> { - ftryopt!(self.client).http.get_guild_integrations(self.id.0) - } - - /// Retrieves the active invites for the guild. - /// - /// **Note**: Requires the [Manage Guild] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - pub fn invites(&self) -> FutureResult<Vec<RichInvite>> { - #[cfg(feature = "cache")] - { - let req = Permissions::MANAGE_GUILD; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.get_guild_invites(self.id.0) - } - /// Checks if the guild is 'large'. A guild is considered large if it has /// more than 250 members. #[inline] @@ -807,47 +181,6 @@ impl Guild { self.members.len() > LARGE_THRESHOLD as usize } - /// Kicks a [`Member`] from the guild. - /// - /// Requires the [Kick Members] permission. - /// - /// [`Member`]: struct.Member.html - /// [Kick Members]: permissions/constant.KICK_MEMBERS.html - #[inline] - pub fn kick<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> { - ftryopt!(self.client).http.kick_member(self.id.0, user_id.into().0) - } - - /// Leaves the guild. - #[inline] - pub fn leave(&self) -> FutureResult<()> { - ftryopt!(self.client).http.leave_guild(self.id.0) - } - - /// Gets a user's [`Member`] for the guild by Id. - /// - /// [`Guild`]: struct.Guild.html - /// [`Member`]: struct.Member.html - #[inline] - pub fn member<U: Into<UserId>>(&self, user_id: U) -> FutureResult<Member> { - ftryopt!(self.client).http.get_member(self.id.0, user_id.into().0) - } - - /// Gets a list of the guild's members. - /// - /// Optionally pass in the `limit` to limit the number of results. Maximum - /// value is 1000. Optionally pass in `after` to offset the results by a - /// [`User`]'s Id. - /// - /// [`User`]: struct.User.html - #[inline] - pub fn members<U>(&self, limit: Option<u64>, after: Option<U>) - -> FutureResult<Vec<Member>> where U: Into<UserId> { - let after = after.map(Into::into).map(|x| x.0); - - ftryopt!(self.client).http.get_guild_members(self.id.0, limit, after) - } - /// Gets a list of all the members (satisfying the status provided to the function) in this /// guild. pub fn members_with_status(&self, status: OnlineStatus) @@ -1251,21 +584,6 @@ impl Guild { permissions } - /// Moves a member to a specific voice channel. - /// - /// Requires the [Move Members] permission. - /// - /// [Move Members]: permissions/constant.MOVE_MEMBERS.html - #[inline] - pub fn move_member<C, U>(&self, user_id: U, channel_id: C) - -> FutureResult<()> where C: Into<ChannelId>, U: Into<UserId> { - ftryopt!(self.client).http.edit_member( - self.id.0, - user_id.into().0, - |f| f.voice_channel(channel_id.into().0), - ) - } - /// Alias for [`permissions_in`]. /// /// [`permissions_in`]: #method.permissions_in @@ -1417,50 +735,6 @@ impl Guild { permissions } - /// Retrieves the count of the number of [`Member`]s that would be pruned - /// with the number of given days. - /// - /// See the documentation on [`GuildPrune`] for more information. - /// - /// **Note**: Requires the [Kick Members] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`GuildPrune`]: struct.GuildPrune.html - /// [`Member`]: struct.Member.html - /// [Kick Members]: permissions/constant.KICK_MEMBERS.html - pub fn prune_count(&self, days: u16) -> FutureResult<GuildPrune> { - #[cfg(feature = "cache")] - { - let req = Permissions::KICK_MEMBERS; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.get_guild_prune_count(self.id.0, days) - } - - /// Re-orders the channels of the guild. - /// - /// Although not required, you should specify all channels' positions, - /// regardless of whether they were updated. Otherwise, positioning can - /// sometimes get weird. - pub fn reorder_channels<It>(&self, channels: It) -> FutureResult<()> - where It: IntoIterator<Item = (ChannelId, u64)> { - ftryopt!(self.client).http.edit_guild_channel_positions( - self.id.0, - channels, - ) - } - /// Returns the Id of the shard associated with the guild. /// /// When the cache is enabled this will automatically retrieve the total @@ -1492,87 +766,6 @@ impl Guild { .map(|icon| format!(cdn!("/splashes/{}/{}.webp"), self.id, icon)) } - /// Starts an integration sync for the given integration Id. - /// - /// Requires the [Manage Guild] permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - #[inline] - pub fn start_integration_sync<I>(&self, integration_id: I) - -> FutureResult<()> where I: Into<IntegrationId> { - ftryopt!(self.client).http.start_integration_sync( - self.id.0, - integration_id.into().0, - ) - } - - /// Starts a prune of [`Member`]s. - /// - /// See the documentation on [`GuildPrune`] for more information. - /// - /// **Note**: Requires the [Kick Members] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`GuildPrune`]: struct.GuildPrune.html - /// [`Member`]: struct.Member.html - /// [Kick Members]: permissions/constant.KICK_MEMBERS.html - pub fn start_prune(&self, days: u16) -> FutureResult<GuildPrune> { - #[cfg(feature = "cache")] - { - let req = Permissions::KICK_MEMBERS; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.start_guild_prune(self.id.0, days) - } - - /// Unbans the given [`User`] from the guild. - /// - /// **Note**: Requires the [Ban Members] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`User`]: struct.User.html - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - pub fn unban<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> { - #[cfg(feature = "cache")] - { - let req = Permissions::BAN_MEMBERS; - - if !self.has_perms(req) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.remove_ban(self.id.0, user_id.into().0) - } - - /// Retrieves the guild's webhooks. - /// - /// **Note**: Requires the [Manage Webhooks] permission. - /// - /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html - #[inline] - pub fn webhooks(&self) -> FutureResult<Vec<Webhook>> { - ftryopt!(self.client).http.get_guild_webhooks(self.id.0) - } - /// Obtain a reference to a role by its name. /// /// **Note**: If two or more roles have the same name, obtained reference will be one of @@ -1752,7 +945,6 @@ impl<'de> Deserialize<'de> for Guild { application_id: application_id, afk_timeout: afk_timeout, channels: channels, - client: None, default_message_notifications: default_message_notifications, emojis: emojis, explicit_content_filter: explicit_content_filter, @@ -1778,13 +970,11 @@ impl<'de> Deserialize<'de> for Guild { } /// Checks if a `&str` contains another `&str`. -#[cfg(feature = "model")] fn contains_case_insensitive(to_look_at: &str, to_find: &str) -> bool { to_look_at.to_lowercase().contains(to_find) } /// Checks if a `&str` starts with another `&str`. -#[cfg(feature = "model")] fn starts_with_case_insensitive(to_look_at: &str, to_find: &str) -> bool { to_look_at.to_lowercase().starts_with(to_find) } @@ -1796,7 +986,6 @@ fn starts_with_case_insensitive(to_look_at: &str, to_find: &str) -> bool { /// expected to contain `origin` as substring. /// If not, using `closest_to_origin` would sort these /// the end. -#[cfg(feature = "model")] fn closest_to_origin(origin: &str, word_a: &str, word_b: &str) -> std::cmp::Ordering { let value_a = match word_a.find(origin) { Some(value) => value + word_a.len(), @@ -1882,7 +1071,6 @@ impl From<u64> for GuildContainer { fn from(id: u64) -> GuildContainer { GuildContainer::Id(GuildId(id)) } } -#[cfg(feature = "model")] impl InviteGuild { /// Returns the formatted URL of the guild's splash image, if one exists. pub fn splash_url(&self) -> Option<String> { @@ -1914,7 +1102,6 @@ pub enum GuildStatus { Offline(GuildUnavailable), } -#[cfg(feature = "model")] impl GuildStatus { /// Retrieves the Id of the inner [`Guild`]. /// diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs index 6b0b3d2..39f158a 100644 --- a/src/model/guild/partial_guild.rs +++ b/src/model/guild/partial_guild.rs @@ -1,13 +1,8 @@ -use futures::{Future, future}; use model::prelude::*; use std::cell::RefCell; use std::rc::Rc; use super::super::utils::{deserialize_emojis, deserialize_roles}; -use super::super::WrappedClient; -use ::FutureResult; -#[cfg(feature = "model")] -use builder::{EditGuild, EditMember, EditRole}; /// Partial information about a [`Guild`]. This does not include information /// like member data. @@ -38,304 +33,9 @@ pub struct PartialGuild { pub roles: HashMap<RoleId, Rc<RefCell<Role>>>, pub splash: Option<String>, pub verification_level: VerificationLevel, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl PartialGuild { - /// Ban a [`User`] from the guild. All messages by the - /// user within the last given number of days given will be deleted. This - /// may be a range between `0` and `7`. - /// - /// **Note**: Requires the [Ban Members] permission. - /// - /// # Examples - /// - /// Ban a member and remove all messages they've sent in the last 4 days: - /// - /// ```rust,ignore - /// // assumes a `user` and `guild` have already been bound - /// let _ = guild.ban(user, 4); - /// ``` - /// - /// # Errors - /// - /// Returns a [`ModelError::DeleteMessageDaysAmount`] if the number of - /// days' worth of messages to delete is over the maximum. - /// - /// [`ModelError::DeleteMessageDaysAmount`]: - /// enum.ModelError.html#variant.DeleteMessageDaysAmount - /// [`User`]: struct.User.html - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - // todo: add ban reason - pub fn ban<U: Into<UserId>>(&self, user_id: U, delete_message_days: u8) - -> FutureResult<()> { - if delete_message_days > 7 { - return Box::new(future::err(Error::Model( - ModelError::DeleteMessageDaysAmount(delete_message_days), - ))); - } - - Box::new(ftryopt!(self.client).http.ban_user( - self.id.0, - user_id.into().0, - delete_message_days, - "", - )) - } - - /// Gets a list of the guild's bans. - /// - /// Requires the [Ban Members] permission. - /// - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - #[inline] - pub fn bans(&self) -> FutureResult<Vec<Ban>> { - ftryopt!(self.client).http.get_bans(self.id.0) - } - - /// Gets all of the guild's channels over the REST API. - /// - /// [`Guild`]: struct.Guild.html - #[inline] - pub fn channels(&self) -> FutureResult<HashMap<ChannelId, GuildChannel>> { - let done = ftryopt!(self.client) - .http - .get_channels(self.id.0) - .map(|channels| { - let mut map = HashMap::with_capacity(channels.len()); - - for channel in channels { - map.insert(channel.id, channel); - } - - map - }); - - Box::new(done) - } - - /// Creates a [`GuildChannel`] in the guild. - /// - /// Refer to [`http::create_channel`] for more information. - /// - /// Requires the [Manage Channels] permission. - /// - /// # Examples - /// - /// Create a voice channel in a guild with the name `test`: - /// - /// ```rust,ignore - /// use serenity::model::ChannelType; - /// - /// guild.create_channel("test", ChannelType::Voice, None); - /// ``` - /// - /// [`GuildChannel`]: struct.GuildChannel.html - /// [`http::create_channel`]: ../http/fn.create_channel.html - /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html - #[inline] - pub fn create_channel<C>(&self, name: &str, kind: ChannelType, category: C) - -> FutureResult<GuildChannel> where C: Into<Option<ChannelId>> { - ftryopt!(self.client).http.create_channel( - self.id.0, - name, - kind, - category.into().map(|x| x.0), - ) - } - - /// Creates an emoji in the guild with a name and base64-encoded image. - /// - /// Refer to the documentation for [`Guild::create_emoji`] for more - /// information. - /// - /// Requires the [Manage Emojis] permission. - /// - /// # Examples - /// - /// See the [`EditProfile::avatar`] example for an in-depth example as to - /// how to read an image from the filesystem and encode it as base64. Most - /// of the example can be applied similarly for this method. - /// - /// [`EditProfile::avatar`]: ../builder/struct.EditProfile.html#method.avatar - /// [`Guild::create_emoji`]: struct.Guild.html#method.create_emoji - /// [`utils::read_image`]: ../utils/fn.read_image.html - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - #[inline] - pub fn create_emoji(&self, name: &str, image: &str) -> FutureResult<Emoji> { - ftryopt!(self.client).http.create_emoji(self.id.0, name, image) - } - - /// Creates an integration for the guild. - /// - /// Requires the [Manage Guild] permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - #[inline] - pub fn create_integration<I>(&self, integration_id: I, kind: &str) - -> FutureResult<()> where I: Into<IntegrationId> { - ftryopt!(self.client).http.create_guild_integration( - self.id.0, - integration_id.into().0, - kind, - ) - } - - /// Creates a new role in the guild with the data set, if any. - /// - /// See the documentation for [`Guild::create_role`] on how to use this. - /// - /// **Note**: Requires the [Manage Roles] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to perform bans. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`Guild::create_role`]: struct.Guild.html#method.create_role - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[inline] - pub fn create_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) - -> FutureResult<Role> { - ftryopt!(self.client).http.create_role(self.id.0, f) - } - - /// Deletes the current guild if the current user is the owner of the - /// guild. - /// - /// **Note**: Requires the current user to be the owner of the guild. - #[inline] - pub fn delete(&self) -> FutureResult<PartialGuild> { - ftryopt!(self.client).http.delete_guild(self.id.0) - } - - /// Deletes an [`Emoji`] from the guild. - /// - /// Requires the [Manage Emojis] permission. - /// - /// [`Emoji`]: struct.Emoji.html - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - #[inline] - pub fn delete_emoji<E: Into<EmojiId>>(&self, emoji_id: E) - -> FutureResult<()> { - ftryopt!(self.client).http.delete_emoji(self.id.0, emoji_id.into().0) - } - - /// Deletes an integration by Id from the guild. - /// - /// Requires the [Manage Guild] permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - #[inline] - pub fn delete_integration<I: Into<IntegrationId>>(&self, integration_id: I) - -> FutureResult<()> { - ftryopt!(self.client).http.delete_guild_integration( - self.id.0, - integration_id.into().0, - ) - } - - /// Deletes a [`Role`] by Id from the guild. - /// - /// Also see [`Role::delete`] if you have the `cache` and `methods` features - /// enabled. - /// - /// Requires the [Manage Roles] permission. - /// - /// [`Role`]: struct.Role.html - /// [`Role::delete`]: struct.Role.html#method.delete - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[inline] - pub fn delete_role<R: Into<RoleId>>(&self, role_id: R) -> FutureResult<()> { - let role_id = role_id.into().0; - - ftryopt!(self.client).http.delete_role(self.id.0, role_id) - } - - /// Edits the current guild with new data where specified. - /// - /// **Note**: Requires the current user to have the [Manage Guild] - /// permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - pub fn edit<'a, F: FnOnce(EditGuild) -> EditGuild>(&self, f: F) - -> FutureResult<PartialGuild> { - ftryopt!(self.client).http.edit_guild(self.id.0, f) - } - - /// Edits an [`Emoji`]'s name in the guild. - /// - /// Also see [`Emoji::edit`] if you have the `cache` and `methods` features - /// enabled. - /// - /// Requires the [Manage Emojis] permission. - /// - /// [`Emoji`]: struct.Emoji.html - /// [`Emoji::edit`]: struct.Emoji.html#method.edit - /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html - #[inline] - pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) - -> FutureResult<Emoji> { - ftryopt!(self.client) - .http - .edit_emoji(self.id.0, emoji_id.into().0, name) - } - - /// Edits the properties of member of the guild, such as muting or - /// nicknaming them. - /// - /// Refer to `EditMember`'s documentation for a full list of methods and - /// permission restrictions. - /// - /// # Examples - /// - /// Mute a member and set their roles to just one role with a predefined Id: - /// - /// ```rust,ignore - /// use serenity::model::GuildId; - /// - /// GuildId(7).edit_member(user_id, |m| m.mute(true).roles(&vec![role_id])); - /// ``` - #[inline] - pub fn edit_member<F, U>(&self, user_id: U, f: F) -> FutureResult<()> - where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> { - ftryopt!(self.client).http.edit_member(self.id.0, user_id.into().0, f) - } - - /// Edits the current user's nickname for the guild. - /// - /// Pass `None` to reset the nickname. - /// - /// **Note**: Requires the [Change Nickname] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have permission to change their own - /// nickname. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html - #[inline] - pub fn edit_nickname(&self, new_nickname: Option<&str>) - -> FutureResult<()> { - ftryopt!(self.client).http.edit_nickname(self.id.0, new_nickname) - } - - /// Kicks a [`Member`] from the guild. - /// - /// Requires the [Kick Members] permission. - /// - /// [`Member`]: struct.Member.html - /// [Kick Members]: permissions/constant.KICK_MEMBERS.html - #[inline] - pub fn kick<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> { - ftryopt!(self.client).http.kick_member(self.id.0, user_id.into().0) - } - /// Returns a formatted URL of the guild's icon, if the guild has an icon. pub fn icon_url(&self) -> Option<String> { self.icon @@ -343,78 +43,6 @@ impl PartialGuild { .map(|icon| format!(cdn!("/icons/{}/{}.webp"), self.id, icon)) } - /// Gets all integration of the guild. - /// - /// This performs a request over the REST API. - #[inline] - pub fn integrations(&self) -> FutureResult<Vec<Integration>> { - ftryopt!(self.client).http.get_guild_integrations(self.id.0) - } - - /// Gets all of the guild's invites. - /// - /// Requires the [Manage Guild] permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - #[inline] - pub fn invites(&self) -> FutureResult<Vec<RichInvite>> { - ftryopt!(self.client).http.get_guild_invites(self.id.0) - } - - /// Leaves the guild. - #[inline] - pub fn leave(&self) -> FutureResult<()> { - ftryopt!(self.client).http.leave_guild(self.id.0) - } - - /// Gets a user's [`Member`] for the guild by Id. - /// - /// [`Guild`]: struct.Guild.html - /// [`Member`]: struct.Member.html - pub fn member<U: Into<UserId>>(&self, user_id: U) -> FutureResult<Member> { - ftryopt!(self.client).http.get_member(self.id.0, user_id.into().0) - } - - /// Gets a list of the guild's members. - /// - /// Optionally pass in the `limit` to limit the number of results. Maximum - /// value is 1000. Optionally pass in `after` to offset the results by a - /// [`User`]'s Id. - /// - /// [`User`]: struct.User.html - pub fn members<U: Into<UserId>>(&self, limit: Option<u64>, after: Option<U>) - -> FutureResult<Vec<Member>> { - let after = after.map(Into::into).map(|x| x.0); - - ftryopt!(self.client).http.get_guild_members(self.id.0, limit, after) - } - - /// Moves a member to a specific voice channel. - /// - /// Requires the [Move Members] permission. - /// - /// [Move Members]: permissions/constant.MOVE_MEMBERS.html - #[inline] - pub fn move_member<C, U>(&self, user_id: U, channel_id: C) - -> FutureResult<()> where C: Into<ChannelId>, U: Into<UserId> { - ftryopt!(self.client) - .http - .edit_member(self.id.0, user_id.into().0, |f| f - .voice_channel(channel_id.into().0)) - } - - /// Gets the number of [`Member`]s that would be pruned with the given - /// number of days. - /// - /// Requires the [Kick Members] permission. - /// - /// [`Member`]: struct.Member.html - /// [Kick Members]: permissions/constant.KICK_MEMBERS.html - #[inline] - pub fn prune_count(&self, days: u16) -> FutureResult<GuildPrune> { - ftryopt!(self.client).http.get_guild_prune_count(self.id.0, days) - } - /// Returns the Id of the shard associated with the guild. /// /// When the cache is enabled this will automatically retrieve the total @@ -446,40 +74,6 @@ impl PartialGuild { .map(|icon| format!(cdn!("/splashes/{}/{}.webp"), self.id, icon)) } - /// Starts an integration sync for the given integration Id. - /// - /// Requires the [Manage Guild] permission. - /// - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - #[inline] - pub fn start_integration_sync<I>(&self, integration_id: I) - -> FutureResult<()> where I: Into<IntegrationId> { - ftryopt!(self.client) - .http - .start_integration_sync(self.id.0, integration_id.into().0) - } - - /// Unbans a [`User`] from the guild. - /// - /// Requires the [Ban Members] permission. - /// - /// [`User`]: struct.User.html - /// [Ban Members]: permissions/constant.BAN_MEMBERS.html - #[inline] - pub fn unban<U: Into<UserId>>(&self, user_id: U) -> FutureResult<()> { - ftryopt!(self.client).http.remove_ban(self.id.0, user_id.into().0) - } - - /// Retrieves the guild's webhooks. - /// - /// **Note**: Requires the [Manage Webhooks] permission. - /// - /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html - #[inline] - pub fn webhooks(&self) -> FutureResult<Vec<Webhook>> { - ftryopt!(self.client).http.get_guild_webhooks(self.id.0) - } - /// Obtain a reference to a role by its name. /// /// **Note**: If two or more roles have the same name, obtained reference will be one of diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs index bcb142a..bdc6e77 100644 --- a/src/model/guild/role.rs +++ b/src/model/guild/role.rs @@ -1,12 +1,5 @@ use model::prelude::*; use std::cmp::Ordering; -use super::super::WrappedClient; -use ::FutureResult; - -#[cfg(all(feature = "builder", feature = "cache", feature = "model"))] -use builder::EditRole; -#[cfg(all(feature = "cache", feature = "model"))] -use internal::prelude::*; /// Information about a role within a guild. A role represents a set of /// permissions, and can be attached to one or multiple users. A role has @@ -57,76 +50,9 @@ pub struct Role { /// /// The `@everyone` role is usually either `-1` or `0`. pub position: i64, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl Role { - /// Deletes the role. - /// - /// **Note** Requires the [Manage Roles] permission. - /// - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[cfg(feature = "cache")] - #[inline] - pub fn delete(&self) -> FutureResult<()> { - let guild_id = ftry!(self.find_guild()); - - ftryopt!(self.client).http.delete_role(guild_id.0, self.id.0) - } - - /// Edits a [`Role`], optionally setting its new fields. - /// - /// Requires the [Manage Roles] permission. - /// - /// # Examples - /// - /// Make a role hoisted: - /// - /// ```rust,no_run - /// # use serenity::model::id::RoleId; - /// # let role = RoleId(7).find().unwrap(); - /// // assuming a `role` has already been bound - // - /// role.edit(|r| r.hoist(true)); - /// ``` - /// - /// [`Role`]: struct.Role.html - /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html - #[cfg(all(feature = "builder", feature = "cache"))] - pub fn edit<F: FnOnce(EditRole) -> EditRole>(&self, f: F) - -> FutureResult<Role> { - let guild_id = ftry!(self.find_guild()); - - ftryopt!(self.client).http.edit_role(guild_id.0, self.id.0, f) - } - - /// Searches the cache for the guild that owns the role. - /// - /// # Errors - /// - /// Returns a [`ModelError::GuildNotFound`] if a guild is not in the cache - /// that contains the role. - /// - /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound - #[cfg(feature = "cache")] - pub fn find_guild(&self) -> Result<GuildId> { - let client = self.client.as_ref().ok_or_else(|| { - Error::Model(ModelError::ClientNotPresent) - })?; - - for guild in client.cache.borrow().guilds.values() { - let guild = guild.borrow(); - - if guild.roles.contains_key(&RoleId(self.id.0)) { - return Ok(guild.id); - } - } - - Err(Error::Model(ModelError::GuildNotFound)) - } - /// Check that the role has the given permission. #[inline] pub fn has_permission(&self, permission: Permissions) -> bool { diff --git a/src/model/invite.rs b/src/model/invite.rs index 3be7f47..63929e9 100644 --- a/src/model/invite.rs +++ b/src/model/invite.rs @@ -1,13 +1,7 @@ //! Models for server and channel invites. use chrono::{DateTime, FixedOffset}; -use futures::future; use super::prelude::*; -use super::WrappedClient; -use ::FutureResult; - -#[cfg(all(feature = "cache", feature = "model"))] -use super::Permissions; /// Information about an invite code. /// @@ -37,43 +31,9 @@ pub struct Invite { /// a representation of the minimal amount of information needed about the /// [`Guild`] being invited to. pub guild: InviteGuild, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl Invite { - /// Deletes the invite. - /// - /// **Note**: Requires the [Manage Guild] permission. - /// - /// # Errors - /// - /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] - /// if the current user does not have the required [permission]. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - /// [permission]: permissions/index.html - pub fn delete(&self) -> FutureResult<Invite> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let cache = ftry!(client.cache.try_borrow()); - - let req = Permissions::MANAGE_GUILD; - - if !ftry!(cache.user_has_perms(self.channel.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.delete_invite(&self.code) - } - /// Returns a URL to use for the invite. /// /// # Examples @@ -126,7 +86,6 @@ pub struct InviteGuild { pub voice_channel_count: Option<u64>, } -#[cfg(feature = "model")] impl InviteGuild { /// Returns the Id of the shard associated with the guild. /// @@ -194,48 +153,9 @@ pub struct RichInvite { pub temporary: bool, /// The amount of times that an invite has been used. pub uses: u64, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl RichInvite { - /// Deletes the invite. - /// - /// Refer to [`http::delete_invite`] for more information. - /// - /// **Note**: Requires the [Manage Guild] permission. - /// - /// # Errors - /// - /// If the `cache` feature is enabled, then this returns a - /// [`ModelError::InvalidPermissions`] if the current user does not have - /// the required [permission]. - /// - /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions - /// [`Invite::delete`]: struct.Invite.html#method.delete - /// [`http::delete_invite`]: ../http/fn.delete_invite.html - /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html - /// [permission]: permissions/index.html - pub fn delete(&self) -> FutureResult<Invite> { - let client = ftryopt!(self.client); - - #[cfg(feature = "cache")] - { - let cache = ftry!(client.cache.try_borrow()); - - let req = Permissions::MANAGE_GUILD; - - if !ftry!(cache.user_has_perms(self.channel.id, req)) { - return Box::new(future::err(Error::Model( - ModelError::InvalidPermissions(req), - ))); - } - } - - ftryopt!(self.client).http.delete_invite(&self.code) - } - /// Returns a URL to use for the invite. /// /// # Examples diff --git a/src/model/mod.rs b/src/model/mod.rs index 44a4a3e..bc059ee 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -40,7 +40,6 @@ pub mod webhook; pub use self::error::Error as ModelError; pub use self::permissions::Permissions; -use client::Client; use internal::prelude::*; use self::utils::*; use serde::de::Visitor; @@ -52,5 +51,3 @@ use std::result::Result as StdResult; #[cfg(feature = "utils")] use utils::Colour; - -type WrappedClient = Option<Rc<Client>>; diff --git a/src/model/permissions.rs b/src/model/permissions.rs index dbf0b69..310f8dd 100644 --- a/src/model/permissions.rs +++ b/src/model/permissions.rs @@ -254,7 +254,6 @@ bitflags! { } } -#[cfg(feature = "model")] impl Permissions { /// Shorthand for checking that the set of permissions contains the /// [Add Reactions] permission. diff --git a/src/model/user.rs b/src/model/user.rs index efc880a..86907cd 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -1,25 +1,14 @@ //! User information-related models. -use futures::{Future, future}; use serde_json; use std::fmt; use super::utils::deserialize_u16; use super::prelude::*; -use super::WrappedClient; -use ::FutureResult; use internal::prelude::*; use model::misc::Mentionable; -#[cfg(feature = "model")] -use builder::{CreateMessage, EditProfile}; -#[cfg(feature = "model")] use chrono::NaiveDateTime; -#[cfg(feature = "model")] -use http::GuildPagination; -#[cfg(feature = "model")] use std::fmt::Write; -#[cfg(feature = "model")] -use utils::{self, VecMap}; /// Information about the current user. #[derive(Clone, Default, Debug, Deserialize, Serialize)] @@ -32,11 +21,8 @@ pub struct CurrentUser { pub mfa_enabled: bool, #[serde(rename = "username")] pub name: String, pub verified: bool, - #[serde(skip)] - pub(crate) client: WrappedClient, } -#[cfg(feature = "model")] impl CurrentUser { /// Returns the formatted URL of the user's icon, if one exists. /// @@ -68,38 +54,6 @@ impl CurrentUser { #[inline] pub fn default_avatar_url(&self) -> String { default_avatar_url(self.discriminator) } - /// Edits the current user's profile settings. - /// - /// This mutates the current user in-place. - /// - /// Refer to `EditProfile`'s documentation for its methods. - /// - /// # Examples - /// - /// Change the avatar: - /// - /// ```rust,ignore - /// use serenity::CACHE; - /// - /// let avatar = serenity::utils::read_image("./avatar.png").unwrap(); - /// - /// CACHE.write().user.edit(|p| p.avatar(Some(&avatar))); - /// ``` - pub fn edit<F: FnOnce(EditProfile) -> EditProfile>(&mut self, f: F) - -> Box<Future<Item = CurrentUser, Error = Error>> { - let mut map = VecMap::new(); - map.insert("username", Value::String(self.name.clone())); - - if let Some(email) = self.email.as_ref() { - map.insert("email", Value::String(email.clone())); - } - - let map = utils::vecmap_to_json_map(f(EditProfile(map)).0); - let value = Value::Object(map); - - ftryopt!(self.client).http.edit_profile(&value) - } - /// Retrieves the URL to the current user's avatar, falling back to the /// default avatar if needed. /// @@ -113,119 +67,6 @@ impl CurrentUser { .unwrap_or_else(|| self.default_avatar_url()) } - /// Gets a list of guilds that the current user is in. - /// - /// # Examples - /// - /// Print out the names of all guilds the current user is in: - /// - /// ```rust,no_run - /// # use serenity::CACHE; - /// # - /// # let cache = CACHE.read(); - /// # - /// // assuming the cache has been unlocked - /// let user = &cache.user; - /// - /// if let Ok(guilds) = user.guilds() { - /// for (index, guild) in guilds.into_iter().enumerate() { - /// println!("{}: {}", index, guild.name); - /// } - /// } - /// ``` - pub fn guilds(&self) -> FutureResult<Vec<GuildInfo>> { - ftryopt!(self.client) - .http - .get_guilds(&GuildPagination::After(GuildId(1)), 100) - } - - /// Returns the invite url for the bot with the given permissions. - /// - /// This queries the REST API for the client id. - /// - /// If the permissions passed are empty, the permissions part will be dropped. - /// - /// # Examples - /// - /// Get the invite url with no permissions set: - /// - /// ```rust,no_run - /// # use serenity::CACHE; - /// # - /// # let mut cache = CACHE.write(); - /// - /// use serenity::model::Permissions; - /// - /// // assuming the cache has been unlocked - /// let url = match cache.user.invite_url(Permissions::empty()) { - /// Ok(v) => v, - /// Err(why) => { - /// println!("Error getting invite url: {:?}", why); - /// - /// return; - /// }, - /// }; - /// - /// assert_eq!(url, "https://discordapp.com/api/oauth2/authorize? \ - /// client_id=249608697955745802&scope=bot"); - /// ``` - /// - /// Get the invite url with some basic permissions set: - /// - /// ```rust,no_run - /// # use serenity::CACHE; - /// # - /// # let mut cache = CACHE.write(); - /// - /// use serenity::model::Permissions; - /// - /// // assuming the cache has been unlocked - /// let url = match cache.user.invite_url(Permissions::READ_MESSAGES | Permissions::SEND_MESSAGES | Permissions::EMBED_LINKS) { - /// Ok(v) => v, - /// Err(why) => { - /// println!("Error getting invite url: {:?}", why); - /// - /// return; - /// }, - /// }; - /// - /// assert_eq!(url, - /// "https://discordapp. - /// com/api/oauth2/authorize?client_id=249608697955745802&scope=bot&permissions=19456"); - /// ``` - /// - /// # Errors - /// - /// Returns an - /// [`HttpError::InvalidRequest(Unauthorized)`][`HttpError::InvalidRequest`] - /// If the user is not authorized for this end point. - /// - /// May return [`Error::Format`] while writing url to the buffer. - /// - /// [`Error::Format`]: ../enum.Error.html#variant.Format - /// [`HttpError::InvalidRequest`]: ../http/enum.HttpError.html#variant.InvalidRequest - pub fn invite_url(&self, permissions: Permissions) -> FutureResult<String> { - let bits = permissions.bits(); - - let done = ftryopt!(self.client) - .http - .get_current_application_info() - .map(move |app| { - let mut url = format!( - "https://discordapp.com/api/oauth2/authorize?client_id={}&scope=bot", - app.id, - ); - - if bits != 0 { - let _ = write!(url, "&permissions={}", bits); - } - - url - }); - - Box::new(done) - } - /// Returns a static formatted URL of the user's icon, if one exists. /// /// This will always produce a WEBP image URL. @@ -356,8 +197,6 @@ pub struct User { /// change if the username+discriminator pair becomes non-unique. #[serde(rename = "username")] pub name: String, - #[serde(skip)] - pub(crate) client: WrappedClient, } use std::hash::{Hash, Hasher}; @@ -376,7 +215,6 @@ impl Hash for User { } } -#[cfg(feature = "model")] impl User { /// Returns the formatted URL of the user's icon, if one exists. /// @@ -384,15 +222,6 @@ impl User { #[inline] pub fn avatar_url(&self) -> Option<String> { avatar_url(self.id, self.avatar.as_ref()) } - /// Creates a direct message channel between the [current user] and the - /// user. This can also retrieve the channel if one already exists. - /// - /// [current user]: struct.CurrentUser.html - #[inline] - pub fn create_dm_channel(&self) -> FutureResult<PrivateChannel> { - ftryopt!(self.client).http.create_private_channel(self.id.0) - } - /// Retrieves the time that this user was created at. #[inline] pub fn created_at(&self) -> NaiveDateTime { self.id.created_at() } @@ -403,163 +232,6 @@ impl User { #[inline] pub fn default_avatar_url(&self) -> String { default_avatar_url(self.discriminator) } - /// Sends a message to a user through a direct message channel. This is a - /// channel that can only be accessed by you and the recipient. - /// - /// # Examples - /// - /// When a user sends a message with a content of `"~help"`, DM the author a - /// help message, and then react with `'👌'` to verify message sending: - /// - /// ```rust,no_run - /// # use serenity::prelude::*; - /// # use serenity::model::prelude::*; - /// # - /// use serenity::model::Permissions; - /// use serenity::CACHE; - /// - /// struct Handler; - /// - /// impl EventHandler for Handler { - /// fn message(&self, _: Context, msg: Message) { - /// if msg.content == "~help" { - /// let cache = CACHE.read(); - /// - /// let url = match cache.user.invite_url(Permissions::empty()) { - /// Ok(v) => v, - /// Err(why) => { - /// println!("Error creating invite url: {:?}", why); - /// - /// return; - /// }, - /// }; - /// - /// let help = format!( - /// "Helpful info here. Invite me with this link: <{}>", - /// url, - /// ); - /// - /// match msg.author.direct_message(|m| m.content(&help)) { - /// Ok(_) => { - /// let _ = msg.react('👌'); - /// }, - /// Err(why) => { - /// println!("Err sending help: {:?}", why); - /// - /// let _ = msg.reply("There was an error DMing you help."); - /// }, - /// }; - /// } - /// } - /// } - /// - /// let mut client = Client::new("token", Handler); - /// ``` - /// - /// # Examples - /// - /// Returns a [`ModelError::MessagingBot`] if the user being direct messaged - /// is a bot user. - /// - /// [`ModelError::MessagingBot`]: enum.ModelError.html#variant.MessagingBot - /// [`PrivateChannel`]: struct.PrivateChannel.html - /// [`User::dm`]: struct.User.html#method.dm - // A tale with Clippy: - // - // A person named Clippy once asked you to unlock a box and take something - // from it, but you never re-locked it, so you'll die and the universe will - // implode because the box must remain locked unless you're there, and you - // can't just borrow that item from it and take it with you forever. - // - // Instead what you do is unlock the box, take the item out of it, make a - // copy of said item, and then re-lock the box, and take your copy of the - // item with you. - // - // The universe is still fine, and nothing implodes. - // - // (AKA: Clippy is wrong and so we have to mark as allowing this lint.) - #[allow(let_and_return)] - #[cfg(feature = "builder")] - pub fn direct_message<'a, F: 'a + FnOnce(CreateMessage) -> CreateMessage>( - &'a self, - f: F, - ) -> Box<Future<Item = Message, Error = Error> + 'a> { - if self.bot { - return Box::new(future::err(Error::Model( - ModelError::MessagingBot, - ))); - } - - let client = ftryopt!(self.client); - - let private_channel_id = feature_cache! {{ - let finding = { - let cache = client.cache.borrow(); - - let finding = cache.private_channels - .values() - .find(|ch| { - let ch = ch.borrow(); - let recipient = ch.recipient.borrow(); - - recipient.id == self.id - }) - .map(|ch| ch.borrow().id); - - finding - }; - - if let Some(finding) = finding { - return Box::new(client.http.send_message(finding.0, f)); - } else { - let done = client - .http - .create_private_channel(self.id.0) - .map(|channel| channel.id); - - Box::new(done) - } - } else { - let done = ftryopt!(self.client) - .http - .create_private_channel(self.id.0) - .map(|channel| channel.id); - - Box::new(done) - }}; - - Box::new(private_channel_id - .and_then(move |id| client.http.send_message(id.0, f))) - } - - /// This is an alias of [direct_message]. - /// - /// # Examples - /// - /// Sending a message: - /// - /// ```rust,ignore - /// // assuming you are in a context - /// - /// let _ = message.author.dm("Hello!"); - /// ``` - /// - /// # Examples - /// - /// Returns a [`ModelError::MessagingBot`] if the user being direct messaged - /// is a bot user. - /// - /// [`ModelError::MessagingBot`]: enum.ModelError.html#variant.MessagingBot - /// [direct_message]: #method.direct_message - #[cfg(feature = "builder")] - #[inline] - pub fn dm<'a, F: 'a + FnOnce(CreateMessage) -> CreateMessage>( - &'a self, - f: F, - ) -> Box<Future<Item = Message, Error = Error> + 'a> { - self.direct_message(f) - } - /// Retrieves the URL to the user's avatar, falling back to the default /// avatar if needed. /// @@ -573,59 +245,6 @@ impl User { .unwrap_or_else(|| self.default_avatar_url()) } - /// Check if a user has a [`Role`]. This will retrieve the [`Guild`] from - /// the [`Cache`] if it is available, and then check if that guild has the - /// given [`Role`]. - /// - /// Three forms of data may be passed in to the guild parameter: either a - /// [`PartialGuild`], a [`GuildId`], or a `u64`. - /// - /// # Examples - /// - /// Check if a guild has a [`Role`] by Id: - /// - /// ```rust,ignore - /// // Assumes a 'guild_id' and `role_id` have already been bound - /// let _ = message.author.has_role(guild_id, role_id); - /// ``` - /// - /// [`Guild`]: struct.Guild.html - /// [`GuildId`]: struct.GuildId.html - /// [`PartialGuild`]: struct.PartialGuild.html - /// [`Role`]: struct.Role.html - /// [`Cache`]: ../cache/struct.Cache.html - // no-cache would warn on guild_id. - pub fn has_role<G, R>(&self, guild: G, role: R) -> bool - where G: Into<GuildContainer>, R: Into<RoleId> { - let role_id = role.into(); - - match guild.into() { - GuildContainer::Guild(guild) => guild.roles.contains_key(&role_id), - GuildContainer::Id(_guild_id) => { - feature_cache! {{ - let client = match self.client.as_ref() { - Some(client) => client, - None => return false, - }; - - let cache = client.cache.borrow(); - - cache - .guilds - .get(&_guild_id) - .map(|g| { - g.borrow().members.get(&self.id) - .map(|m| m.borrow().roles.contains(&role_id)) - .unwrap_or(false) - }) - .unwrap_or(false) - } else { - true - }} - }, - } - } - /// Returns a static formatted URL of the user's icon, if one exists. /// /// This will always produce a WEBP image URL. @@ -699,7 +318,6 @@ impl<'a> From<&'a User> for UserId { fn from(user: &User) -> UserId { user.id } } -#[cfg(feature = "model")] fn avatar_url(user_id: UserId, hash: Option<&String>) -> Option<String> { hash.map(|hash| { let ext = if hash.starts_with("a_") { @@ -712,17 +330,14 @@ fn avatar_url(user_id: UserId, hash: Option<&String>) -> Option<String> { }) } -#[cfg(feature = "model")] fn default_avatar_url(discriminator: u16) -> String { cdn!("/embed/avatars/{}.png", discriminator % 5u16) } -#[cfg(feature = "model")] fn static_avatar_url(user_id: UserId, hash: Option<&String>) -> Option<String> { hash.map(|hash| cdn!("/avatars/{}/{}.webp?size=1024", user_id, hash)) } -#[cfg(feature = "model")] fn tag(name: &str, discriminator: u16) -> String { // 32: max length of username // 1: `#` diff --git a/src/model/utils.rs b/src/model/utils.rs index 9ed34e1..f18f19a 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -258,17 +258,6 @@ impl PermissionCheck for Cache { } } -macro_rules! ftryopt { - ($code:expr) => { - match $code { - Some(ref v) => v, - None => return Box::new(::futures::future::err(::Error::Model( - ::model::ModelError::ClientNotPresent, - ))), - } - }; -} - macro_rules! num_visitors { ($($visitor:ident: $type:ty),*) => { $( diff --git a/src/model/webhook.rs b/src/model/webhook.rs index f3003e7..9bd374a 100644 --- a/src/model/webhook.rs +++ b/src/model/webhook.rs @@ -1,17 +1,7 @@ //! Webhook model and implementations. -use futures::Future; use super::id::{ChannelId, GuildId, WebhookId}; use super::user::User; -use super::WrappedClient; -use ::FutureResult; - -#[cfg(feature = "model")] -use builder::ExecuteWebhook; -#[cfg(feature = "model")] -use internal::prelude::*; -#[cfg(feature = "model")] -use super::channel::Message; /// A representation of a webhook, which is a low-effort way to post messages to /// channels. They do not necessarily require a bot user or authentication to @@ -44,139 +34,4 @@ pub struct Webhook { /// /// **Note**: This is not received when getting a webhook by its token. pub user: Option<User>, - #[serde(skip)] - pub(crate) client: WrappedClient, -} - -#[cfg(feature = "model")] -impl Webhook { - /// Deletes the webhook. - /// - /// As this calls the [`http::delete_webhook_with_token`] function, - /// authentication is not required. - /// - /// [`http::delete_webhook_with_token`]: ../http/fn.delete_webhook_with_token.html - #[inline] - pub fn delete(&self) -> FutureResult<()> { - let done = ftryopt!(self.client) - .http - .delete_webhook_with_token(self.id.0, &self.token); - - Box::new(done) - } - - /// - /// Edits the webhook in-place. All fields are optional. - /// - /// To nullify the avatar, pass `Some("")`. Otherwise, passing `None` will - /// not modify the avatar. - /// - /// Refer to [`http::edit_webhook`] for httprictions on editing webhooks. - /// - /// As this calls the [`http::edit_webhook_with_token`] function, - /// authentication is not required. - /// - /// # Examples - /// - /// Editing a webhook's name: - /// - /// ```rust,no_run - /// use serenity::http; - /// - /// let id = 245037420704169985; - /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV"; - /// - /// let mut webhook = http::get_webhook_with_token(id, token) - /// .expect("valid webhook"); - /// - /// let _ = webhook.edit(Some("new name"), None).expect("Error editing"); - /// ``` - /// - /// Setting a webhook's avatar: - /// - /// ```rust,no_run - /// use serenity::http; - /// - /// let id = 245037420704169985; - /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV"; - /// - /// let mut webhook = http::get_webhook_with_token(id, token) - /// .expect("valid webhook"); - /// - /// let image = serenity::utils::read_image("./webhook_img.png") - /// .expect("Error reading image"); - /// - /// let _ = webhook.edit(None, Some(&image)).expect("Error editing"); - /// ``` - /// - /// [`http::edit_webhook`]: ../http/fn.edit_webhook.html - /// [`http::edit_webhook_with_token`]: ../http/fn.edit_webhook_with_token.html - pub fn edit(&mut self, name: Option<&str>, avatar: Option<&str>) - -> Box<Future<Item = Webhook, Error = Error>> { - let done = ftryopt!(self.client) - .http - .edit_webhook_with_token(self.id.0, &self.token, name, avatar); - - Box::new(done) - } - - /// Executes a webhook with the fields set via the given builder. - /// - /// The builder provides a method of setting only the fields you need, - /// without needing to pass a long set of arguments. - /// - /// # Examples - /// - /// Execute a webhook with message content of `test`: - /// - /// ```rust,no_run - /// use serenity::http; - /// - /// let id = 245037420704169985; - /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV"; - /// - /// let mut webhook = http::get_webhook_with_token(id, token) - /// .expect("valid webhook"); - /// - /// let _ = webhook.execute(false, |w| w.content("test")).expect("Error executing"); - /// ``` - /// - /// Execute a webhook with message content of `test`, overriding the - /// username to `serenity`, and sending an embed: - /// - /// ```rust,no_run - /// use serenity::http; - /// use serenity::model::channel::Embed; - /// - /// let id = 245037420704169985; - /// let token = "ig5AO-wdVWpCBtUUMxmgsWryqgsW3DChbKYOINftJ4DCrUbnkedoYZD0VOH1QLr-S3sV"; - /// - /// let mut webhook = http::get_webhook_with_token(id, token) - /// .expect("valid webhook"); - /// - /// let embed = Embed::fake(|e| e - /// .title("Rust's website") - /// .description("Rust is a systems programming language that runs - /// blazingly fast, prevents segfaults, and guarantees - /// thread safety.") - /// .url("https://rust-lang.org")); - /// - /// let _ = webhook.execute(false, |w| w - /// .content("test") - /// .username("serenity") - /// .embeds(vec![embed])) - /// .expect("Error executing"); - /// ``` - #[inline] - pub fn execute<'a, F: FnOnce(ExecuteWebhook) -> ExecuteWebhook>( - &'a self, - wait: bool, - f: F, - ) -> Box<Future<Item = Option<Message>, Error = Error> + 'a> { - let done = ftryopt!(self.client) - .http - .execute_webhook(self.id.0, &self.token, wait, f); - - Box::new(done) - } } |