diff options
| author | Austin Hellyer <[email protected]> | 2017-01-23 12:16:06 -0800 |
|---|---|---|
| committer | Austin Hellyer <[email protected]> | 2017-01-23 12:16:06 -0800 |
| commit | 651c618f17cb92d3ea9bbd1d5f5c92a015ff64e0 (patch) | |
| tree | dcf452e8fe73411af331678a769cb769a32dd12e /src/model/channel.rs | |
| parent | Fix no-framework compilation (diff) | |
| download | serenity-651c618f17cb92d3ea9bbd1d5f5c92a015ff64e0.tar.xz serenity-651c618f17cb92d3ea9bbd1d5f5c92a015ff64e0.zip | |
Switch to a mostly-fully OOP approach
The context is now strictly in relation to the context of the current
channel related to the event, if any. See Context::say for a list of
events that the context can be used for.
Diffstat (limited to 'src/model/channel.rs')
| -rw-r--r-- | src/model/channel.rs | 1363 |
1 files changed, 1240 insertions, 123 deletions
diff --git a/src/model/channel.rs b/src/model/channel.rs index a773cc7..bda26e5 100644 --- a/src/model/channel.rs +++ b/src/model/channel.rs @@ -1,5 +1,9 @@ +use hyper::Client as HyperClient; +use serde_json::builder::ObjectBuilder; use std::borrow::Cow; use std::fmt::{self, Write}; +use std::io::Read; +use std::mem; use super::utils::{ decode_id, into_map, @@ -8,34 +12,29 @@ use super::utils::{ remove, }; use super::*; +use ::client::rest; use ::constants; use ::internal::prelude::*; +use ::utils::builder::{ + CreateEmbed, + CreateInvite, + CreateMessage, + EditChannel, + GetMessages, + Search +}; use ::utils::decode_array; -#[cfg(feature="methods")] -use hyper::Client as HyperClient; -#[cfg(feature="methods")] -use serde_json::builder::ObjectBuilder; -#[cfg(feature="methods")] -use std::io::Read; -#[cfg(feature="methods")] -use std::mem; -#[cfg(all(feature="cache", feature="methods"))] +#[cfg(feature="cache")] use super::utils; - -#[cfg(all(feature="cache", feature="methods"))] +#[cfg(feature="cache")] use ::client::CACHE; -#[cfg(feature="methods")] -use ::client::rest; -#[cfg(all(feature="cache", feature="methods"))] +#[cfg(feature="cache")] use ::ext::cache::ChannelRef; -#[cfg(feature="methods")] -use ::utils::builder::{CreateEmbed, CreateInvite, EditChannel, Search}; impl Attachment { /// If this attachment is an image, then a tuple of the width and height /// in pixels is returned. - #[cfg(feature="methods")] pub fn dimensions(&self) -> Option<(u64, u64)> { if let (Some(width), Some(height)) = (self.width, self.height) { Some((width, height)) @@ -110,7 +109,6 @@ impl Attachment { /// [`Error::Hyper`]: ../enum.Error.html#variant.Hyper /// [`Error::Io`]: ../enum.Error.html#variant.Io /// [`Message`]: struct.Message.html - #[cfg(feature="methods")] pub fn download(&self) -> Result<Vec<u8>> { let hyper = HyperClient::new(); let mut response = hyper.get(&self.url).send()?; @@ -123,6 +121,50 @@ impl Attachment { } impl Channel { + /// Marks the channel as being read up to a certain [`Message`]. + /// + /// Refer to the documentation for [`rest::ack_message`] for more + /// information. + /// + /// # Errors + /// + /// If the `cache` is enabled, returns a + /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot + /// user. + /// + /// [`Channel`]: enum.Channel.html + /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsUser + /// [`Message`]: struct.Message.html + /// [`rest::ack_message`]: rest/fn.ack_message.html + pub fn ack<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + #[cfg(feature="cache")] + { + if CACHE.read().unwrap().user.bot { + return Err(Error::Client(ClientError::InvalidOperationAsBot)); + } + } + + self.id().ack(message_id) + } + + /// 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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + self.id().create_reaction(message_id, reaction_type) + } + #[doc(hidden)] pub fn decode(value: Value) -> Result<Channel> { let map = into_map(value)?; @@ -144,7 +186,6 @@ impl Channel { /// closest functionality is leaving it. /// /// [`Group`]: struct.Group.html - #[cfg(feature="methods")] pub fn delete(&self) -> Result<()> { match *self { Channel::Group(ref group) => { @@ -161,6 +202,122 @@ impl Channel { Ok(()) } + /// 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. + /// + /// (in practice, please do not do this) + /// + /// [`Message`]: struct.Message.html + /// [`Message::delete`]: struct.Message.html#method.delete + /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html + #[inline] + pub fn delete_message<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + self.id().delete_message(message_id) + } + + /// Deletes all messages by Ids from the given vector in the channel. + /// + /// The minimum amount of messages is 2 and the maximum amount is 100. + /// + /// Requires the [Manage Messages] permission. + /// + /// **Note**: This uses bulk delete endpoint which is not available + /// for user accounts. + /// + /// **Note**: Messages that are older than 2 weeks can't be deleted using + /// this method. + /// + /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html + #[inline] + pub fn delete_messages(&self, message_ids: &[MessageId]) -> Result<()> { + self.id().delete_messages(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) -> Result<()> { + self.id().delete_permission(permission_type) + } + + /// 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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + self.id().delete_reaction(message_id, user_id, reaction_type) + } + + /// Gets a message from the channel. + /// + /// Requires the [Read Message History] permission. + /// + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> { + self.id().get_message(message_id) + } + + /// Gets messages from the channel. + /// + /// Requires the [Read Message History] permission. + /// + /// # Examples + /// + /// ```rust,ignore + /// use serenity::model::ChannelId; + /// + /// let messages = channel.get_messages(|g| g + /// .before(20) + /// .after(100)); // Maximum is 100. + /// ``` + /// + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_messages<F>(&self, f: F) -> Result<Vec<Message>> + where F: FnOnce(GetMessages) -> GetMessages { + self.id().get_messages(f) + } + + /// 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 get_reaction_users<M, R, U>(&self, + message_id: M, + reaction_type: R, + limit: Option<u8>, + after: Option<U>) + -> Result<Vec<User>> where M: Into<MessageId>, R: Into<ReactionType>, U: Into<UserId> { + self.id().get_reaction_users(message_id, reaction_type, limit, after) + } + /// Retrieves the Id of the inner [`Group`], [`GuildChannel`], or /// [`PrivateChannel`]. /// @@ -191,7 +348,6 @@ impl Channel { /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot /// [`Message`]: struct.Message.html /// [`Search`]: ../utils/builder/struct.Search.html - #[cfg(feature="methods")] pub fn search<F>(&self, f: F) -> Result<SearchResult> where F: FnOnce(Search) -> Search { #[cfg(feature="cache")] @@ -201,13 +357,18 @@ impl Channel { } } - let id = match *self { - Channel::Group(ref group) => group.channel_id.0, - Channel::Guild(ref channel) => channel.id.0, - Channel::Private(ref channel) => channel.id.0, - }; + self.id().search(f) + } - rest::search_channel_messages(id, f(Search::default()).0) + /// 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) -> Result<()> { + self.id().unpin(message_id) } } @@ -236,20 +397,509 @@ impl fmt::Display for Channel { } } +impl ChannelId { + /// Marks a [`Channel`] as being read up to a certain [`Message`]. + /// + /// Refer to the documentation for [`rest::ack_message`] for more + /// information. + /// + /// [`Channel`]: enum.Channel.html + /// [`Message`]: struct.Message.html + /// [`rest::ack_message`]: rest/fn.ack_message.html + #[inline] + pub fn ack<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + rest::ack_message(self.0, message_id.into().0) + } + + /// Broadcasts that the current user is typing to a channel for the next 5 + /// seconds. + /// + /// After 5 seconds, another request must be made to continue broadcasting + /// that the current user is typing. + /// + /// This should rarely be used for bots, and should likely only be used for + /// signifying that a long-running command is still being executed. + /// + /// **Note**: Requires the [Send Messages] permission. + /// + /// # Examples + /// + /// ```rust,ignore + /// use serenity::model::ChannelId; + /// + /// let _successful = ChannelId(7).broadcast_typing(); + /// ``` + /// + /// [Send Messages]: permissions/constant.SEND_MESSAGES.html + #[inline] + pub fn broadcast_typing(&self) -> Result<()> { + rest::broadcast_typing(self.0) + } + + /// Creates a [permission overwrite][`PermissionOverwrite`] for either a + /// single [`Member`] or [`Role`] within the channel. + /// + /// Refer to the documentation for [`GuildChannel::create_permission`] for + /// more information. + /// + /// Requires the [Manage Channels] permission. + /// + /// [`GuildChannel::create_permission`]: struct.GuildChannel.html#method.create_permission + /// [`Member`]: struct.Member.html + /// [`PermissionOverwrite`]: struct.PermissionOverWrite.html + /// [`Role`]: struct.Role.html + /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html + pub fn create_permission(&self, target: PermissionOverwrite) + -> Result<()> { + let (id, kind) = match target.kind { + PermissionOverwriteType::Member(id) => (id.0, "member"), + PermissionOverwriteType::Role(id) => (id.0, "role"), + }; + + let map = ObjectBuilder::new() + .insert("allow", target.allow.bits()) + .insert("deny", target.deny.bits()) + .insert("id", id) + .insert("type", kind) + .build(); + + rest::create_permission(self.0, id, map) + } + + /// 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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + rest::create_reaction(self.0, message_id.into().0, reaction_type.into()) + } + + /// Deletes this channel, returning the channel on a successful deletion. + #[inline] + pub fn delete(&self) -> Result<Channel> { + rest::delete_channel(self.0) + } + + /// 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. + /// + /// (in practice, please do not do this) + /// + /// [`Message`]: struct.Message.html + /// [`Message::delete`]: struct.Message.html#method.delete + /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html + #[inline] + pub fn delete_message<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + rest::delete_message(self.0, message_id.into().0) + } + + /// Deletes all messages by Ids from the given vector in the given channel. + /// + /// Refer to the documentation for [`Channel::delete_messages`] for more + /// information. + /// + /// Requires the [Manage Messages] permission. + /// + /// **Note**: This uses bulk delete endpoint which is not available + /// for user accounts. + /// + /// **Note**: Messages that are older than 2 weeks can't be deleted using this method. + /// + /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages + /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html + pub fn delete_messages(&self, message_ids: &[MessageId]) -> Result<()> { + let ids = message_ids.into_iter() + .map(|message_id| message_id.0) + .collect::<Vec<u64>>(); + + let map = ObjectBuilder::new().insert("messages", ids).build(); + + rest::delete_messages(self.0, map) + } + + /// 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 + pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> { + rest::delete_permission(self.0, match permission_type { + PermissionOverwriteType::Member(id) => id.0, + PermissionOverwriteType::Role(id) => id.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 + pub fn delete_reaction<M, R>(&self, message_id: M, user_id: Option<UserId>, reaction_type: R) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + rest::delete_reaction(self.0, + message_id.into().0, + user_id.map(|uid| uid.0), + reaction_type.into()) + } + + + /// Edits the settings of a [`Channel`], optionally setting new values. + /// + /// Refer to `EditChannel`'s documentation for its methods. + /// + /// Requires the [Manage Channel] permission. + /// + /// # Examples + /// + /// Change a voice channel's name and bitrate: + /// + /// ```rust,ignore + /// context.edit_channel(channel_id, |c| c + /// .name("test") + /// .bitrate(64000)); + /// ``` + /// + /// # Errors + /// + /// Returns a [`ClientError::NoChannelId`] if the current context is not + /// related to a channel. + /// + /// [`Channel`]: enum.Channel.html + /// [`ClientError::NoChannelId`]: ../client/enum.ClientError.html#variant.NoChannelId + #[inline] + pub fn edit<F: FnOnce(EditChannel) -> EditChannel>(&self, f: F) -> Result<GuildChannel> { + rest::edit_channel(self.0, f(EditChannel::default()).0.build()) + } + + /// Edits a [`Message`] in the channel given its Id. + /// + /// Pass an empty string (`""`) to `text` if you are editing a message with + /// an embed or file but no content. Otherwise, `text` must be given. + /// + /// **Note**: Requires that the current user be the author of the message. + /// + /// # Errors + /// + /// Returns a [`ClientError::NoChannelId`] if the current context is not + /// related to a channel. + /// + /// [`ClientError::NoChannelId`]: ../client/enum.ClientError.html#variant.NoChannelId + /// [`Message`]: struct.Message.html + pub fn edit_message<F, M>(&self, message_id: M, text: &str, f: F) -> Result<Message> + where F: FnOnce(CreateEmbed) -> CreateEmbed, M: Into<MessageId> { + let mut map = ObjectBuilder::new().insert("content", text); + + let embed = f(CreateEmbed::default()).0; + + if embed.len() > 1 { + map = map.insert("embed", Value::Object(embed)); + } + + rest::edit_message(self.0, message_id.into().0, map.build()) + } + + /// Search the cache for the channel with the Id. + #[cfg(feature="cache")] + pub fn find(&self) -> Option<Channel> { + CACHE.read().unwrap().get_channel(*self).map(|x| x.clone_inner()) + } + + /// Search the cache for the channel. If it can't be found, the channel is + /// requested over REST. + pub fn get(&self) -> Result<Channel> { + #[cfg(feature="cache")] + { + if let Some(channel) = CACHE.read().unwrap().get_channel(*self) { + return Ok(channel.clone_inner()); + } + } + + rest::get_channel(self.0) + } + + /// Gets all of the channel's invites. + /// + /// Requires the [Manage Channels] permission. + /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html + #[inline] + pub fn get_invites(&self) -> Result<Vec<RichInvite>> { + rest::get_channel_invites(self.0) + } + + /// Gets a message from the channel. + /// + /// Requires the [Read Message History] permission. + /// + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> { + rest::get_message(self.0, message_id.into().0) + } + + /// Gets messages from the channel. + /// + /// Refer to [`Channel::get_messages`] for more information. + /// + /// Requires the [Read Message History] permission. + /// + /// [`Channel::get_messages`]: enum.Channel.html#method.get_messages + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + pub fn get_messages<F>(&self, f: F) -> Result<Vec<Message>> + where F: FnOnce(GetMessages) -> GetMessages { + let mut map = f(GetMessages::default()).0; + let mut query = format!("?limit={}", map.remove("limit").unwrap_or(50)); + + if let Some(after) = map.remove("after") { + write!(query, "&after={}", after)?; + } else if let Some(around) = map.remove("around") { + write!(query, "&around={}", around)?; + } else if let Some(before) = map.remove("before") { + write!(query, "&before={}", before)?; + } + + rest::get_messages(self.0, &query) + } + + /// Gets the list of [`User`]s who have reacted to a [`Message`] with a + /// certain [`Emoji`]. + /// + /// Refer to [`Channel::get_reaction_users`] for more information. + /// + /// **Note**: Requires the [Read Message History] permission. + /// + /// [`Channel::get_reaction_users`]: enum.Channel.html#variant.get_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 get_reaction_users<M, R, U>(&self, + message_id: M, + reaction_type: R, + limit: Option<u8>, + after: Option<U>) + -> Result<Vec<User>> where M: Into<MessageId>, R: Into<ReactionType>, U: Into<UserId> { + let limit = limit.map_or(50, |x| if x > 100 { 100 } else { x }); + + rest::get_reaction_users(self.0, + message_id.into().0, + reaction_type.into(), + limit, + after.map(|u| u.into().0)) + } + + /// Pins a [`Message`] to the channel. + #[inline] + pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + rest::pin_message(self.0, message_id.into().0) + } + + /// Gets the list of [`Message`]s which are pinned to the channel. + #[inline] + pub fn pins(&self) -> Result<Vec<Message>> { + rest::get_pins(self.0) + } + + /// Searches the channel's messages by providing query parameters via the + /// search builder. + /// + /// Refer to the documentation for the [`Search`] builder for restrictions + /// and defaults parameters, as well as potentially advanced usage. + /// + /// **Note**: Bot users can not search. + /// + /// # Examples + /// + /// Refer to the [`Search`] builder's documentation for examples, + /// specifically the section on [searching a channel][search channel]. + /// + /// [`Search`]: ../utils/builder/struct.Search.html + #[inline] + pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> { + rest::search_channel_messages(self.0, f(Search::default()).0) + } + + /// Sends a file along with optional message contents. The filename _must_ + /// be specified. + /// + /// Message contents may be passed by using the [`CreateMessage::content`] + /// method. + /// + /// An embed can _not_ be sent when sending a file. If you set one, it will + /// be automatically removed. + /// + /// Requires 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. + /// + /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong + /// [`CreateMessage::content`]: ../utils/builder/struct.CreateMessage.html#method.content + /// [`GuildChannel`]: struct.GuildChannel.html + /// [Attach Files]: permissions/constant.ATTACH_FILES.html + /// [Send Messages]: permissions/constant.SEND_MESSAGES.html + pub fn send_file<F, R>(&self, file: R, filename: &str, f: F) -> Result<Message> + where F: FnOnce(CreateMessage) -> CreateMessage, R: Read { + let mut map = f(CreateMessage::default()).0; + + if let Some(content) = map.get("content") { + if let Value::String(ref content) = *content { + if let Some(length_over) = Message::overflow_length(content) { + return Err(Error::Client(ClientError::MessageTooLong(length_over))); + } + } + } + + let _ = map.remove("embed"); + + rest::send_file(self.0, file, filename, map) + } + + /// Sends a message to the channel. + /// + /// Refer to the documentation for [`CreateMessage`] for more information + /// regarding message restrictions and requirements. + /// + /// Requires the [Send Messages] permission is required. + /// + /// **Note**: Message contents must be under 2000 unicode code points. + /// + /// # Errors + /// + /// Returns a [`ClientError::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 + /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong + /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html + /// [Send Messages]: permissions/constant.SEND_MESSAGES.html + pub fn send_message<F>(&self, f: F) -> Result<Message> + where F: FnOnce(CreateMessage) -> CreateMessage { + let map = f(CreateMessage::default()).0; + + if let Some(content) = map.get(&"content".to_owned()) { + if let Value::String(ref content) = *content { + if let Some(length_over) = Message::overflow_length(content) { + return Err(Error::Client(ClientError::MessageTooLong(length_over))); + } + } + } + + rest::send_message(self.0, Value::Object(map)) + } + + /// 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) -> Result<()> { + rest::unpin_message(self.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) -> Result<Vec<Webhook>> { + rest::get_channel_webhooks(self.0) + } +} + +impl From<Channel> for ChannelId { + /// Gets the Id of a `Channel`. + fn from(channel: Channel) -> ChannelId { + match channel { + Channel::Group(group) => group.channel_id, + Channel::Guild(channel) => channel.id, + Channel::Private(channel) => channel.id, + } + } +} + +impl From<PrivateChannel> for ChannelId { + /// Gets the Id of a private channel. + fn from(private_channel: PrivateChannel) -> ChannelId { + private_channel.id + } +} + +impl From<GuildChannel> for ChannelId { + /// Gets the Id of a guild channel. + fn from(public_channel: GuildChannel) -> ChannelId { + public_channel.id + } +} + +impl fmt::Display for ChannelId { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} + impl Embed { /// Creates a fake Embed, giving back a `serde_json` map. /// /// This should only be useful in conjunction with [`Webhook::execute`]. /// /// [`Webhook::execute`]: struct.Webhook.html - #[cfg(feature="methods")] - #[inline(always)] + #[inline] pub fn fake<F>(f: F) -> Value where F: FnOnce(CreateEmbed) -> CreateEmbed { Value::Object(f(CreateEmbed::default()).0) } } impl Group { + /// Marks the group as being read up to a certain [`Message`]. + /// + /// Refer to the documentation for [`rest::ack_message`] for more + /// information. + /// + /// # Errors + /// + /// If the `cache` is enabled, returns a + /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot + /// user. + /// + /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsUser + /// [`Message`]: struct.Message.html + /// [`rest::ack_message`]: rest/fn.ack_message.html + pub fn ack<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + #[cfg(feature="cache")] + { + if CACHE.read().unwrap().user.bot { + return Err(Error::Client(ClientError::InvalidOperationAsBot)); + } + } + + self.channel_id.ack(message_id) + } + /// Adds the given user to the group. If the user is already in the group, /// then nothing is done. /// @@ -259,7 +909,6 @@ impl Group { /// user. /// /// [`rest::add_group_recipient`]: ../client/rest/fn.add_group_recipient.html - #[cfg(feature="methods")] pub fn add_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> { let user = user.into(); @@ -272,43 +921,113 @@ impl Group { } /// Broadcasts that the current user is typing in the group. - #[cfg(feature="methods")] + #[inline] pub fn broadcast_typing(&self) -> Result<()> { - rest::broadcast_typing(self.channel_id.0) + self.channel_id.broadcast_typing() } - /// Deletes multiple messages in the group. + /// React to a [`Message`] with a custom [`Emoji`] or unicode character. /// - /// Refer to - /// [`Context::delete_messages`] for more information. + /// [`Message::react`] may be a more suited method of reacting in most + /// cases. /// - /// **Note**: Only 2 to 100 messages may be deleted in a single request. + /// Requires the [Add Reactions] permission, _if_ the current user is the + /// first user to perform a react with a certain emoji. /// - /// **Note**: Messages that are older than 2 weeks can't be deleted using this method. + /// [`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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + self.channel_id.create_reaction(message_id, reaction_type) + } + + /// Deletes all messages by Ids from the given vector in the channel. /// - /// # Errors + /// Refer to [`Channel::delete_messages`] for more information. /// - /// Returns a - /// [`ClientError::DeleteMessageDaysAmount`] if the number of messages to - /// delete is not within the valid range. + /// Requires the [Manage Messages] permission. /// - /// [`ClientError::DeleteMessageDaysAmount`]: ../client/enum.ClientError.html#variant.DeleteMessageDaysAmount - /// [`Context::delete_messages`]: ../client/struct.Context.html#delete_messages - #[cfg(feature="methods")] + /// **Note**: This uses bulk delete endpoint which is not available + /// for user accounts. + /// + /// **Note**: Messages that are older than 2 weeks can't be deleted using + /// this method. + /// + /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages + /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html + #[inline] pub fn delete_messages(&self, message_ids: &[MessageId]) -> Result<()> { - if message_ids.len() < 2 || message_ids.len() > 100 { - return Err(Error::Client(ClientError::BulkDeleteAmount)); - } + self.channel_id.delete_messages(message_ids) + } - let ids: Vec<u64> = message_ids.into_iter() - .map(|message_id| message_id.0) - .collect(); + /// 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) -> Result<()> { + self.channel_id.delete_permission(permission_type) + } - let map = ObjectBuilder::new() - .insert("messages", ids) - .build(); + /// 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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + self.channel_id.delete_reaction(message_id, user_id, reaction_type) + } - rest::delete_messages(self.channel_id.0, map) + /// Gets a message from the channel. + /// + /// Requires the [Read Message History] permission. + /// + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> { + self.channel_id.get_message(message_id) + } + + /// Gets messages from the channel. + /// + /// Requires the [Read Message History] permission. + /// + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_messages<F>(&self, f: F) -> Result<Vec<Message>> + where F: FnOnce(GetMessages) -> GetMessages { + self.channel_id.get_messages(f) + } + + /// Gets the list of [`User`]s who have reacted to a [`Message`] with a + /// certain [`Emoji`]. + /// + /// Refer to [`Channel::get_reaction_users`] for more information. + /// + /// **Note**: Requires the [Read Message History] permission. + /// + /// [`Channel::get_reaction_users`]: enum.Channel.html#variant.get_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 get_reaction_users<M, R, U>(&self, + message_id: M, + reaction_type: R, + limit: Option<u8>, + after: Option<U>) + -> Result<Vec<User>> where M: Into<MessageId>, R: Into<ReactionType>, U: Into<UserId> { + self.channel_id.get_reaction_users(message_id, reaction_type, limit, after) } /// Returns the formatted URI of the group's icon if one exists. @@ -318,7 +1037,7 @@ impl Group { } /// Leaves the group. - #[cfg(feature="methods")] + #[inline] pub fn leave(&self) -> Result<Group> { rest::leave_group(self.channel_id.0) } @@ -347,16 +1066,15 @@ impl Group { } /// Retrieves the list of messages that have been pinned in the group. - #[cfg(feature="methods")] + #[inline] pub fn pins(&self) -> Result<Vec<Message>> { - rest::get_pins(self.channel_id.0) + self.channel_id.pins() } /// 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. - #[cfg(feature="methods")] pub fn remove_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> { let user = user.into(); @@ -384,10 +1102,10 @@ impl Group { /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot /// [`Message`]: struct.Message.html /// [`Search`]: ../utils/builder/struct.Search.html - #[cfg(feature="methods")] + #[inline] pub fn search<F>(&self, f: F) -> Result<SearchResult> where F: FnOnce(Search) -> Search { - rest::search_channel_messages(self.channel_id.0, f(Search::default()).0) + self.channel_id.search(f) } /// Sends a message to the group with the given content. @@ -397,19 +1115,50 @@ impl Group { /// **Note**: Requires the [Send Messages] permission. /// /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - #[cfg(feature="methods")] + #[inline] pub fn send_message(&self, content: &str) -> Result<Message> { - let map = ObjectBuilder::new() - .insert("content", content) - .insert("nonce", "") - .insert("tts", false) - .build(); + self.channel_id.send_message(|m| m.content(content)) + } - rest::send_message(self.channel_id.0, map) + /// 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) -> Result<()> { + self.channel_id.unpin(message_id.into().0) } } impl Message { + /// Marks the [`Channel`] as being read up to the message. + /// + /// Refer to the documentation for [`rest::ack_message`] for more + /// information. + /// + /// # Errors + /// + /// If the `cache` is enabled, returns a + /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot + /// user. + /// + /// [`Channel`]: enum.Channel.html + /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsUser + /// [`Message`]: struct.Message.html + /// [`rest::ack_message`]: rest/fn.ack_message.html + pub fn ack<M: Into<MessageId>>(&self) -> Result<()> { + #[cfg(feature="cache")] + { + if CACHE.read().unwrap().user.bot { + return Err(Error::Client(ClientError::InvalidOperationAsBot)); + } + } + + self.channel_id.ack(self.id) + } + /// Deletes the message. /// /// **Note**: The logged in user must either be the author of the message or @@ -424,7 +1173,6 @@ impl Message { /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[cfg(feature="methods")] pub fn delete(&self) -> Result<()> { #[cfg(feature="cache")] { @@ -437,7 +1185,7 @@ impl Message { } } - rest::delete_message(self.channel_id.0, self.id.0) + self.channel_id.delete_message(self.id) } /// Deletes all of the [`Reaction`]s associated with the message. @@ -453,7 +1201,6 @@ impl Message { /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [`Reaction`]: struct.Reaction.html /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[cfg(feature="methods")] pub fn delete_reactions(&self) -> Result<()> { #[cfg(feature="cache")] { @@ -490,8 +1237,7 @@ impl Message { /// over the limit. /// /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser - /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong - #[cfg(feature="methods")] + /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong pub fn edit<F>(&mut self, new_content: &str, embed: F) -> Result<()> where F: FnOnce(CreateEmbed) -> CreateEmbed { if let Some(length_over) = Message::overflow_length(new_content) { @@ -525,7 +1271,7 @@ impl Message { /// Returns message content, but with user and role mentions replaced with /// names and everyone/here mentions cancelled. - #[cfg(all(feature="cache", feature="methods"))] + #[cfg(feature="cache")] pub fn content_safe(&self) -> String { let mut result = self.content.clone(); @@ -550,12 +1296,34 @@ impl Message { .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 get_reaction_users<R, U>(&self, reaction_type: R, limit: Option<u8>, after: Option<U>) + -> Result<Vec<User>> where R: Into<ReactionType>, U: Into<UserId> { + self.id.get_reaction_users(self.channel_id, reaction_type, limit, after) + } + /// 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(all(feature="cache", feature="methods"))] + #[cfg(feature="cache")] pub fn guild_id(&self) -> Option<GuildId> { match CACHE.read().unwrap().get_channel(self.channel_id) { Some(ChannelRef::Guild(channel)) => Some(channel.guild_id), @@ -564,7 +1332,7 @@ impl Message { } /// True if message was sent using direct messages. - #[cfg(all(feature="cache", feature="methods"))] + #[cfg(feature="cache")] pub fn is_private(&self) -> bool { match CACHE.read().unwrap().get_channel(self.channel_id) { Some(ChannelRef::Group(_)) | Some(ChannelRef::Private(_)) => true, @@ -603,7 +1371,6 @@ impl Message { /// /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[cfg(feature="methods")] pub fn pin(&self) -> Result<()> { #[cfg(feature="cache")] { @@ -614,7 +1381,7 @@ impl Message { } } - rest::pin_message(self.channel_id.0, self.id.0) + self.channel_id.pin(self.id.0) } /// React to the message with a custom [`Emoji`] or unicode character. @@ -631,7 +1398,6 @@ impl Message { /// [`Emoji`]: struct.Emoji.html /// [Add Reactions]: permissions/constant.ADD_REACTIONS.html /// [permissions]: permissions - #[cfg(feature="methods")] pub fn react<R: Into<ReactionType>>(&self, reaction_type: R) -> Result<()> { #[cfg(feature="cache")] { @@ -667,9 +1433,8 @@ impl Message { /// over the limit. /// /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions - /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong + /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - #[cfg(feature="methods")] pub fn reply(&self, content: &str) -> Result<Message> { if let Some(length_over) = Message::overflow_length(content) { return Err(Error::Client(ClientError::MessageTooLong(length_over))); @@ -709,7 +1474,6 @@ impl Message { /// /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html - #[cfg(feature="methods")] pub fn unpin(&self) -> Result<()> { #[cfg(feature="cache")] { @@ -724,6 +1488,46 @@ impl Message { } } +impl MessageId { + /// 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 + pub fn get_reaction_users<C, R, U>(&self, + channel_id: C, + reaction_type: R, + limit: Option<u8>, + after: Option<U>) + -> Result<Vec<User>> where C: Into<ChannelId>, R: Into<ReactionType>, U: Into<UserId> { + let limit = limit.map_or(50, |x| if x > 100 { 100 } else { x }); + + rest::get_reaction_users(channel_id.into().0, + self.0, + reaction_type.into(), + limit, + after.map(|u| u.into().0)) + } +} + +impl From<Message> for MessageId { + /// Gets the Id of a `Message`. + fn from(message: Message) -> MessageId { + message.id + } +} + impl PermissionOverwrite { #[doc(hidden)] pub fn decode(value: Value) -> Result<PermissionOverwrite> { @@ -745,12 +1549,53 @@ impl PermissionOverwrite { } impl PrivateChannel { + /// Marks the channel as being read up to a certain [`Message`]. + /// + /// Refer to the documentation for [`rest::ack_message`] for more + /// information. + /// + /// # Errors + /// + /// If the `cache` is enabled, returns a + /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot + /// user. + /// + /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsUser + /// [`Message`]: struct.Message.html + /// [`rest::ack_message`]: rest/fn.ack_message.html + pub fn ack<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + #[cfg(feature="cache")] + { + if CACHE.read().unwrap().user.bot { + return Err(Error::Client(ClientError::InvalidOperationAsBot)); + } + } + + rest::ack_message(self.id.0, message_id.into().0) + } + /// Broadcasts that the current user is typing to the recipient. - #[cfg(feature="methods")] pub fn broadcast_typing(&self) -> Result<()> { rest::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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + self.id.create_reaction(message_id, reaction_type) + } + #[doc(hidden)] pub fn decode(value: Value) -> Result<PrivateChannel> { let mut map = into_map(value)?; @@ -769,50 +1614,111 @@ impl PrivateChannel { /// 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. - #[cfg(feature="methods")] + #[inline] pub fn delete(&self) -> Result<Channel> { - rest::delete_channel(self.id.0) + self.id.delete() } - /// Deletes the given message Ids from the private channel. + /// Deletes all messages by Ids from the given vector in the channel. + /// + /// Refer to [`Channel::delete_messages`] for more information. /// - /// **Note** This method is only available to bot users and they can't - /// delete their recipient's messages. + /// Requires the [Manage Messages] permission. + /// + /// **Note**: This uses bulk delete endpoint which is not available + /// for user accounts. /// /// **Note**: Messages that are older than 2 weeks can't be deleted using /// this method. /// - /// # Errors + /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages + /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html + #[inline] + pub fn delete_messages(&self, message_ids: &[MessageId]) -> Result<()> { + self.id.delete_messages(message_ids) + } + + /// Deletes all permission overrides in the channel from a member + /// or role. /// - /// If the `cache` is enabled, then returns a - /// [`ClientError::InvalidUser`] if the current user is not a bot user. + /// **Note**: Requires the [Manage Channel] permission. /// - /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidOperationAsUser - #[cfg(feature="methods")] - pub fn delete_messages(&self, message_ids: &[MessageId]) -> Result<()> { - #[cfg(feature="cache")] - { - if !CACHE.read().unwrap().user.bot { - return Err(Error::Client(ClientError::InvalidOperationAsUser)); - } - } + /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html + #[inline] + pub fn delete_permission(&self, permission_type: PermissionOverwriteType) -> Result<()> { + self.id.delete_permission(permission_type) + } - let ids: Vec<u64> = message_ids.into_iter() - .map(|message_id| message_id.0) - .collect(); + /// 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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + self.id.delete_reaction(message_id, user_id, reaction_type) + } - let map = ObjectBuilder::new() - .insert("messages", ids) - .build(); + /// Gets a message from the channel. + /// + /// Requires the [Read Message History] permission. + /// + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> { + self.id.get_message(message_id) + } + + /// Gets messages from the channel. + /// + /// Refer to [`Channel::get_messages`] for more information. + /// + /// Requires the [Read Message History] permission. + /// + /// [`Channel::get_messages`]: enum.Channel.html#method.get_messages + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_messages<F>(&self, f: F) -> Result<Vec<Message>> + where F: FnOnce(GetMessages) -> GetMessages { + self.id.get_messages(f) + } + + /// Gets the list of [`User`]s who have reacted to a [`Message`] with a + /// certain [`Emoji`]. + /// + /// Refer to [`Channel::get_reaction_users`] for more information. + /// + /// **Note**: Requires the [Read Message History] permission. + /// + /// [`Channel::get_reaction_users`]: enum.Channel.html#variant.get_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 get_reaction_users<M, R, U>(&self, + message_id: M, + reaction_type: R, + limit: Option<u8>, + after: Option<U>) + -> Result<Vec<User>> where M: Into<MessageId>, R: Into<ReactionType>, U: Into<UserId> { + self.id.get_reaction_users(message_id, reaction_type, limit, after) + } - rest::delete_messages(self.id.0, map) + /// Pins a [`Message`] to the channel. + #[inline] + pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + self.id.pin(message_id) } /// Retrieves the list of messages that have been pinned in the private /// channel. - #[cfg(feature="methods")] + #[inline] pub fn pins(&self) -> Result<Vec<Message>> { - rest::get_pins(self.id.0) + self.id.pins() } /// Performs a search request to the API for the channel's [`Message`]s. @@ -830,7 +1736,6 @@ impl PrivateChannel { /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot /// [`Message`]: struct.Message.html /// [`Search`]: ../utils/builder/struct.Search.html - #[cfg(feature="methods")] pub fn search<F>(&self, f: F) -> Result<SearchResult> where F: FnOnce(Search) -> Search { #[cfg(feature="cache")] @@ -840,7 +1745,7 @@ impl PrivateChannel { } } - rest::search_channel_messages(self.id.0, f(Search::default()).0) + self.id.search(f) } /// Sends a message to the channel with the given content. @@ -854,7 +1759,6 @@ impl PrivateChannel { /// over the limit. /// /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong - #[cfg(feature="methods")] pub fn send_message(&self, content: &str) -> Result<Message> { if let Some(length_over) = Message::overflow_length(content) { return Err(Error::Client(ClientError::MessageTooLong(length_over))); @@ -868,6 +1772,17 @@ impl PrivateChannel { rest::send_message(self.id.0, map) } + + /// 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) -> Result<()> { + self.id.unpin(message_id.into().0) + } } impl fmt::Display for PrivateChannel { @@ -878,6 +1793,31 @@ impl fmt::Display for PrivateChannel { } impl GuildChannel { + /// Marks the channel as being read up to a certain [`Message`]. + /// + /// Refer to the documentation for [`rest::ack_message`] for more + /// information. + /// + /// # Errors + /// + /// If the `cache` is enabled, returns a + /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot + /// user. + /// + /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsUser + /// [`Message`]: struct.Message.html + /// [`rest::ack_message`]: rest/fn.ack_message.html + pub fn ack<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + #[cfg(feature="cache")] + { + if CACHE.read().unwrap().user.bot { + return Err(Error::Client(ClientError::InvalidOperationAsBot)); + } + } + + rest::ack_message(self.id.0, message_id.into().0) + } + /// Broadcasts to the channel that the current user is typing. /// /// For bots, this is a good indicator for long-running commands. @@ -892,7 +1832,6 @@ impl GuildChannel { /// /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [Send Messages]: permissions/constants.SEND_MESSAGES.html - #[cfg(feature="methods")] pub fn broadcast_typing(&self) -> Result<()> { rest::broadcast_typing(self.id.0) } @@ -907,7 +1846,6 @@ impl GuildChannel { /// let invite = channel.create_invite(|i| i /// .max_uses(5)); /// ``` - #[cfg(feature="methods")] pub fn create_invite<F>(&self, f: F) -> Result<RichInvite> where F: FnOnce(CreateInvite) -> CreateInvite { #[cfg(feature="cache")] @@ -924,6 +1862,78 @@ impl GuildChannel { rest::create_invite(self.id.0, map) } + /// 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,ignore + /// use serenity::model::{ChannelId, PermissionOverwrite, permissions}; + /// + /// // assuming you are in a context + /// + /// let channel_id = 7; + /// let user_id = 8; + /// + /// 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 _result = context.create_permission(channel_id, overwrite); + /// ``` + /// + /// 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,ignore + /// use serenity::model::{ChannelId, PermissionOverwrite, permissions}; + /// + /// // assuming you are in a context + /// + /// let channel_id = 7; + /// let user_id = 8; + /// + /// 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 _result = context.create_permission(channel_id, overwrite); + /// ``` + /// + /// [`Channel`]: enum.Channel.html + /// [`Member`]: struct.Member.html + /// [`PermissionOverwrite`]: struct.PermissionOverWrite.html + /// [`PermissionOverwrite::Member`]: struct.PermissionOverwrite.html#variant.Member + /// [`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 TTS Messages]: permissions/constant.SEND_TTS_MESSAGES.html + #[inline] + pub fn create_permission(&self, target: PermissionOverwrite) -> Result<()> { + self.id.create_permission(target) + } + #[doc(hidden)] pub fn decode(value: Value) -> Result<GuildChannel> { let mut map = into_map(value)?; @@ -953,7 +1963,6 @@ impl GuildChannel { } /// Deletes this channel, returning the channel on a successful deletion. - #[cfg(feature="methods")] pub fn delete(&self) -> Result<Channel> { #[cfg(feature="cache")] { @@ -964,7 +1973,50 @@ impl GuildChannel { } } - rest::delete_channel(self.id.0) + self.id.delete() + } + + /// 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**: This uses bulk delete endpoint which is not available + /// for user accounts. + /// + /// **Note**: Messages that are older than 2 weeks can't be deleted using + /// this method. + /// + /// [`Channel::delete_messages`]: enum.Channel.html#method.delete_messages + /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html + #[inline] + pub fn delete_messages(&self, message_ids: &[MessageId]) -> Result<()> { + self.id.delete_messages(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) -> Result<()> { + self.id.delete_permission(permission_type) + } + + /// 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) + -> Result<()> where M: Into<MessageId>, R: Into<ReactionType> { + self.id.delete_reaction(message_id, user_id, reaction_type) } /// Modifies a channel's settings, such as its position or name. @@ -980,7 +2032,6 @@ impl GuildChannel { /// .name("test") /// .bitrate(86400)); /// ``` - #[cfg(feature="methods")] pub fn edit<F>(&mut self, f: F) -> Result<()> where F: FnOnce(EditChannel) -> EditChannel { @@ -1010,19 +2061,79 @@ impl GuildChannel { } } + /// Gets all of the channel's invites. + /// + /// Requires the [Manage Channels] permission. + /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html + #[inline] + pub fn get_invites(&self) -> Result<Vec<RichInvite>> { + self.id.get_invites() + } + + /// Gets a message from the channel. + /// + /// Requires the [Read Message History] permission. + /// + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_message<M: Into<MessageId>>(&self, message_id: M) -> Result<Message> { + self.id.get_message(message_id) + } + + /// Gets messages from the channel. + /// + /// Refer to [`Channel::get_messages`] for more information. + /// + /// Requires the [Read Message History] permission. + /// + /// [`Channel::get_messages`]: enum.Channel.html#method.get_messages + /// [Read Message History]: permission/constant.READ_MESSAGE_HISTORY.html + #[inline] + pub fn get_messages<F>(&self, f: F) -> Result<Vec<Message>> + where F: FnOnce(GetMessages) -> GetMessages { + self.id.get_messages(f) + } + + /// Gets the list of [`User`]s who have reacted to a [`Message`] with a + /// certain [`Emoji`]. + /// + /// Refer to [`Channel::get_reaction_users`] for more information. + /// + /// **Note**: Requires the [Read Message History] permission. + /// + /// [`Channel::get_reaction_users`]: enum.Channel.html#variant.get_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 get_reaction_users<M, R, U>(&self, + message_id: M, + reaction_type: R, + limit: Option<u8>, + after: Option<U>) + -> Result<Vec<User>> where M: Into<MessageId>, R: Into<ReactionType>, U: Into<UserId> { + self.id.get_reaction_users(message_id, reaction_type, limit, after) + } + /// 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(all(feature="cache", feature="methods"))] + #[cfg(feature="cache")] pub fn guild(&self) -> Option<Guild> { CACHE.read().unwrap().get_guild(self.guild_id).cloned() } + /// Pins a [`Message`] to the channel. + #[inline] + pub fn pin<M: Into<MessageId>>(&self, message_id: M) -> Result<()> { + self.id.pin(message_id) + } + /// Gets all channel's pins. - #[cfg(feature="methods")] + #[inline] pub fn pins(&self) -> Result<Vec<Message>> { - rest::get_pins(self.id.0) + self.id.pins() } /// Performs a search request for the channel's [`Message`]s. @@ -1040,7 +2151,6 @@ impl GuildChannel { /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot /// [`Message`]: struct.Message.html /// [`Search`]: ../utils/builder/struct.Search.html - #[cfg(feature="methods")] pub fn search<F>(&self, f: F) -> Result<SearchResult> where F: FnOnce(Search) -> Search { #[cfg(feature="cache")] @@ -1072,7 +2182,6 @@ impl GuildChannel { /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong /// [`Message`]: struct.Message.html /// [Send Messages]: permissions/constant.SEND_MESSAGES.html - #[cfg(feature="methods")] pub fn send_message(&self, content: &str) -> Result<Message> { if let Some(length_over) = Message::overflow_length(content) { return Err(Error::Client(ClientError::MessageTooLong(length_over))); @@ -1096,12 +2205,22 @@ impl GuildChannel { rest::send_message(self.id.0, map) } + /// 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) -> Result<()> { + self.id.unpin(message_id.into().0) + } + /// Retrieves the channel's webhooks. /// /// **Note**: Requires the [Manage Webhooks] permission. /// /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html - #[cfg(feature="methods")] #[inline] pub fn webhooks(&self) -> Result<Vec<Webhook>> { rest::get_channel_webhooks(self.id.0) @@ -1131,7 +2250,6 @@ impl Reaction { /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html /// [permissions]: permissions - #[cfg(feature="methods")] pub fn delete(&self) -> Result<()> { let user_id = feature_cache! {{ let user = if self.user_id == CACHE.read().unwrap().user.id { @@ -1188,7 +2306,6 @@ impl Reaction { /// [`User`]: struct.User.html /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html /// [permissions]: permissions - #[cfg(feature="methods")] pub fn users<R, U>(&self, reaction_type: R, limit: Option<u8>, |