From 2fc70a60fb1d3e390c27e547da725de504264d8d Mon Sep 17 00:00:00 2001 From: Abdulla Abdurakhmanov Date: Fri, 19 Jun 2020 15:08:51 +0300 Subject: [PATCH] Slack Conversations API updates --- src/client/src/api/conversations.rs | 336 +++++++++++++++++++++++++++- src/models/src/common/channel.rs | 23 ++ src/models/src/common/mod.rs | 19 ++ src/models/src/messages/mod.rs | 4 +- 4 files changed, 368 insertions(+), 14 deletions(-) diff --git a/src/client/src/api/conversations.rs b/src/client/src/api/conversations.rs index ff69f5a..a0c3a64 100644 --- a/src/client/src/api/conversations.rs +++ b/src/client/src/api/conversations.rs @@ -3,14 +3,14 @@ //! use rsb_derive::Builder; +use rvstruct::ValueStruct; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use rvstruct::ValueStruct; use crate::*; +use futures::future::{BoxFuture, FutureExt}; use slack_morphism_models::*; use std::collections::HashSet; -use futures::future::{BoxFuture, FutureExt}; impl<'a> SlackClientSession<'a> { /// @@ -57,7 +57,7 @@ impl<'a> SlackClientSession<'a> { ("channel", req.channel.as_ref().map(|x| x.value())), ("cursor", req.cursor.as_ref().map(|x| x.value())), ("limit", req.limit.map(|v| v.to_string()).as_ref()), - ("inclusive",req.inclusive.map(|v| v.to_string()).as_ref()), + ("inclusive", req.inclusive.map(|v| v.to_string()).as_ref()), ("latest", req.latest.as_ref().map(|x| x.value())), ("oldest", req.oldest.as_ref().map(|x| x.value())), ], @@ -77,13 +77,147 @@ impl<'a> SlackClientSession<'a> { "conversations.info", &vec![ ("channel", Some(req.channel.value())), - ("include_num_members", req.include_num_members.map(|v| v.to_string()).as_ref()), - ("include_locale",req.include_locale.map(|v| v.to_string()).as_ref()) + ( + "include_num_members", + req.include_num_members.map(|v| v.to_string()).as_ref(), + ), + ( + "include_locale", + req.include_locale.map(|v| v.to_string()).as_ref(), + ), ], ) .await } + /// + /// https://api.slack.com/methods/conversations.invite + /// + pub async fn conversations_invite( + &self, + req: &SlackApiConversationsInviteRequest, + ) -> ClientResult { + self.http_api.http_post("conversations.invite", req).await + } + + /// + /// https://api.slack.com/methods/conversations.join + /// + pub async fn conversations_join( + &self, + req: &SlackApiConversationsJoinRequest, + ) -> ClientResult { + self.http_api.http_post("conversations.join", req).await + } + + /// + /// https://api.slack.com/methods/conversations.kick + /// + pub async fn conversations_kick( + &self, + req: &SlackApiConversationsKickRequest, + ) -> ClientResult { + self.http_api.http_post("conversations.kick", req).await + } + + /// + /// https://api.slack.com/methods/conversations.leave + /// + pub async fn conversations_leave( + &self, + req: &SlackApiConversationsLeaveRequest, + ) -> ClientResult { + self.http_api.http_post("conversations.leave", req).await + } + + /// + /// https://api.slack.com/methods/conversations.list + /// + pub async fn conversations_list( + &self, + req: &SlackApiConversationsListRequest, + ) -> ClientResult { + self.http_api + .http_get( + "conversations.list", + &vec![ + ("cursor", req.cursor.as_ref().map(|x| x.value())), + ("limit", req.limit.map(|v| v.to_string()).as_ref()), + ( + "exclude_archived", + req.exclude_archived.map(|v| v.to_string()).as_ref(), + ), + ( + "types", + req.types + .as_ref() + .map(|xs| { + xs.iter() + .map(|x| x.to_string()) + .collect::>() + .join(",") + }) + .as_ref(), + ), + ], + ) + .await + } + + /// + /// https://api.slack.com/methods/conversations.members + /// + pub async fn conversations_members( + &self, + req: &SlackApiConversationsMembersRequest, + ) -> ClientResult { + self.http_api + .http_get( + "conversations.members", + &vec![ + ("channel", req.channel.as_ref().map(|x| x.value())), + ("cursor", req.cursor.as_ref().map(|x| x.value())), + ("limit", req.limit.map(|v| v.to_string()).as_ref()), + ], + ) + .await + } + + /// + /// https://api.slack.com/methods/conversations.open + /// return_im is set to None + /// + pub async fn conversations_open( + &self, + req: &SlackApiConversationsOpenRequest, + ) -> ClientResult> { + self.http_api + .http_post("conversations.open", &req.clone().without_return_im()) + .await + } + + /// + /// https://api.slack.com/methods/conversations.open + /// return_im is set to Some(true) + /// + pub async fn conversations_open_full( + &self, + req: &SlackApiConversationsOpenRequest, + ) -> ClientResult> { + self.http_api + .http_post("conversations.open", &req.clone().with_return_im(true)) + .await + } + + /// + /// https://api.slack.com/methods/conversations.rename + /// + pub async fn conversations_rename( + &self, + req: &SlackApiConversationsRenameRequest, + ) -> ClientResult { + self.http_api.http_post("conversations.rename", req).await + } } #[skip_serializing_none] @@ -131,7 +265,7 @@ pub struct SlackApiConversationsHistoryRequest { pub latest: Option, pub limit: Option, pub oldest: Option, - pub inclusive : Option + pub inclusive: Option, } #[skip_serializing_none] @@ -178,14 +312,192 @@ impl SlackApiScrollableResponse for SlackApiConversationsHistoryResponse { #[skip_serializing_none] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] -pub struct SlackApiConversationsInfoRequest{ - pub channel : SlackChannelId, +pub struct SlackApiConversationsInfoRequest { + pub channel: SlackChannelId, pub include_locale: Option, - pub include_num_members: Option + pub include_num_members: Option, } #[skip_serializing_none] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] -pub struct SlackApiConversationsInfoResponse{ - pub channel: SlackChannelInfo -} \ No newline at end of file +pub struct SlackApiConversationsInfoResponse { + pub channel: SlackChannelInfo, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsInviteRequest { + pub channel: SlackChannelId, + pub users: Vec, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsInviteResponse { + pub channel: SlackChannelInfo, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsJoinRequest { + pub channel: SlackChannelId, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsJoinResponse { + pub channel: SlackChannelInfo, + pub response_metadata: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsKickRequest { + pub channel: SlackChannelId, + pub user: SlackUserId, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsKickResponse {} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsLeaveRequest { + pub channel: SlackChannelId, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsLeaveResponse { + pub not_in_channel: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsListRequest { + pub cursor: Option, + pub limit: Option, + pub exclude_archived: Option, + pub types: Option>, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsListResponse { + pub channels: Vec, + pub response_metadata: Option, +} + +impl SlackApiScrollableRequest for SlackApiConversationsListRequest { + type ResponseType = SlackApiConversationsListResponse; + type CursorType = SlackCursorId; + type ResponseItemType = SlackChannelInfo; + + fn with_new_cursor(&self, new_cursor: Option<&Self::CursorType>) -> Self { + self.clone().opt_cursor(new_cursor.cloned()) + } + + fn scroll<'a, 's>( + &'a self, + session: &'a SlackClientSession<'s>, + ) -> BoxFuture<'a, ClientResult> { + async move { session.conversations_list(&self).await }.boxed() + } +} + +impl SlackApiScrollableResponse for SlackApiConversationsListResponse { + type CursorType = SlackCursorId; + type ResponseItemType = SlackChannelInfo; + + fn next_cursor(&self) -> Option<&Self::CursorType> { + self.response_metadata + .as_ref() + .map(|rm| rm.next_cursor.as_ref()) + .flatten() + } + + fn scrollable_items<'a>(&'a self) -> Box + 'a> { + Box::new(self.channels.iter()) + } +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsMembersRequest { + pub channel: Option, + pub cursor: Option, + pub limit: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsMembersResponse { + pub members: Vec, + pub response_metadata: Option, +} + +impl SlackApiScrollableRequest for SlackApiConversationsMembersRequest { + type ResponseType = SlackApiConversationsMembersResponse; + type CursorType = SlackCursorId; + type ResponseItemType = SlackUserId; + + fn with_new_cursor(&self, new_cursor: Option<&Self::CursorType>) -> Self { + self.clone().opt_cursor(new_cursor.cloned()) + } + + fn scroll<'a, 's>( + &'a self, + session: &'a SlackClientSession<'s>, + ) -> BoxFuture<'a, ClientResult> { + async move { session.conversations_members(&self).await }.boxed() + } +} + +impl SlackApiScrollableResponse for SlackApiConversationsMembersResponse { + type CursorType = SlackCursorId; + type ResponseItemType = SlackUserId; + + fn next_cursor(&self) -> Option<&Self::CursorType> { + self.response_metadata + .as_ref() + .map(|rm| rm.next_cursor.as_ref()) + .flatten() + } + + fn scrollable_items<'a>(&'a self) -> Box + 'a> { + Box::new(self.members.iter()) + } +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsOpenRequest { + pub channel: Option, + pub return_im: Option, + pub users: Option>, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsOpenResponse +where + T: HasChannelInfo, +{ + pub channel: T, + pub already_open: Option, + pub no_op: Option, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsRenameRequest { + pub channel: SlackChannelId, + pub name: String, +} + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackApiConversationsRenameResponse { + pub channel: SlackChannelInfo, +} diff --git a/src/models/src/common/channel.rs b/src/models/src/common/channel.rs index d22cde9..c481c0e 100644 --- a/src/models/src/common/channel.rs +++ b/src/models/src/common/channel.rs @@ -4,6 +4,10 @@ use rsb_derive::Builder; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +pub trait HasChannelInfo { + fn get_channel_id(&self) -> &SlackChannelId; +} + #[skip_serializing_none] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] pub struct SlackChannelInfo { @@ -23,6 +27,12 @@ pub struct SlackChannelInfo { pub last_state: SlackChannelCurrentState, } +impl HasChannelInfo for SlackChannelInfo { + fn get_channel_id(&self) -> &SlackChannelId { + &self.id + } +} + pub type SlackChannelTopicInfo = SlackChannelDetails; pub type SlackChannelPurposeInfo = SlackChannelDetails; @@ -57,3 +67,16 @@ pub struct SlackChannelCurrentState { unread_count: Option, unread_count_display: Option, } + +#[skip_serializing_none] +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] +pub struct SlackBasicChannelInfo { + pub id: SlackChannelId, + pub name: Option, +} + +impl HasChannelInfo for SlackBasicChannelInfo { + fn get_channel_id(&self) -> &SlackChannelId { + &self.id + } +} diff --git a/src/models/src/common/mod.rs b/src/models/src/common/mod.rs index a7cdd3d..ccee723 100644 --- a/src/models/src/common/mod.rs +++ b/src/models/src/common/mod.rs @@ -76,3 +76,22 @@ pub struct SlackResponseMetadata { #[serde(with = "serde_with::rust::string_empty_as_none")] pub next_cursor: Option, } + +#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] +pub enum SlackConversationType { + IM, + MPIM, + PRIVATE, + PUBLIC, +} + +impl ToString for SlackConversationType { + fn to_string(&self) -> String { + match self { + SlackConversationType::IM => "im".into(), + SlackConversationType::MPIM => "mpim".into(), + SlackConversationType::PRIVATE => "private".into(), + SlackConversationType::PUBLIC => "public".into(), + } + } +} diff --git a/src/models/src/messages/mod.rs b/src/models/src/messages/mod.rs index e7f653a..525dfa5 100644 --- a/src/models/src/messages/mod.rs +++ b/src/models/src/messages/mod.rs @@ -1,9 +1,9 @@ use crate::blocks::*; use crate::common::*; +use crate::events::SlackMessageEventType; use rsb_derive::Builder; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use crate::events::SlackMessageEventType; #[skip_serializing_none] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)] @@ -38,4 +38,4 @@ pub struct SlackHistoryMessage { #[serde(flatten)] pub content: SlackMessageContent, pub subtype: Option, -} \ No newline at end of file +}