1use crate::api::{
6 SlackApiUsersConversationsRequest, SlackApiUsersConversationsResponse,
7 SlackApiUsersProfileSetRequest, SlackApiUsersProfileSetResponse,
8};
9use crate::models::*;
10use crate::multipart_form::FileMultipartData;
11use crate::ratectl::*;
12use crate::{ClientResult, SlackClientHttpConnector};
13use crate::{SlackApiScrollableRequest, SlackApiScrollableResponse, SlackClientSession};
14use futures_util::future::BoxFuture;
15use futures_util::FutureExt;
16use rsb_derive::Builder;
17use rvstruct::ValueStruct;
18use serde::{Deserialize, Serialize, Serializer};
19use serde_with::skip_serializing_none;
20
21impl<'a, SCHC> SlackClientSession<'a, SCHC>
22where
23 SCHC: SlackClientHttpConnector + Send,
24{
25 pub async fn files_info(
29 &self,
30 req: &SlackApiFilesInfoRequest,
31 ) -> ClientResult<SlackApiFilesInfoResponse> {
32 self.http_session_api
33 .http_get(
34 "files.info",
35 &vec![("file", Some(req.file.value()))],
36 Some(&SLACK_TIER4_METHOD_CONFIG),
37 )
38 .await
39 }
40
41 pub async fn files_list(
45 &self,
46 req: &SlackApiFilesListRequest,
47 ) -> ClientResult<SlackApiFilesListResponse> {
48 self.http_session_api
49 .http_get(
50 "files.list",
51 &vec![
52 ("channel", req.channel.as_ref().map(|x| x.value())),
53 ("user", req.user.as_ref().map(|x| x.value())),
54 ("types", req.types.as_ref()),
55 ("count", req.count.map(|x| x.to_string()).as_ref()),
56 ("page", req.page.map(|x| x.to_string()).as_ref()),
57 ("ts_from", req.ts_from.map(|x| x.to_string()).as_ref()),
58 ("ts_to", req.ts_to.map(|x| x.to_string()).as_ref()),
59 (
60 "show_files_hidden_by_limit",
61 req.show_files_hidden_by_limit
62 .map(|x| x.to_string())
63 .as_ref(),
64 ),
65 ],
66 Some(&SLACK_TIER3_METHOD_CONFIG),
67 )
68 .await
69 }
70
71 #[deprecated(
75 note = "Deprecated by Slack. Use `getUploadURLExternal/files_upload_via_url/completeUploadExternal` instead."
76 )]
77 pub async fn files_upload(
78 &self,
79 req: &SlackApiFilesUploadRequest,
80 ) -> ClientResult<SlackApiFilesUploadResponse> {
81 let maybe_file = req.binary_content.as_ref().map(|file_data| {
82 let filename = req.filename.clone().unwrap_or("file".to_string());
83 let file_content_type = req.file_content_type.clone().unwrap_or_else(|| {
84 let file_mime = mime_guess::MimeGuess::from_path(&filename).first_or_octet_stream();
85 file_mime.to_string()
86 });
87 FileMultipartData {
88 name: filename,
89 content_type: file_content_type,
90 data: file_data.as_slice(),
91 }
92 });
93 self.http_session_api
94 .http_post_multipart_form(
95 "files.upload",
96 maybe_file,
97 &vec![
98 (
99 "channels",
100 req.channels
101 .as_ref()
102 .map(|xs| {
103 xs.iter()
104 .map(|x| x.to_string())
105 .collect::<Vec<String>>()
106 .join(",")
107 })
108 .as_ref(),
109 ),
110 ("content", req.content.as_ref()),
111 ("filename", req.filename.as_ref()),
112 ("filetype", req.filetype.as_ref().map(|x| x.value())),
113 ("initial_comment", req.initial_comment.as_ref()),
114 ("thread_ts", req.thread_ts.as_ref().map(|x| x.value())),
115 ("title", req.title.as_ref()),
116 ],
117 Some(&SLACK_TIER2_METHOD_CONFIG),
118 )
119 .await
120 }
121
122 pub async fn get_upload_url_external(
126 &self,
127 req: &SlackApiFilesGetUploadUrlExternalRequest,
128 ) -> ClientResult<SlackApiFilesGetUploadUrlExternalResponse> {
129 self.http_session_api
130 .http_get(
131 "files.getUploadURLExternal",
132 &vec![
133 ("filename", Some(&req.filename)),
134 ("length", Some(&req.length.to_string())),
135 ("alt_txt", req.alt_txt.as_ref()),
136 ("snippet_type", req.snippet_type.as_ref().map(|v| v.value())),
137 ],
138 Some(&SLACK_TIER4_METHOD_CONFIG),
139 )
140 .await
141 }
142
143 pub async fn files_upload_via_url(
144 &self,
145 req: &SlackApiFilesUploadViaUrlRequest,
146 ) -> ClientResult<SlackApiFilesUploadViaUrlResponse> {
147 self.http_session_api
148 .http_post_uri_binary(
149 req.upload_url.value().clone(),
150 req.content_type.clone(),
151 &req.content,
152 Some(&SLACK_TIER4_METHOD_CONFIG),
153 )
154 .await
155 }
156
157 pub async fn files_complete_upload_external(
161 &self,
162 req: &SlackApiFilesCompleteUploadExternalRequest,
163 ) -> ClientResult<SlackApiFilesCompleteUploadExternalResponse> {
164 self.http_session_api
165 .http_post(
166 "files.completeUploadExternal",
167 req,
168 Some(&SLACK_TIER4_METHOD_CONFIG),
169 )
170 .await
171 }
172
173 pub async fn files_delete(
177 &self,
178 req: &SlackApiFilesDeleteRequest,
179 ) -> ClientResult<SlackApiFilesDeleteResponse> {
180 self.http_session_api
181 .http_post("files.delete", req, Some(&SLACK_TIER3_METHOD_CONFIG))
182 .await
183 }
184}
185
186#[skip_serializing_none]
187#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
188pub struct SlackApiFilesInfoRequest {
189 pub file: SlackFileId,
190}
191
192#[skip_serializing_none]
193#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
194pub struct SlackApiFilesInfoResponse {
195 pub file: SlackFile,
196}
197
198#[skip_serializing_none]
199#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
200pub struct SlackApiFilesListRequest {
201 pub channel: Option<SlackChannelId>,
202 pub user: Option<SlackUserId>,
203 pub types: Option<String>,
204 pub count: Option<u32>,
205 pub page: Option<u32>,
206 pub ts_from: Option<i64>,
207 pub ts_to: Option<i64>,
208 pub show_files_hidden_by_limit: Option<bool>,
209}
210
211#[skip_serializing_none]
212#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
213pub struct SlackApiFilesListResponse {
214 pub files: Vec<SlackFile>,
215 pub paging: Option<SlackApiFilesListPaging>,
216}
217
218#[skip_serializing_none]
219#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
220pub struct SlackApiFilesListPaging {
221 pub count: Option<u32>,
222 pub total: Option<u32>,
223 pub page: Option<u32>,
224 pub pages: Option<u32>,
225}
226
227impl<SCHC> SlackApiScrollableRequest<SCHC> for SlackApiFilesListRequest
228where
229 SCHC: SlackClientHttpConnector + Send + Sync + Clone + 'static,
230{
231 type ResponseType = SlackApiFilesListResponse;
232 type CursorType = u32;
233 type ResponseItemType = SlackFile;
234
235 fn with_new_cursor(&self, new_cursor: Option<&Self::CursorType>) -> Self {
236 self.clone().opt_page(new_cursor.cloned())
237 }
238
239 fn scroll<'a, 's>(
240 &'a self,
241 session: &'a SlackClientSession<'s, SCHC>,
242 ) -> BoxFuture<'a, ClientResult<Self::ResponseType>> {
243 async move { session.files_list(self).await }.boxed()
244 }
245}
246
247impl SlackApiScrollableResponse for SlackApiFilesListResponse {
248 type CursorType = u32;
249 type ResponseItemType = SlackFile;
250
251 fn next_cursor(&self) -> Option<Self::CursorType> {
252 self.paging
253 .as_ref()
254 .into_iter()
255 .filter_map(|paging| match (paging.page, paging.pages) {
256 (Some(page), Some(pages)) if page < pages => Some(page + 1),
257 _ => None,
258 })
259 .next()
260 }
261
262 fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {
263 Box::new(self.files.iter())
264 }
265}
266
267#[skip_serializing_none]
268#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
269pub struct SlackApiFilesUploadRequest {
270 #[serde(serialize_with = "to_csv")]
271 pub channels: Option<Vec<SlackChannelId>>,
272 pub content: Option<String>,
273 pub binary_content: Option<Vec<u8>>,
274 pub filename: Option<String>,
275 pub filetype: Option<SlackFileType>,
276 pub initial_comment: Option<String>,
277 pub thread_ts: Option<SlackTs>,
278 pub title: Option<String>,
279 pub file_content_type: Option<String>,
280}
281
282#[skip_serializing_none]
283#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
284pub struct SlackApiFilesUploadResponse {
285 pub file: SlackFile,
286}
287
288#[skip_serializing_none]
289#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
290pub struct SlackApiFilesGetUploadUrlExternalRequest {
291 pub filename: String,
292 pub length: usize,
293 pub alt_txt: Option<String>,
294 pub snippet_type: Option<SlackFileSnippetType>,
295}
296
297#[skip_serializing_none]
298#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
299pub struct SlackApiFilesGetUploadUrlExternalResponse {
300 pub upload_url: SlackFileUploadUrl,
301 pub file_id: SlackFileId,
302}
303
304#[skip_serializing_none]
305#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
306pub struct SlackApiFilesUploadViaUrlRequest {
307 pub upload_url: SlackFileUploadUrl,
308 pub content: Vec<u8>,
309 pub content_type: String,
310}
311
312#[skip_serializing_none]
313#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
314pub struct SlackApiFilesUploadViaUrlResponse {}
315
316#[skip_serializing_none]
317#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
318pub struct SlackApiFilesCompleteUploadExternalRequest {
319 pub files: Vec<SlackApiFilesComplete>,
320 pub channel_id: Option<SlackChannelId>,
321 pub initial_comment: Option<String>,
322 pub thread_ts: Option<SlackTs>,
323}
324
325#[skip_serializing_none]
326#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
327pub struct SlackApiFilesCompleteUploadExternalResponse {
328 pub files: Vec<SlackFile>,
329}
330
331#[skip_serializing_none]
332#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
333pub struct SlackApiFilesComplete {
334 pub id: SlackFileId,
335 pub title: Option<String>,
336}
337
338#[skip_serializing_none]
339#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
340pub struct SlackApiFilesDeleteRequest {
341 pub file: SlackFileId,
342}
343
344#[skip_serializing_none]
345#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
346pub struct SlackApiFilesDeleteResponse {}
347
348fn to_csv<S: Serializer>(x: &Option<Vec<SlackChannelId>>, s: S) -> Result<S::Ok, S::Error> {
349 match x {
350 None => s.serialize_none(),
351 Some(ids) => {
352 let y: Vec<String> = ids.iter().map(|v| v.0.clone()).collect();
353 y.join(",").serialize(s)
354 }
355 }
356}