aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAustin Hellyer <[email protected]>2016-11-05 14:44:11 -0700
committerAustin Hellyer <[email protected]>2016-11-05 14:44:11 -0700
commite3416d3f511894553b7625c501043077977ccb4d (patch)
treec2f9f5d35a90c649dcc67c6d15e4a9ae8478cdeb /src
parentUpdate URLs to GitLab (diff)
downloadserenity-e3416d3f511894553b7625c501043077977ccb4d.tar.xz
serenity-e3416d3f511894553b7625c501043077977ccb4d.zip
Add message reactions
Add message reaction structs and an enum to differentiate between the two types of reactions, as well as event decoding and event handlers with dispatches. The following is, more or less, what is added: - `reactions` field to the `Message` struct; - `MessageReaction` struct, which is a consolidated form of reaction, containing the type of reaction, the number of them, and whether the current user has performed that type of reaction; - `Reaction`, a struct containing the information about a reaction - `ReactionType`, an enum to differentiate between the two types of reactions: `Custom` (a guild's custom emoji) and `Unicode` (twemoji); - Decoding for `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE`; - Permission flag `ADD_REACTIONS`; - `Message::react` method; - Three `http` payload senders: `create_reaction`, `delete_reaction`, and `get_reaction_users`; - Three `Context` methods of equal names to the above.
Diffstat (limited to 'src')
-rw-r--r--src/client/context.rs86
-rw-r--r--src/client/dispatch.rs24
-rw-r--r--src/client/event_store.rs2
-rw-r--r--src/client/http.rs56
-rw-r--r--src/client/mod.rs20
-rw-r--r--src/client/ratelimiting.rs1
-rw-r--r--src/model/channel.rs202
-rw-r--r--src/model/gateway.rs30
-rw-r--r--src/model/guild.rs3
-rw-r--r--src/model/permissions.rs24
-rw-r--r--src/model/utils.rs4
11 files changed, 441 insertions, 11 deletions
diff --git a/src/client/context.rs b/src/client/context.rs
index 6402e9b..2d3d3e4 100644
--- a/src/client/context.rs
+++ b/src/client/context.rs
@@ -232,6 +232,26 @@ impl Context {
http::create_private_channel(map)
}
+ /// React to a [`Message`] with a custom [`Emoji`] or unicode character.
+ ///
+ /// **Note**: Requires the [Add Reactions] permission.
+ ///
+ /// [`Emoji`]: ../models/struct.Emoji.html
+ /// [`Message`]: ../models/struct.Message.html
+ /// [Add Reactions]: ../models/permissions/constant.ADD_REACTIONS.html
+ pub fn create_reaction<C, M, R>(&self,
+ channel_id: C,
+ message_id: M,
+ reaction_type: R)
+ -> Result<()>
+ where C: Into<ChannelId>,
+ M: Into<MessageId>,
+ R: Into<ReactionType> {
+ http::create_reaction(channel_id.into().0,
+ message_id.into().0,
+ reaction_type.into())
+ }
+
pub fn create_role<F, G>(&self, guild_id: G, f: F) -> Result<Role>
where F: FnOnce(EditRole) -> EditRole, G: Into<GuildId> {
let id = guild_id.into().0;
@@ -331,6 +351,30 @@ impl Context {
http::delete_permission(channel_id.into().0, id)
}
+
+ /// Deletes the given [`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.
+ ///
+ /// [`Reaction`]: ../models/struct.Reaction.html
+ /// [Manage Messages]: ../models/permissions/constant.MANAGE_MESSAGES.html
+ pub fn delete_reaction<C, M, R>(&self,
+ channel_id: C,
+ message_id: M,
+ user_id: Option<UserId>,
+ reaction_type: R)
+ -> Result<()>
+ where C: Into<ChannelId>,
+ M: Into<MessageId>,
+ R: Into<ReactionType> {
+ http::delete_reaction(channel_id.into().0,
+ message_id.into().0,
+ user_id.map(|uid| uid.0),
+ reaction_type.into())
+ }
+
pub fn delete_role<G, R>(&self, guild_id: G, role_id: R) -> Result<()>
where G: Into<GuildId>, R: Into<RoleId> {
http::delete_role(guild_id.into().0, role_id.into().0)
@@ -650,6 +694,48 @@ impl Context {
http::get_messages(channel_id.into().0, &query)
}
+ /// 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
+ pub fn get_reaction_users<C, M, R, U>(&self,
+ channel_id: C,
+ message_id: M,
+ reaction_type: R,
+ limit: Option<u8>,
+ after: Option<U>)
+ -> Result<Vec<User>>
+ where C: Into<ChannelId>,
+ M: Into<MessageId>,
+ R: Into<ReactionType>,
+ U: Into<UserId> {
+ let limit = limit.map(|x| if x > 100 { 100 } else { x }).unwrap_or(50);
+
+ http::get_reaction_users(channel_id.into().0,
+ message_id.into().0,
+ reaction_type.into(),
+ limit,
+ after.map(|u| u.into().0))
+ }
+
pub fn get_voice_regions(&self) -> Result<Vec<VoiceRegion>> {
http::get_voice_regions()
}
diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs
index 5e206ee..567bc6e 100644
--- a/src/client/dispatch.rs
+++ b/src/client/dispatch.rs
@@ -488,6 +488,30 @@ pub fn dispatch(event: Result<Event>,
});
}
},
+ Ok(Event::ReactionAdd(event)) => {
+ if let Some(ref handler) = handler!(on_reaction_add, event_store) {
+ let context = context(Some(event.reaction.channel_id),
+ conn,
+ login_type);
+ let handler = handler.clone();
+
+ thread::spawn(move || {
+ (handler)(context, event.reaction);
+ });
+ }
+ },
+ Ok(Event::ReactionRemove(event)) => {
+ if let Some(ref handler) = handler!(on_reaction_remove, event_store) {
+ let context = context(Some(event.reaction.channel_id),
+ conn,
+ login_type);
+ let handler = handler.clone();
+
+ thread::spawn(move || {
+ (handler)(context, event.reaction);
+ });
+ }
+ },
Ok(Event::Ready(event)) => {
if let Some(ref handler) = handler!(on_ready, event_store) {
update!(update_with_ready, event);
diff --git a/src/client/event_store.rs b/src/client/event_store.rs
index 4bd8459..0ba5301 100644
--- a/src/client/event_store.rs
+++ b/src/client/event_store.rs
@@ -55,6 +55,8 @@ pub struct EventStore {
pub on_message_ack: Option<Arc<Fn(Context, ChannelId, Option<MessageId>) + Send + Sync + 'static>>,
pub on_message_delete: Option<Arc<Fn(Context, ChannelId, MessageId) + Send + Sync + 'static>>,
pub on_message_delete_bulk: Option<Arc<Fn(Context, ChannelId, Vec<MessageId>) + Send + Sync + 'static>>,
+ pub on_reaction_add: Option<Arc<Fn(Context, Reaction) + Send + Sync + 'static>>,
+ pub on_reaction_remove: Option<Arc<Fn(Context, Reaction) + Send + Sync + 'static>>,
pub on_message_update: Option<Arc<Fn(Context, MessageUpdateEvent) + Send + Sync + 'static>>,
pub on_note_update: Option<Arc<Fn(Context, UserId, String) + Send + Sync + 'static>>,
pub on_presence_replace: Option<Arc<Fn(Context, Vec<Presence>) + Send + Sync + 'static>>,
diff --git a/src/client/http.rs b/src/client/http.rs
index 3421359..9d29ae6 100644
--- a/src/client/http.rs
+++ b/src/client/http.rs
@@ -116,8 +116,7 @@ pub fn create_guild(map: Value) -> Result<Guild> {
Guild::decode(try!(serde_json::from_reader(response)))
}
-pub fn create_guild_integration(
- guild_id: u64,
+pub fn create_guild_integration(guild_id: u64,
integration_id: u64,
map: Value) -> Result<()> {
let body = try!(serde_json::to_string(&map));
@@ -161,6 +160,18 @@ pub fn create_private_channel(map: Value)
PrivateChannel::decode(try!(serde_json::from_reader(response)))
}
+pub fn create_reaction(channel_id: u64,
+ message_id: u64,
+ reaction_type: ReactionType)
+ -> Result<()> {
+ verify(204, request!(Route::ChannelsIdMessagesIdReactionsUserIdType,
+ put,
+ "/channels/{}/messages/{}/reactions/{}/@me",
+ channel_id,
+ message_id,
+ reaction_type.as_data()))
+}
+
pub fn create_role(guild_id: u64) -> Result<Role> {
let body = String::from("{}");
let response = request!(Route::GuildsIdRoles,
@@ -236,6 +247,22 @@ pub fn delete_permission(channel_id: u64, target_id: u64)
target_id))
}
+pub fn delete_reaction(channel_id: u64,
+ message_id: u64,
+ user_id: Option<u64>,
+ reaction_type: ReactionType)
+ -> Result<()> {
+ let user = user_id.map(|uid| uid.to_string()).unwrap_or("@me".to_string());
+
+ verify(204, request!(Route::ChannelsIdMessagesIdReactionsUserIdType,
+ delete,
+ "/channels/{}/messages/{}/reactions/{}/{}",
+ channel_id,
+ message_id,
+ reaction_type.as_data(),
+ user))
+}
+
pub fn delete_role(guild_id: u64, role_id: u64) -> Result<()> {
verify(204, request!(Route::GuildsIdRolesId,
delete,
@@ -508,6 +535,31 @@ pub fn get_pins(channel_id: u64) -> Result<Vec<Message>> {
decode_array(try!(serde_json::from_reader(response)), Message::decode)
}
+pub fn get_reaction_users(channel_id: u64,
+ message_id: u64,
+ reaction_type: ReactionType,
+ limit: u8,
+ after: Option<u64>)
+ -> Result<Vec<User>> {
+ let mut uri = format!("/channels/{}/messages/{}/reactions/{}?limit={}",
+ channel_id,
+ message_id,
+ reaction_type.as_data(),
+ limit);
+
+ if let Some(user_id) = after {
+ uri.push_str("&after=");
+ uri.push_str(&user_id.to_string());
+ }
+
+ let response = request!(Route::ChannelsIdMessagesIdReactionsUserIdType,
+ get,
+ "{}",
+ uri);
+
+ decode_array(try!(serde_json::from_reader(response)), User::decode)
+}
+
pub fn get_user(user_id: u64) -> Result<CurrentUser> {
let response = request!(Route::UsersId, get, "/users/{}", user_id);
diff --git a/src/client/mod.rs b/src/client/mod.rs
index 8e291fb..50461c8 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -685,6 +685,26 @@ impl Client {
.on_presence_update = Some(Arc::new(handler));
}
+ /// Attached a handler for when a [`ReactionAdd`] is received.
+ ///
+ /// [`ReactionAdd`]: ../model/enum.Event.html#ReactionAdd.v
+ pub fn on_reaction_add<F>(&mut self, handler: F)
+ where F: Fn(Context, Reaction) + Send + Sync + 'static {
+ self.event_store.lock()
+ .unwrap()
+ .on_reaction_add = Some(Arc::new(handler))
+ }
+
+ /// Attached a handler for when a [`ReactionRemove`] is received.
+ ///
+ /// [`ReactionRemove`]: ../model/enum.Event.html#ReactionRemove.v
+ pub fn on_reaction_remove<F>(&mut self, handler: F)
+ where F: Fn(Context, Reaction) + Send + Sync + 'static {
+ self.event_store.lock()
+ .unwrap()
+ .on_reaction_remove = Some(Arc::new(handler))
+ }
+
/// Register an event to be called whenever a Ready event is received.
///
/// Registering a handler for the ready event is good for noting when your
diff --git a/src/client/ratelimiting.rs b/src/client/ratelimiting.rs
index f5f7cdb..ae886a9 100644
--- a/src/client/ratelimiting.rs
+++ b/src/client/ratelimiting.rs
@@ -22,6 +22,7 @@ pub enum Route {
ChannelsIdMessages,
ChannelsIdMessagesBulkDelete,
ChannelsIdMessagesId,
+ ChannelsIdMessagesIdReactionsUserIdType,
ChannelsIdPermissionsOverwriteId,
ChannelsIdPins,
ChannelsIdPinsMessageId,
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),
+ }
+ }
+}
diff --git a/src/model/gateway.rs b/src/model/gateway.rs
index b267164..cf226df 100644
--- a/src/model/gateway.rs
+++ b/src/model/gateway.rs
@@ -212,6 +212,16 @@ pub struct PresencesReplaceEvent {
pub presences: Vec<Presence>,
}
+#[derive(Clone, Debug)]
+pub struct ReactionAddEvent {
+ pub reaction: Reaction,
+}
+
+#[derive(Clone, Debug)]
+pub struct ReactionRemoveEvent {
+ pub reaction: Reaction,
+}
+
/// The "Ready" event, containing initial state
#[derive(Clone, Debug)]
pub struct ReadyEvent {
@@ -439,6 +449,18 @@ pub enum Event {
PresenceUpdate(PresenceUpdateEvent),
/// The precense list of the user's friends should be replaced entirely
PresencesReplace(PresencesReplaceEvent),
+ /// A reaction was added to a message.
+ ///
+ /// Fires the [`on_message_reaction_add`] event handler.
+ ///
+ /// [`on_message_reaction_add`]: ../client/struct.Client.html#method.on_message_reaction_add
+ ReactionAdd(ReactionAddEvent),
+ /// A reaction was removed to a message.
+ ///
+ /// Fires the [`on_message_reaction_remove`] event handler.
+ ///
+ /// [`on_message_reaction_remove`]: ../client/struct.Client.html#method.on_message_reaction_remove
+ ReactionRemove(ReactionRemoveEvent),
/// The first event in a connection, containing the initial state.
///
/// May also be received at a later time in the event of a reconnect.
@@ -629,6 +651,14 @@ impl Event {
channel_id: try!(remove(&mut value, "channel_id").and_then(ChannelId::decode)),
ids: try!(decode_array(try!(remove(&mut value, "ids")), MessageId::decode)),
}))
+ } else if kind == "MESSAGE_REACTION_ADD" {
+ Ok(Event::ReactionAdd(ReactionAddEvent {
+ reaction: try!(Reaction::decode(Value::Object(value)))
+ }))
+ } else if kind == "MESSAG_REACTION_REMOVE" {
+ Ok(Event::ReactionRemove(ReactionRemoveEvent {
+ reaction: try!(Reaction::decode(Value::Object(value)))
+ }))
} else if kind == "MESSAGE_UPDATE" {
missing!(value, Event::MessageUpdate(MessageUpdateEvent {
id: try!(remove(&mut value, "id").and_then(MessageId::decode)),
diff --git a/src/model/guild.rs b/src/model/guild.rs
index 6bc14c5..1e4fea8 100644
--- a/src/model/guild.rs
+++ b/src/model/guild.rs
@@ -95,7 +95,8 @@ impl Emoji {
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>`.
+ ///
+ /// This is in the format of: `<:NAME:EMOJI_ID>`.
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(f.write_str("<:"));
try!(f.write_str(&self.name));
diff --git a/src/model/permissions.rs b/src/model/permissions.rs
index 7eccc5c..3bcf219 100644
--- a/src/model/permissions.rs
+++ b/src/model/permissions.rs
@@ -42,6 +42,7 @@ use ::prelude::*;
///
/// This includes the following permissions:
///
+/// - [Add Reactions]
/// - [Attach Files]
/// - [Change Nickname]
/// - [Connect]
@@ -65,6 +66,7 @@ use ::prelude::*;
/// permissions::general().toggle(permissions::SEND_TTS_MESSAGES);
/// ```
///
+/// [Add Reactions]: constant.ADD_REACTIONS.html
/// [Attach Files]: constant.ATTACH_FILES.html
/// [Change Nickname]: constant.CHANGE_NICKNAME.html
/// [Connect]: constant.CONNECT.html
@@ -81,9 +83,9 @@ use ::prelude::*;
pub fn general() -> Permissions {
use self::*;
- ATTACH_FILES | CHANGE_NICKNAME | CONNECT | CREATE_INVITE | EMBED_LINKS |
- MENTION_EVERYONE | READ_MESSAGE_HISTORY | READ_MESSAGES | SEND_MESSAGES |
- SEND_TTS_MESSAGES | SPEAK | USE_VAD | USE_EXTERNAL_EMOJIS
+ ADD_REACTIONS | ATTACH_FILES | CHANGE_NICKNAME | CONNECT | CREATE_INVITE |
+ EMBED_LINKS | MENTION_EVERYONE | READ_MESSAGE_HISTORY | READ_MESSAGES |
+ SEND_MESSAGES | SEND_TTS_MESSAGES | SPEAK | USE_VAD | USE_EXTERNAL_EMOJIS
}
/// Returns a set of text-only permissions with the original `@everyone`
@@ -91,6 +93,7 @@ pub fn general() -> Permissions {
///
/// This includes the text permissions given via [`general`]:
///
+/// - [Add Reactions]
/// - [Attach Files]
/// - [Change Nickname]
/// - [Create Invite]
@@ -103,6 +106,7 @@ pub fn general() -> Permissions {
/// - [Use External Emojis]
///
/// [`general`]: fn.general.html
+/// [Add Reactions]: constant.ADD_REACTIONS.html
/// [Attach Files]: constant.ATTACH_FILES.html
/// [Change Nickname]: constant.CHANGE_NICKNAME.html
/// [Create Invite]: constant.CREATE_INVITE.html
@@ -116,9 +120,9 @@ pub fn general() -> Permissions {
pub fn text() -> Permissions {
use self::*;
- ATTACH_FILES | CHANGE_NICKNAME | CREATE_INVITE | EMBED_LINKS |
- MENTION_EVERYONE | READ_MESSAGE_HISTORY | READ_MESSAGES | SEND_MESSAGES |
- SEND_TTS_MESSAGES | USE_EXTERNAL_EMOJIS
+ ADD_REACTIONS | ATTACH_FILES | CHANGE_NICKNAME | CREATE_INVITE |
+ EMBED_LINKS | MENTION_EVERYONE | READ_MESSAGE_HISTORY | READ_MESSAGES |
+ SEND_MESSAGES | SEND_TTS_MESSAGES | USE_EXTERNAL_EMOJIS
}
/// Returns a set of voice-only permissions with the original `@everyone`
@@ -166,6 +170,14 @@ bitflags! {
///
/// [guild]: ../struct.LiveGuild.html
const MANAGE_GUILD = 1 << 5,
+ /// [`Member`]s with this permission can add new [`Reaction`]s to a
+ /// [`Message`]. Members can still react using reactions already added
+ /// to messages without this permission.
+ ///
+ /// [`Member`]: ../struct.Member.html
+ /// [`Message`]: ../struct.Message.html
+ /// [`Reaction`]: ../struct.Reaction.html
+ const ADD_REACTIONS = 1 << 6,
/// Allows reading messages in a guild channel. If a user does not have
/// this permission, then they will not be able to see the channel.
const READ_MESSAGES = 1 << 10,
diff --git a/src/model/utils.rs b/src/model/utils.rs
index 4ad97bf..0dbc41d 100644
--- a/src/model/utils.rs
+++ b/src/model/utils.rs
@@ -277,8 +277,8 @@ pub fn remove(map: &mut BTreeMap<String, Value>, key: &str) -> Result<Value> {
#[doc(hidden)]
pub fn user_has_perms(channel_id: ChannelId,
- mut permissions: Permissions)
- -> Result<bool> {
+ mut permissions: Permissions)
+ -> Result<bool> {
let state = STATE.lock().unwrap();
let current_user = &state.user;