diff options
| author | Austin Hellyer <[email protected]> | 2016-11-05 14:44:11 -0700 |
|---|---|---|
| committer | Austin Hellyer <[email protected]> | 2016-11-05 14:44:11 -0700 |
| commit | e3416d3f511894553b7625c501043077977ccb4d (patch) | |
| tree | c2f9f5d35a90c649dcc67c6d15e4a9ae8478cdeb /src/client | |
| parent | Update URLs to GitLab (diff) | |
| download | serenity-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/client')
| -rw-r--r-- | src/client/context.rs | 86 | ||||
| -rw-r--r-- | src/client/dispatch.rs | 24 | ||||
| -rw-r--r-- | src/client/event_store.rs | 2 | ||||
| -rw-r--r-- | src/client/http.rs | 56 | ||||
| -rw-r--r-- | src/client/mod.rs | 20 | ||||
| -rw-r--r-- | src/client/ratelimiting.rs | 1 |
6 files changed, 187 insertions, 2 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, |