aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorZeyla Hellyer <[email protected]>2017-03-25 15:41:47 -0700
committerZeyla Hellyer <[email protected]>2017-03-25 15:41:47 -0700
commit9114963daf708cfaeaf54d8c788206ccfbae5df8 (patch)
tree08d6aba5b8ad40189b312865ac776bb6fa30c45d /src
parentAdd slightly more documentation (diff)
downloadserenity-9114963daf708cfaeaf54d8c788206ccfbae5df8.tar.xz
serenity-9114963daf708cfaeaf54d8c788206ccfbae5df8.zip
Rework the models directory
Diffstat (limited to 'src')
-rw-r--r--src/model/channel.rs2723
-rw-r--r--src/model/channel/attachment.rs92
-rw-r--r--src/model/channel/channel_id.rs511
-rw-r--r--src/model/channel/embed.rs15
-rw-r--r--src/model/channel/group.rs329
-rw-r--r--src/model/channel/guild_channel.rs504
-rw-r--r--src/model/channel/message.rs393
-rw-r--r--src/model/channel/mod.rs415
-rw-r--r--src/model/channel/private_channel.rs309
-rw-r--r--src/model/channel/reaction.rs188
-rw-r--r--src/model/guild.rs2587
-rw-r--r--src/model/guild/emoji.rs100
-rw-r--r--src/model/guild/guild_id.rs525
-rw-r--r--src/model/guild/integration.rs8
-rw-r--r--src/model/guild/member.rs279
-rw-r--r--src/model/guild/mod.rs1052
-rw-r--r--src/model/guild/partial_guild.rs482
-rw-r--r--src/model/guild/role.rs151
-rw-r--r--src/model/mod.rs1
19 files changed, 5353 insertions, 5311 deletions
diff --git a/src/model/channel.rs b/src/model/channel.rs
deleted file mode 100644
index 8824932..0000000
--- a/src/model/channel.rs
+++ /dev/null
@@ -1,2723 +0,0 @@
-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 std::sync::{Arc, RwLock};
-use super::utils::{
- decode_id,
- into_map,
- into_string,
- opt,
- 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="cache")]
-use super::utils;
-#[cfg(feature="cache")]
-use ::client::CACHE;
-
-impl Attachment {
- /// If this attachment is an image, then a tuple of the width and height
- /// in pixels is returned.
- pub fn dimensions(&self) -> Option<(u64, u64)> {
- if let (Some(width), Some(height)) = (self.width, self.height) {
- Some((width, height))
- } else {
- None
- }
- }
-
- /// Downloads the attachment, returning back a vector of bytes.
- ///
- /// # Examples
- ///
- /// Download all of the attachments associated with a [`Message`]:
- ///
- /// ```rust,no_run
- /// use serenity::Client;
- /// use std::env;
- /// use std::fs::File;
- /// use std::io::Write;
- /// use std::path::Path;
- ///
- /// let token = env::var("DISCORD_TOKEN").expect("token in environment");
- /// let mut client = Client::login_bot(&token);
- ///
- /// client.on_message(|_, message| {
- /// for attachment in message.attachments {
- /// let content = match attachment.download() {
- /// Ok(content) => content,
- /// Err(why) => {
- /// println!("Error downloading attachment: {:?}", why);
- /// let _ = message.channel_id.say("Error downloading attachment");
- ///
- /// return;
- /// },
- /// };
- ///
- /// let mut file = match File::create(&attachment.filename) {
- /// Ok(file) => file,
- /// Err(why) => {
- /// println!("Error creating file: {:?}", why);
- /// let _ = message.channel_id.say("Error creating file");
- ///
- /// return;
- /// },
- /// };
- ///
- /// if let Err(why) = file.write(&content) {
- /// println!("Error writing to file: {:?}", why);
- ///
- /// return;
- /// }
- ///
- /// let _ = message.channel_id.say(&format!("Saved {:?}", attachment.filename));
- /// }
- /// });
- ///
- /// client.on_ready(|_context, ready| {
- /// println!("{} is connected!", ready.user.name);
- /// });
- ///
- /// let _ = client.start();
- /// ```
- ///
- /// # Errors
- ///
- /// Returns an [`Error::Io`] when there is a problem reading the contents
- /// of the HTTP response.
- ///
- /// Returns an [`Error::Hyper`] when there is a problem retrieving the
- /// attachment.
- ///
- /// [`Error::Hyper`]: ../enum.Error.html#variant.Hyper
- /// [`Error::Io`]: ../enum.Error.html#variant.Io
- /// [`Message`]: struct.Message.html
- pub fn download(&self) -> Result<Vec<u8>> {
- let hyper = HyperClient::new();
- let mut response = hyper.get(&self.url).send()?;
-
- let mut bytes = vec![];
- response.read_to_end(&mut bytes)?;
-
- Ok(bytes)
- }
-}
-
-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`]: ../client/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)?;
- match req!(map.get("type").and_then(|x| x.as_u64())) {
- 0 | 2 => GuildChannel::decode(Value::Object(map))
- .map(|x| Channel::Guild(Arc::new(RwLock::new(x)))),
- 1 => PrivateChannel::decode(Value::Object(map))
- .map(|x| Channel::Private(Arc::new(RwLock::new(x)))),
- 3 => Group::decode(Value::Object(map))
- .map(|x| Channel::Group(Arc::new(RwLock::new(x)))),
- other => Err(Error::Decode("Expected value Channel type",
- Value::U64(other))),
- }
- }
-
- /// Deletes the inner channel.
- ///
- /// **Note**: There is no real function as _deleting_ a [`Group`]. The
- /// closest functionality is leaving it.
- ///
- /// [`Group`]: struct.Group.html
- pub fn delete(&self) -> Result<()> {
- match *self {
- Channel::Group(ref group) => {
- let _ = group.read().unwrap().leave()?;
- },
- Channel::Guild(ref public_channel) => {
- let _ = public_channel.read().unwrap().delete()?;
- },
- Channel::Private(ref private_channel) => {
- let _ = private_channel.read().unwrap().delete()?;
- },
- }
-
- 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.
- ///
- /// [`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)
- }
-
- /// Edits a [`Message`] in the channel given its Id.
- ///
- /// Message editing preserves all unchanged message data.
- ///
- /// Refer to the documentation for [`CreateMessage`] for more information
- /// regarding message restrictions and requirements.
- ///
- /// **Note**: Requires that the current user be the author of the message.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::MessageTooLong`] if the content of the message
- /// is over the [`the limit`], containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
- /// [`Message`]: struct.Message.html
- /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
- #[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
- self.id().edit_message(message_id, f)
- }
-
- /// Gets a message from the channel.
- ///
- /// Requires the [Read Message History] permission.
- ///
- /// [Read Message History]: permissions/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::MessageId;
- ///
- /// let id = MessageId(81392407232380928);
- ///
- /// // Maximum is 100.
- /// let _messages = channel.get_messages(|g| g.after(id).limit(100));
- /// ```
- ///
- /// [Read Message History]: permissions/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`].
- ///
- /// [`Group`]: struct.Group.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`PrivateChannel`]: struct.PrivateChannel.html
- pub fn id(&self) -> ChannelId {
- match *self {
- Channel::Group(ref group) => group.read().unwrap().channel_id,
- Channel::Guild(ref channel) => channel.read().unwrap().id,
- Channel::Private(ref channel) => channel.read().unwrap().id,
- }
- }
-
- /// Sends a message with just the given message content in the channel.
- ///
- /// # 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.
- ///
- /// [`ChannelId`]: ../model/struct.ChannelId.html
- /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
- #[inline]
- pub fn say(&self, content: &str) -> Result<Message> {
- self.id().say(content)
- }
-
- /// Performs a search request to the API for the inner channel's
- /// [`Message`]s.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- pub fn search<F>(&self, f: F) -> Result<SearchResult>
- where F: FnOnce(Search) -> Search {
- #[cfg(feature="cache")]
- {
- if CACHE.read().unwrap().user.bot {
- return Err(Error::Client(ClientError::InvalidOperationAsBot));
- }
- }
-
- self.id().search(f)
- }
-
- /// Sends a file along with optional message contents. The filename _must_
- /// be specified.
- ///
- /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [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 {
- self.id().send_file(file, filename, 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 [`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
- #[inline]
- pub fn send_message<F>(&self, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage {
- self.id().send_message(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) -> Result<()> {
- self.id().unpin(message_id)
- }
-}
-
-impl fmt::Display for Channel {
- /// Formats the channel into a "mentioned" string.
- ///
- /// This will return a different format for each type of channel:
- ///
- /// - [`Group`]s: the generated name retrievable via [`Group::name`];
- /// - [`PrivateChannel`]s: the recipient's name;
- /// - [`GuildChannel`]s: a string mentioning the channel that users who can
- /// see the channel can click on.
- ///
- /// [`Group`]: struct.Group.html
- /// [`Group::name`]: struct.Group.html#method.name
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`PrivateChannel`]: struct.PrivateChannel.html
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Channel::Group(ref group) => {
- fmt::Display::fmt(&group.read().unwrap().name(), f)
- },
- Channel::Guild(ref ch) => {
- fmt::Display::fmt(&ch.read().unwrap().id.mention(), f)
- },
- Channel::Private(ref ch) => {
- let channel = ch.read().unwrap();
- let recipient = channel.recipient.read().unwrap();
-
- fmt::Display::fmt(&recipient.name, f)
- },
- }
- }
-}
-
-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.
- ///
- /// [`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
- /// // assuming a `channel_id` has been bound
- ///
- /// channel_id.edit(|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
- /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
- #[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.
- ///
- /// Message editing preserves all unchanged message data.
- ///
- /// Refer to the documentation for [`CreateMessage`] for more information
- /// regarding message restrictions and requirements.
- ///
- /// **Note**: Requires that the current user be the author of the message.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::MessageTooLong`] if the content of the message
- /// is over the [`the limit`], containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
- /// [`Message`]: struct.Message.html
- /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
- let 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)));
- }
- }
- }
-
- rest::edit_message(self.0, message_id.into().0, &Value::Object(map))
- }
-
- /// Search the cache for the channel with the Id.
- #[cfg(feature="cache")]
- pub fn find(&self) -> Option<Channel> {
- CACHE.read().unwrap().get_channel(*self)
- }
-
- /// 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);
- }
- }
-
- 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]: permissions/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]: permissions/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))
- }
-
- /// Retrieves the channel's webhooks.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
- rest::get_channel_webhooks(self.0)
- }
-
- /// Pins a [`Message`] to the channel.
- ///
- /// [`Message`]: struct.Message.html
- #[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.
- ///
- /// [`Message`]: struct.Message.html
- #[inline]
- pub fn pins(&self) -> Result<Vec<Message>> {
- rest::get_pins(self.0)
- }
-
- /// Sends a message with just the given message content in the channel.
- ///
- /// # 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.
- ///
- /// [`ChannelId`]: ../model/struct.ChannelId.html
- /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
- #[inline]
- pub fn say(&self, content: &str) -> Result<Message> {
- self.send_message(|m| m.content(content))
- }
-
- /// 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.
- ///
- /// The [Attach Files] and [Send Messages] permissions are required.
- ///
- /// **Note**: Message contents must be under 2000 unicode code points.
- ///
- /// # Examples
- ///
- /// Send a file with the filename `my_file.jpg`:
- ///
- /// ```rust,no_run
- /// use serenity::model::ChannelId;
- /// use std::fs::File;
- ///
- /// let channel_id = ChannelId(7);
- /// let filename = "my_file.jpg";
- /// let file = File::open(filename).unwrap();
- ///
- /// let _ = channel_id.send_file(file, filename, |m| m.content("a file"));
- /// ```
- ///
- /// # 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.
- ///
- /// **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)
- }
-}
-
-impl From<Channel> for ChannelId {
- /// Gets the Id of a `Channel`.
- fn from(channel: Channel) -> ChannelId {
- match channel {
- Channel::Group(group) => group.read().unwrap().channel_id,
- Channel::Guild(ch) => ch.read().unwrap().id,
- Channel::Private(ch) => ch.read().unwrap().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
- #[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`]: ../client/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.
- ///
- /// Refer to [`rest::add_group_recipient`] for more information.
- ///
- /// **Note**: Groups have a limit of 10 recipients, including the current
- /// user.
- ///
- /// [`rest::add_group_recipient`]: ../client/rest/fn.add_group_recipient.html
- pub fn add_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> {
- let user = user.into();
-
- // If the group already contains the recipient, do nothing.
- if self.recipients.contains_key(&user) {
- return Ok(());
- }
-
- rest::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) -> Result<()> {
- self.channel_id.broadcast_typing()
- }
-
- /// 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.channel_id.create_reaction(message_id, reaction_type)
- }
-
- /// 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.channel_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.channel_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.channel_id.delete_reaction(message_id, user_id, reaction_type)
- }
-
- /// Edits a [`Message`] in the channel given its Id.
- ///
- /// Message editing preserves all unchanged message data.
- ///
- /// Refer to the documentation for [`CreateMessage`] for more information
- /// regarding message restrictions and requirements.
- ///
- /// **Note**: Requires that the current user be the author of the message.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::MessageTooLong`] if the content of the message
- /// is over the [`the limit`], containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
- /// [`Message`]: struct.Message.html
- /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
- #[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
- self.channel_id.edit_message(message_id, f)
- }
-
- /// Gets a message from the channel.
- ///
- /// Requires the [Read Message History] permission.
- ///
- /// [Read Message History]: permissions/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]: permissions/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.
- pub fn icon_url(&self) -> Option<String> {
- self.icon.as_ref().map(|icon|
- format!(cdn!("/channel-icons/{}/{}.webp"), self.channel_id, icon))
- }
-
- /// Leaves the group.
- #[inline]
- pub fn leave(&self) -> Result<Group> {
- rest::leave_group(self.channel_id.0)
- }
-
- /// Generates a name for the group.
- ///
- /// If there are no recipients in the group, the name will be "Empty Group".
- /// Otherwise, the name is generated in a Comma Separated Value list, such
- /// as "person 1, person 2, person 3".
- pub fn name(&self) -> Cow<str> {
- match self.name {
- Some(ref name) => Cow::Borrowed(name),
- None => {
- let mut name = match self.recipients.values().nth(0) {
- Some(recipient) => recipient.read().unwrap().name.clone(),
- None => return Cow::Borrowed("Empty Group"),
- };
-
- for recipient in self.recipients.values().skip(1) {
- let _ = write!(name, ", {}", recipient.read().unwrap().name);
- }
-
- Cow::Owned(name)
- }
- }
- }
-
- /// Retrieves the list of messages that have been pinned in the group.
- #[inline]
- pub fn pins(&self) -> Result<Vec<Message>> {
- 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.
- pub fn remove_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> {
- let user = user.into();
-
- // If the group does not contain the recipient already, do nothing.
- if !self.recipients.contains_key(&user) {
- return Ok(());
- }
-
- rest::remove_group_recipient(self.channel_id.0, user.0)
- }
-
- /// Sends a message with just the given message content in the channel.
- ///
- /// # 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.
- ///
- /// [`ChannelId`]: ../model/struct.ChannelId.html
- /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
- #[inline]
- pub fn say(&self, content: &str) -> Result<Message> {
- self.channel_id.say(content)
- }
-
- /// Performs a search request to the API for the group's channel's
- /// [`Message`]s.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- #[inline]
- pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
- self.channel_id.search(f)
- }
-
- /// Sends a file along with optional message contents. The filename _must_
- /// be specified.
- ///
- /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [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 {
- self.channel_id.send_file(file, filename, 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`]: ../utils/builder/struct.CreateMessage.html
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- #[inline]
- pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
- self.channel_id.send_message(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) -> Result<()> {
- self.channel_id.unpin(message_id)
- }
-}
-
-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`]: ../client/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
- /// have the [Manage Messages] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` feature is enabled, then returns a
- /// [`ClientError::InvalidPermissions`] if the current user does not have
- /// the required permissions.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn delete(&self) -> Result<()> {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_MESSAGES;
- let is_author = self.author.id == CACHE.read().unwrap().user.id;
- let has_perms = utils::user_has_perms(self.channel_id, req)?;
-
- if !is_author && !has_perms {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.channel_id.delete_message(self.id)
- }
-
- /// 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
- /// [`ClientError::InvalidPermissions`] if the current user does not have
- /// the required permissions.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`Reaction`]: struct.Reaction.html
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn delete_reactions(&self) -> Result<()> {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_MESSAGES;
-
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- rest::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 [`CreateMessage`] 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 [`ClientError::InvalidUser`] if the
- /// current user is not the author.
- ///
- /// Returns a [`ClientError::MessageTooLong`] if the content of the message
- /// is over [`the limit`], containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
- /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(CreateMessage) -> CreateMessage {
- #[cfg(feature="cache")]
- {
- if self.author.id != CACHE.read().unwrap().user.id {
- return Err(Error::Client(ClientError::InvalidUser));
- }
- }
-
- let mut builder = CreateMessage::default();
-
- if !self.content.is_empty() {
- builder = builder.content(&self.content);
- }
-
- if let Some(embed) = self.embeds.get(0) {
- builder = builder.embed(|_| CreateEmbed::from(embed.clone()));
- }
-
- if self.tts {
- builder = builder.tts(true);
- }
-
- let map = f(builder).0;
-
- match rest::edit_message(self.channel_id.0, self.id.0, &Value::Object(map)) {
- Ok(edited) => {
- mem::replace(self, edited);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
- }
-
- /// 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 {
- result = result.replace(&u.mention(), &u.distinct());
- }
-
- // Then replace all role mentions.
- for id in &self.mention_roles {
- let mention = id.mention();
-
- if let Some(role) = id.find() {
- 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 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.channel_id.get_reaction_users(self.id, reaction_type, limit, after)
- }
-
- /// 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<Arc<RwLock<Guild>>> {
- self.guild_id().and_then(|guild_id| CACHE.read().unwrap().get_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 CACHE.read().unwrap().get_channel(self.channel_id) {
- Some(Channel::Guild(ch)) => Some(ch.read().unwrap().guild_id),
- _ => None,
- }
- }
-
- /// True if message was sent using direct messages.
- #[cfg(feature="cache")]
- pub fn is_private(&self) -> bool {
- match CACHE.read().unwrap().get_channel(self.channel_id) {
- Some(Channel::Group(_)) | Some(Channel::Private(_)) => true,
- _ => false,
- }
- }
-
- /// Checks the length of a string to ensure that it is within Discord's
- /// maximum message length limit.
- ///
- /// Returns `None` if the message is within the limit, otherwise returns
- /// `Some` with an inner value of how many unicode code points the message
- /// is over.
- pub fn overflow_length(content: &str) -> Option<u64> {
- // Check if the content is over the maximum number of unicode code
- // points.
- let count = content.chars().count() as i64;
- let diff = count - (constants::MESSAGE_CODE_LIMIT as i64);
-
- if diff > 0 {
- Some(diff as u64)
- } else {
- None
- }
- }
-
- /// Pins this message to its channel.
- ///
- /// **Note**: Requires the [Manage Messages] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidPermissions`] if the current user does not have
- /// the required permissions.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn pin(&self) -> Result<()> {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_MESSAGES;
-
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.channel_id.pin(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
- /// [`ClientError::InvalidPermissions`] if the current user does not have
- /// the required [permissions].
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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) -> Result<()> {
- #[cfg(feature="cache")]
- {
- let req = permissions::ADD_REACTIONS;
-
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- rest::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
- /// [`ClientError::InvalidPermissions`] if the current user does not have
- /// the required permissions.
- ///
- /// 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.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- 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)));
- }
-
- #[cfg(feature="cache")]
- {
- let req = permissions::SEND_MESSAGES;
-
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- let mut gen = self.author.mention();
- gen.push_str(": ");
- gen.push_str(content);
-
- let map = ObjectBuilder::new()
- .insert("content", gen)
- .insert("tts", false)
- .build();
-
- rest::send_message(self.channel_id.0, &map)
- }
-
- /// Unpins the message from its channel.
- ///
- /// **Note**: Requires the [Manage Messages] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidPermissions`] if the current user does not have
- /// the required permissions.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- pub fn unpin(&self) -> Result<()> {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_MESSAGES;
-
- if !utils::user_has_perms(self.channel_id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- rest::unpin_message(self.channel_id.0, self.id.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> {
- let mut map = into_map(value)?;
- let id = remove(&mut map, "id").and_then(decode_id)?;
- let kind = remove(&mut map, "type").and_then(into_string)?;
- let kind = match &*kind {
- "member" => PermissionOverwriteType::Member(UserId(id)),
- "role" => PermissionOverwriteType::Role(RoleId(id)),
- _ => return Err(Error::Decode("Expected valid PermissionOverwrite type", Value::String(kind))),
- };
-
- Ok(PermissionOverwrite {
- kind: kind,
- allow: remove(&mut map, "allow").and_then(Permissions::decode)?,
- deny: remove(&mut map, "deny").and_then(Permissions::decode)?,
- })
- }
-}
-
-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`]: ../client/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)
- }
-
- /// Broadcasts that the current user is typing to the recipient.
- pub fn broadcast_typing(&self) -> Result<()> {
- self.id.broadcast_typing()
- }
-
- /// 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)?;
- let mut recipients = decode_array(remove(&mut map, "recipients")?,
- User::decode)?;
-
- Ok(PrivateChannel {
- id: remove(&mut map, "id").and_then(ChannelId::decode)?,
- kind: remove(&mut map, "type").and_then(ChannelType::decode)?,
- last_message_id: opt(&mut map, "last_message_id", MessageId::decode)?,
- last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
- recipient: Arc::new(RwLock::new(recipients.remove(0))),
- })
- }
-
- /// 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) -> Result<Channel> {
- 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)
- }
-
- /// Edits a [`Message`] in the channel given its Id.
- ///
- /// Message editing preserves all unchanged message data.
- ///
- /// Refer to the documentation for [`CreateMessage`] for more information
- /// regarding message restrictions and requirements.
- ///
- /// **Note**: Requires that the current user be the author of the message.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::MessageTooLong`] if the content of the message
- /// is over the [`the limit`], containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
- /// [`Message`]: struct.Message.html
- /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
- #[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
- self.id.edit_message(message_id, f)
- }
-
- /// Gets a message from the channel.
- ///
- /// Requires the [Read Message History] permission.
- ///
- /// [Read Message History]: permissions/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]: permissions/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)
- }
-
- /// Pins a [`Message`] to the channel.
- ///
- /// [`Message`]: struct.Message.html
- #[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.
- #[inline]
- pub fn pins(&self) -> Result<Vec<Message>> {
- self.id.pins()
- }
-
- /// Sends a message with just the given message content in the channel.
- ///
- /// # 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.
- ///
- /// [`ChannelId`]: ../model/struct.ChannelId.html
- /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
- #[inline]
- pub fn say(&self, content: &str) -> Result<Message> {
- self.id.say(content)
- }
-
- /// Performs a search request to the API for the channel's [`Message`]s.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- pub fn search<F>(&self, f: F) -> Result<SearchResult>
- where F: FnOnce(Search) -> Search {
- #[cfg(feature="cache")]
- {
- if CACHE.read().unwrap().user.bot {
- return Err(Error::Client(ClientError::InvalidOperationAsBot));
- }
- }
-
- self.id.search(f)
- }
-
- /// Sends a file along with optional message contents. The filename _must_
- /// be specified.
- ///
- /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [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 {
- self.id.send_file(file, filename, 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 [`ClientError::MessageTooLong`] if the content of the message
- /// is over the above limit, containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
- /// [`Message`]: struct.Message.html
- #[inline]
- pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
- self.id.send_message(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) -> Result<()> {
- self.id.unpin(message_id)
- }
-}
-
-impl fmt::Display for PrivateChannel {
- /// Formats the private channel, displaying the recipient's username.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str(&self.recipient.read().unwrap().name)
- }
-}
-
-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`]: ../client/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.
- ///
- /// **Note**: Requires the [Send Messages] permission.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::InvalidPermissions`] if the current user does
- /// not have the required permissions.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
- pub fn broadcast_typing(&self) -> Result<()> {
- self.id.broadcast_typing()
- }
-
- /// 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));
- /// ```
- pub fn create_invite<F>(&self, f: F) -> Result<RichInvite>
- where F: FnOnce(CreateInvite) -> CreateInvite {
- #[cfg(feature="cache")]
- {
- let req = permissions::CREATE_INVITE;
-
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- let map = f(CreateInvite::default()).0.build();
-
- 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::client::CACHE;
- /// use serenity::model::{ChannelId, PermissionOverwrite, permissions};
- ///
- /// 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 cache = CACHE.read().unwrap();
- /// let channel = cache.get_guild_channel(channel_id).unwrap();
- ///
- /// let _ = channel.create_permission(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::client::CACHE;
- /// use serenity::model::{ChannelId, PermissionOverwrite, permissions};
- ///
- /// 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 cache = CACHE.read().unwrap();
- /// let channel = cache.get_guild_channel(channel_id).unwrap();
- ///
- /// let _ = channel.create_permission(overwrite);
- /// ```
- ///
- /// [`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 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)?;
-
- let id = remove(&mut map, "guild_id").and_then(GuildId::decode)?;
-
- GuildChannel::decode_guild(Value::Object(map), id)
- }
-
- #[doc(hidden)]
- pub fn decode_guild(value: Value, guild_id: GuildId) -> Result<GuildChannel> {
- let mut map = into_map(value)?;
-
- Ok(GuildChannel {
- id: remove(&mut map, "id").and_then(ChannelId::decode)?,
- name: remove(&mut map, "name").and_then(into_string)?,
- guild_id: guild_id,
- topic: opt(&mut map, "topic", into_string)?,
- position: req!(remove(&mut map, "position")?.as_i64()),
- kind: remove(&mut map, "type").and_then(ChannelType::decode)?,
- last_message_id: opt(&mut map, "last_message_id", MessageId::decode)?,
- permission_overwrites: decode_array(remove(&mut map, "permission_overwrites")?, PermissionOverwrite::decode)?,
- bitrate: remove(&mut map, "bitrate").ok().and_then(|v| v.as_u64()),
- user_limit: remove(&mut map, "user_limit").ok().and_then(|v| v.as_u64()),
- last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
- })
- }
-
- /// Deletes this channel, returning the channel on a successful deletion.
- pub fn delete(&self) -> Result<Channel> {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_CHANNELS;
-
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- 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.
- ///
- /// 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));
- /// ```
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(EditChannel) -> EditChannel {
-
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_CHANNELS;
-
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- let map = ObjectBuilder::new()
- .insert("name", &self.name)
- .insert("position", self.position)
- .insert("type", self.kind.name());
-
- let edited = f(EditChannel(map)).0.build();
-
- match rest::edit_channel(self.id.0, &edited) {
- Ok(channel) => {
- mem::replace(self, channel);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
- }
-
- /// Edits a [`Message`] in the channel given its Id.
- ///
- /// Message editing preserves all unchanged message data.
- ///
- /// Refer to the documentation for [`CreateMessage`] for more information
- /// regarding message restrictions and requirements.
- ///
- /// **Note**: Requires that the current user be the author of the message.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::MessageTooLong`] if the content of the message
- /// is over the [`the limit`], containing the number of unicode code points
- /// over the limit.
- ///
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
- /// [`Message`]: struct.Message.html
- /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
- #[inline]
- pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
- where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
- self.id.edit_message(message_id, f)
- }
-
- /// 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]: permissions/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]: permissions/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)
- }
-
- /// Retrieves the channel's webhooks.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
- self.id.get_webhooks()
- }
-
- /// 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<Arc<RwLock<Guild>>> {
- CACHE.read().unwrap().get_guild(self.guild_id)
- }
-
- /// 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.
- #[inline]
- pub fn pins(&self) -> Result<Vec<Message>> {
- self.id.pins()
- }
-
- /// Sends a message with just the given message content in the channel.
- ///
- /// # 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.
- ///
- /// [`ChannelId`]: ../model/struct.ChannelId.html
- /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
- #[inline]
- pub fn say(&self, content: &str) -> Result<Message> {
- self.id.say(content)
- }
-
- /// Performs a search request for the channel's [`Message`]s.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
- #[cfg(feature="cache")]
- {
- if CACHE.read().unwrap().user.bot {
- return Err(Error::Client(ClientError::InvalidOperationAsBot));
- }
- }
-
- self.id.search(f)
- }
-
- /// Sends a file along with optional message contents. The filename _must_
- /// be specified.
- ///
- /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
- /// [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 {
- self.id.send_file(file, filename, 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 [`ClientError::MessageTooLong`] if the content of the message
- /// is over the above limit, containing the number of unicode code points
- /// over the limit.
- ///
- /// Returns a [`ClientError::InvalidPermissions`] if the current user does
- /// not have the required permissions.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.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) -> Result<Message> {
- #[cfg(feature="cache")]
- {
- let req = permissions::SEND_MESSAGES;
-
- if !utils::user_has_perms(self.id, req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.send_message(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) -> Result<()> {
- self.id.unpin(message_id)
- }
-}
-
-impl fmt::Display for GuildChannel {
- /// Formats the channel, creating a mention of it.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.id.mention(), f)
- }
-}
-
-impl Reaction {
- /// Deletes the reaction, but only if the current user is the user who made
- /// the reaction or has permission to.
- ///
- /// **Note**: Requires the [Manage Messages] permission, _if_ the current
- /// user did not perform the reaction.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, then returns a
- /// [`ClientError::InvalidPermissions`] if the current user does not have
- /// the required [permissions].
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
- /// [permissions]: permissions
- pub fn delete(&self) -> Result<()> {
- let user_id = feature_cache! {{
- let user = if self.user_id == CACHE.read().unwrap().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 !utils::user_has_perms(self.channel_id, req).unwrap_or(true) {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- user
- } else {
- Some(self.user_id.0)
- }};
-
- rest::delete_reaction(self.channel_id.0,
- self.message_id.0,
- user_id,
- &self.emoji)
- }
-
- /// 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.
- ///
- /// **Note**: Requires the [Read Message History] permission.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::InvalidPermissions`] if the current user does
- /// not have the required [permissions].
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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>)
- -> Result<Vec<User>>
- where R: Into<ReactionType>,
- U: Into<UserId> {
- rest::get_reaction_users(self.channel_id.0,
- self.message_id.0,
- &reaction_type.into(),
- limit.unwrap_or(50),
- after.map(|u| u.into().0))
- }
-}
-
-/// The type of a [`Reaction`] sent.
-///
-/// [`Reaction`]: struct.Reaction.html
-#[derive(Clone, Debug)]
-pub enum ReactionType {
- /// A reaction with a [`Guild`]s custom [`Emoji`], which is unique to the
- /// guild.
- ///
- /// [`Emoji`]: struct.Emoji.html
- /// [`Guild`]: struct.Guild.html
- Custom {
- /// The Id of the custom [`Emoji`].
- ///
- /// [`Emoji`]: struct.Emoji.html
- id: EmojiId,
- /// The name of the custom emoji. This is primarily used for decoration
- /// and distinguishing the emoji client-side.
- name: String,
- },
- /// A reaction with a twemoji.
- Unicode(String),
-}
-
-impl ReactionType {
- /// Creates a data-esque display of the type. This is not very useful for
- /// displaying, as the primary client can not render it, but can be useful
- /// for debugging.
- ///
- /// **Note**: This is mainly for use internally. There is otherwise most
- /// likely little use for it.
- pub fn as_data(&self) -> String {
- match *self {
- ReactionType::Custom { id, ref name } => {
- format!("{}:{}", name, id)
- },
- ReactionType::Unicode(ref unicode) => unicode.clone(),
- }
- }
-
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Self> {
- let mut map = into_map(value)?;
- let name = remove(&mut map, "name").and_then(into_string)?;
-
- // Only custom emoji reactions (`ReactionType::Custom`) have an Id.
- Ok(match opt(&mut map, "id", EmojiId::decode)? {
- Some(id) => ReactionType::Custom {
- id: id,
- name: name,
- },
- None => ReactionType::Unicode(name),
- })
- }
-}
-
-impl From<Emoji> for ReactionType {
- fn from(emoji: Emoji) -> ReactionType {
- ReactionType::Custom {
- id: emoji.id,
- name: emoji.name,
- }
- }
-}
-
-impl From<String> for ReactionType {
- fn from(unicode: String) -> ReactionType {
- ReactionType::Unicode(unicode)
- }
-}
-
-impl fmt::Display for ReactionType {
- /// Formats the reaction type, displaying the associated emoji in a
- /// way that clients can understand.
- ///
- /// If the type is a [custom][`ReactionType::Custom`] emoji, then refer to
- /// the documentation for [emoji's formatter][`Emoji::fmt`] on how this is
- /// displayed. Otherwise, if the type is a
- /// [unicode][`ReactionType::Unicode`], then the inner unicode is displayed.
- ///
- /// [`Emoji::fmt`]: struct.Emoji.html#method.fmt
- /// [`ReactionType::Custom`]: enum.ReactionType.html#variant.Custom
- /// [`ReactionType::Unicode`]: enum.ReactionType.html#variant.Unicode
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- ReactionType::Custom { id, ref name } => {
- f.write_char('<')?;
- f.write_char(':')?;
- f.write_str(name)?;
- f.write_char(':')?;
- fmt::Display::fmt(&id, f)?;
- f.write_char('>')
- },
- ReactionType::Unicode(ref unicode) => f.write_str(unicode),
- }
- }
-}
diff --git a/src/model/channel/attachment.rs b/src/model/channel/attachment.rs
new file mode 100644
index 0000000..387edaf
--- /dev/null
+++ b/src/model/channel/attachment.rs
@@ -0,0 +1,92 @@
+use hyper::Client as HyperClient;
+use std::io::Read;
+use ::internal::prelude::*;
+use ::model::Attachment;
+
+impl Attachment {
+ /// If this attachment is an image, then a tuple of the width and height
+ /// in pixels is returned.
+ pub fn dimensions(&self) -> Option<(u64, u64)> {
+ if let (Some(width), Some(height)) = (self.width, self.height) {
+ Some((width, height))
+ } else {
+ None
+ }
+ }
+
+ /// Downloads the attachment, returning back a vector of bytes.
+ ///
+ /// # Examples
+ ///
+ /// Download all of the attachments associated with a [`Message`]:
+ ///
+ /// ```rust,no_run
+ /// use serenity::Client;
+ /// use std::env;
+ /// use std::fs::File;
+ /// use std::io::Write;
+ /// use std::path::Path;
+ ///
+ /// let token = env::var("DISCORD_TOKEN").expect("token in environment");
+ /// let mut client = Client::login_bot(&token);
+ ///
+ /// client.on_message(|_, message| {
+ /// for attachment in message.attachments {
+ /// let content = match attachment.download() {
+ /// Ok(content) => content,
+ /// Err(why) => {
+ /// println!("Error downloading attachment: {:?}", why);
+ /// let _ = message.channel_id.say("Error downloading attachment");
+ ///
+ /// return;
+ /// },
+ /// };
+ ///
+ /// let mut file = match File::create(&attachment.filename) {
+ /// Ok(file) => file,
+ /// Err(why) => {
+ /// println!("Error creating file: {:?}", why);
+ /// let _ = message.channel_id.say("Error creating file");
+ ///
+ /// return;
+ /// },
+ /// };
+ ///
+ /// if let Err(why) = file.write(&content) {
+ /// println!("Error writing to file: {:?}", why);
+ ///
+ /// return;
+ /// }
+ ///
+ /// let _ = message.channel_id.say(&format!("Saved {:?}", attachment.filename));
+ /// }
+ /// });
+ ///
+ /// client.on_ready(|_context, ready| {
+ /// println!("{} is connected!", ready.user.name);
+ /// });
+ ///
+ /// let _ = client.start();
+ /// ```
+ ///
+ /// # Errors
+ ///
+ /// Returns an [`Error::Io`] when there is a problem reading the contents
+ /// of the HTTP response.
+ ///
+ /// Returns an [`Error::Hyper`] when there is a problem retrieving the
+ /// attachment.
+ ///
+ /// [`Error::Hyper`]: ../enum.Error.html#variant.Hyper
+ /// [`Error::Io`]: ../enum.Error.html#variant.Io
+ /// [`Message`]: struct.Message.html
+ pub fn download(&self) -> Result<Vec<u8>> {
+ let hyper = HyperClient::new();
+ let mut response = hyper.get(&self.url).send()?;
+
+ let mut bytes = vec![];
+ response.read_to_end(&mut bytes)?;
+
+ Ok(bytes)
+ }
+}
diff --git a/src/model/channel/channel_id.rs b/src/model/channel/channel_id.rs
new file mode 100644
index 0000000..2d09367
--- /dev/null
+++ b/src/model/channel/channel_id.rs
@@ -0,0 +1,511 @@
+use serde_json::builder::ObjectBuilder;
+use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
+use std::io::Read;
+use ::client::{CACHE, rest};
+use ::model::*;
+use ::utils::builder::{CreateMessage, EditChannel, GetMessages, Search};
+
+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.
+ ///
+ /// [`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
+ /// // assuming a `channel_id` has been bound
+ ///
+ /// channel_id.edit(|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
+ /// [Manage Channel]: permissions/constant.MANAGE_CHANNELS.html
+ #[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.
+ ///
+ /// Message editing preserves all unchanged message data.
+ ///
+ /// Refer to the documentation for [`CreateMessage`] for more information
+ /// regarding message restrictions and requirements.
+ ///
+ /// **Note**: Requires that the current user be the author of the message.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::MessageTooLong`] if the content of the message
+ /// is over the [`the limit`], containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
+ /// [`Message`]: struct.Message.html
+ /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
+ pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
+ let 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)));
+ }
+ }
+ }
+
+ rest::edit_message(self.0, message_id.into().0, &Value::Object(map))
+ }
+
+ /// Search the cache for the channel with the Id.
+ #[cfg(feature="cache")]
+ pub fn find(&self) -> Option<Channel> {
+ CACHE.read().unwrap().get_channel(*self)
+ }
+
+ /// 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);
+ }
+ }
+
+ 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]: permissions/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]: permissions/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))
+ }
+
+ /// Retrieves the channel's webhooks.
+ ///
+ /// **Note**: Requires the [Manage Webhooks] permission.
+ ///
+ /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ #[inline]
+ pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
+ rest::get_channel_webhooks(self.0)
+ }
+
+ /// Pins a [`Message`] to the channel.
+ ///
+ /// [`Message`]: struct.Message.html
+ #[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.
+ ///
+ /// [`Message`]: struct.Message.html
+ #[inline]
+ pub fn pins(&self) -> Result<Vec<Message>> {
+ rest::get_pins(self.0)
+ }
+
+ /// Sends a message with just the given message content in the channel.
+ ///
+ /// # 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.
+ ///
+ /// [`ChannelId`]: ../model/struct.ChannelId.html
+ /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
+ #[inline]
+ pub fn say(&self, content: &str) -> Result<Message> {
+ self.send_message(|m| m.content(content))
+ }
+
+ /// 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.
+ ///
+ /// The [Attach Files] and [Send Messages] permissions are required.
+ ///
+ /// **Note**: Message contents must be under 2000 unicode code points.
+ ///
+ /// # Examples
+ ///
+ /// Send a file with the filename `my_file.jpg`:
+ ///
+ /// ```rust,no_run
+ /// use serenity::model::ChannelId;
+ /// use std::fs::File;
+ ///
+ /// let channel_id = ChannelId(7);
+ /// let filename = "my_file.jpg";
+ /// let file = File::open(filename).unwrap();
+ ///
+ /// let _ = channel_id.send_file(file, filename, |m| m.content("a file"));
+ /// ```
+ ///
+ /// # 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.
+ ///
+ /// **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)
+ }
+}
+
+impl From<Channel> for ChannelId {
+ /// Gets the Id of a `Channel`.
+ fn from(channel: Channel) -> ChannelId {
+ match channel {
+ Channel::Group(group) => group.read().unwrap().channel_id,
+ Channel::Guild(ch) => ch.read().unwrap().id,
+ Channel::Private(ch) => ch.read().unwrap().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 Display for ChannelId {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ Display::fmt(&self.0, f)
+ }
+}
diff --git a/src/model/channel/embed.rs b/src/model/channel/embed.rs
new file mode 100644
index 0000000..32c3722
--- /dev/null
+++ b/src/model/channel/embed.rs
@@ -0,0 +1,15 @@
+use serde_json::Value;
+use ::model::Embed;
+use ::utils::builder::CreateEmbed;
+
+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
+ #[inline]
+ pub fn fake<F>(f: F) -> Value where F: FnOnce(CreateEmbed) -> CreateEmbed {
+ Value::Object(f(CreateEmbed::default()).0)
+ }
+}
diff --git a/src/model/channel/group.rs b/src/model/channel/group.rs
new file mode 100644
index 0000000..9d3335f
--- /dev/null
+++ b/src/model/channel/group.rs
@@ -0,0 +1,329 @@
+use std::borrow::Cow;
+use std::fmt::Write as FmtWrite;
+use std::io::Read;
+use ::client::{CACHE, rest};
+use ::model::*;
+use ::utils::builder::{CreateMessage, GetMessages, Search};
+
+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`]: ../client/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.
+ ///
+ /// Refer to [`rest::add_group_recipient`] for more information.
+ ///
+ /// **Note**: Groups have a limit of 10 recipients, including the current
+ /// user.
+ ///
+ /// [`rest::add_group_recipient`]: ../client/rest/fn.add_group_recipient.html
+ pub fn add_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> {
+ let user = user.into();
+
+ // If the group already contains the recipient, do nothing.
+ if self.recipients.contains_key(&user) {
+ return Ok(());
+ }
+
+ rest::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) -> Result<()> {
+ self.channel_id.broadcast_typing()
+ }
+
+ /// 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.channel_id.create_reaction(message_id, reaction_type)
+ }
+
+ /// 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.channel_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.channel_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.channel_id.delete_reaction(message_id, user_id, reaction_type)
+ }
+
+ /// Edits a [`Message`] in the channel given its Id.
+ ///
+ /// Message editing preserves all unchanged message data.
+ ///
+ /// Refer to the documentation for [`CreateMessage`] for more information
+ /// regarding message restrictions and requirements.
+ ///
+ /// **Note**: Requires that the current user be the author of the message.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::MessageTooLong`] if the content of the message
+ /// is over the [`the limit`], containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
+ /// [`Message`]: struct.Message.html
+ /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
+ #[inline]
+ pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
+ self.channel_id.edit_message(message_id, f)
+ }
+
+ /// Gets a message from the channel.
+ ///
+ /// Requires the [Read Message History] permission.
+ ///
+ /// [Read Message History]: permissions/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]: permissions/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.
+ pub fn icon_url(&self) -> Option<String> {
+ self.icon.as_ref().map(|icon|
+ format!(cdn!("/channel-icons/{}/{}.webp"), self.channel_id, icon))
+ }
+
+ /// Leaves the group.
+ #[inline]
+ pub fn leave(&self) -> Result<Group> {
+ rest::leave_group(self.channel_id.0)
+ }
+
+ /// Generates a name for the group.
+ ///
+ /// If there are no recipients in the group, the name will be "Empty Group".
+ /// Otherwise, the name is generated in a Comma Separated Value list, such
+ /// as "person 1, person 2, person 3".
+ pub fn name(&self) -> Cow<str> {
+ match self.name {
+ Some(ref name) => Cow::Borrowed(name),
+ None => {
+ let mut name = match self.recipients.values().nth(0) {
+ Some(recipient) => recipient.read().unwrap().name.clone(),
+ None => return Cow::Borrowed("Empty Group"),
+ };
+
+ for recipient in self.recipients.values().skip(1) {
+ let _ = write!(name, ", {}", recipient.read().unwrap().name);
+ }
+
+ Cow::Owned(name)
+ }
+ }
+ }
+
+ /// Retrieves the list of messages that have been pinned in the group.
+ #[inline]
+ pub fn pins(&self) -> Result<Vec<Message>> {
+ 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.
+ pub fn remove_recipient<U: Into<UserId>>(&self, user: U) -> Result<()> {
+ let user = user.into();
+
+ // If the group does not contain the recipient already, do nothing.
+ if !self.recipients.contains_key(&user) {
+ return Ok(());
+ }
+
+ rest::remove_group_recipient(self.channel_id.0, user.0)
+ }
+
+ /// Sends a message with just the given message content in the channel.
+ ///
+ /// # 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.
+ ///
+ /// [`ChannelId`]: ../model/struct.ChannelId.html
+ /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
+ #[inline]
+ pub fn say(&self, content: &str) -> Result<Message> {
+ self.channel_id.say(content)
+ }
+
+ /// Performs a search request to the API for the group's channel's
+ /// [`Message`]s.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ #[inline]
+ pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
+ self.channel_id.search(f)
+ }
+
+ /// Sends a file along with optional message contents. The filename _must_
+ /// be specified.
+ ///
+ /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [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 {
+ self.channel_id.send_file(file, filename, 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`]: ../utils/builder/struct.CreateMessage.html
+ /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ #[inline]
+ pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
+ self.channel_id.send_message(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) -> Result<()> {
+ self.channel_id.unpin(message_id)
+ }
+}
diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs
new file mode 100644
index 0000000..677e77b
--- /dev/null
+++ b/src/model/channel/guild_channel.rs
@@ -0,0 +1,504 @@
+use serde_json::builder::ObjectBuilder;
+use std::fmt::{Display, Formatter, Result as FmtResult};
+use std::io::Read;
+use std::mem;
+use ::client::{CACHE, rest};
+use ::internal::prelude::*;
+use ::model::*;
+use ::utils::builder::{CreateInvite, CreateMessage, EditChannel, GetMessages, Search};
+
+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`]: ../client/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.
+ ///
+ /// **Note**: Requires the [Send Messages] permission.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::InvalidPermissions`] if the current user does
+ /// not have the required permissions.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ pub fn broadcast_typing(&self) -> Result<()> {
+ self.id.broadcast_typing()
+ }
+
+ /// 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));
+ /// ```
+ pub fn create_invite<F>(&self, f: F) -> Result<RichInvite>
+ where F: FnOnce(CreateInvite) -> CreateInvite {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::CREATE_INVITE;
+
+ if !utils::user_has_perms(self.id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ let map = f(CreateInvite::default()).0.build();
+
+ 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::client::CACHE;
+ /// use serenity::model::{ChannelId, PermissionOverwrite, permissions};
+ ///
+ /// 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 cache = CACHE.read().unwrap();
+ /// let channel = cache.get_guild_channel(channel_id).unwrap();
+ ///
+ /// let _ = channel.create_permission(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::client::CACHE;
+ /// use serenity::model::{ChannelId, PermissionOverwrite, permissions};
+ ///
+ /// 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 cache = CACHE.read().unwrap();
+ /// let channel = cache.get_guild_channel(channel_id).unwrap();
+ ///
+ /// let _ = channel.create_permission(overwrite);
+ /// ```
+ ///
+ /// [`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 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)?;
+
+ let id = remove(&mut map, "guild_id").and_then(GuildId::decode)?;
+
+ GuildChannel::decode_guild(Value::Object(map), id)
+ }
+
+ #[doc(hidden)]
+ pub fn decode_guild(value: Value, guild_id: GuildId) -> Result<GuildChannel> {
+ let mut map = into_map(value)?;
+
+ Ok(GuildChannel {
+ id: remove(&mut map, "id").and_then(ChannelId::decode)?,
+ name: remove(&mut map, "name").and_then(into_string)?,
+ guild_id: guild_id,
+ topic: opt(&mut map, "topic", into_string)?,
+ position: req!(remove(&mut map, "position")?.as_i64()),
+ kind: remove(&mut map, "type").and_then(ChannelType::decode)?,
+ last_message_id: opt(&mut map, "last_message_id", MessageId::decode)?,
+ permission_overwrites: decode_array(remove(&mut map, "permission_overwrites")?, PermissionOverwrite::decode)?,
+ bitrate: remove(&mut map, "bitrate").ok().and_then(|v| v.as_u64()),
+ user_limit: remove(&mut map, "user_limit").ok().and_then(|v| v.as_u64()),
+ last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
+ })
+ }
+
+ /// Deletes this channel, returning the channel on a successful deletion.
+ pub fn delete(&self) -> Result<Channel> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_CHANNELS;
+
+ if !utils::user_has_perms(self.id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ 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.
+ ///
+ /// 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));
+ /// ```
+ pub fn edit<F>(&mut self, f: F) -> Result<()>
+ where F: FnOnce(EditChannel) -> EditChannel {
+
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_CHANNELS;
+
+ if !utils::user_has_perms(self.id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ let map = ObjectBuilder::new()
+ .insert("name", &self.name)
+ .insert("position", self.position)
+ .insert("type", self.kind.name());
+
+ let edited = f(EditChannel(map)).0.build();
+
+ match rest::edit_channel(self.id.0, &edited) {
+ Ok(channel) => {
+ mem::replace(self, channel);
+
+ Ok(())
+ },
+ Err(why) => Err(why),
+ }
+ }
+
+ /// Edits a [`Message`] in the channel given its Id.
+ ///
+ /// Message editing preserves all unchanged message data.
+ ///
+ /// Refer to the documentation for [`CreateMessage`] for more information
+ /// regarding message restrictions and requirements.
+ ///
+ /// **Note**: Requires that the current user be the author of the message.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::MessageTooLong`] if the content of the message
+ /// is over the [`the limit`], containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
+ /// [`Message`]: struct.Message.html
+ /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
+ #[inline]
+ pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
+ self.id.edit_message(message_id, f)
+ }
+
+ /// 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]: permissions/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]: permissions/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)
+ }
+
+ /// Retrieves the channel's webhooks.
+ ///
+ /// **Note**: Requires the [Manage Webhooks] permission.
+ ///
+ /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ #[inline]
+ pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
+ self.id.get_webhooks()
+ }
+
+ /// 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<Arc<RwLock<Guild>>> {
+ CACHE.read().unwrap().get_guild(self.guild_id)
+ }
+
+ /// 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.
+ #[inline]
+ pub fn pins(&self) -> Result<Vec<Message>> {
+ self.id.pins()
+ }
+
+ /// Sends a message with just the given message content in the channel.
+ ///
+ /// # 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.
+ ///
+ /// [`ChannelId`]: ../model/struct.ChannelId.html
+ /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
+ #[inline]
+ pub fn say(&self, content: &str) -> Result<Message> {
+ self.id.say(content)
+ }
+
+ /// Performs a search request for the channel's [`Message`]s.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
+ #[cfg(feature="cache")]
+ {
+ if CACHE.read().unwrap().user.bot {
+ return Err(Error::Client(ClientError::InvalidOperationAsBot));
+ }
+ }
+
+ self.id.search(f)
+ }
+
+ /// Sends a file along with optional message contents. The filename _must_
+ /// be specified.
+ ///
+ /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [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 {
+ self.id.send_file(file, filename, 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 [`ClientError::MessageTooLong`] if the content of the message
+ /// is over the above limit, containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// Returns a [`ClientError::InvalidPermissions`] if the current user does
+ /// not have the required permissions.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.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) -> Result<Message> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::SEND_MESSAGES;
+
+ if !utils::user_has_perms(self.id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.send_message(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) -> Result<()> {
+ self.id.unpin(message_id)
+ }
+}
+
+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
new file mode 100644
index 0000000..84d726d
--- /dev/null
+++ b/src/model/channel/message.rs
@@ -0,0 +1,393 @@
+use serde_json::builder::ObjectBuilder;
+use std::mem;
+use ::constants;
+use ::client::{CACHE, rest};
+use ::model::*;
+use ::utils::builder::{CreateEmbed, CreateMessage};
+
+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`]: ../client/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
+ /// have the [Manage Messages] permission.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` feature is enabled, then returns a
+ /// [`ClientError::InvalidPermissions`] if the current user does not have
+ /// the required permissions.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser
+ /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ pub fn delete(&self) -> Result<()> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_MESSAGES;
+ let is_author = self.author.id == CACHE.read().unwrap().user.id;
+ let has_perms = utils::user_has_perms(self.channel_id, req)?;
+
+ if !is_author && !has_perms {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.channel_id.delete_message(self.id)
+ }
+
+ /// 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
+ /// [`ClientError::InvalidPermissions`] if the current user does not have
+ /// the required permissions.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`Reaction`]: struct.Reaction.html
+ /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ pub fn delete_reactions(&self) -> Result<()> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_MESSAGES;
+
+ if !utils::user_has_perms(self.channel_id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ rest::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 [`CreateMessage`] 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 [`ClientError::InvalidUser`] if the
+ /// current user is not the author.
+ ///
+ /// Returns a [`ClientError::MessageTooLong`] if the content of the message
+ /// is over [`the limit`], containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
+ /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
+ pub fn edit<F>(&mut self, f: F) -> Result<()>
+ where F: FnOnce(CreateMessage) -> CreateMessage {
+ #[cfg(feature="cache")]
+ {
+ if self.author.id != CACHE.read().unwrap().user.id {
+ return Err(Error::Client(ClientError::InvalidUser));
+ }
+ }
+
+ let mut builder = CreateMessage::default();
+
+ if !self.content.is_empty() {
+ builder = builder.content(&self.content);
+ }
+
+ if let Some(embed) = self.embeds.get(0) {
+ builder = builder.embed(|_| CreateEmbed::from(embed.clone()));
+ }
+
+ if self.tts {
+ builder = builder.tts(true);
+ }
+
+ let map = f(builder).0;
+
+ match rest::edit_message(self.channel_id.0, self.id.0, &Value::Object(map)) {
+ Ok(edited) => {
+ mem::replace(self, edited);
+
+ Ok(())
+ },
+ Err(why) => Err(why),
+ }
+ }
+
+ /// 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 {
+ result = result.replace(&u.mention(), &u.distinct());
+ }
+
+ // Then replace all role mentions.
+ for id in &self.mention_roles {
+ let mention = id.mention();
+
+ if let Some(role) = id.find() {
+ 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 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.channel_id.get_reaction_users(self.id, reaction_type, limit, after)
+ }
+
+ /// 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<Arc<RwLock<Guild>>> {
+ self.guild_id().and_then(|guild_id| CACHE.read().unwrap().get_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 CACHE.read().unwrap().get_channel(self.channel_id) {
+ Some(Channel::Guild(ch)) => Some(ch.read().unwrap().guild_id),
+ _ => None,
+ }
+ }
+
+ /// True if message was sent using direct messages.
+ #[cfg(feature="cache")]
+ pub fn is_private(&self) -> bool {
+ match CACHE.read().unwrap().get_channel(self.channel_id) {
+ Some(Channel::Group(_)) | Some(Channel::Private(_)) => true,
+ _ => false,
+ }
+ }
+
+ /// Checks the length of a string to ensure that it is within Discord's
+ /// maximum message length limit.
+ ///
+ /// Returns `None` if the message is within the limit, otherwise returns
+ /// `Some` with an inner value of how many unicode code points the message
+ /// is over.
+ pub fn overflow_length(content: &str) -> Option<u64> {
+ // Check if the content is over the maximum number of unicode code
+ // points.
+ let count = content.chars().count() as i64;
+ let diff = count - (constants::MESSAGE_CODE_LIMIT as i64);
+
+ if diff > 0 {
+ Some(diff as u64)
+ } else {
+ None
+ }
+ }
+
+ /// Pins this message to its channel.
+ ///
+ /// **Note**: Requires the [Manage Messages] permission.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidPermissions`] if the current user does not have
+ /// the required permissions.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ pub fn pin(&self) -> Result<()> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_MESSAGES;
+
+ if !utils::user_has_perms(self.channel_id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.channel_id.pin(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
+ /// [`ClientError::InvalidPermissions`] if the current user does not have
+ /// the required [permissions].
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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) -> Result<()> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::ADD_REACTIONS;
+
+ if !utils::user_has_perms(self.channel_id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ rest::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
+ /// [`ClientError::InvalidPermissions`] if the current user does not have
+ /// the required permissions.
+ ///
+ /// 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.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [Send Messages]: permissions/constant.SEND_MESSAGES.html
+ 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)));
+ }
+
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::SEND_MESSAGES;
+
+ if !utils::user_has_perms(self.channel_id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ let mut gen = self.author.mention();
+ gen.push_str(": ");
+ gen.push_str(content);
+
+ let map = ObjectBuilder::new()
+ .insert("content", gen)
+ .insert("tts", false)
+ .build();
+
+ rest::send_message(self.channel_id.0, &map)
+ }
+
+ /// Unpins the message from its channel.
+ ///
+ /// **Note**: Requires the [Manage Messages] permission.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidPermissions`] if the current user does not have
+ /// the required permissions.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ pub fn unpin(&self) -> Result<()> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_MESSAGES;
+
+ if !utils::user_has_perms(self.channel_id, req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ rest::unpin_message(self.channel_id.0, self.id.0)
+ }
+}
+
+impl From<Message> for MessageId {
+ /// Gets the Id of a `Message`.
+ fn from(message: Message) -> MessageId {
+ message.id
+ }
+}
diff --git a/src/model/channel/mod.rs b/src/model/channel/mod.rs
new file mode 100644
index 0000000..ff12084
--- /dev/null
+++ b/src/model/channel/mod.rs
@@ -0,0 +1,415 @@
+mod attachment;
+mod channel_id;
+mod embed;
+mod group;
+mod guild_channel;
+mod message;
+mod private_channel;
+mod reaction;
+
+pub use self::attachment::*;
+pub use self::channel_id::*;
+pub use self::embed::*;
+pub use self::group::*;
+pub use self::guild_channel::*;
+pub use self::message::*;
+pub use self::private_channel::*;
+pub use self::reaction::*;
+
+use std::fmt::{Display, Formatter, Result as FmtResult};
+use std::io::Read;
+use ::client::CACHE;
+use ::model::*;
+use ::utils::builder::{CreateMessage, GetMessages, Search};
+
+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`]: ../client/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)?;
+ match req!(map.get("type").and_then(|x| x.as_u64())) {
+ 0 | 2 => GuildChannel::decode(Value::Object(map))
+ .map(|x| Channel::Guild(Arc::new(RwLock::new(x)))),
+ 1 => PrivateChannel::decode(Value::Object(map))
+ .map(|x| Channel::Private(Arc::new(RwLock::new(x)))),
+ 3 => Group::decode(Value::Object(map))
+ .map(|x| Channel::Group(Arc::new(RwLock::new(x)))),
+ other => Err(Error::Decode("Expected value Channel type",
+ Value::U64(other))),
+ }
+ }
+
+ /// Deletes the inner channel.
+ ///
+ /// **Note**: There is no real function as _deleting_ a [`Group`]. The
+ /// closest functionality is leaving it.
+ ///
+ /// [`Group`]: struct.Group.html
+ pub fn delete(&self) -> Result<()> {
+ match *self {
+ Channel::Group(ref group) => {
+ let _ = group.read().unwrap().leave()?;
+ },
+ Channel::Guild(ref public_channel) => {
+ let _ = public_channel.read().unwrap().delete()?;
+ },
+ Channel::Private(ref private_channel) => {
+ let _ = private_channel.read().unwrap().delete()?;
+ },
+ }
+
+ 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.
+ ///
+ /// [`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)
+ }
+
+ /// Edits a [`Message`] in the channel given its Id.
+ ///
+ /// Message editing preserves all unchanged message data.
+ ///
+ /// Refer to the documentation for [`CreateMessage`] for more information
+ /// regarding message restrictions and requirements.
+ ///
+ /// **Note**: Requires that the current user be the author of the message.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::MessageTooLong`] if the content of the message
+ /// is over the [`the limit`], containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
+ /// [`Message`]: struct.Message.html
+ /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
+ #[inline]
+ pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
+ self.id().edit_message(message_id, f)
+ }
+
+ /// Gets a message from the channel.
+ ///
+ /// Requires the [Read Message History] permission.
+ ///
+ /// [Read Message History]: permissions/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::MessageId;
+ ///
+ /// let id = MessageId(81392407232380928);
+ ///
+ /// // Maximum is 100.
+ /// let _messages = channel.get_messages(|g| g.after(id).limit(100));
+ /// ```
+ ///
+ /// [Read Message History]: permissions/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`].
+ ///
+ /// [`Group`]: struct.Group.html
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`PrivateChannel`]: struct.PrivateChannel.html
+ pub fn id(&self) -> ChannelId {
+ match *self {
+ Channel::Group(ref group) => group.read().unwrap().channel_id,
+ Channel::Guild(ref channel) => channel.read().unwrap().id,
+ Channel::Private(ref channel) => channel.read().unwrap().id,
+ }
+ }
+
+ /// Sends a message with just the given message content in the channel.
+ ///
+ /// # 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.
+ ///
+ /// [`ChannelId`]: ../model/struct.ChannelId.html
+ /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
+ #[inline]
+ pub fn say(&self, content: &str) -> Result<Message> {
+ self.id().say(content)
+ }
+
+ /// Performs a search request to the API for the inner channel's
+ /// [`Message`]s.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ pub fn search<F>(&self, f: F) -> Result<SearchResult>
+ where F: FnOnce(Search) -> Search {
+ #[cfg(feature="cache")]
+ {
+ if CACHE.read().unwrap().user.bot {
+ return Err(Error::Client(ClientError::InvalidOperationAsBot));
+ }
+ }
+
+ self.id().search(f)
+ }
+
+ /// Sends a file along with optional message contents. The filename _must_
+ /// be specified.
+ ///
+ /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [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 {
+ self.id().send_file(file, filename, 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 [`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
+ #[inline]
+ pub fn send_message<F>(&self, f: F) -> Result<Message>
+ where F: FnOnce(CreateMessage) -> CreateMessage {
+ self.id().send_message(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) -> Result<()> {
+ self.id().unpin(message_id)
+ }
+}
+
+impl Display for Channel {
+ /// Formats the channel into a "mentioned" string.
+ ///
+ /// This will return a different format for each type of channel:
+ ///
+ /// - [`Group`]s: the generated name retrievable via [`Group::name`];
+ /// - [`PrivateChannel`]s: the recipient's name;
+ /// - [`GuildChannel`]s: a string mentioning the channel that users who can
+ /// see the channel can click on.
+ ///
+ /// [`Group`]: struct.Group.html
+ /// [`Group::name`]: struct.Group.html#method.name
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`PrivateChannel`]: struct.PrivateChannel.html
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ match *self {
+ Channel::Group(ref group) => {
+ Display::fmt(&group.read().unwrap().name(), f)
+ },
+ Channel::Guild(ref ch) => {
+ Display::fmt(&ch.read().unwrap().id.mention(), f)
+ },
+ Channel::Private(ref ch) => {
+ let channel = ch.read().unwrap();
+ let recipient = channel.recipient.read().unwrap();
+
+ Display::fmt(&recipient.name, f)
+ },
+ }
+ }
+}
+
+impl PermissionOverwrite {
+ #[doc(hidden)]
+ pub fn decode(value: Value) -> Result<PermissionOverwrite> {
+ let mut map = into_map(value)?;
+ let id = remove(&mut map, "id").and_then(decode_id)?;
+ let kind = remove(&mut map, "type").and_then(into_string)?;
+ let kind = match &*kind {
+ "member" => PermissionOverwriteType::Member(UserId(id)),
+ "role" => PermissionOverwriteType::Role(RoleId(id)),
+ _ => return Err(Error::Decode("Expected valid PermissionOverwrite type", Value::String(kind))),
+ };
+
+ Ok(PermissionOverwrite {
+ kind: kind,
+ allow: remove(&mut map, "allow").and_then(Permissions::decode)?,
+ deny: remove(&mut map, "deny").and_then(Permissions::decode)?,
+ })
+ }
+}
diff --git a/src/model/channel/private_channel.rs b/src/model/channel/private_channel.rs
new file mode 100644
index 0000000..ae64d7e
--- /dev/null
+++ b/src/model/channel/private_channel.rs
@@ -0,0 +1,309 @@
+use std::fmt::{Display, Formatter, Result as FmtResult};
+use std::io::Read;
+use ::client::CACHE;
+use ::model::*;
+use ::utils::builder::{CreateMessage, GetMessages, Search};
+
+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`]: ../client/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)
+ }
+
+ /// Broadcasts that the current user is typing to the recipient.
+ pub fn broadcast_typing(&self) -> Result<()> {
+ self.id.broadcast_typing()
+ }
+
+ /// 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)?;
+ let mut recipients = decode_array(remove(&mut map, "recipients")?,
+ User::decode)?;
+
+ Ok(PrivateChannel {
+ id: remove(&mut map, "id").and_then(ChannelId::decode)?,
+ kind: remove(&mut map, "type").and_then(ChannelType::decode)?,
+ last_message_id: opt(&mut map, "last_message_id", MessageId::decode)?,
+ last_pin_timestamp: opt(&mut map, "last_pin_timestamp", into_string)?,
+ recipient: Arc::new(RwLock::new(recipients.remove(0))),
+ })
+ }
+
+ /// 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) -> Result<Channel> {
+ 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)
+ }
+
+ /// Edits a [`Message`] in the channel given its Id.
+ ///
+ /// Message editing preserves all unchanged message data.
+ ///
+ /// Refer to the documentation for [`CreateMessage`] for more information
+ /// regarding message restrictions and requirements.
+ ///
+ /// **Note**: Requires that the current user be the author of the message.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::MessageTooLong`] if the content of the message
+ /// is over the [`the limit`], containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
+ /// [`Message`]: struct.Message.html
+ /// [`the limit`]: ../utils/builder/struct.CreateMessage.html#method.content
+ #[inline]
+ pub fn edit_message<F, M>(&self, message_id: M, f: F) -> Result<Message>
+ where F: FnOnce(CreateMessage) -> CreateMessage, M: Into<MessageId> {
+ self.id.edit_message(message_id, f)
+ }
+
+ /// Gets a message from the channel.
+ ///
+ /// Requires the [Read Message History] permission.
+ ///
+ /// [Read Message History]: permissions/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]: permissions/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)
+ }
+
+ /// Pins a [`Message`] to the channel.
+ ///
+ /// [`Message`]: struct.Message.html
+ #[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.
+ #[inline]
+ pub fn pins(&self) -> Result<Vec<Message>> {
+ self.id.pins()
+ }
+
+ /// Sends a message with just the given message content in the channel.
+ ///
+ /// # 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.
+ ///
+ /// [`ChannelId`]: ../model/struct.ChannelId.html
+ /// [`ClientError::MessageTooLong`]: enum.ClientError.html#variant.MessageTooLong
+ #[inline]
+ pub fn say(&self, content: &str) -> Result<Message> {
+ self.id.say(content)
+ }
+
+ /// Performs a search request to the API for the channel's [`Message`]s.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ pub fn search<F>(&self, f: F) -> Result<SearchResult>
+ where F: FnOnce(Search) -> Search {
+ #[cfg(feature="cache")]
+ {
+ if CACHE.read().unwrap().user.bot {
+ return Err(Error::Client(ClientError::InvalidOperationAsBot));
+ }
+ }
+
+ self.id.search(f)
+ }
+
+ /// Sends a file along with optional message contents. The filename _must_
+ /// be specified.
+ ///
+ /// Refer to [`ChannelId::send_file`] 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_file`]: struct.ChannelId.html#method.send_file
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [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 {
+ self.id.send_file(file, filename, 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 [`ClientError::MessageTooLong`] if the content of the message
+ /// is over the above limit, containing the number of unicode code points
+ /// over the limit.
+ ///
+ /// [`ClientError::MessageTooLong`]: ../client/enum.ClientError.html#variant.MessageTooLong
+ /// [`CreateMessage`]: ../utils/builder/struct.CreateMessage.html
+ /// [`Message`]: struct.Message.html
+ #[inline]
+ pub fn send_message<F: FnOnce(CreateMessage) -> CreateMessage>(&self, f: F) -> Result<Message> {
+ self.id.send_message(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) -> Result<()> {
+ self.id.unpin(message_id)
+ }
+}
+
+impl Display for PrivateChannel {
+ /// Formats the private channel, displaying the recipient's username.
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ f.write_str(&self.recipient.read().unwrap().name)
+ }
+}
diff --git a/src/model/channel/reaction.rs b/src/model/channel/reaction.rs
new file mode 100644
index 0000000..eaeb2cc
--- /dev/null
+++ b/src/model/channel/reaction.rs
@@ -0,0 +1,188 @@
+use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
+use ::client::{CACHE, rest};
+use ::internal::prelude::*;
+use ::model::*;
+
+impl Reaction {
+ /// Deletes the reaction, but only if the current user is the user who made
+ /// the reaction or has permission to.
+ ///
+ /// **Note**: Requires the [Manage Messages] permission, _if_ the current
+ /// user did not perform the reaction.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, then returns a
+ /// [`ClientError::InvalidPermissions`] if the current user does not have
+ /// the required [permissions].
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Manage Messages]: permissions/constant.MANAGE_MESSAGES.html
+ /// [permissions]: permissions
+ pub fn delete(&self) -> Result<()> {
+ let user_id = feature_cache! {{
+ let user = if self.user_id == CACHE.read().unwrap().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 !utils::user_has_perms(self.channel_id, req).unwrap_or(true) {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ user
+ } else {
+ Some(self.user_id.0)
+ }};
+
+ rest::delete_reaction(self.channel_id.0,
+ self.message_id.0,
+ user_id,
+ &self.emoji)
+ }
+
+ /// 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.
+ ///
+ /// **Note**: Requires the [Read Message History] permission.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::InvalidPermissions`] if the current user does
+ /// not have the required [permissions].
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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>)
+ -> Result<Vec<User>>
+ where R: Into<ReactionType>,
+ U: Into<UserId> {
+ rest::get_reaction_users(self.channel_id.0,
+ self.message_id.0,
+ &reaction_type.into(),
+ limit.unwrap_or(50),
+ after.map(|u| u.into().0))
+ }
+}
+
+/// The type of a [`Reaction`] sent.
+///
+/// [`Reaction`]: struct.Reaction.html
+#[derive(Clone, Debug)]
+pub enum ReactionType {
+ /// A reaction with a [`Guild`]s custom [`Emoji`], which is unique to the
+ /// guild.
+ ///
+ /// [`Emoji`]: struct.Emoji.html
+ /// [`Guild`]: struct.Guild.html
+ Custom {
+ /// The Id of the custom [`Emoji`].
+ ///
+ /// [`Emoji`]: struct.Emoji.html
+ id: EmojiId,
+ /// The name of the custom emoji. This is primarily used for decoration
+ /// and distinguishing the emoji client-side.
+ name: String,
+ },
+ /// A reaction with a twemoji.
+ Unicode(String),
+}
+
+impl ReactionType {
+ /// Creates a data-esque display of the type. This is not very useful for
+ /// displaying, as the primary client can not render it, but can be useful
+ /// for debugging.
+ ///
+ /// **Note**: This is mainly for use internally. There is otherwise most
+ /// likely little use for it.
+ pub fn as_data(&self) -> String {
+ match *self {
+ ReactionType::Custom { id, ref name } => {
+ format!("{}:{}", name, id)
+ },
+ ReactionType::Unicode(ref unicode) => unicode.clone(),
+ }
+ }
+
+ #[doc(hidden)]
+ pub fn decode(value: Value) -> Result<Self> {
+ let mut map = into_map(value)?;
+ let name = remove(&mut map, "name").and_then(into_string)?;
+
+ // Only custom emoji reactions (`ReactionType::Custom`) have an Id.
+ Ok(match opt(&mut map, "id", EmojiId::decode)? {
+ Some(id) => ReactionType::Custom {
+ id: id,
+ name: name,
+ },
+ None => ReactionType::Unicode(name),
+ })
+ }
+}
+
+impl From<Emoji> for ReactionType {
+ fn from(emoji: Emoji) -> ReactionType {
+ ReactionType::Custom {
+ id: emoji.id,
+ name: emoji.name,
+ }
+ }
+}
+
+impl From<String> for ReactionType {
+ fn from(unicode: String) -> ReactionType {
+ ReactionType::Unicode(unicode)
+ }
+}
+
+impl Display for ReactionType {
+ /// Formats the reaction type, displaying the associated emoji in a
+ /// way that clients can understand.
+ ///
+ /// If the type is a [custom][`ReactionType::Custom`] emoji, then refer to
+ /// the documentation for [emoji's formatter][`Emoji::fmt`] on how this is
+ /// displayed. Otherwise, if the type is a
+ /// [unicode][`ReactionType::Unicode`], then the inner unicode is displayed.
+ ///
+ /// [`Emoji::fmt`]: struct.Emoji.html#method.fmt
+ /// [`ReactionType::Custom`]: enum.ReactionType.html#variant.Custom
+ /// [`ReactionType::Unicode`]: enum.ReactionType.html#variant.Unicode
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ match *self {
+ ReactionType::Custom { id, ref name } => {
+ f.write_char('<')?;
+ f.write_char(':')?;
+ f.write_str(name)?;
+ f.write_char(':')?;
+ Display::fmt(&id, f)?;
+ f.write_char('>')
+ },
+ ReactionType::Unicode(ref unicode) => f.write_str(unicode),
+ }
+ }
+}
diff --git a/src/model/guild.rs b/src/model/guild.rs
deleted file mode 100644
index b3879b1..0000000
--- a/src/model/guild.rs
+++ /dev/null
@@ -1,2587 +0,0 @@
-use serde_json::builder::ObjectBuilder;
-use std::borrow::Cow;
-use std::cmp::Ordering;
-use std::collections::HashMap;
-use std::sync::{Arc, RwLock};
-use std::fmt;
-use super::utils::{
- decode_emojis,
- decode_members,
- decode_presences,
- decode_roles,
- decode_voice_states,
- into_map,
- into_string,
- opt,
- remove,
-};
-use super::*;
-use ::client::rest;
-use ::constants::LARGE_THRESHOLD;
-use ::internal::prelude::*;
-use ::utils::builder::{EditGuild, EditMember, EditRole, Search};
-use ::utils::decode_array;
-
-#[cfg(feature="cache")]
-use std::mem;
-
-#[cfg(feature="cache")]
-use ::client::CACHE;
-#[cfg(feature="cache")]
-use ::utils::Colour;
-
-impl From<PartialGuild> for GuildContainer {
- fn from(guild: PartialGuild) -> GuildContainer {
- GuildContainer::Guild(guild)
- }
-}
-
-impl From<GuildId> for GuildContainer {
- fn from(guild_id: GuildId) -> GuildContainer {
- GuildContainer::Id(guild_id)
- }
-}
-
-impl From<u64> for GuildContainer {
- fn from(id: u64) -> GuildContainer {
- GuildContainer::Id(GuildId(id))
- }
-}
-
-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
- #[cfg(feature="cache")]
- pub fn delete(&self) -> Result<()> {
- match self.find_guild_id() {
- Some(guild_id) => rest::delete_emoji(guild_id.0, self.id.0),
- None => Err(Error::Client(ClientError::ItemMissing)),
- }
- }
-
- /// 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
- #[cfg(feature="cache")]
- pub fn edit(&mut self, name: &str) -> Result<()> {
- match self.find_guild_id() {
- Some(guild_id) => {
- let map = ObjectBuilder::new()
- .insert("name", name)
- .build();
-
- match rest::edit_emoji(guild_id.0, self.id.0, &map) {
- Ok(emoji) => {
- mem::replace(self, emoji);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
- },
- None => Err(Error::Client(ClientError::ItemMissing)),
- }
- }
-
- /// Finds the [`Guild`] that owns the emoji by looking through the Cache.
- ///
- /// [`Guild`]: struct.Guild.html
- #[cfg(feature="cache")]
- pub fn find_guild_id(&self) -> Option<GuildId> {
- for guild in CACHE.read().unwrap().guilds.values() {
- let guild = guild.read().unwrap();
-
- if guild.emojis.contains_key(&self.id) {
- return Some(guild.id);
- }
- }
-
- None
- }
-
- /// Generates a URL to the emoji's image.
- #[inline]
- pub fn url(&self) -> String {
- format!(cdn!("/emojis/{}.png"), self.id)
- }
-}
-
-impl fmt::Display for Emoji {
- /// Formats the emoji into a string that will cause Discord clients to
- /// render the emoji.
- ///
- /// This is in the format of: `<:NAME:EMOJI_ID>`.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_str("<:")?;
- f.write_str(&self.name)?;
- fmt::Write::write_char(f, ':')?;
- fmt::Display::fmt(&self.id, f)?;
- fmt::Write::write_char(f, '>')
- }
-}
-
-impl fmt::Display for EmojiId {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.0, f)
- }
-}
-
-impl From<Emoji> for EmojiId {
- /// Gets the Id of an `Emoji`.
- fn from(emoji: Emoji) -> EmojiId {
- emoji.id
- }
-}
-
-impl GuildInfo {
- /// Returns the formatted URL of the guild's icon, if the guild has an icon.
- pub fn icon_url(&self) -> Option<String> {
- self.icon.as_ref().map(|icon|
- format!(cdn!("/icons/{}/{}.webp"), self.id, icon))
- }
-}
-
-impl InviteGuild {
- /// Returns the formatted URL of the guild's splash image, if one exists.
- pub fn splash_url(&self) -> Option<String> {
- self.icon.as_ref().map(|icon|
- format!(cdn!("/splashes/{}/{}.webp"), self.id, icon))
- }
-}
-
-impl Guild {
- #[cfg(feature="cache")]
- fn has_perms(&self, mut permissions: Permissions) -> Result<bool> {
- let member = match self.members.get(&CACHE.read().unwrap().user.id) {
- Some(member) => member,
- None => return Err(Error::Client(ClientError::ItemMissing)),
- };
-
- let perms = self.permissions_for(ChannelId(self.id.0), member.user.read().unwrap().id);
- permissions.remove(perms);
-
- Ok(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 [`ClientError::InvalidPermissions`] if the current user does
- /// not have permission to perform bans.
- ///
- /// Returns a [`ClientError::DeleteMessageDaysAmount`] if the number of
- /// days' worth of messages to delete is over the maximum.
- ///
- /// [`ClientError::DeleteMessageDaysAmount`]: ../client/enum.ClientError.html#variant.DeleteMessageDaysAmount
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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>>(&self, user: U, delete_message_days: u8)
- -> Result<()> {
- if delete_message_days > 7 {
- return Err(Error::Client(ClientError::DeleteMessageDaysAmount(delete_message_days)));
- }
-
- #[cfg(feature="cache")]
- {
- let req = permissions::BAN_MEMBERS;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.ban(user, delete_message_days)
- }
-
- /// Retrieves a list of [`Ban`]s for the guild.
- ///
- /// **Note**: Requires the [Ban Members] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`Ban`]: struct.Ban.html
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn bans(&self) -> Result<Vec<Ban>> {
- #[cfg(feature="cache")]
- {
- let req = permissions::BAN_MEMBERS;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.get_bans()
- }
-
- /// Creates a guild with the data provided.
- ///
- /// Only a [`PartialGuild`] will be immediately returned, and a full
- /// [`Guild`] will be received over a [`Shard`].
- ///
- /// **Note**: This endpoint is usually only available for user accounts.
- /// Refer to Discord's information for the endpoint [here][whitelist] for
- /// more information. If you require this as a bot, re-think what you are
- /// doing and if it _really_ needs to be doing this.
- ///
- /// # Examples
- ///
- /// Create a guild called `"test"` in the [US West region] with no icon:
- ///
- /// ```rust,ignore
- /// use serenity::model::{Guild, Region};
- ///
- /// let _guild = Guild::create_guild("test", Region::UsWest, None);
- /// ```
- ///
- /// [`Guild`]: struct.Guild.html
- /// [`PartialGuild`]: struct.PartialGuild.html
- /// [`Shard`]: ../client/gateway/struct.Shard.html
- /// [US West region]: enum.Region.html#variant.UsWest
- /// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
- pub fn create(name: &str, region: Region, icon: Option<&str>) -> Result<PartialGuild> {
- let map = ObjectBuilder::new()
- .insert("icon", icon)
- .insert("name", name)
- .insert("region", region.name())
- .build();
-
- rest::create_guild(&map)
- }
-
- /// 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);
- /// ```
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`Channel`]: struct.Channel.html
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- pub fn create_channel(&mut self, name: &str, kind: ChannelType) -> Result<GuildChannel> {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_CHANNELS;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.create_channel(name, kind)
- }
-
- /// 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`]: ../utils/builder/struct.EditProfile.html#method.avatar
- /// [`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) -> Result<Emoji> {
- self.id.create_emoji(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) -> Result<()>
- where I: Into<IntegrationId> {
- self.id.create_integration(integration_id, 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 [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`Context::create_role`]: ../client/struct.Context.html#method.create_role
- /// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- pub fn create_role<F>(&self, f: F) -> Result<Role>
- where F: FnOnce(EditRole) -> EditRole {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_ROLES;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.create_role(f)
- }
-
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Guild> {
- let mut map = into_map(value)?;
-
- let id = remove(&mut map, "id").and_then(GuildId::decode)?;
-
- let channels = {
- let mut channels = HashMap::new();
-
- let vals = decode_array(remove(&mut map, "channels")?,
- |v| GuildChannel::decode_guild(v, id))?;
-
- for channel in vals {
- channels.insert(channel.id, Arc::new(RwLock::new(channel)));
- }
-
- channels
- };
-
- Ok(Guild {
- afk_channel_id: opt(&mut map, "afk_channel_id", ChannelId::decode)?,
- afk_timeout: req!(remove(&mut map, "afk_timeout")?.as_u64()),
- channels: channels,
- default_message_notifications: req!(remove(&mut map, "default_message_notifications")?.as_u64()),
- emojis: remove(&mut map, "emojis").and_then(decode_emojis)?,
- features: remove(&mut map, "features").and_then(|v| decode_array(v, Feature::decode_str))?,
- icon: opt(&mut map, "icon", into_string)?,
- id: id,
- joined_at: remove(&mut map, "joined_at").and_then(into_string)?,
- large: req!(remove(&mut map, "large")?.as_bool()),
- member_count: req!(remove(&mut map, "member_count")?.as_u64()),
- members: remove(&mut map, "members").and_then(decode_members)?,
- mfa_level: req!(remove(&mut map, "mfa_level")?.as_u64()),
- name: remove(&mut map, "name").and_then(into_string)?,
- owner_id: remove(&mut map, "owner_id").and_then(UserId::decode)?,
- presences: remove(&mut map, "presences").and_then(decode_presences)?,
- region: remove(&mut map, "region").and_then(into_string)?,
- roles: remove(&mut map, "roles").and_then(decode_roles)?,
- splash: opt(&mut map, "splash", into_string)?,
- verification_level: remove(&mut map, "verification_level").and_then(VerificationLevel::decode)?,
- voice_states: remove(&mut map, "voice_states").and_then(decode_voice_states)?,
- })
- }
-
- /// 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 [`ClientError::InvalidUser`]
- /// if the current user is not the guild owner.
- ///
- /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser
- pub fn delete(&self) -> Result<PartialGuild> {
- #[cfg(feature="cache")]
- {
- if self.owner_id != CACHE.read().unwrap().user.id {
- let req = permissions::MANAGE_GUILD;
-
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.delete()
- }
-
- /// 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) -> Result<()> {
- self.id.delete_emoji(emoji_id)
- }
-
- /// 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) -> Result<()> {
- self.id.delete_integration(integration_id)
- }
-
- /// 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) -> Result<()> {
- self.id.delete_role(role_id)
- }
-
- /// 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 [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`Context::edit_guild`]: ../client/struct.Context.html#method.edit_guild
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(EditGuild) -> EditGuild {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_GUILD;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- match self.id.edit(f) {
- Ok(guild) => {
- self.afk_channel_id = guild.afk_channel_id;
- self.afk_timeout = guild.afk_timeout;
- self.default_message_notifications = guild.default_message_notifications;
- self.emojis = guild.emojis;
- self.features = guild.features;
- self.icon = guild.icon;
- self.mfa_level = guild.mfa_level;
- self.name = guild.name;
- self.owner_id = guild.owner_id;
- self.region = guild.region;
- self.roles = guild.roles;
- self.splash = guild.splash;
- self.verification_level = guild.verification_level;
-
- Ok(())
- },
- Err(why) => Err(why),
- }
- }
-
- /// 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) -> Result<Emoji> {
- self.id.edit_emoji(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) -> Result<()>
- where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
- self.id.edit_member(user_id, 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 [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to change their own
- /// nickname.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
- pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
- #[cfg(feature="cache")]
- {
- let req = permissions::CHANGE_NICKNAME;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.edit_nickname(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) -> Result<Role>
- where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
- self.id.edit_role(role_id, f)
- }
-
- /// Gets a partial amount of guild data by its Id.
- ///
- /// Requires that the current user be in the guild.
- #[inline]
- pub fn get<G: Into<GuildId>>(guild_id: G) -> Result<PartialGuild> {
- guild_id.into().get()
- }
-
- /// Gets a list of the guild's bans.
- ///
- /// Requires the [Ban Members] permission.
- #[inline]
- pub fn get_bans(&self) -> Result<Vec<Ban>> {
- self.id.get_bans()
- }
-
- /// Gets all of the guild's channels over the REST API.
- ///
- /// [`Guild`]: struct.Guild.html
- #[inline]
- pub fn get_channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
- self.id.get_channels()
- }
-
- /// Gets an emoji in the guild by Id.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- #[inline]
- pub fn get_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<Emoji> {
- self.id.get_emoji(emoji_id)
- }
-
- /// Gets a list of all of the guild's emojis.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- #[inline]
- pub fn get_emojis(&self) -> Result<Vec<Emoji>> {
- self.id.get_emojis()
- }
-
- /// Gets all integration of the guild.
- ///
- /// This performs a request over the REST API.
- #[inline]
- pub fn get_integrations(&self) -> Result<Vec<Integration>> {
- self.id.get_integrations()
- }
-
- /// Retrieves the active invites for the guild.
- ///
- /// **Note**: Requires the [Manage Guild] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn get_invites(&self) -> Result<Vec<RichInvite>> {
- #[cfg(feature="cache")]
- {
- let req = permissions::MANAGE_GUILD;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.get_invites()
- }
-
- /// Gets a user's [`Member`] for the guild by Id.
- ///
- /// [`Guild`]: struct.Guild.html
- /// [`Member`]: struct.Member.html
- #[inline]
- pub fn get_member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
- self.id.get_member(user_id)
- }
-
- /// 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 get_members<U>(&self, limit: Option<u64>, after: Option<U>)
- -> Result<Vec<Member>> where U: Into<UserId> {
- self.id.get_members(limit, after)
- }
-
- /// Retrieves the first [`Member`] found that matches the name - with an
- /// optional discriminator - provided.
- ///
- /// Searching with a discriminator given is the most precise form of lookup,
- /// as no two people can share the same username *and* discriminator.
- ///
- /// If a member can not be found by username or username#discriminator,
- /// then a search will be done for the nickname. When searching by nickname,
- /// the hash (`#`) and everything after it is included in the search.
- ///
- /// The following are valid types of searches:
- ///
- /// - **username**: "zey"
- /// - **username and discriminator**: "zey#5479"
- /// - **nickname**: "zeyla" or "zeylas#nick"
- ///
- /// [`Member`]: struct.Member.html
- pub fn get_member_named(&self, name: &str) -> Option<&Member> {
- let hash_pos = name.find('#');
-
- let (name, discrim) = if let Some(pos) = hash_pos {
- let split = name.split_at(pos);
-
- (split.0, Some(split.1))
- } else {
- (&name[..], None)
- };
-
- self.members
- .values()
- .find(|member| {
- let name_matches = member.user.read().unwrap().name == name;
- let discrim_matches = match discrim {
- Some(discrim) => member.user.read().unwrap().discriminator == discrim,
- None => true,
- };
-
- name_matches && discrim_matches
- }).or_else(|| self.members.values().find(|member| {
- member.nick.as_ref().map_or(false, |nick| nick == name)
- }))
- }
-
- /// 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 [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`GuildPrune`]: struct.GuildPrune.html
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
- pub fn get_prune_count(&self, days: u16) -> Result<GuildPrune> {
- #[cfg(feature="cache")]
- {
- let req = permissions::KICK_MEMBERS;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.get_prune_count(days)
- }
-
- /// Retrieves the guild's webhooks.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
- self.id.get_webhooks()
- }
-
- /// Returns the formatted URL of the guild's icon, if one exists.
- pub fn icon_url(&self) -> Option<String> {
- self.icon.as_ref().map(|icon|
- format!(cdn!("/icons/{}/{}.webp"), self.id, icon))
- }
-
- /// Checks if the guild is 'large'. A guild is considered large if it has
- /// more than 250 members.
- #[inline]
- pub fn is_large(&self) -> bool {
- 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) -> Result<()> {
- self.id.kick(user_id)
- }
-
- /// Leaves the guild.
- #[inline]
- pub fn leave(&self) -> Result<PartialGuild> {
- self.id.leave()
- }
-
- /// 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) -> Result<()>
- where C: Into<ChannelId>, U: Into<UserId> {
- self.id.move_member(user_id, channel_id)
- }
-
- /// Calculate a [`User`]'s permissions in a given channel in the guild.
- ///
- /// [`User`]: struct.User.html
- pub fn permissions_for<C, U>(&self, channel_id: C, user_id: U)
- -> Permissions where C: Into<ChannelId>, U: Into<UserId> {
- use super::permissions::*;
-
- let user_id = user_id.into();
-
- // The owner has all permissions in all cases.
- if user_id == self.owner_id {
- return Permissions::all();
- }
-
- let channel_id = channel_id.into();
-
- // Start by retrieving the @everyone role's permissions.
- let everyone = match self.roles.get(&RoleId(self.id.0)) {
- Some(everyone) => everyone,
- None => {
- error!("(╯°□°)╯︵ ┻━┻ @everyone role ({}) missing in '{}'",
- self.id,
- self.name);
-
- return Permissions::empty();
- },
- };
-
- // Create a base set of permissions, starting with `@everyone`s.
- let mut permissions = everyone.permissions;
-
- let member = match self.members.get(&user_id) {
- Some(member) => member,
- None => return everyone.permissions,
- };
-
- for &role in &member.roles {
- if let Some(role) = self.roles.get(&role) {
- permissions |= role.permissions;
- } else {
- warn!("(╯°□°)╯︵ ┻━┻ {} on {} has non-existent role {:?}",
- member.user.read().unwrap().id,
- self.id,
- role);
- }
- }
-
- // Administrators have all permissions in any channel.
- if permissions.contains(ADMINISTRATOR) {
- return Permissions::all();
- }
-
- if let Some(channel) = self.channels.get(&channel_id) {
- let channel = channel.read().unwrap();
-
- // If this is a text channel, then throw out voice permissions.
- if channel.kind == ChannelType::Text {
- permissions &= !(CONNECT | SPEAK | MUTE_MEMBERS |
- DEAFEN_MEMBERS | MOVE_MEMBERS | USE_VAD);
- }
-
- // Apply the permission overwrites for the channel for each of the
- // overwrites that - first - applies to the member's roles, and then
- // the member itself.
- //
- // First apply the denied permission overwrites for each, then apply
- // the allowed.
-
- // Roles
- for overwrite in &channel.permission_overwrites {
- if let PermissionOverwriteType::Role(role) = overwrite.kind {
- if !member.roles.contains(&role) || role.0 == self.id.0 {
- continue;
- }
-
- permissions = (permissions & !overwrite.deny) | overwrite.allow;
- }
- }
-
- // Member
- for overwrite in &channel.permission_overwrites {
- if PermissionOverwriteType::Member(user_id) != overwrite.kind {
- continue;
- }
-
- permissions = (permissions & !overwrite.deny) | overwrite.allow;
- }
- } else {
- warn!("(╯°□°)╯︵ ┻━┻ Guild {} does not contain channel {}",
- self.id,
- channel_id);
- }
-
- // The default channel is always readable.
- if channel_id.0 == self.id.0 {
- permissions |= READ_MESSAGES;
- }
-
- // No SEND_MESSAGES => no message-sending-related actions
- // If the member does not have the `SEND_MESSAGES` permission, then
- // throw out message-able permissions.
- if !permissions.contains(SEND_MESSAGES) {
- permissions &= !(SEND_TTS_MESSAGES |
- MENTION_EVERYONE |
- EMBED_LINKS |
- ATTACH_FILES);
- }
-
- // If the member does not have the `READ_MESSAGES` permission, then
- // throw out actionable permissions.
- if !permissions.contains(READ_MESSAGES) {
- permissions &= KICK_MEMBERS | BAN_MEMBERS | ADMINISTRATOR |
- MANAGE_GUILD | CHANGE_NICKNAME | MANAGE_NICKNAMES;
- }
-
- permissions
- }
-
- /// Performs a search request to the API for the guild's [`Message`]s.
- ///
- /// This will search all of the guild's [`Channel`]s at once, that you have
- /// the [Read Message History] permission to. Use [`search_channels`] to
- /// specify a list of [channel][`GuildChannel`]s to search, where all other
- /// channels will be excluded.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Channel`]: enum.Channel.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- /// [`search_channels`]: #method.search_channels
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
- #[cfg(feature="cache")]
- {
- if CACHE.read().unwrap().user.bot {
- return Err(Error::Client(ClientError::InvalidOperationAsBot));
- }
- }
-
- self.id.search(f)
- }
-
- /// Performs a search request to the API for the guild's [`Message`]s in
- /// given channels.
- ///
- /// This will search all of the messages in the guild's provided
- /// [`Channel`]s by Id that you have the [Read Message History] permission
- /// to. Use [`search`] to search all of a guild's [channel][`GuildChannel`]s
- /// at once.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Channel`]: enum.Channel.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- /// [`search`]: #method.search
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- pub fn search_channels<F>(&self, channel_ids: &[ChannelId], f: F)
- -> Result<SearchResult> where F: FnOnce(Search) -> Search {
- #[cfg(feature="cache")]
- {
- if CACHE.read().unwrap().user.bot {
- return Err(Error::Client(ClientError::InvalidOperationAsBot));
- }
- }
-
- self.id.search_channels(channel_ids, f)
- }
-
- /// Returns the formatted URL of the guild's splash image, if one exists.
- pub fn splash_url(&self) -> Option<String> {
- self.icon.as_ref().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: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- self.id.start_integration_sync(integration_id)
- }
-
- /// 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 [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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) -> Result<GuildPrune> {
- #[cfg(feature="cache")]
- {
- let req = permissions::KICK_MEMBERS;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.start_prune(days)
- }
-
- /// Unbans the given [`User`] from the guild.
- ///
- /// **Note**: Requires the [Ban Members] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
- #[cfg(feature="cache")]
- {
- let req = permissions::BAN_MEMBERS;
-
- if !self.has_perms(req)? {
- return Err(Error::Client(ClientError::InvalidPermissions(req)));
- }
- }
-
- self.id.unban(user_id)
- }
-}
-
-impl GuildId {
- /// Converts the guild Id into the default channel's Id.
- #[inline]
- pub fn as_channel_id(&self) -> ChannelId {
- ChannelId(self.0)
- }
-
- /// 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
- /// use serenity::model::GuildId;
- ///
- /// // assuming a `user` has already been bound
- /// let _ = GuildId(81384788765712384).ban(user, 4);
- /// ```
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::DeleteMessageDaysAmount`] if the number of
- /// days' worth of messages to delete is over the maximum.
- ///
- /// [`ClientError::DeleteMessageDaysAmount`]: ../client/enum.ClientError.html#variant.DeleteMessageDaysAmount
- /// [`Guild::ban`]: struct.Guild.html#method.ban
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn ban<U: Into<UserId>>(&self, user: U, delete_message_days: u8)
- -> Result<()> {
- if delete_message_days > 7 {
- return Err(Error::Client(ClientError::DeleteMessageDaysAmount(delete_message_days)));
- }
-
- rest::ban_user(self.0, user.into().0, delete_message_days)
- }
-
- /// Creates a [`GuildChannel`] in the the guild.
- ///
- /// Refer to [`rest::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, GuildId};
- ///
- /// let _channel = GuildId(7).create_channel("test", ChannelType::Voice);
- /// ```
- ///
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`rest::create_channel`]: ../client/rest/fn.create_channel.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- pub fn create_channel(&self, name: &str, kind: ChannelType) -> Result<GuildChannel> {
- let map = ObjectBuilder::new()
- .insert("name", name)
- .insert("type", kind.name())
- .build();
-
- rest::create_channel(self.0, &map)
- }
-
- /// 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`]: ../utils/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
- pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
- let map = ObjectBuilder::new()
- .insert("name", name)
- .insert("image", image)
- .build();
-
- rest::create_emoji(self.0, &map)
- }
-
- /// Creates an integration for the guild.
- ///
- /// Requires the [Manage Guild] permission.
- ///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn create_integration<I>(&self, integration_id: I, kind: &str)
- -> Result<()> where I: Into<IntegrationId> {
- let integration_id = integration_id.into();
- let map = ObjectBuilder::new()
- .insert("id", integration_id.0)
- .insert("type", kind)
- .build();
-
- rest::create_guild_integration(self.0, integration_id.0, &map)
- }
-
- /// 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.
- ///
- /// [`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) -> Result<Role> {
- rest::create_role(self.0, &f(EditRole::default()).0.build())
- }
-
- /// Deletes the current guild if the current account is the owner of the
- /// guild.
- ///
- /// Refer to [`Guild::delete`] for more information.
- ///
- /// **Note**: Requires the current user to be the owner of the guild.
- ///
- /// [`Guild::delete`]: struct.Guild.html#method.delete
- #[inline]
- pub fn delete(&self) -> Result<PartialGuild> {
- rest::delete_guild(self.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) -> Result<()> {
- rest::delete_emoji(self.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) -> Result<()> {
- rest::delete_guild_integration(self.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) -> Result<()> {
- rest::delete_role(self.0, role_id.into().0)
- }
-
- /// Edits the current guild with new data where specified.
- ///
- /// Refer to [`Guild::edit`] for more information.
- ///
- /// **Note**: Requires the current user to have the [Manage Guild]
- /// permission.
- ///
- /// [`Guild::edit`]: struct.Guild.html#method.edit
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- #[inline]
- pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&mut self, f: F) -> Result<PartialGuild> {
- rest::edit_guild(self.0, &f(EditGuild::default()).0.build())
- }
-
- /// 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
- pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
- let map = ObjectBuilder::new().insert("name", name).build();
-
- rest::edit_emoji(self.0, emoji_id.into().0, &map)
- }
-
- /// 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) -> Result<()>
- where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
- rest::edit_member(self.0, user_id.into().0, &f(EditMember::default()).0.build())
- }
-
- /// Edits the current user's nickname for the guild.
- ///
- /// Pass `None` to reset the nickname.
- ///
- /// Requires the [Change Nickname] permission.
- ///
- /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
- #[inline]
- pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
- rest::edit_nickname(self.0, new_nickname)
- }
-
- /// Edits a [`Role`], optionally setting its new fields.
- ///
- /// Requires the [Manage Roles] permission.
- ///
- /// # Examples
- ///
- /// Make a role hoisted:
- ///
- /// ```rust,ignore
- /// use serenity::model::{GuildId, RoleId};
- ///
- /// GuildId(7).edit_role(RoleId(8), |r| r.hoist(true));
- /// ```
- ///
- /// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- #[inline]
- pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role>
- where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
- rest::edit_role(self.0, role_id.into().0, &f(EditRole::default()).0.build())
- }
-
- /// Search the cache for the guild.
- #[cfg(feature="cache")]
- pub fn find(&self) -> Option<Arc<RwLock<Guild>>> {
- CACHE.read().unwrap().get_guild(*self)
- }
-
- /// Requests the guild over REST.
- ///
- /// Note that this will not be a complete guild, as REST does not send
- /// all data with a guild retrieval.
- #[inline]
- pub fn get(&self) -> Result<PartialGuild> {
- rest::get_guild(self.0)
- }
-
- /// Gets a list of the guild's bans.
- ///
- /// Requires the [Ban Members] permission.
- ///
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- #[inline]
- pub fn get_bans(&self) -> Result<Vec<Ban>> {
- rest::get_bans(self.0)
- }
-
- /// Gets all of the guild's channels over the REST API.
- ///
- /// [`Guild`]: struct.Guild.html
- pub fn get_channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
- let mut channels = HashMap::new();
-
- for channel in rest::get_channels(self.0)? {
- channels.insert(channel.id, channel);
- }
-
- Ok(channels)
- }
-
- /// Gets an emoji in the guild by Id.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- #[inline]
- pub fn get_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<Emoji> {
- rest::get_emoji(self.0, emoji_id.into().0)
- }
-
- /// Gets a list of all of the guild's emojis.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- #[inline]
- pub fn get_emojis(&self) -> Result<Vec<Emoji>> {
- rest::get_emojis(self.0)
- }
-
- /// Gets all integration of the guild.
- ///
- /// This performs a request over the REST API.
- #[inline]
- pub fn get_integrations(&self) -> Result<Vec<Integration>> {
- rest::get_guild_integrations(self.0)
- }
-
- /// Gets all of the guild's invites.
- ///
- /// Requires the [Manage Guild] permission.
- ///
- /// [Manage Guild]: permissions/struct.MANAGE_GUILD.html
- #[inline]
- pub fn get_invites(&self) -> Result<Vec<RichInvite>> {
- rest::get_guild_invites(self.0)
- }
-
- /// Gets a user's [`Member`] for the guild by Id.
- ///
- /// [`Guild`]: struct.Guild.html
- /// [`Member`]: struct.Member.html
- #[inline]
- pub fn get_member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
- rest::get_member(self.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 get_members<U>(&self, limit: Option<u64>, after: Option<U>)
- -> Result<Vec<Member>> where U: Into<UserId> {
- rest::get_guild_members(self.0, limit, after.map(|x| x.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
- pub fn get_prune_count(&self, days: u16) -> Result<GuildPrune> {
- let map = ObjectBuilder::new().insert("days", days).build();
-
- rest::get_guild_prune_count(self.0, &map)
- }
-
- /// Retrieves the guild's webhooks.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
- rest::get_guild_webhooks(self.0)
- }
-
- /// 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) -> Result<()> {
- rest::kick_member(self.0, user_id.into().0)
- }
-
- /// Leaves the guild.
- #[inline]
- pub fn leave(&self) -> Result<PartialGuild> {
- rest::leave_guild(self.0)
- }
-
- /// Moves a member to a specific voice channel.
- ///
- /// Requires the [Move Members] permission.
- ///
- /// [Move Members]: permissions/constant.MOVE_MEMBERS.html
- pub fn move_member<C, U>(&self, user_id: U, channel_id: C)
- -> Result<()> where C: Into<ChannelId>, U: Into<UserId> {
- let map = ObjectBuilder::new().insert("channel_id", channel_id.into().0).build();
-
- rest::edit_member(self.0, user_id.into().0, &map)
- }
-
- /// Performs a search request to the API for the guild's [`Message`]s.
- ///
- /// This will search all of the guild's [`Channel`]s at once, that you have
- /// the [Read Message History] permission to. Use [`search_channels`] to
- /// specify a list of [channel][`GuildChannel`]s to search, where all other
- /// channels will be excluded.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// [`Channel`]: enum.Channel.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- /// [`search_channels`]: #method.search_channels
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- #[inline]
- pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
- rest::search_guild_messages(self.0, &[], f(Search::default()).0)
- }
-
- /// Performs a search request to the API for the guild's [`Message`]s in
- /// given channels.
- ///
- /// Refer to [`Guild::search_channels`] for more information.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// [`Guild::search_channels`]: struct.Guild.html#method.search_channels
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- pub fn search_channels<F>(&self, channel_ids: &[ChannelId], f: F)
- -> Result<SearchResult> where F: FnOnce(Search) -> Search {
- let ids = channel_ids.iter().map(|x| x.0).collect::<Vec<u64>>();
-
- rest::search_guild_messages(self.0, &ids, f(Search::default()).0)
- }
-
- /// 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: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- rest::start_integration_sync(self.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.
- ///
- /// [`GuildPrune`]: struct.GuildPrune.html
- /// [`Member`]: struct.Member.html
- /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
- #[inline]
- pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
- rest::start_guild_prune(self.0, &ObjectBuilder::new().insert("days", days).build())
- }
-
- /// 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) -> Result<()> {
- rest::remove_ban(self.0, user_id.into().0)
- }
-}
-
-impl fmt::Display for GuildId {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.0, f)
- }
-}
-
-impl From<PartialGuild> for GuildId {
- /// Gets the Id of a partial guild.
- fn from(guild: PartialGuild) -> GuildId {
- guild.id
- }
-}
-
-impl From<GuildInfo> for GuildId {
- /// Gets the Id of Guild information struct.
- fn from(guild_info: GuildInfo) -> GuildId {
- guild_info.id
- }
-}
-
-impl From<InviteGuild> for GuildId {
- /// Gets the Id of Invite Guild struct.
- fn from(invite_guild: InviteGuild) -> GuildId {
- invite_guild.id
- }
-}
-
-impl From<Guild> for GuildId {
- /// Gets the Id of Guild.
- fn from(live_guild: Guild) -> GuildId {
- live_guild.id
- }
-}
-
-impl From<Integration> for IntegrationId {
- /// Gets the Id of integration.
- fn from(integration: Integration) -> IntegrationId {
- integration.id
- }
-}
-
-impl Member {
- /// Adds a [`Role`] to the member, editing its roles in-place if the request
- /// was successful.
- ///
- /// **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) -> Result<()> {
- let role_id = role_id.into();
-
- if self.roles.contains(&role_id) {
- return Ok(());
- }
-
- let guild_id = self.find_guild()?;
-
- match rest::add_member_role(guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
- Ok(()) => {
- self.roles.push(role_id);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
- }
-
- /// Adds one or multiple [`Role`]s to the member, editing
- /// its roles in-place if the request was successful.
- ///
- /// **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]) -> Result<()> {
- let guild_id = self.find_guild()?;
- self.roles.extend_from_slice(role_ids);
-
- let map = EditMember::default().roles(&self.roles).0.build();
-
- match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map) {
- Ok(()) => Ok(()),
- Err(why) => {
- self.roles.retain(|r| !role_ids.contains(r));
-
- Err(why)
- }
- }
- }
-
- /// Ban the member from its guild, deleting the last X number of
- /// days' worth of messages.
- ///
- /// **Note**: Requires the [Ban Members] role.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::GuildNotFound`] if the guild could not be
- /// found.
- ///
- /// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound
- ///
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- #[cfg(feature="cache")]
- pub fn ban(&self, delete_message_days: u8) -> Result<()> {
- rest::ban_user(self.find_guild()?.0, self.user.read().unwrap().id.0, delete_message_days)
- }
-
- /// Determines the member's colour.
- #[cfg(feature="cache")]
- pub fn colour(&self) -> Option<Colour> {
- let guild_id = match self.find_guild() {
- Ok(guild_id) => guild_id,
- Err(_) => return None,
- };
-
- let cache = CACHE.read().unwrap();
- let guild = match cache.guilds.get(&guild_id) {
- Some(guild) => guild.read().unwrap(),
- None => return None,
- };
-
- let mut roles = self.roles
- .iter()
- .filter_map(|role_id| guild.roles.get(role_id))
- .collect::<Vec<&Role>>();
- roles.sort_by(|a, b| b.cmp(a));
-
- let default = Colour::default();
-
- roles.iter().find(|r| r.colour.0 != default.0).map(|r| r.colour)
- }
-
- #[doc(hidden)]
- pub fn decode_guild(guild_id: GuildId, mut value: Value) -> Result<Member> {
- if let Some(v) = value.as_object_mut() {
- v.insert("guild_id".to_owned(), Value::U64(guild_id.0));
- }
-
- Self::decode(value)
- }
-
- /// Calculates the member's display name.
- ///
- /// The nickname takes priority over the member's username if it exists.
- #[inline]
- pub fn display_name(&self) -> Cow<String> {
- self.nick.as_ref()
- .map(Cow::Borrowed)
- .unwrap_or_else(|| Cow::Owned(self.user.read().unwrap().name.clone()))
- }
-
- /// Returns the DiscordTag of a Member, taking possible nickname into account.
- #[inline]
- pub fn distinct(&self) -> String {
- format!("{}#{}", self.display_name(), self.user.read().unwrap().discriminator)
- }
-
- /// Edits the member with the given data. See [`Context::edit_member`] for
- /// more information.
- ///
- /// See [`EditMember`] for the permission(s) required for separate builder
- /// methods, as well as usage of this.
- ///
- /// [`Context::edit_member`]: ../client/struct.Context.html#method.edit_member
- /// [`EditMember`]: ../builder/struct.EditMember.html
- #[cfg(feature="cache")]
- pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> {
- let guild_id = self.find_guild()?;
- let map = f(EditMember::default()).0.build();
-
- rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map)
- }
-
- /// Finds the Id of the [`Guild`] that the member is in.
- ///
- /// If some value is present in [`guild_id`], then that value is returned.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::GuildNotFound`] if the guild could not be
- /// found.
- ///
- /// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound
- /// [`Guild`]: struct.Guild.html
- /// [`guild_id`]: #structfield.guild_id
- #[cfg(feature="cache")]
- pub fn find_guild(&self) -> Result<GuildId> {
- if let Some(guild_id) = self.guild_id {
- return Ok(guild_id);
- }
-
- for guild in CACHE.read().unwrap().guilds.values() {
- let guild = guild.read().unwrap();
-
- let predicate = guild.members
- .values()
- .any(|m| m.joined_at == self.joined_at && m.user.read().unwrap().id == self.user.read().unwrap().id);
-
- if predicate {
- return Ok(guild.id);
- }
- }
-
- Err(Error::Client(ClientError::GuildNotFound))
- }
-
- /// Removes a [`Role`] from the member, editing its roles in-place if the
- /// request was successful.
- ///
- /// **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) -> Result<()> {
- let role_id = role_id.into();
-
- if !self.roles.contains(&role_id) {
- return Ok(());
- }
-
- let guild_id = self.find_guild()?;
-
- match rest::remove_member_role(guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
- Ok(()) => {
- self.roles.retain(|r| r.0 != role_id.0);
-
- Ok(())
- },
- Err(why) => Err(why),
- }
- }
-
- /// 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]) -> Result<()> {
- let guild_id = self.find_guild()?;
- self.roles.retain(|r| !role_ids.contains(r));
-
- let map = EditMember::default().roles(&self.roles).0.build();
-
- match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map) {
- Ok(()) => Ok(()),
- Err(why) => {
- self.roles.extend_from_slice(role_ids);
-
- Err(why)
- },
- }
- }
-
- /// 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<Role>> {
- CACHE.read().unwrap()
- .guilds
- .values()
- .find(|guild| guild
- .read()
- .unwrap()
- .members
- .values()
- .any(|m| m.user.read().unwrap().id == self.user.read().unwrap().id && m.joined_at == *self.joined_at))
- .map(|guild| guild
- .read()
- .unwrap()
- .roles
- .values()
- .filter(|role| self.roles.contains(&role.id))
- .cloned()
- .collect())
- }
-
- /// Unbans the [`User`] from the guild.
- ///
- /// **Note**: Requires the [Ban Members] permission.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- #[cfg(feature="cache")]
- pub fn unban(&self) -> Result<()> {
- rest::remove_ban(self.find_guild()?.0, self.user.read().unwrap().id.0)
- }
-}
-
-impl fmt::Display for Member {
- /// Mentions the user so that they receive a notification.
- ///
- /// # Examples
- ///
- /// ```rust,ignore
- /// // assumes a `member` has already been bound
- /// println!("{} is a member!", member);
- /// ```
- ///
- // This is in the format of `<@USER_ID>`.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.user.read().unwrap().mention(), f)
- }
-}
-
-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 [`ClientError::DeleteMessageDaysAmount`] if the number of
- /// days' worth of messages to delete is over the maximum.
- ///
- /// [`ClientError::DeleteMessageDaysAmount`]: ../client/enum.ClientError.html#variant.DeleteMessageDaysAmount
- /// [`User`]: struct.User.html
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- pub fn ban<U: Into<UserId>>(&self, user: U, delete_message_days: u8) -> Result<()> {
- if delete_message_days > 7 {
- return Err(Error::Client(ClientError::DeleteMessageDaysAmount(delete_message_days)));
- }
-
- self.id.ban(user, delete_message_days)
- }
-
- /// Creates a [`GuildChannel`] in the guild.
- ///
- /// Refer to [`rest::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);
- /// ```
- ///
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`rest::create_channel`]: ../client/rest/fn.create_channel.html
- /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
- #[inline]
- pub fn create_channel(&self, name: &str, kind: ChannelType) -> Result<GuildChannel> {
- self.id.create_channel(name, kind)
- }
-
- /// 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`]: ../utils/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) -> Result<Emoji> {
- self.id.create_emoji(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) -> Result<()>
- where I: Into<IntegrationId> {
- self.id.create_integration(integration_id, 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 [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to perform bans.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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) -> Result<Role> {
- self.id.create_role(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) -> Result<PartialGuild> {
- self.id.delete()
- }
-
- /// 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) -> Result<()> {
- self.id.delete_emoji(emoji_id)
- }
-
- /// 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) -> Result<()> {
- self.id.delete_integration(integration_id)
- }
-
- /// 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) -> Result<()> {
- self.id.delete_role(role_id)
- }
-
- /// Edits the current guild with new data where specified.
- ///
- /// **Note**: Requires the current user to have the [Manage Guild]
- /// permission.
- ///
- /// [`Context::edit_guild`]: ../client/struct.Context.html#method.edit_guild
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- pub fn edit<F>(&mut self, f: F) -> Result<()>
- where F: FnOnce(EditGuild) -> EditGuild {
- match self.id.edit(f) {
- Ok(guild) => {
- self.afk_channel_id = guild.afk_channel_id;
- self.afk_timeout = guild.afk_timeout;
- self.default_message_notifications = guild.default_message_notifications;
- self.emojis = guild.emojis;
- self.features = guild.features;
- self.icon = guild.icon;
- self.mfa_level = guild.mfa_level;
- self.name = guild.name;
- self.owner_id = guild.owner_id;
- self.region = guild.region;
- self.roles = guild.roles;
- self.splash = guild.splash;
- self.verification_level = guild.verification_level;
-
- Ok(())
- },
- Err(why) => Err(why),
- }
- }
-
- /// 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) -> Result<Emoji> {
- self.id.edit_emoji(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
- /// 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) -> Result<()>
- where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
- self.id.edit_member(user_id, 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 [`ClientError::InvalidPermissions`]
- /// if the current user does not have permission to change their own
- /// nickname.
- ///
- /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
- /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
- #[inline]
- pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
- self.id.edit_nickname(new_nickname)
- }
-
- /// Gets a partial amount of guild data by its Id.
- ///
- /// Requires that the current user be in the guild.
- #[inline]
- pub fn get<G: Into<GuildId>>(guild_id: G) -> Result<PartialGuild> {
- guild_id.into().get()
- }
-
- /// Gets a list of the guild's bans.
- ///
- /// Requires the [Ban Members] permission.
- ///
- /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
- #[inline]
- pub fn get_bans(&self) -> Result<Vec<Ban>> {
- self.id.get_bans()
- }
-
- /// Gets all of the guild's channels over the REST API.
- ///
- /// [`Guild`]: struct.Guild.html
- #[inline]
- pub fn get_channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
- self.id.get_channels()
- }
-
- /// Gets an emoji in the guild by Id.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- #[inline]
- pub fn get_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<Emoji> {
- self.id.get_emoji(emoji_id)
- }
-
- /// Gets a list of all of the guild's emojis.
- ///
- /// Requires the [Manage Emojis] permission.
- ///
- /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
- #[inline]
- pub fn get_emojis(&self) -> Result<Vec<Emoji>> {
- self.id.get_emojis()
- }
-
- /// Gets all integration of the guild.
- ///
- /// This performs a request over the REST API.
- #[inline]
- pub fn get_integrations(&self) -> Result<Vec<Integration>> {
- self.id.get_integrations()
- }
-
- /// Gets all of the guild's invites.
- ///
- /// Requires the [Manage Guild] permission.
- ///
- /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
- #[inline]
- pub fn get_invites(&self) -> Result<Vec<RichInvite>> {
- self.id.get_invites()
- }
-
- /// Gets a user's [`Member`] for the guild by Id.
- ///
- /// [`Guild`]: struct.Guild.html
- /// [`Member`]: struct.Member.html
- pub fn get_member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
- self.id.get_member(user_id)
- }
-
- /// 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 get_members<U>(&self, limit: Option<u64>, after: Option<U>)
- -> Result<Vec<Member>> where U: Into<UserId> {
- self.id.get_members(limit, after)
- }
-
- /// 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 get_prune_count(&self, days: u16) -> Result<GuildPrune> {
- self.id.get_prune_count(days)
- }
-
- /// Retrieves the guild's webhooks.
- ///
- /// **Note**: Requires the [Manage Webhooks] permission.
- ///
- /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
- #[inline]
- pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
- self.id.get_webhooks()
- }
-
- /// 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) -> Result<()> {
- self.id.kick(user_id)
- }
-
- /// Returns a formatted URL of the guild's icon, if the guild has an icon.
- pub fn icon_url(&self) -> Option<String> {
- self.icon.as_ref().map(|icon|
- format!(cdn!("/icons/{}/{}.webp"), self.id, icon))
- }
-
- /// Leaves the guild.
- #[inline]
- pub fn leave(&self) -> Result<PartialGuild> {
- self.id.leave()
- }
-
- /// 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) -> Result<()>
- where C: Into<ChannelId>, U: Into<UserId> {
- self.id.move_member(user_id, channel_id)
- }
-
- /// Performs a search request to the API for the guild's [`Message`]s.
- ///
- /// This will search all of the guild's [`Channel`]s at once, that you have
- /// the [Read Message History] permission to. Use [`search_channels`] to
- /// specify a list of [channel][`GuildChannel`]s to search, where all other
- /// channels will be excluded.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Channel`]: enum.Channel.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- /// [`search_channels`]: #method.search_channels
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
- #[cfg(feature="cache")]
- {
- if CACHE.read().unwrap().user.bot {
- return Err(Error::Client(ClientError::InvalidOperationAsBot));
- }
- }
-
- self.id.search(f)
- }
-
- /// Performs a search request to the API for the guild's [`Message`]s in
- /// given channels.
- ///
- /// This will search all of the messages in the guild's provided
- /// [`Channel`]s by Id that you have the [Read Message History] permission
- /// to. Use [`search`] to search all of a guild's [channel][`GuildChannel`]s
- /// at once.
- ///
- /// Refer to the documentation for the [`Search`] builder for examples and
- /// more information.
- ///
- /// **Note**: Bot users can not search.
- ///
- /// # Errors
- ///
- /// If the `cache` is enabled, returns a
- /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
- ///
- /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
- /// [`Channel`]: enum.Channel.html
- /// [`GuildChannel`]: struct.GuildChannel.html
- /// [`Message`]: struct.Message.html
- /// [`Search`]: ../utils/builder/struct.Search.html
- /// [`search`]: #method.search
- /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
- pub fn search_channels<F>(&self, channel_ids: &[ChannelId], f: F)
- -> Result<SearchResult> where F: FnOnce(Search) -> Search {
- #[cfg(feature="cache")]
- {
- if CACHE.read().unwrap().user.bot {
- return Err(Error::Client(ClientError::InvalidOperationAsBot));
- }
- }
-
- self.id.search_channels(channel_ids, f)
- }
-
- /// Returns the formatted URL of the guild's splash image, if one exists.
- pub fn splash_url(&self) -> Option<String> {
- self.icon.as_ref().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: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
- self.id.start_integration_sync(integration_id)
- }
-
- /// 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) -> Result<()> {
- self.id.unban(user_id)
- }
-}
-
-impl PossibleGuild<Guild> {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Self> {
- let mut value = into_map(value)?;
- if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
- remove(&mut value, "id").and_then(GuildId::decode).map(PossibleGuild::Offline)
- } else {
- Guild::decode(Value::Object(value)).map(PossibleGuild::Online)
- }
- }
-
- /// Retrieves the Id of the inner [`Guild`].
- ///
- /// [`Guild`]: struct.Guild.html
- pub fn id(&self) -> GuildId {
- match *self {
- PossibleGuild::Offline(guild_id) => guild_id,
- PossibleGuild::Online(ref live_guild) => live_guild.id,
- }
- }
-}
-
-impl PossibleGuild<PartialGuild> {
- #[doc(hidden)]
- pub fn decode(value: Value) -> Result<Self> {
- let mut value = into_map(value)?;
- if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
- remove(&mut value, "id").and_then(GuildId::decode).map(PossibleGuild::Offline)
- } else {
- PartialGuild::decode(Value::Object(value)).map(PossibleGuild::Online)
- }
- }
-
- /// Retrieves the Id of the inner [`Guild`].
- ///
- /// [`Guild`]: struct.Guild.html
- pub fn id(&self) -> GuildId {
- match *self {
- PossibleGuild::Offline(id) => id,
- PossibleGuild::Online(ref live_guild) => live_guild.id,
- }
- }
-}
-
-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) -> Result<()> {
- rest::delete_role(self.find_guild()?.0, self.id.0)
- }
-
- /// Edits a [`Role`], optionally setting its new fields.
- ///
- /// Requires the [Manage Roles] permission.
- ///
- /// # Examples
- ///
- /// Make a role hoisted:
- ///
- /// ```rust,ignore
- /// // assuming a `guild` and `role_id` have been bound
- //
- /// guild.edit_role(role_id, |r| r.hoist(true));
- /// ```
- ///
- /// [`Role`]: struct.Role.html
- /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
- #[cfg(feature="cache")]
- pub fn edit_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
- match self.find_guild() {
- Ok(guild_id) => guild_id.edit_role(self.id, f),
- Err(why) => Err(why),
- }
- }
-
- /// Searches the cache for the guild that owns the role.
- ///
- /// # Errors
- ///
- /// Returns a [`ClientError::GuildNotFound`] if a guild is not in the cache
- /// that contains the role.
- ///
- /// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound
- #[cfg(feature="cache")]
- pub fn find_guild(&self) -> Result<GuildId> {
- for guild in CACHE.read().unwrap().guilds.values() {
- let guild = guild.read().unwrap();
-
- if guild.roles.contains_key(&RoleId(self.id.0)) {
- return Ok(guild.id);
- }
- }
-
- Err(Error::Client(ClientError::GuildNotFound))
- }
-
- /// Check that the role has the given permission.
- #[inline]
- pub fn has_permission(&self, permission: Permissions) -> bool {
- self.permissions.contains(permission)
- }
-
- /// Checks whether the role has all of the given permissions.
- ///
- /// The 'precise' argument is used to check if the role's permissions are
- /// precisely equivalent to the given permissions. If you need only check
- /// that the role has at least the given permissions, pass `false`.
- pub fn has_permissions(&self, permissions: Permissions, precise: bool)
- -> bool {
- if precise {
- self.permissions == permissions
- } else {
- self.permissions.contains(permissions)
- }
- }
-}
-
-impl fmt::Display for Role {
- /// Format a mention for the role, pinging its members.
- // This is in the format of: `<@&ROLE_ID>`.
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.mention(), f)
- }
-}
-
-impl Eq for Role {}
-
-impl Ord for Role {
- fn cmp(&self, other: &Role) -> Ordering {
- if self.position == other.position {
- self.id.cmp(&other.id)
- } else {
- self.position.cmp(&other.position)
- }
- }
-}
-
-impl PartialEq for Role {
- fn eq(&self, other: &Role) -> bool {
- self.id == other.id
- }
-}
-
-impl PartialOrd for Role {
- fn partial_cmp(&self, other: &Role) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl RoleId {
- /// Search the cache for the role.
- #[cfg(feature="cache")]
- pub fn find(&self) -> Option<Role> {
- let cache = CACHE.read().unwrap();
-
- for guild in cache.guilds.values() {
- let guild = guild.read().unwrap();
-
- if !guild.roles.contains_key(self) {
- continue;
- }
-
- if let Some(role) = guild.roles.get(self) {
- return Some(role.clone());
- }
- }
-
- None
- }
-}
-
-impl fmt::Display for RoleId {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.0, f)
- }
-}
-
-impl From<Role> for RoleId {
- /// Gets the Id of a role.
- fn from(role: Role) -> RoleId {
- role.id
- }
-}
diff --git a/src/model/guild/emoji.rs b/src/model/guild/emoji.rs
new file mode 100644
index 0000000..43e57a2
--- /dev/null
+++ b/src/model/guild/emoji.rs
@@ -0,0 +1,100 @@
+use serde_json::builder::ObjectBuilder;
+use std::fmt::{Display, Formatter, Result as FmtResult, Write as FmtWrite};
+use std::mem;
+use ::client::{CACHE, rest};
+use ::model::{Emoji, EmojiId, GuildId};
+use ::internal::prelude::*;
+
+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
+ #[cfg(feature="cache")]
+ pub fn delete(&self) -> Result<()> {
+ match self.find_guild_id() {
+ Some(guild_id) => rest::delete_emoji(guild_id.0, self.id.0),
+ None => Err(Error::Client(ClientError::ItemMissing)),
+ }
+ }
+
+ /// 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
+ #[cfg(feature="cache")]
+ pub fn edit(&mut self, name: &str) -> Result<()> {
+ match self.find_guild_id() {
+ Some(guild_id) => {
+ let map = ObjectBuilder::new()
+ .insert("name", name)
+ .build();
+
+ match rest::edit_emoji(guild_id.0, self.id.0, &map) {
+ Ok(emoji) => {
+ mem::replace(self, emoji);
+
+ Ok(())
+ },
+ Err(why) => Err(why),
+ }
+ },
+ None => Err(Error::Client(ClientError::ItemMissing)),
+ }
+ }
+
+ /// Finds the [`Guild`] that owns the emoji by looking through the Cache.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ #[cfg(feature="cache")]
+ pub fn find_guild_id(&self) -> Option<GuildId> {
+ for guild in CACHE.read().unwrap().guilds.values() {
+ let guild = guild.read().unwrap();
+
+ if guild.emojis.contains_key(&self.id) {
+ return Some(guild.id);
+ }
+ }
+
+ None
+ }
+
+ /// Generates a URL to the emoji's image.
+ #[inline]
+ pub fn url(&self) -> String {
+ format!(cdn!("/emojis/{}.png"), self.id)
+ }
+}
+
+impl Display for Emoji {
+ /// Formats the emoji into a string that will cause Discord clients to
+ /// render the emoji.
+ ///
+ /// This is in the format of: `<:NAME:EMOJI_ID>`.
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ f.write_str("<:")?;
+ f.write_str(&self.name)?;
+ FmtWrite::write_char(f, ':')?;
+ Display::fmt(&self.id, f)?;
+ FmtWrite::write_char(f, '>')
+ }
+}
+
+impl Display for EmojiId {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl From<Emoji> for EmojiId {
+ /// Gets the Id of an `Emoji`.
+ fn from(emoji: Emoji) -> EmojiId {
+ emoji.id
+ }
+}
diff --git a/src/model/guild/guild_id.rs b/src/model/guild/guild_id.rs
new file mode 100644
index 0000000..83dc19a
--- /dev/null
+++ b/src/model/guild/guild_id.rs
@@ -0,0 +1,525 @@
+use serde_json::builder::ObjectBuilder;
+use std::fmt::{Display, Formatter, Result as FmtResult};
+use ::client::{CACHE, rest};
+use ::internal::prelude::*;
+use ::model::*;
+use ::utils::builder::{EditGuild, EditMember, EditRole, Search};
+
+impl GuildId {
+ /// Converts the guild Id into the default channel's Id.
+ #[inline]
+ pub fn as_channel_id(&self) -> ChannelId {
+ ChannelId(self.0)
+ }
+
+ /// 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
+ /// use serenity::model::GuildId;
+ ///
+ /// // assuming a `user` has already been bound
+ /// let _ = GuildId(81384788765712384).ban(user, 4);
+ /// ```
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::DeleteMessageDaysAmount`] if the number of
+ /// days' worth of messages to delete is over the maximum.
+ ///
+ /// [`ClientError::DeleteMessageDaysAmount`]: ../client/enum.ClientError.html#variant.DeleteMessageDaysAmount
+ /// [`Guild::ban`]: struct.Guild.html#method.ban
+ /// [`User`]: struct.User.html
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ pub fn ban<U: Into<UserId>>(&self, user: U, delete_message_days: u8)
+ -> Result<()> {
+ if delete_message_days > 7 {
+ return Err(Error::Client(ClientError::DeleteMessageDaysAmount(delete_message_days)));
+ }
+
+ rest::ban_user(self.0, user.into().0, delete_message_days)
+ }
+
+ /// Creates a [`GuildChannel`] in the the guild.
+ ///
+ /// Refer to [`rest::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, GuildId};
+ ///
+ /// let _channel = GuildId(7).create_channel("test", ChannelType::Voice);
+ /// ```
+ ///
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`rest::create_channel`]: ../client/rest/fn.create_channel.html
+ /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ pub fn create_channel(&self, name: &str, kind: ChannelType) -> Result<GuildChannel> {
+ let map = ObjectBuilder::new()
+ .insert("name", name)
+ .insert("type", kind.name())
+ .build();
+
+ rest::create_channel(self.0, &map)
+ }
+
+ /// 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`]: ../utils/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
+ pub fn create_emoji(&self, name: &str, image: &str) -> Result<Emoji> {
+ let map = ObjectBuilder::new()
+ .insert("name", name)
+ .insert("image", image)
+ .build();
+
+ rest::create_emoji(self.0, &map)
+ }
+
+ /// Creates an integration for the guild.
+ ///
+ /// Requires the [Manage Guild] permission.
+ ///
+ /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ pub fn create_integration<I>(&self, integration_id: I, kind: &str)
+ -> Result<()> where I: Into<IntegrationId> {
+ let integration_id = integration_id.into();
+ let map = ObjectBuilder::new()
+ .insert("id", integration_id.0)
+ .insert("type", kind)
+ .build();
+
+ rest::create_guild_integration(self.0, integration_id.0, &map)
+ }
+
+ /// 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.
+ ///
+ /// [`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) -> Result<Role> {
+ rest::create_role(self.0, &f(EditRole::default()).0.build())
+ }
+
+ /// Deletes the current guild if the current account is the owner of the
+ /// guild.
+ ///
+ /// Refer to [`Guild::delete`] for more information.
+ ///
+ /// **Note**: Requires the current user to be the owner of the guild.
+ ///
+ /// [`Guild::delete`]: struct.Guild.html#method.delete
+ #[inline]
+ pub fn delete(&self) -> Result<PartialGuild> {
+ rest::delete_guild(self.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) -> Result<()> {
+ rest::delete_emoji(self.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) -> Result<()> {
+ rest::delete_guild_integration(self.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) -> Result<()> {
+ rest::delete_role(self.0, role_id.into().0)
+ }
+
+ /// Edits the current guild with new data where specified.
+ ///
+ /// Refer to [`Guild::edit`] for more information.
+ ///
+ /// **Note**: Requires the current user to have the [Manage Guild]
+ /// permission.
+ ///
+ /// [`Guild::edit`]: struct.Guild.html#method.edit
+ /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ #[inline]
+ pub fn edit<F: FnOnce(EditGuild) -> EditGuild>(&mut self, f: F) -> Result<PartialGuild> {
+ rest::edit_guild(self.0, &f(EditGuild::default()).0.build())
+ }
+
+ /// 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
+ pub fn edit_emoji<E: Into<EmojiId>>(&self, emoji_id: E, name: &str) -> Result<Emoji> {
+ let map = ObjectBuilder::new().insert("name", name).build();
+
+ rest::edit_emoji(self.0, emoji_id.into().0, &map)
+ }
+
+ /// 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) -> Result<()>
+ where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
+ rest::edit_member(self.0, user_id.into().0, &f(EditMember::default()).0.build())
+ }
+
+ /// Edits the current user's nickname for the guild.
+ ///
+ /// Pass `None` to reset the nickname.
+ ///
+ /// Requires the [Change Nickname] permission.
+ ///
+ /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
+ #[inline]
+ pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
+ rest::edit_nickname(self.0, new_nickname)
+ }
+
+ /// Edits a [`Role`], optionally setting its new fields.
+ ///
+ /// Requires the [Manage Roles] permission.
+ ///
+ /// # Examples
+ ///
+ /// Make a role hoisted:
+ ///
+ /// ```rust,ignore
+ /// use serenity::model::{GuildId, RoleId};
+ ///
+ /// GuildId(7).edit_role(RoleId(8), |r| r.hoist(true));
+ /// ```
+ ///
+ /// [`Role`]: struct.Role.html
+ /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ #[inline]
+ pub fn edit_role<F, R>(&self, role_id: R, f: F) -> Result<Role>
+ where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
+ rest::edit_role(self.0, role_id.into().0, &f(EditRole::default()).0.build())
+ }
+
+ /// Search the cache for the guild.
+ #[cfg(feature="cache")]
+ pub fn find(&self) -> Option<Arc<RwLock<Guild>>> {
+ CACHE.read().unwrap().get_guild(*self)
+ }
+
+ /// Requests the guild over REST.
+ ///
+ /// Note that this will not be a complete guild, as REST does not send
+ /// all data with a guild retrieval.
+ #[inline]
+ pub fn get(&self) -> Result<PartialGuild> {
+ rest::get_guild(self.0)
+ }
+
+ /// Gets a list of the guild's bans.
+ ///
+ /// Requires the [Ban Members] permission.
+ ///
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ #[inline]
+ pub fn get_bans(&self) -> Result<Vec<Ban>> {
+ rest::get_bans(self.0)
+ }
+
+ /// Gets all of the guild's channels over the REST API.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ pub fn get_channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
+ let mut channels = HashMap::new();
+
+ for channel in rest::get_channels(self.0)? {
+ channels.insert(channel.id, channel);
+ }
+
+ Ok(channels)
+ }
+
+ /// Gets an emoji in the guild by Id.
+ ///
+ /// Requires the [Manage Emojis] permission.
+ ///
+ /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ #[inline]
+ pub fn get_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<Emoji> {
+ rest::get_emoji(self.0, emoji_id.into().0)
+ }
+
+ /// Gets a list of all of the guild's emojis.
+ ///
+ /// Requires the [Manage Emojis] permission.
+ ///
+ /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ #[inline]
+ pub fn get_emojis(&self) -> Result<Vec<Emoji>> {
+ rest::get_emojis(self.0)
+ }
+
+ /// Gets all integration of the guild.
+ ///
+ /// This performs a request over the REST API.
+ #[inline]
+ pub fn get_integrations(&self) -> Result<Vec<Integration>> {
+ rest::get_guild_integrations(self.0)
+ }
+
+ /// Gets all of the guild's invites.
+ ///
+ /// Requires the [Manage Guild] permission.
+ ///
+ /// [Manage Guild]: permissions/struct.MANAGE_GUILD.html
+ #[inline]
+ pub fn get_invites(&self) -> Result<Vec<RichInvite>> {
+ rest::get_guild_invites(self.0)
+ }
+
+ /// Gets a user's [`Member`] for the guild by Id.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ /// [`Member`]: struct.Member.html
+ #[inline]
+ pub fn get_member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
+ rest::get_member(self.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 get_members<U>(&self, limit: Option<u64>, after: Option<U>)
+ -> Result<Vec<Member>> where U: Into<UserId> {
+ rest::get_guild_members(self.0, limit, after.map(|x| x.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
+ pub fn get_prune_count(&self, days: u16) -> Result<GuildPrune> {
+ let map = ObjectBuilder::new().insert("days", days).build();
+
+ rest::get_guild_prune_count(self.0, &map)
+ }
+
+ /// Retrieves the guild's webhooks.
+ ///
+ /// **Note**: Requires the [Manage Webhooks] permission.
+ ///
+ /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ #[inline]
+ pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
+ rest::get_guild_webhooks(self.0)
+ }
+
+ /// 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) -> Result<()> {
+ rest::kick_member(self.0, user_id.into().0)
+ }
+
+ /// Leaves the guild.
+ #[inline]
+ pub fn leave(&self) -> Result<PartialGuild> {
+ rest::leave_guild(self.0)
+ }
+
+ /// Moves a member to a specific voice channel.
+ ///
+ /// Requires the [Move Members] permission.
+ ///
+ /// [Move Members]: permissions/constant.MOVE_MEMBERS.html
+ pub fn move_member<C, U>(&self, user_id: U, channel_id: C)
+ -> Result<()> where C: Into<ChannelId>, U: Into<UserId> {
+ let map = ObjectBuilder::new().insert("channel_id", channel_id.into().0).build();
+
+ rest::edit_member(self.0, user_id.into().0, &map)
+ }
+
+ /// Performs a search request to the API for the guild's [`Message`]s.
+ ///
+ /// This will search all of the guild's [`Channel`]s at once, that you have
+ /// the [Read Message History] permission to. Use [`search_channels`] to
+ /// specify a list of [channel][`GuildChannel`]s to search, where all other
+ /// channels will be excluded.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// [`Channel`]: enum.Channel.html
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ /// [`search_channels`]: #method.search_channels
+ /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ #[inline]
+ pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
+ rest::search_guild_messages(self.0, &[], f(Search::default()).0)
+ }
+
+ /// Performs a search request to the API for the guild's [`Message`]s in
+ /// given channels.
+ ///
+ /// Refer to [`Guild::search_channels`] for more information.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// [`Guild::search_channels`]: struct.Guild.html#method.search_channels
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ pub fn search_channels<F>(&self, channel_ids: &[ChannelId], f: F)
+ -> Result<SearchResult> where F: FnOnce(Search) -> Search {
+ let ids = channel_ids.iter().map(|x| x.0).collect::<Vec<u64>>();
+
+ rest::search_guild_messages(self.0, &ids, f(Search::default()).0)
+ }
+
+ /// 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: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
+ rest::start_integration_sync(self.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.
+ ///
+ /// [`GuildPrune`]: struct.GuildPrune.html
+ /// [`Member`]: struct.Member.html
+ /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ #[inline]
+ pub fn start_prune(&self, days: u16) -> Result<GuildPrune> {
+ rest::start_guild_prune(self.0, &ObjectBuilder::new().insert("days", days).build())
+ }
+
+ /// 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) -> Result<()> {
+ rest::remove_ban(self.0, user_id.into().0)
+ }
+}
+
+impl Display for GuildId {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl From<PartialGuild> for GuildId {
+ /// Gets the Id of a partial guild.
+ fn from(guild: PartialGuild) -> GuildId {
+ guild.id
+ }
+}
+
+impl From<GuildInfo> for GuildId {
+ /// Gets the Id of Guild information struct.
+ fn from(guild_info: GuildInfo) -> GuildId {
+ guild_info.id
+ }
+}
+
+impl From<InviteGuild> for GuildId {
+ /// Gets the Id of Invite Guild struct.
+ fn from(invite_guild: InviteGuild) -> GuildId {
+ invite_guild.id
+ }
+}
+
+impl From<Guild> for GuildId {
+ /// Gets the Id of Guild.
+ fn from(live_guild: Guild) -> GuildId {
+ live_guild.id
+ }
+}
diff --git a/src/model/guild/integration.rs b/src/model/guild/integration.rs
new file mode 100644
index 0000000..d7f9967
--- /dev/null
+++ b/src/model/guild/integration.rs
@@ -0,0 +1,8 @@
+use ::model::{Integration, IntegrationId};
+
+impl From<Integration> for IntegrationId {
+ /// Gets the Id of integration.
+ fn from(integration: Integration) -> IntegrationId {
+ integration.id
+ }
+}
diff --git a/src/model/guild/member.rs b/src/model/guild/member.rs
new file mode 100644
index 0000000..67d9dae
--- /dev/null
+++ b/src/model/guild/member.rs
@@ -0,0 +1,279 @@
+use std::borrow::Cow;
+use std::fmt::{Display, Formatter, Result as FmtResult};
+use ::client::{CACHE, rest};
+use ::internal::prelude::*;
+use ::model::*;
+use ::utils::builder::EditMember;
+use ::utils::Colour;
+
+impl Member {
+ /// Adds a [`Role`] to the member, editing its roles in-place if the request
+ /// was successful.
+ ///
+ /// **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) -> Result<()> {
+ let role_id = role_id.into();
+
+ if self.roles.contains(&role_id) {
+ return Ok(());
+ }
+
+ let guild_id = self.find_guild()?;
+
+ match rest::add_member_role(guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
+ Ok(()) => {
+ self.roles.push(role_id);
+
+ Ok(())
+ },
+ Err(why) => Err(why),
+ }
+ }
+
+ /// Adds one or multiple [`Role`]s to the member, editing
+ /// its roles in-place if the request was successful.
+ ///
+ /// **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]) -> Result<()> {
+ let guild_id = self.find_guild()?;
+ self.roles.extend_from_slice(role_ids);
+
+ let map = EditMember::default().roles(&self.roles).0.build();
+
+ match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map) {
+ Ok(()) => Ok(()),
+ Err(why) => {
+ self.roles.retain(|r| !role_ids.contains(r));
+
+ Err(why)
+ }
+ }
+ }
+
+ /// Ban the member from its guild, deleting the last X number of
+ /// days' worth of messages.
+ ///
+ /// **Note**: Requires the [Ban Members] role.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::GuildNotFound`] if the guild could not be
+ /// found.
+ ///
+ /// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound
+ ///
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ #[cfg(feature="cache")]
+ pub fn ban(&self, delete_message_days: u8) -> Result<()> {
+ rest::ban_user(self.find_guild()?.0, self.user.read().unwrap().id.0, delete_message_days)
+ }
+
+ /// Determines the member's colour.
+ #[cfg(feature="cache")]
+ pub fn colour(&self) -> Option<Colour> {
+ let guild_id = match self.find_guild() {
+ Ok(guild_id) => guild_id,
+ Err(_) => return None,
+ };
+
+ let cache = CACHE.read().unwrap();
+ let guild = match cache.guilds.get(&guild_id) {
+ Some(guild) => guild.read().unwrap(),
+ None => return None,
+ };
+
+ let mut roles = self.roles
+ .iter()
+ .filter_map(|role_id| guild.roles.get(role_id))
+ .collect::<Vec<&Role>>();
+ roles.sort_by(|a, b| b.cmp(a));
+
+ let default = Colour::default();
+
+ roles.iter().find(|r| r.colour.0 != default.0).map(|r| r.colour)
+ }
+
+ #[doc(hidden)]
+ pub fn decode_guild(guild_id: GuildId, mut value: Value) -> Result<Member> {
+ if let Some(v) = value.as_object_mut() {
+ v.insert("guild_id".to_owned(), Value::U64(guild_id.0));
+ }
+
+ Self::decode(value)
+ }
+
+ /// Calculates the member's display name.
+ ///
+ /// The nickname takes priority over the member's username if it exists.
+ #[inline]
+ pub fn display_name(&self) -> Cow<String> {
+ self.nick.as_ref()
+ .map(Cow::Borrowed)
+ .unwrap_or_else(|| Cow::Owned(self.user.read().unwrap().name.clone()))
+ }
+
+ /// Returns the DiscordTag of a Member, taking possible nickname into account.
+ #[inline]
+ pub fn distinct(&self) -> String {
+ format!("{}#{}", self.display_name(), self.user.read().unwrap().discriminator)
+ }
+
+ /// Edits the member with the given data. See [`Context::edit_member`] for
+ /// more information.
+ ///
+ /// See [`EditMember`] for the permission(s) required for separate builder
+ /// methods, as well as usage of this.
+ ///
+ /// [`Context::edit_member`]: ../client/struct.Context.html#method.edit_member
+ /// [`EditMember`]: ../builder/struct.EditMember.html
+ #[cfg(feature="cache")]
+ pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> {
+ let guild_id = self.find_guild()?;
+ let map = f(EditMember::default()).0.build();
+
+ rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map)
+ }
+
+ /// Finds the Id of the [`Guild`] that the member is in.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::GuildNotFound`] if the guild could not be
+ /// found.
+ ///
+ /// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound
+ /// [`Guild`]: struct.Guild.html
+ #[cfg(feature="cache")]
+ pub fn find_guild(&self) -> Result<GuildId> {
+ for guild in CACHE.read().unwrap().guilds.values() {
+ let guild = guild.read().unwrap();
+
+ let predicate = guild.members
+ .values()
+ .any(|m| m.joined_at == self.joined_at && m.user.read().unwrap().id == self.user.read().unwrap().id);
+
+ if predicate {
+ return Ok(guild.id);
+ }
+ }
+
+ Err(Error::Client(ClientError::GuildNotFound))
+ }
+
+ /// Removes a [`Role`] from the member, editing its roles in-place if the
+ /// request was successful.
+ ///
+ /// **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) -> Result<()> {
+ let role_id = role_id.into();
+
+ if !self.roles.contains(&role_id) {
+ return Ok(());
+ }
+
+ let guild_id = self.find_guild()?;
+
+ match rest::remove_member_role(guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
+ Ok(()) => {
+ self.roles.retain(|r| r.0 != role_id.0);
+
+ Ok(())
+ },
+ Err(why) => Err(why),
+ }
+ }
+
+ /// 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]) -> Result<()> {
+ let guild_id = self.find_guild()?;
+ self.roles.retain(|r| !role_ids.contains(r));
+
+ let map = EditMember::default().roles(&self.roles).0.build();
+
+ match rest::edit_member(guild_id.0, self.user.read().unwrap().id.0, &map) {
+ Ok(()) => Ok(()),
+ Err(why) => {
+ self.roles.extend_from_slice(role_ids);
+
+ Err(why)
+ },
+ }
+ }
+
+ /// 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<Role>> {
+ CACHE.read().unwrap()
+ .guilds
+ .values()
+ .find(|guild| guild
+ .read()
+ .unwrap()
+ .members
+ .values()
+ .any(|m| m.user.read().unwrap().id == self.user.read().unwrap().id && m.joined_at == *self.joined_at))
+ .map(|guild| guild
+ .read()
+ .unwrap()
+ .roles
+ .values()
+ .filter(|role| self.roles.contains(&role.id))
+ .cloned()
+ .collect())
+ }
+
+ /// Unbans the [`User`] from the guild.
+ ///
+ /// **Note**: Requires the [Ban Members] permission.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`User`]: struct.User.html
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ #[cfg(feature="cache")]
+ pub fn unban(&self) -> Result<()> {
+ rest::remove_ban(self.find_guild()?.0, self.user.read().unwrap().id.0)
+ }
+}
+
+impl Display for Member {
+ /// Mentions the user so that they receive a notification.
+ ///
+ /// # Examples
+ ///
+ /// ```rust,ignore
+ /// // assumes a `member` has already been bound
+ /// println!("{} is a member!", member);
+ /// ```
+ ///
+ // This is in the format of `<@USER_ID>`.
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ Display::fmt(&self.user.read().unwrap().mention(), f)
+ }
+}
diff --git a/src/model/guild/mod.rs b/src/model/guild/mod.rs
new file mode 100644
index 0000000..0b008c8
--- /dev/null
+++ b/src/model/guild/mod.rs
@@ -0,0 +1,1052 @@
+use serde_json::builder::ObjectBuilder;
+use ::client::{CACHE, rest};
+use ::constants::LARGE_THRESHOLD;
+use ::model::*;
+use ::utils::builder::{EditGuild, EditMember, EditRole, Search};
+
+mod emoji;
+mod guild_id;
+mod integration;
+mod member;
+mod partial_guild;
+mod role;
+
+pub use self::emoji::*;
+pub use self::guild_id::*;
+pub use self::integration::*;
+pub use self::member::*;
+pub use self::partial_guild::*;
+pub use self::role::*;
+
+impl Guild {
+ #[cfg(feature="cache")]
+ fn has_perms(&self, mut permissions: Permissions) -> Result<bool> {
+ let member = match self.members.get(&CACHE.read().unwrap().user.id) {
+ Some(member) => member,
+ None => return Err(Error::Client(ClientError::ItemMissing)),
+ };
+
+ let perms = self.permissions_for(ChannelId(self.id.0), member.user.read().unwrap().id);
+ permissions.remove(perms);
+
+ Ok(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 [`ClientError::InvalidPermissions`] if the current user does
+ /// not have permission to perform bans.
+ ///
+ /// Returns a [`ClientError::DeleteMessageDaysAmount`] if the number of
+ /// days' worth of messages to delete is over the maximum.
+ ///
+ /// [`ClientError::DeleteMessageDaysAmount`]: ../client/enum.ClientError.html#variant.DeleteMessageDaysAmount
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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>>(&self, user: U, delete_message_days: u8)
+ -> Result<()> {
+ if delete_message_days > 7 {
+ return Err(Error::Client(ClientError::DeleteMessageDaysAmount(delete_message_days)));
+ }
+
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::BAN_MEMBERS;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.ban(user, delete_message_days)
+ }
+
+ /// Retrieves a list of [`Ban`]s for the guild.
+ ///
+ /// **Note**: Requires the [Ban Members] permission.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`Ban`]: struct.Ban.html
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ pub fn bans(&self) -> Result<Vec<Ban>> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::BAN_MEMBERS;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.get_bans()
+ }
+
+ /// Creates a guild with the data provided.
+ ///
+ /// Only a [`PartialGuild`] will be immediately returned, and a full
+ /// [`Guild`] will be received over a [`Shard`].
+ ///
+ /// **Note**: This endpoint is usually only available for user accounts.
+ /// Refer to Discord's information for the endpoint [here][whitelist] for
+ /// more information. If you require this as a bot, re-think what you are
+ /// doing and if it _really_ needs to be doing this.
+ ///
+ /// # Examples
+ ///
+ /// Create a guild called `"test"` in the [US West region] with no icon:
+ ///
+ /// ```rust,ignore
+ /// use serenity::model::{Guild, Region};
+ ///
+ /// let _guild = Guild::create_guild("test", Region::UsWest, None);
+ /// ```
+ ///
+ /// [`Guild`]: struct.Guild.html
+ /// [`PartialGuild`]: struct.PartialGuild.html
+ /// [`Shard`]: ../client/gateway/struct.Shard.html
+ /// [US West region]: enum.Region.html#variant.UsWest
+ /// [whitelist]: https://discordapp.com/developers/docs/resources/guild#create-guild
+ pub fn create(name: &str, region: Region, icon: Option<&str>) -> Result<PartialGuild> {
+ let map = ObjectBuilder::new()
+ .insert("icon", icon)
+ .insert("name", name)
+ .insert("region", region.name())
+ .build();
+
+ rest::create_guild(&map)
+ }
+
+ /// 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);
+ /// ```
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`Channel`]: struct.Channel.html
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ pub fn create_channel(&mut self, name: &str, kind: ChannelType) -> Result<GuildChannel> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_CHANNELS;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.create_channel(name, kind)
+ }
+
+ /// 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`]: ../utils/builder/struct.EditProfile.html#method.avatar
+ /// [`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) -> Result<Emoji> {
+ self.id.create_emoji(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) -> Result<()>
+ where I: Into<IntegrationId> {
+ self.id.create_integration(integration_id, 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 [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`Context::create_role`]: ../client/struct.Context.html#method.create_role
+ /// [`Role`]: struct.Role.html
+ /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ pub fn create_role<F>(&self, f: F) -> Result<Role>
+ where F: FnOnce(EditRole) -> EditRole {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_ROLES;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.create_role(f)
+ }
+
+ #[doc(hidden)]
+ pub fn decode(value: Value) -> Result<Guild> {
+ let mut map = into_map(value)?;
+
+ let id = remove(&mut map, "id").and_then(GuildId::decode)?;
+
+ let channels = {
+ let mut channels = HashMap::new();
+
+ let vals = decode_array(remove(&mut map, "channels")?,
+ |v| GuildChannel::decode_guild(v, id))?;
+
+ for channel in vals {
+ channels.insert(channel.id, Arc::new(RwLock::new(channel)));
+ }
+
+ channels
+ };
+
+ Ok(Guild {
+ afk_channel_id: opt(&mut map, "afk_channel_id", ChannelId::decode)?,
+ afk_timeout: req!(remove(&mut map, "afk_timeout")?.as_u64()),
+ channels: channels,
+ default_message_notifications: req!(remove(&mut map, "default_message_notifications")?.as_u64()),
+ emojis: remove(&mut map, "emojis").and_then(decode_emojis)?,
+ features: remove(&mut map, "features").and_then(|v| decode_array(v, Feature::decode_str))?,
+ icon: opt(&mut map, "icon", into_string)?,
+ id: id,
+ joined_at: remove(&mut map, "joined_at").and_then(into_string)?,
+ large: req!(remove(&mut map, "large")?.as_bool()),
+ member_count: req!(remove(&mut map, "member_count")?.as_u64()),
+ members: remove(&mut map, "members").and_then(decode_members)?,
+ mfa_level: req!(remove(&mut map, "mfa_level")?.as_u64()),
+ name: remove(&mut map, "name").and_then(into_string)?,
+ owner_id: remove(&mut map, "owner_id").and_then(UserId::decode)?,
+ presences: remove(&mut map, "presences").and_then(decode_presences)?,
+ region: remove(&mut map, "region").and_then(into_string)?,
+ roles: remove(&mut map, "roles").and_then(decode_roles)?,
+ splash: opt(&mut map, "splash", into_string)?,
+ verification_level: remove(&mut map, "verification_level").and_then(VerificationLevel::decode)?,
+ voice_states: remove(&mut map, "voice_states").and_then(decode_voice_states)?,
+ })
+ }
+
+ /// 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 [`ClientError::InvalidUser`]
+ /// if the current user is not the guild owner.
+ ///
+ /// [`ClientError::InvalidUser`]: ../client/enum.ClientError.html#variant.InvalidUser
+ pub fn delete(&self) -> Result<PartialGuild> {
+ #[cfg(feature="cache")]
+ {
+ if self.owner_id != CACHE.read().unwrap().user.id {
+ let req = permissions::MANAGE_GUILD;
+
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.delete()
+ }
+
+ /// 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) -> Result<()> {
+ self.id.delete_emoji(emoji_id)
+ }
+
+ /// 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) -> Result<()> {
+ self.id.delete_integration(integration_id)
+ }
+
+ /// 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) -> Result<()> {
+ self.id.delete_role(role_id)
+ }
+
+ /// 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 [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`Context::edit_guild`]: ../client/struct.Context.html#method.edit_guild
+ /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ pub fn edit<F>(&mut self, f: F) -> Result<()>
+ where F: FnOnce(EditGuild) -> EditGuild {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_GUILD;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ match self.id.edit(f) {
+ Ok(guild) => {
+ self.afk_channel_id = guild.afk_channel_id;
+ self.afk_timeout = guild.afk_timeout;
+ self.default_message_notifications = guild.default_message_notifications;
+ self.emojis = guild.emojis;
+ self.features = guild.features;
+ self.icon = guild.icon;
+ self.mfa_level = guild.mfa_level;
+ self.name = guild.name;
+ self.owner_id = guild.owner_id;
+ self.region = guild.region;
+ self.roles = guild.roles;
+ self.splash = guild.splash;
+ self.verification_level = guild.verification_level;
+
+ Ok(())
+ },
+ Err(why) => Err(why),
+ }
+ }
+
+ /// 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) -> Result<Emoji> {
+ self.id.edit_emoji(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) -> Result<()>
+ where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
+ self.id.edit_member(user_id, 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 [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to change their own
+ /// nickname.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
+ pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::CHANGE_NICKNAME;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.edit_nickname(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) -> Result<Role>
+ where F: FnOnce(EditRole) -> EditRole, R: Into<RoleId> {
+ self.id.edit_role(role_id, f)
+ }
+
+ /// Gets a partial amount of guild data by its Id.
+ ///
+ /// Requires that the current user be in the guild.
+ #[inline]
+ pub fn get<G: Into<GuildId>>(guild_id: G) -> Result<PartialGuild> {
+ guild_id.into().get()
+ }
+
+ /// Gets a list of the guild's bans.
+ ///
+ /// Requires the [Ban Members] permission.
+ #[inline]
+ pub fn get_bans(&self) -> Result<Vec<Ban>> {
+ self.id.get_bans()
+ }
+
+ /// Gets all of the guild's channels over the REST API.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ #[inline]
+ pub fn get_channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
+ self.id.get_channels()
+ }
+
+ /// Gets an emoji in the guild by Id.
+ ///
+ /// Requires the [Manage Emojis] permission.
+ ///
+ /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ #[inline]
+ pub fn get_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<Emoji> {
+ self.id.get_emoji(emoji_id)
+ }
+
+ /// Gets a list of all of the guild's emojis.
+ ///
+ /// Requires the [Manage Emojis] permission.
+ ///
+ /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ #[inline]
+ pub fn get_emojis(&self) -> Result<Vec<Emoji>> {
+ self.id.get_emojis()
+ }
+
+ /// Gets all integration of the guild.
+ ///
+ /// This performs a request over the REST API.
+ #[inline]
+ pub fn get_integrations(&self) -> Result<Vec<Integration>> {
+ self.id.get_integrations()
+ }
+
+ /// Retrieves the active invites for the guild.
+ ///
+ /// **Note**: Requires the [Manage Guild] permission.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ pub fn get_invites(&self) -> Result<Vec<RichInvite>> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::MANAGE_GUILD;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.get_invites()
+ }
+
+ /// Gets a user's [`Member`] for the guild by Id.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ /// [`Member`]: struct.Member.html
+ #[inline]
+ pub fn get_member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
+ self.id.get_member(user_id)
+ }
+
+ /// 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 get_members<U>(&self, limit: Option<u64>, after: Option<U>)
+ -> Result<Vec<Member>> where U: Into<UserId> {
+ self.id.get_members(limit, after)
+ }
+
+ /// Retrieves the first [`Member`] found that matches the name - with an
+ /// optional discriminator - provided.
+ ///
+ /// Searching with a discriminator given is the most precise form of lookup,
+ /// as no two people can share the same username *and* discriminator.
+ ///
+ /// If a member can not be found by username or username#discriminator,
+ /// then a search will be done for the nickname. When searching by nickname,
+ /// the hash (`#`) and everything after it is included in the search.
+ ///
+ /// The following are valid types of searches:
+ ///
+ /// - **username**: "zey"
+ /// - **username and discriminator**: "zey#5479"
+ /// - **nickname**: "zeyla" or "zeylas#nick"
+ ///
+ /// [`Member`]: struct.Member.html
+ pub fn get_member_named(&self, name: &str) -> Option<&Member> {
+ let hash_pos = name.find('#');
+
+ let (name, discrim) = if let Some(pos) = hash_pos {
+ let split = name.split_at(pos);
+
+ (split.0, Some(split.1))
+ } else {
+ (&name[..], None)
+ };
+
+ self.members
+ .values()
+ .find(|member| {
+ let name_matches = member.user.read().unwrap().name == name;
+ let discrim_matches = match discrim {
+ Some(discrim) => member.user.read().unwrap().discriminator == discrim,
+ None => true,
+ };
+
+ name_matches && discrim_matches
+ }).or_else(|| self.members.values().find(|member| {
+ member.nick.as_ref().map_or(false, |nick| nick == name)
+ }))
+ }
+
+ /// 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 [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`GuildPrune`]: struct.GuildPrune.html
+ /// [`Member`]: struct.Member.html
+ /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
+ pub fn get_prune_count(&self, days: u16) -> Result<GuildPrune> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::KICK_MEMBERS;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.get_prune_count(days)
+ }
+
+ /// Retrieves the guild's webhooks.
+ ///
+ /// **Note**: Requires the [Manage Webhooks] permission.
+ ///
+ /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ #[inline]
+ pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
+ self.id.get_webhooks()
+ }
+
+ /// Returns the formatted URL of the guild's icon, if one exists.
+ pub fn icon_url(&self) -> Option<String> {
+ self.icon.as_ref().map(|icon|
+ format!(cdn!("/icons/{}/{}.webp"), self.id, icon))
+ }
+
+ /// Checks if the guild is 'large'. A guild is considered large if it has
+ /// more than 250 members.
+ #[inline]
+ pub fn is_large(&self) -> bool {
+ 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) -> Result<()> {
+ self.id.kick(user_id)
+ }
+
+ /// Leaves the guild.
+ #[inline]
+ pub fn leave(&self) -> Result<PartialGuild> {
+ self.id.leave()
+ }
+
+ /// 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) -> Result<()>
+ where C: Into<ChannelId>, U: Into<UserId> {
+ self.id.move_member(user_id, channel_id)
+ }
+
+ /// Calculate a [`User`]'s permissions in a given channel in the guild.
+ ///
+ /// [`User`]: struct.User.html
+ pub fn permissions_for<C, U>(&self, channel_id: C, user_id: U)
+ -> Permissions where C: Into<ChannelId>, U: Into<UserId> {
+ use super::permissions::*;
+
+ let user_id = user_id.into();
+
+ // The owner has all permissions in all cases.
+ if user_id == self.owner_id {
+ return Permissions::all();
+ }
+
+ let channel_id = channel_id.into();
+
+ // Start by retrieving the @everyone role's permissions.
+ let everyone = match self.roles.get(&RoleId(self.id.0)) {
+ Some(everyone) => everyone,
+ None => {
+ error!("(╯°□°)╯︵ ┻━┻ @everyone role ({}) missing in '{}'",
+ self.id,
+ self.name);
+
+ return Permissions::empty();
+ },
+ };
+
+ // Create a base set of permissions, starting with `@everyone`s.
+ let mut permissions = everyone.permissions;
+
+ let member = match self.members.get(&user_id) {
+ Some(member) => member,
+ None => return everyone.permissions,
+ };
+
+ for &role in &member.roles {
+ if let Some(role) = self.roles.get(&role) {
+ permissions |= role.permissions;
+ } else {
+ warn!("(╯°□°)╯︵ ┻━┻ {} on {} has non-existent role {:?}",
+ member.user.read().unwrap().id,
+ self.id,
+ role);
+ }
+ }
+
+ // Administrators have all permissions in any channel.
+ if permissions.contains(ADMINISTRATOR) {
+ return Permissions::all();
+ }
+
+ if let Some(channel) = self.channels.get(&channel_id) {
+ let channel = channel.read().unwrap();
+
+ // If this is a text channel, then throw out voice permissions.
+ if channel.kind == ChannelType::Text {
+ permissions &= !(CONNECT | SPEAK | MUTE_MEMBERS |
+ DEAFEN_MEMBERS | MOVE_MEMBERS | USE_VAD);
+ }
+
+ // Apply the permission overwrites for the channel for each of the
+ // overwrites that - first - applies to the member's roles, and then
+ // the member itself.
+ //
+ // First apply the denied permission overwrites for each, then apply
+ // the allowed.
+
+ // Roles
+ for overwrite in &channel.permission_overwrites {
+ if let PermissionOverwriteType::Role(role) = overwrite.kind {
+ if !member.roles.contains(&role) || role.0 == self.id.0 {
+ continue;
+ }
+
+ permissions = (permissions & !overwrite.deny) | overwrite.allow;
+ }
+ }
+
+ // Member
+ for overwrite in &channel.permission_overwrites {
+ if PermissionOverwriteType::Member(user_id) != overwrite.kind {
+ continue;
+ }
+
+ permissions = (permissions & !overwrite.deny) | overwrite.allow;
+ }
+ } else {
+ warn!("(╯°□°)╯︵ ┻━┻ Guild {} does not contain channel {}",
+ self.id,
+ channel_id);
+ }
+
+ // The default channel is always readable.
+ if channel_id.0 == self.id.0 {
+ permissions |= READ_MESSAGES;
+ }
+
+ // No SEND_MESSAGES => no message-sending-related actions
+ // If the member does not have the `SEND_MESSAGES` permission, then
+ // throw out message-able permissions.
+ if !permissions.contains(SEND_MESSAGES) {
+ permissions &= !(SEND_TTS_MESSAGES |
+ MENTION_EVERYONE |
+ EMBED_LINKS |
+ ATTACH_FILES);
+ }
+
+ // If the member does not have the `READ_MESSAGES` permission, then
+ // throw out actionable permissions.
+ if !permissions.contains(READ_MESSAGES) {
+ permissions &= KICK_MEMBERS | BAN_MEMBERS | ADMINISTRATOR |
+ MANAGE_GUILD | CHANGE_NICKNAME | MANAGE_NICKNAMES;
+ }
+
+ permissions
+ }
+
+ /// Performs a search request to the API for the guild's [`Message`]s.
+ ///
+ /// This will search all of the guild's [`Channel`]s at once, that you have
+ /// the [Read Message History] permission to. Use [`search_channels`] to
+ /// specify a list of [channel][`GuildChannel`]s to search, where all other
+ /// channels will be excluded.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Channel`]: enum.Channel.html
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ /// [`search_channels`]: #method.search_channels
+ /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
+ #[cfg(feature="cache")]
+ {
+ if CACHE.read().unwrap().user.bot {
+ return Err(Error::Client(ClientError::InvalidOperationAsBot));
+ }
+ }
+
+ self.id.search(f)
+ }
+
+ /// Performs a search request to the API for the guild's [`Message`]s in
+ /// given channels.
+ ///
+ /// This will search all of the messages in the guild's provided
+ /// [`Channel`]s by Id that you have the [Read Message History] permission
+ /// to. Use [`search`] to search all of a guild's [channel][`GuildChannel`]s
+ /// at once.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Channel`]: enum.Channel.html
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ /// [`search`]: #method.search
+ /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ pub fn search_channels<F>(&self, channel_ids: &[ChannelId], f: F)
+ -> Result<SearchResult> where F: FnOnce(Search) -> Search {
+ #[cfg(feature="cache")]
+ {
+ if CACHE.read().unwrap().user.bot {
+ return Err(Error::Client(ClientError::InvalidOperationAsBot));
+ }
+ }
+
+ self.id.search_channels(channel_ids, f)
+ }
+
+ /// Returns the formatted URL of the guild's splash image, if one exists.
+ pub fn splash_url(&self) -> Option<String> {
+ self.icon.as_ref().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: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
+ self.id.start_integration_sync(integration_id)
+ }
+
+ /// 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 [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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) -> Result<GuildPrune> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::KICK_MEMBERS;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.start_prune(days)
+ }
+
+ /// Unbans the given [`User`] from the guild.
+ ///
+ /// **Note**: Requires the [Ban Members] permission.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [`User`]: struct.User.html
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ pub fn unban<U: Into<UserId>>(&self, user_id: U) -> Result<()> {
+ #[cfg(feature="cache")]
+ {
+ let req = permissions::BAN_MEMBERS;
+
+ if !self.has_perms(req)? {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+ }
+
+ self.id.unban(user_id)
+ }
+}
+
+impl GuildInfo {
+ /// Returns the formatted URL of the guild's icon, if the guild has an icon.
+ pub fn icon_url(&self) -> Option<String> {
+ self.icon.as_ref().map(|icon|
+ format!(cdn!("/icons/{}/{}.webp"), self.id, icon))
+ }
+}
+
+impl From<PartialGuild> for GuildContainer {
+ fn from(guild: PartialGuild) -> GuildContainer {
+ GuildContainer::Guild(guild)
+ }
+}
+
+impl From<GuildId> for GuildContainer {
+ fn from(guild_id: GuildId) -> GuildContainer {
+ GuildContainer::Id(guild_id)
+ }
+}
+
+impl From<u64> for GuildContainer {
+ fn from(id: u64) -> GuildContainer {
+ GuildContainer::Id(GuildId(id))
+ }
+}
+
+impl InviteGuild {
+ /// Returns the formatted URL of the guild's splash image, if one exists.
+ pub fn splash_url(&self) -> Option<String> {
+ self.icon.as_ref().map(|icon|
+ format!(cdn!("/splashes/{}/{}.webp"), self.id, icon))
+ }
+}
+
+impl PossibleGuild<Guild> {
+ #[doc(hidden)]
+ pub fn decode(value: Value) -> Result<Self> {
+ let mut value = into_map(value)?;
+ if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
+ remove(&mut value, "id").and_then(GuildId::decode).map(PossibleGuild::Offline)
+ } else {
+ Guild::decode(Value::Object(value)).map(PossibleGuild::Online)
+ }
+ }
+
+ /// Retrieves the Id of the inner [`Guild`].
+ ///
+ /// [`Guild`]: struct.Guild.html
+ pub fn id(&self) -> GuildId {
+ match *self {
+ PossibleGuild::Offline(guild_id) => guild_id,
+ PossibleGuild::Online(ref live_guild) => live_guild.id,
+ }
+ }
+}
+
+impl PossibleGuild<PartialGuild> {
+ #[doc(hidden)]
+ pub fn decode(value: Value) -> Result<Self> {
+ let mut value = into_map(value)?;
+ if remove(&mut value, "unavailable").ok().and_then(|v| v.as_bool()).unwrap_or(false) {
+ remove(&mut value, "id").and_then(GuildId::decode).map(PossibleGuild::Offline)
+ } else {
+ PartialGuild::decode(Value::Object(value)).map(PossibleGuild::Online)
+ }
+ }
+
+ /// Retrieves the Id of the inner [`Guild`].
+ ///
+ /// [`Guild`]: struct.Guild.html
+ pub fn id(&self) -> GuildId {
+ match *self {
+ PossibleGuild::Offline(id) => id,
+ PossibleGuild::Online(ref live_guild) => live_guild.id,
+ }
+ }
+}
diff --git a/src/model/guild/partial_guild.rs b/src/model/guild/partial_guild.rs
new file mode 100644
index 0000000..e5d463e
--- /dev/null
+++ b/src/model/guild/partial_guild.rs
@@ -0,0 +1,482 @@
+use ::client::CACHE;
+use ::model::*;
+use ::utils::builder::{EditGuild, EditMember, EditRole, Search};
+
+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 [`ClientError::DeleteMessageDaysAmount`] if the number of
+ /// days' worth of messages to delete is over the maximum.
+ ///
+ /// [`ClientError::DeleteMessageDaysAmount`]: ../client/enum.ClientError.html#variant.DeleteMessageDaysAmount
+ /// [`User`]: struct.User.html
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ pub fn ban<U: Into<UserId>>(&self, user: U, delete_message_days: u8) -> Result<()> {
+ if delete_message_days > 7 {
+ return Err(Error::Client(ClientError::DeleteMessageDaysAmount(delete_message_days)));
+ }
+
+ self.id.ban(user, delete_message_days)
+ }
+
+ /// Creates a [`GuildChannel`] in the guild.
+ ///
+ /// Refer to [`rest::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);
+ /// ```
+ ///
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`rest::create_channel`]: ../client/rest/fn.create_channel.html
+ /// [Manage Channels]: permissions/constant.MANAGE_CHANNELS.html
+ #[inline]
+ pub fn create_channel(&self, name: &str, kind: ChannelType) -> Result<GuildChannel> {
+ self.id.create_channel(name, kind)
+ }
+
+ /// 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`]: ../utils/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) -> Result<Emoji> {
+ self.id.create_emoji(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) -> Result<()>
+ where I: Into<IntegrationId> {
+ self.id.create_integration(integration_id, 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 [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to perform bans.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.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) -> Result<Role> {
+ self.id.create_role(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) -> Result<PartialGuild> {
+ self.id.delete()
+ }
+
+ /// 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) -> Result<()> {
+ self.id.delete_emoji(emoji_id)
+ }
+
+ /// 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) -> Result<()> {
+ self.id.delete_integration(integration_id)
+ }
+
+ /// 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) -> Result<()> {
+ self.id.delete_role(role_id)
+ }
+
+ /// Edits the current guild with new data where specified.
+ ///
+ /// **Note**: Requires the current user to have the [Manage Guild]
+ /// permission.
+ ///
+ /// [`Context::edit_guild`]: ../client/struct.Context.html#method.edit_guild
+ /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ pub fn edit<F>(&mut self, f: F) -> Result<()>
+ where F: FnOnce(EditGuild) -> EditGuild {
+ match self.id.edit(f) {
+ Ok(guild) => {
+ self.afk_channel_id = guild.afk_channel_id;
+ self.afk_timeout = guild.afk_timeout;
+ self.default_message_notifications = guild.default_message_notifications;
+ self.emojis = guild.emojis;
+ self.features = guild.features;
+ self.icon = guild.icon;
+ self.mfa_level = guild.mfa_level;
+ self.name = guild.name;
+ self.owner_id = guild.owner_id;
+ self.region = guild.region;
+ self.roles = guild.roles;
+ self.splash = guild.splash;
+ self.verification_level = guild.verification_level;
+
+ Ok(())
+ },
+ Err(why) => Err(why),
+ }
+ }
+
+ /// 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) -> Result<Emoji> {
+ self.id.edit_emoji(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
+ /// 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) -> Result<()>
+ where F: FnOnce(EditMember) -> EditMember, U: Into<UserId> {
+ self.id.edit_member(user_id, 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 [`ClientError::InvalidPermissions`]
+ /// if the current user does not have permission to change their own
+ /// nickname.
+ ///
+ /// [`ClientError::InvalidPermissions`]: ../client/enum.ClientError.html#variant.InvalidPermissions
+ /// [Change Nickname]: permissions/constant.CHANGE_NICKNAME.html
+ #[inline]
+ pub fn edit_nickname(&self, new_nickname: Option<&str>) -> Result<()> {
+ self.id.edit_nickname(new_nickname)
+ }
+
+ /// Gets a partial amount of guild data by its Id.
+ ///
+ /// Requires that the current user be in the guild.
+ #[inline]
+ pub fn get<G: Into<GuildId>>(guild_id: G) -> Result<PartialGuild> {
+ guild_id.into().get()
+ }
+
+ /// Gets a list of the guild's bans.
+ ///
+ /// Requires the [Ban Members] permission.
+ ///
+ /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
+ #[inline]
+ pub fn get_bans(&self) -> Result<Vec<Ban>> {
+ self.id.get_bans()
+ }
+
+ /// Gets all of the guild's channels over the REST API.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ #[inline]
+ pub fn get_channels(&self) -> Result<HashMap<ChannelId, GuildChannel>> {
+ self.id.get_channels()
+ }
+
+ /// Gets an emoji in the guild by Id.
+ ///
+ /// Requires the [Manage Emojis] permission.
+ ///
+ /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ #[inline]
+ pub fn get_emoji<E: Into<EmojiId>>(&self, emoji_id: E) -> Result<Emoji> {
+ self.id.get_emoji(emoji_id)
+ }
+
+ /// Gets a list of all of the guild's emojis.
+ ///
+ /// Requires the [Manage Emojis] permission.
+ ///
+ /// [Manage Emojis]: permissions/constant.MANAGE_EMOJIS.html
+ #[inline]
+ pub fn get_emojis(&self) -> Result<Vec<Emoji>> {
+ self.id.get_emojis()
+ }
+
+ /// Gets all integration of the guild.
+ ///
+ /// This performs a request over the REST API.
+ #[inline]
+ pub fn get_integrations(&self) -> Result<Vec<Integration>> {
+ self.id.get_integrations()
+ }
+
+ /// Gets all of the guild's invites.
+ ///
+ /// Requires the [Manage Guild] permission.
+ ///
+ /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
+ #[inline]
+ pub fn get_invites(&self) -> Result<Vec<RichInvite>> {
+ self.id.get_invites()
+ }
+
+ /// Gets a user's [`Member`] for the guild by Id.
+ ///
+ /// [`Guild`]: struct.Guild.html
+ /// [`Member`]: struct.Member.html
+ pub fn get_member<U: Into<UserId>>(&self, user_id: U) -> Result<Member> {
+ self.id.get_member(user_id)
+ }
+
+ /// 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 get_members<U>(&self, limit: Option<u64>, after: Option<U>)
+ -> Result<Vec<Member>> where U: Into<UserId> {
+ self.id.get_members(limit, after)
+ }
+
+ /// 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 get_prune_count(&self, days: u16) -> Result<GuildPrune> {
+ self.id.get_prune_count(days)
+ }
+
+ /// Retrieves the guild's webhooks.
+ ///
+ /// **Note**: Requires the [Manage Webhooks] permission.
+ ///
+ /// [Manage Webhooks]: permissions/constant.MANAGE_WEBHOOKS.html
+ #[inline]
+ pub fn get_webhooks(&self) -> Result<Vec<Webhook>> {
+ self.id.get_webhooks()
+ }
+
+ /// 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) -> Result<()> {
+ self.id.kick(user_id)
+ }
+
+ /// Returns a formatted URL of the guild's icon, if the guild has an icon.
+ pub fn icon_url(&self) -> Option<String> {
+ self.icon.as_ref().map(|icon|
+ format!(cdn!("/icons/{}/{}.webp"), self.id, icon))
+ }
+
+ /// Leaves the guild.
+ #[inline]
+ pub fn leave(&self) -> Result<PartialGuild> {
+ self.id.leave()
+ }
+
+ /// 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) -> Result<()>
+ where C: Into<ChannelId>, U: Into<UserId> {
+ self.id.move_member(user_id, channel_id)
+ }
+
+ /// Performs a search request to the API for the guild's [`Message`]s.
+ ///
+ /// This will search all of the guild's [`Channel`]s at once, that you have
+ /// the [Read Message History] permission to. Use [`search_channels`] to
+ /// specify a list of [channel][`GuildChannel`]s to search, where all other
+ /// channels will be excluded.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Channel`]: enum.Channel.html
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ /// [`search_channels`]: #method.search_channels
+ /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ pub fn search<F: FnOnce(Search) -> Search>(&self, f: F) -> Result<SearchResult> {
+ #[cfg(feature="cache")]
+ {
+ if CACHE.read().unwrap().user.bot {
+ return Err(Error::Client(ClientError::InvalidOperationAsBot));
+ }
+ }
+
+ self.id.search(f)
+ }
+
+ /// Performs a search request to the API for the guild's [`Message`]s in
+ /// given channels.
+ ///
+ /// This will search all of the messages in the guild's provided
+ /// [`Channel`]s by Id that you have the [Read Message History] permission
+ /// to. Use [`search`] to search all of a guild's [channel][`GuildChannel`]s
+ /// at once.
+ ///
+ /// Refer to the documentation for the [`Search`] builder for examples and
+ /// more information.
+ ///
+ /// **Note**: Bot users can not search.
+ ///
+ /// # Errors
+ ///
+ /// If the `cache` is enabled, returns a
+ /// [`ClientError::InvalidOperationAsBot`] if the current user is a bot.
+ ///
+ /// [`ClientError::InvalidOperationAsBot`]: ../client/enum.ClientError.html#variant.InvalidOperationAsBot
+ /// [`Channel`]: enum.Channel.html
+ /// [`GuildChannel`]: struct.GuildChannel.html
+ /// [`Message`]: struct.Message.html
+ /// [`Search`]: ../utils/builder/struct.Search.html
+ /// [`search`]: #method.search
+ /// [Read Message History]: permissions/constant.READ_MESSAGE_HISTORY.html
+ pub fn search_channels<F>(&self, channel_ids: &[ChannelId], f: F)
+ -> Result<SearchResult> where F: FnOnce(Search) -> Search {
+ #[cfg(feature="cache")]
+ {
+ if CACHE.read().unwrap().user.bot {
+ return Err(Error::Client(ClientError::InvalidOperationAsBot));
+ }
+ }
+
+ self.id.search_channels(channel_ids, f)
+ }
+
+ /// Returns the formatted URL of the guild's splash image, if one exists.
+ pub fn splash_url(&self) -> Option<String> {
+ self.icon.as_ref().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: Into<IntegrationId>>(&self, integration_id: I) -> Result<()> {
+ self.id.start_integration_sync(integration_id)
+ }
+
+ /// 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) -> Result<()> {
+ self.id.unban(user_id)
+ }
+}
diff --git a/src/model/guild/role.rs b/src/model/guild/role.rs
new file mode 100644
index 0000000..79ffdf8
--- /dev/null
+++ b/src/model/guild/role.rs
@@ -0,0 +1,151 @@
+use std::cmp::Ordering;
+use std::fmt::{Display, Formatter, Result as FmtResult};
+use ::client::{CACHE, rest};
+use ::internal::prelude::*;
+use ::model::*;
+use ::utils::builder::EditRole;
+
+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) -> Result<()> {
+ rest::delete_role(self.find_guild()?.0, self.id.0)
+ }
+
+ /// Edits a [`Role`], optionally setting its new fields.
+ ///
+ /// Requires the [Manage Roles] permission.
+ ///
+ /// # Examples
+ ///
+ /// Make a role hoisted:
+ ///
+ /// ```rust,ignore
+ /// // assuming a `guild` and `role_id` have been bound
+ //
+ /// guild.edit_role(role_id, |r| r.hoist(true));
+ /// ```
+ ///
+ /// [`Role`]: struct.Role.html
+ /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
+ #[cfg(feature="cache")]
+ pub fn edit_role<F: FnOnce(EditRole) -> EditRole>(&self, f: F) -> Result<Role> {
+ match self.find_guild() {
+ Ok(guild_id) => guild_id.edit_role(self.id, f),
+ Err(why) => Err(why),
+ }
+ }
+
+ /// Searches the cache for the guild that owns the role.
+ ///
+ /// # Errors
+ ///
+ /// Returns a [`ClientError::GuildNotFound`] if a guild is not in the cache
+ /// that contains the role.
+ ///
+ /// [`ClientError::GuildNotFound`]: ../client/enum.ClientError.html#variant.GuildNotFound
+ #[cfg(feature="cache")]
+ pub fn find_guild(&self) -> Result<GuildId> {
+ for guild in CACHE.read().unwrap().guilds.values() {
+ let guild = guild.read().unwrap();
+
+ if guild.roles.contains_key(&RoleId(self.id.0)) {
+ return Ok(guild.id);
+ }
+ }
+
+ Err(Error::Client(ClientError::GuildNotFound))
+ }
+
+ /// Check that the role has the given permission.
+ #[inline]
+ pub fn has_permission(&self, permission: Permissions) -> bool {
+ self.permissions.contains(permission)
+ }
+
+ /// Checks whether the role has all of the given permissions.
+ ///
+ /// The 'precise' argument is used to check if the role's permissions are
+ /// precisely equivalent to the given permissions. If you need only check
+ /// that the role has at least the given permissions, pass `false`.
+ pub fn has_permissions(&self, permissions: Permissions, precise: bool)
+ -> bool {
+ if precise {
+ self.permissions == permissions
+ } else {
+ self.permissions.contains(permissions)
+ }
+ }
+}
+
+impl Display for Role {
+ /// Format a mention for the role, pinging its members.
+ // This is in the format of: `<@&ROLE_ID>`.
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ Display::fmt(&self.mention(), f)
+ }
+}
+
+impl Eq for Role {}
+
+impl Ord for Role {
+ fn cmp(&self, other: &Role) -> Ordering {
+ if self.position == other.position {
+ self.id.cmp(&other.id)
+ } else {
+ self.position.cmp(&other.position)
+ }
+ }
+}
+
+impl PartialEq for Role {
+ fn eq(&self, other: &Role) -> bool {
+ self.id == other.id
+ }
+}
+
+impl PartialOrd for Role {
+ fn partial_cmp(&self, other: &Role) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl RoleId {
+ /// Search the cache for the role.
+ #[cfg(feature="cache")]
+ pub fn find(&self) -> Option<Role> {
+ let cache = CACHE.read().unwrap();
+
+ for guild in cache.guilds.values() {
+ let guild = guild.read().unwrap();
+
+ if !guild.roles.contains_key(self) {
+ continue;
+ }
+
+ if let Some(role) = guild.roles.get(self) {
+ return Some(role.clone());
+ }
+ }
+
+ None
+ }
+}
+
+impl Display for RoleId {
+ fn fmt(&self, f: &mut Formatter) -> FmtResult {
+ Display::fmt(&self.0, f)
+ }
+}
+
+impl From<Role> for RoleId {
+ /// Gets the Id of a role.
+ fn from(role: Role) -> RoleId {
+ role.id
+ }
+}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index c871f90..ad70076 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -19,7 +19,6 @@ mod utils;
pub mod event;
pub mod permissions;
-
mod channel;
mod gateway;
mod guild;