aboutsummaryrefslogtreecommitdiff
path: root/src/model/channel.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/model/channel.rs')
-rw-r--r--src/model/channel.rs202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/model/channel.rs b/src/model/channel.rs
index ca9a3f8..fa26bc5 100644
--- a/src/model/channel.rs
+++ b/src/model/channel.rs
@@ -317,6 +317,31 @@ impl Message {
http::pin_message(self.channel_id.0, self.id.0)
}
+ /// React to the message with a custom [`Emoji`] or unicode character.
+ ///
+ /// **Note**: Requires the [Add Reactions] permission.
+ ///
+ /// # Errors
+ ///
+ /// 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<()> {
+ let req = permissions::ADD_REACTIONS;
+
+ if !try!(utils::user_has_perms(self.channel_id, req)) {
+ return Err(Error::Client(ClientError::InvalidPermissions(req)));
+ }
+
+ http::create_reaction(self.channel_id.0,
+ self.id.0,
+ reaction_type.into())
+ }
+
/// Replies to the user, mentioning them prior to the content in the form
/// of: `@<USER_ID>: YOUR_CONTENT`.
///
@@ -603,3 +628,180 @@ impl fmt::Display for PublicChannel {
fmt::Display::fmt(&self.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
+ ///
+ /// 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 = if self.user_id == STATE.lock().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)));
+ }
+ }
+
+ http::delete_reaction(self.channel_id.0,
+ self.message_id.0,
+ user,
+ self.emoji.clone())
+ }
+
+ /// 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> {
+ http::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.
+ #[inline(always)]
+ pub fn as_data(&self) -> String {
+ match *self {
+ ReactionType::Custom { id, ref name } => {
+ format!("{}:{}", id, name)
+ },
+ ReactionType::Unicode(ref unicode) => unicode.clone(),
+ }
+ }
+
+ pub fn decode(value: Value) -> Result<Self> {
+ let mut map = try!(into_map(value));
+ let name = try!(remove(&mut map, "name").and_then(into_string));
+
+ // Only custom emoji reactions (`ReactionType::Custom`) have an Id.
+ Ok(match try!(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 } => {
+ try!(f.write_char('<'));
+ try!(f.write_char(':'));
+ try!(f.write_str(&name));
+ try!(f.write_char(':'));
+ try!(fmt::Display::fmt(&id, f));
+ f.write_char('>')
+ },
+ ReactionType::Unicode(ref unicode) => f.write_str(&unicode),
+ }
+ }
+}