1use rsb_derive::Builder;
2use rvstruct::ValueStruct;
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5use url::Url;
6
7use crate::*;
8
9#[skip_serializing_none]
10#[derive(Debug, PartialEq, Clone, Eq, Hash, Serialize, Deserialize, ValueStruct)]
11pub struct SlackBlockId(pub String);
12
13#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
14#[serde(tag = "type")]
15pub enum SlackBlock {
16 #[serde(rename = "section")]
17 Section(SlackSectionBlock),
18 #[serde(rename = "header")]
19 Header(SlackHeaderBlock),
20 #[serde(rename = "divider")]
21 Divider(SlackDividerBlock),
22 #[serde(rename = "image")]
23 Image(SlackImageBlock),
24 #[serde(rename = "actions")]
25 Actions(SlackActionsBlock),
26 #[serde(rename = "context")]
27 Context(SlackContextBlock),
28 #[serde(rename = "input")]
29 Input(SlackInputBlock),
30 #[serde(rename = "file")]
31 File(SlackFileBlock),
32 #[serde(rename = "video")]
33 Video(SlackVideoBlock),
34 #[serde(rename = "markdown")]
35 Markdown(SlackMarkdownBlock),
36 #[serde(rename = "rich_text")]
37 RichText(SlackRichTextBlock),
38 #[serde(rename = "share_shortcut")]
39 ShareShortcut(serde_json::Value),
40 #[serde(rename = "event")]
41 Event(serde_json::Value),
42}
43
44#[skip_serializing_none]
45#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
46pub struct SlackSectionBlock {
47 pub block_id: Option<SlackBlockId>,
48 pub text: Option<SlackBlockText>,
49 pub fields: Option<Vec<SlackBlockText>>,
50 pub accessory: Option<SlackSectionBlockElement>,
51}
52
53impl From<SlackSectionBlock> for SlackBlock {
54 fn from(block: SlackSectionBlock) -> Self {
55 SlackBlock::Section(block)
56 }
57}
58
59#[skip_serializing_none]
60#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
61pub struct SlackHeaderBlock {
62 pub block_id: Option<SlackBlockId>,
63 pub text: SlackBlockPlainTextOnly,
64}
65
66impl From<SlackHeaderBlock> for SlackBlock {
67 fn from(block: SlackHeaderBlock) -> Self {
68 SlackBlock::Header(block)
69 }
70}
71
72#[skip_serializing_none]
73#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
74pub struct SlackDividerBlock {
75 pub block_id: Option<SlackBlockId>,
76}
77
78impl From<SlackDividerBlock> for SlackBlock {
79 fn from(block: SlackDividerBlock) -> Self {
80 SlackBlock::Divider(block)
81 }
82}
83
84#[skip_serializing_none]
85#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
86pub struct SlackImageBlock {
87 pub block_id: Option<SlackBlockId>,
88 #[serde(flatten)]
89 pub image_url_or_file: SlackImageUrlOrFile,
90 pub alt_text: String,
91 pub title: Option<SlackBlockPlainTextOnly>,
92}
93
94impl From<SlackImageBlock> for SlackBlock {
95 fn from(block: SlackImageBlock) -> Self {
96 SlackBlock::Image(block)
97 }
98}
99
100#[skip_serializing_none]
101#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
102pub struct SlackActionsBlock {
103 pub block_id: Option<SlackBlockId>,
104 pub elements: Vec<SlackActionBlockElement>,
105}
106
107impl From<SlackActionsBlock> for SlackBlock {
108 fn from(block: SlackActionsBlock) -> Self {
109 SlackBlock::Actions(block)
110 }
111}
112
113#[skip_serializing_none]
114#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
115pub struct SlackContextBlock {
116 pub block_id: Option<SlackBlockId>,
117 pub elements: Vec<SlackContextBlockElement>,
118}
119
120impl From<SlackContextBlock> for SlackBlock {
121 fn from(block: SlackContextBlock) -> Self {
122 SlackBlock::Context(block)
123 }
124}
125
126#[skip_serializing_none]
127#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
128pub struct SlackInputBlock {
129 pub block_id: Option<SlackBlockId>,
130 pub label: SlackBlockPlainTextOnly,
131 pub element: SlackInputBlockElement,
132 pub hint: Option<SlackBlockPlainTextOnly>,
133 pub optional: Option<bool>,
134 pub dispatch_action: Option<bool>,
135}
136
137impl From<SlackInputBlock> for SlackBlock {
138 fn from(block: SlackInputBlock) -> Self {
139 SlackBlock::Input(block)
140 }
141}
142
143const SLACK_FILE_BLOCK_SOURCE_DEFAULT: &str = "remote";
144
145#[skip_serializing_none]
146#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
147pub struct SlackFileBlock {
148 pub block_id: Option<SlackBlockId>,
149 pub external_id: String,
150 #[default = "SLACK_FILE_BLOCK_SOURCE_DEFAULT.into()"]
151 pub source: String,
152}
153
154impl From<SlackFileBlock> for SlackBlock {
155 fn from(block: SlackFileBlock) -> Self {
156 SlackBlock::File(block)
157 }
158}
159
160#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
161#[serde(tag = "type")]
162pub enum SlackSectionBlockElement {
163 #[serde(rename = "image")]
164 Image(SlackBlockImageElement),
165 #[serde(rename = "button")]
166 Button(SlackBlockButtonElement),
167 #[serde(rename = "static_select")]
168 StaticSelect(SlackBlockStaticSelectElement),
169 #[serde(rename = "multi_static_select")]
170 MultiStaticSelect(SlackBlockMultiStaticSelectElement),
171 #[serde(rename = "external_select")]
172 ExternalSelect(SlackBlockExternalSelectElement),
173 #[serde(rename = "multi_external_select")]
174 MultiExternalSelect(SlackBlockMultiExternalSelectElement),
175 #[serde(rename = "users_select")]
176 UsersSelect(SlackBlockUsersSelectElement),
177 #[serde(rename = "multi_users_select")]
178 MultiUsersSelect(SlackBlockMultiUsersSelectElement),
179 #[serde(rename = "conversations_select")]
180 ConversationsSelect(SlackBlockConversationsSelectElement),
181 #[serde(rename = "multi_conversations_select")]
182 MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
183 #[serde(rename = "channels_select")]
184 ChannelsSelect(SlackBlockChannelsSelectElement),
185 #[serde(rename = "multi_channels_select")]
186 MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
187 #[serde(rename = "overflow")]
188 Overflow(SlackBlockOverflowElement),
189 #[serde(rename = "datepicker")]
190 DatePicker(SlackBlockDatePickerElement),
191 #[serde(rename = "timepicker")]
192 TimePicker(SlackBlockTimePickerElement),
193 #[serde(rename = "plain_text_input")]
194 PlainTextInput(SlackBlockPlainTextInputElement),
195 #[serde(rename = "number_input")]
196 NumberInput(SlackBlockNumberInputElement),
197 #[serde(rename = "url_text_input")]
198 UrlInput(SlackBlockUrlInputElement),
199 #[serde(rename = "radio_buttons")]
200 RadioButtons(SlackBlockRadioButtonsElement),
201 #[serde(rename = "checkboxes")]
202 Checkboxes(SlackBlockCheckboxesElement),
203}
204
205#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
206#[serde(tag = "type")]
207pub enum SlackActionBlockElement {
208 #[serde(rename = "button")]
209 Button(SlackBlockButtonElement),
210 #[serde(rename = "overflow")]
211 Overflow(SlackBlockOverflowElement),
212 #[serde(rename = "datepicker")]
213 DatePicker(SlackBlockDatePickerElement),
214 #[serde(rename = "timepicker")]
215 TimePicker(SlackBlockTimePickerElement),
216 #[serde(rename = "datetimepicker")]
217 DateTimePicker(SlackBlockDateTimePickerElement),
218 #[serde(rename = "plain_text_input")]
219 PlainTextInput(SlackBlockPlainTextInputElement),
220 #[serde(rename = "number_input")]
221 NumberInput(SlackBlockNumberInputElement),
222 #[serde(rename = "url_text_input")]
223 UrlInput(SlackBlockUrlInputElement),
224 #[serde(rename = "radio_buttons")]
225 RadioButtons(SlackBlockRadioButtonsElement),
226 #[serde(rename = "checkboxes")]
227 Checkboxes(SlackBlockCheckboxesElement),
228 #[serde(rename = "static_select")]
229 StaticSelect(SlackBlockStaticSelectElement),
230 #[serde(rename = "external_select")]
231 ExternalSelect(SlackBlockExternalSelectElement),
232 #[serde(rename = "users_select")]
233 UsersSelect(SlackBlockUsersSelectElement),
234 #[serde(rename = "conversations_select")]
235 ConversationsSelect(SlackBlockConversationsSelectElement),
236 #[serde(rename = "channels_select")]
237 ChannelsSelect(SlackBlockChannelsSelectElement),
238}
239
240#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
241#[serde(tag = "type")]
242pub enum SlackContextBlockElement {
243 #[serde(rename = "image")]
244 Image(SlackBlockImageElement),
245 #[serde(rename = "plain_text")]
246 Plain(SlackBlockPlainText),
247 #[serde(rename = "mrkdwn")]
248 MarkDown(SlackBlockMarkDownText),
249}
250
251#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
252#[serde(tag = "type")]
253pub enum SlackInputBlockElement {
254 #[serde(rename = "static_select")]
255 StaticSelect(SlackBlockStaticSelectElement),
256 #[serde(rename = "multi_static_select")]
257 MultiStaticSelect(SlackBlockMultiStaticSelectElement),
258 #[serde(rename = "external_select")]
259 ExternalSelect(SlackBlockExternalSelectElement),
260 #[serde(rename = "multi_external_select")]
261 MultiExternalSelect(SlackBlockMultiExternalSelectElement),
262 #[serde(rename = "users_select")]
263 UsersSelect(SlackBlockUsersSelectElement),
264 #[serde(rename = "multi_users_select")]
265 MultiUsersSelect(SlackBlockMultiUsersSelectElement),
266 #[serde(rename = "conversations_select")]
267 ConversationsSelect(SlackBlockConversationsSelectElement),
268 #[serde(rename = "multi_conversations_select")]
269 MultiConversationsSelect(SlackBlockMultiConversationsSelectElement),
270 #[serde(rename = "channels_select")]
271 ChannelsSelect(SlackBlockChannelsSelectElement),
272 #[serde(rename = "multi_channels_select")]
273 MultiChannelsSelect(SlackBlockMultiChannelsSelectElement),
274 #[serde(rename = "datepicker")]
275 DatePicker(SlackBlockDatePickerElement),
276 #[serde(rename = "timepicker")]
277 TimePicker(SlackBlockTimePickerElement),
278 #[serde(rename = "datetimepicker")]
279 DateTimePicker(SlackBlockDateTimePickerElement),
280 #[serde(rename = "plain_text_input")]
281 PlainTextInput(SlackBlockPlainTextInputElement),
282 #[serde(rename = "number_input")]
283 NumberInput(SlackBlockNumberInputElement),
284 #[serde(rename = "url_text_input")]
285 UrlInput(SlackBlockUrlInputElement),
286 #[serde(rename = "radio_buttons")]
287 RadioButtons(SlackBlockRadioButtonsElement),
288 #[serde(rename = "checkboxes")]
289 Checkboxes(SlackBlockCheckboxesElement),
290 #[serde(rename = "email_text_input")]
291 EmailInput(SlackBlockEmailInputElement),
292 #[serde(rename = "rich_text_input")]
293 RichTextInput(SlackBlockRichTextInputElement),
294}
295
296#[skip_serializing_none]
297#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
298pub struct SlackBlockImageElement {
299 #[serde(flatten)]
300 pub image_url_or_file: SlackImageUrlOrFile,
301 pub alt_text: String,
302}
303
304impl From<SlackBlockImageElement> for SlackSectionBlockElement {
305 fn from(element: SlackBlockImageElement) -> Self {
306 SlackSectionBlockElement::Image(element)
307 }
308}
309
310impl From<SlackBlockImageElement> for SlackContextBlockElement {
311 fn from(element: SlackBlockImageElement) -> Self {
312 SlackContextBlockElement::Image(element)
313 }
314}
315
316#[skip_serializing_none]
317#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
318pub struct SlackBlockButtonElement {
319 pub action_id: SlackActionId,
320 pub text: SlackBlockPlainTextOnly,
321 pub url: Option<Url>,
322 pub value: Option<String>,
323 pub style: Option<String>,
324 pub confirm: Option<SlackBlockConfirmItem>,
325}
326
327impl From<SlackBlockButtonElement> for SlackSectionBlockElement {
328 fn from(element: SlackBlockButtonElement) -> Self {
329 SlackSectionBlockElement::Button(element)
330 }
331}
332
333impl From<SlackBlockButtonElement> for SlackActionBlockElement {
334 fn from(element: SlackBlockButtonElement) -> Self {
335 SlackActionBlockElement::Button(element)
336 }
337}
338
339#[skip_serializing_none]
340#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
341pub struct SlackBlockConfirmItem {
342 pub title: SlackBlockPlainTextOnly,
343 pub text: SlackBlockText,
344 pub confirm: SlackBlockPlainTextOnly,
345 pub deny: SlackBlockPlainTextOnly,
346 pub style: Option<String>,
347}
348
349#[skip_serializing_none]
350#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
351pub struct SlackBlockChoiceItem<T: Into<SlackBlockText>> {
352 pub text: T,
353 pub value: String,
354 pub url: Option<Url>,
355}
356
357#[skip_serializing_none]
358#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
359pub struct SlackBlockOptionGroup<T: Into<SlackBlockText>> {
360 pub label: SlackBlockPlainTextOnly,
361 pub options: Vec<SlackBlockChoiceItem<T>>,
362}
363
364#[skip_serializing_none]
365#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
366pub struct SlackBlockStaticSelectElement {
367 pub action_id: SlackActionId,
368 pub placeholder: Option<SlackBlockPlainTextOnly>,
369 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
370 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
371 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
372 pub confirm: Option<SlackBlockConfirmItem>,
373 pub focus_on_load: Option<bool>,
374}
375
376impl From<SlackBlockStaticSelectElement> for SlackSectionBlockElement {
377 fn from(element: SlackBlockStaticSelectElement) -> Self {
378 SlackSectionBlockElement::StaticSelect(element)
379 }
380}
381
382impl From<SlackBlockStaticSelectElement> for SlackInputBlockElement {
383 fn from(element: SlackBlockStaticSelectElement) -> Self {
384 SlackInputBlockElement::StaticSelect(element)
385 }
386}
387
388impl From<SlackBlockStaticSelectElement> for SlackActionBlockElement {
389 fn from(element: SlackBlockStaticSelectElement) -> Self {
390 SlackActionBlockElement::StaticSelect(element)
391 }
392}
393
394#[skip_serializing_none]
395#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
396pub struct SlackBlockMultiStaticSelectElement {
397 pub action_id: SlackActionId,
398 pub placeholder: Option<SlackBlockPlainTextOnly>,
399 pub options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
400 pub option_groups: Option<Vec<SlackBlockOptionGroup<SlackBlockPlainTextOnly>>>,
401 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
402 pub confirm: Option<SlackBlockConfirmItem>,
403 pub max_selected_items: Option<u64>,
404 pub focus_on_load: Option<bool>,
405}
406
407impl From<SlackBlockMultiStaticSelectElement> for SlackSectionBlockElement {
408 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
409 SlackSectionBlockElement::MultiStaticSelect(element)
410 }
411}
412
413impl From<SlackBlockMultiStaticSelectElement> for SlackInputBlockElement {
414 fn from(element: SlackBlockMultiStaticSelectElement) -> Self {
415 SlackInputBlockElement::MultiStaticSelect(element)
416 }
417}
418
419#[skip_serializing_none]
420#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
421pub struct SlackBlockExternalSelectElement {
422 pub action_id: SlackActionId,
423 pub placeholder: Option<SlackBlockPlainTextOnly>,
424 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
425 pub confirm: Option<SlackBlockConfirmItem>,
426 pub focus_on_load: Option<bool>,
427 pub min_query_length: Option<u64>,
428}
429
430impl From<SlackBlockExternalSelectElement> for SlackSectionBlockElement {
431 fn from(element: SlackBlockExternalSelectElement) -> Self {
432 SlackSectionBlockElement::ExternalSelect(element)
433 }
434}
435
436impl From<SlackBlockExternalSelectElement> for SlackInputBlockElement {
437 fn from(element: SlackBlockExternalSelectElement) -> Self {
438 SlackInputBlockElement::ExternalSelect(element)
439 }
440}
441
442impl From<SlackBlockExternalSelectElement> for SlackActionBlockElement {
443 fn from(element: SlackBlockExternalSelectElement) -> Self {
444 SlackActionBlockElement::ExternalSelect(element)
445 }
446}
447
448#[skip_serializing_none]
449#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
450pub struct SlackBlockMultiExternalSelectElement {
451 pub action_id: SlackActionId,
452 pub placeholder: Option<SlackBlockPlainTextOnly>,
453 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>>,
454 pub confirm: Option<SlackBlockConfirmItem>,
455 pub max_selected_items: Option<u64>,
456 pub focus_on_load: Option<bool>,
457 pub min_query_length: Option<u64>,
458}
459
460impl From<SlackBlockMultiExternalSelectElement> for SlackSectionBlockElement {
461 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
462 SlackSectionBlockElement::MultiExternalSelect(element)
463 }
464}
465
466impl From<SlackBlockMultiExternalSelectElement> for SlackInputBlockElement {
467 fn from(element: SlackBlockMultiExternalSelectElement) -> Self {
468 SlackInputBlockElement::MultiExternalSelect(element)
469 }
470}
471
472#[skip_serializing_none]
473#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
474pub struct SlackBlockUsersSelectElement {
475 pub action_id: SlackActionId,
476 pub placeholder: Option<SlackBlockPlainTextOnly>,
477 pub initial_user: Option<String>,
478 pub confirm: Option<SlackBlockConfirmItem>,
479 pub focus_on_load: Option<bool>,
480}
481
482impl From<SlackBlockUsersSelectElement> for SlackSectionBlockElement {
483 fn from(element: SlackBlockUsersSelectElement) -> Self {
484 SlackSectionBlockElement::UsersSelect(element)
485 }
486}
487
488impl From<SlackBlockUsersSelectElement> for SlackInputBlockElement {
489 fn from(element: SlackBlockUsersSelectElement) -> Self {
490 SlackInputBlockElement::UsersSelect(element)
491 }
492}
493
494impl From<SlackBlockUsersSelectElement> for SlackActionBlockElement {
495 fn from(element: SlackBlockUsersSelectElement) -> Self {
496 SlackActionBlockElement::UsersSelect(element)
497 }
498}
499
500#[skip_serializing_none]
501#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
502pub struct SlackBlockMultiUsersSelectElement {
503 pub action_id: SlackActionId,
504 pub placeholder: Option<SlackBlockPlainTextOnly>,
505 pub initial_users: Option<Vec<String>>,
506 pub confirm: Option<SlackBlockConfirmItem>,
507 pub max_selected_items: Option<u64>,
508 pub focus_on_load: Option<bool>,
509}
510
511impl From<SlackBlockMultiUsersSelectElement> for SlackSectionBlockElement {
512 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
513 SlackSectionBlockElement::MultiUsersSelect(element)
514 }
515}
516
517impl From<SlackBlockMultiUsersSelectElement> for SlackInputBlockElement {
518 fn from(element: SlackBlockMultiUsersSelectElement) -> Self {
519 SlackInputBlockElement::MultiUsersSelect(element)
520 }
521}
522
523#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
524pub enum SlackConversationFilterInclude {
525 #[serde(rename = "im")]
526 Im,
527 #[serde(rename = "mpim")]
528 Mpim,
529 #[serde(rename = "public")]
530 Public,
531 #[serde(rename = "private")]
532 Private,
533}
534
535#[skip_serializing_none]
536#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
537pub struct SlackBlockConversationFilter {
538 pub include: Option<Vec<SlackConversationFilterInclude>>,
539 pub exclude_external_shared_channels: Option<bool>,
540 pub exclude_bot_users: Option<bool>,
541}
542
543#[skip_serializing_none]
544#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
545pub struct SlackBlockConversationsSelectElement {
546 pub action_id: SlackActionId,
547 pub placeholder: Option<SlackBlockPlainTextOnly>,
548 pub initial_conversation: Option<SlackConversationId>,
549 pub default_to_current_conversation: Option<bool>,
550 pub confirm: Option<SlackBlockConfirmItem>,
551 pub response_url_enabled: Option<bool>,
552 pub focus_on_load: Option<bool>,
553 pub filter: Option<SlackBlockConversationFilter>,
554}
555
556impl From<SlackBlockConversationsSelectElement> for SlackSectionBlockElement {
557 fn from(element: SlackBlockConversationsSelectElement) -> Self {
558 SlackSectionBlockElement::ConversationsSelect(element)
559 }
560}
561
562impl From<SlackBlockConversationsSelectElement> for SlackInputBlockElement {
563 fn from(element: SlackBlockConversationsSelectElement) -> Self {
564 SlackInputBlockElement::ConversationsSelect(element)
565 }
566}
567
568impl From<SlackBlockConversationsSelectElement> for SlackActionBlockElement {
569 fn from(element: SlackBlockConversationsSelectElement) -> Self {
570 SlackActionBlockElement::ConversationsSelect(element)
571 }
572}
573
574#[skip_serializing_none]
575#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
576pub struct SlackBlockMultiConversationsSelectElement {
577 pub action_id: SlackActionId,
578 pub placeholder: Option<SlackBlockPlainTextOnly>,
579 pub initial_conversations: Option<Vec<SlackConversationId>>,
580 pub default_to_current_conversation: Option<bool>,
581 pub confirm: Option<SlackBlockConfirmItem>,
582 pub max_selected_items: Option<u64>,
583 pub focus_on_load: Option<bool>,
584 pub filter: Option<SlackBlockConversationFilter>,
585}
586
587impl From<SlackBlockMultiConversationsSelectElement> for SlackSectionBlockElement {
588 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
589 SlackSectionBlockElement::MultiConversationsSelect(element)
590 }
591}
592
593impl From<SlackBlockMultiConversationsSelectElement> for SlackInputBlockElement {
594 fn from(element: SlackBlockMultiConversationsSelectElement) -> Self {
595 SlackInputBlockElement::MultiConversationsSelect(element)
596 }
597}
598
599#[skip_serializing_none]
600#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
601pub struct SlackBlockChannelsSelectElement {
602 pub action_id: SlackActionId,
603 pub placeholder: Option<SlackBlockPlainTextOnly>,
604 pub initial_channel: Option<SlackChannelId>,
605 pub confirm: Option<SlackBlockConfirmItem>,
606 pub response_url_enabled: Option<bool>,
607 pub focus_on_load: Option<bool>,
608}
609
610impl From<SlackBlockChannelsSelectElement> for SlackSectionBlockElement {
611 fn from(element: SlackBlockChannelsSelectElement) -> Self {
612 SlackSectionBlockElement::ChannelsSelect(element)
613 }
614}
615
616impl From<SlackBlockChannelsSelectElement> for SlackInputBlockElement {
617 fn from(element: SlackBlockChannelsSelectElement) -> Self {
618 SlackInputBlockElement::ChannelsSelect(element)
619 }
620}
621
622impl From<SlackBlockChannelsSelectElement> for SlackActionBlockElement {
623 fn from(element: SlackBlockChannelsSelectElement) -> Self {
624 SlackActionBlockElement::ChannelsSelect(element)
625 }
626}
627
628#[skip_serializing_none]
629#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
630pub struct SlackBlockMultiChannelsSelectElement {
631 pub action_id: SlackActionId,
632 pub placeholder: Option<SlackBlockPlainTextOnly>,
633 pub initial_channels: Option<Vec<SlackChannelId>>,
634 pub confirm: Option<SlackBlockConfirmItem>,
635 pub max_selected_items: Option<u64>,
636 pub focus_on_load: Option<bool>,
637}
638
639impl From<SlackBlockMultiChannelsSelectElement> for SlackSectionBlockElement {
640 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
641 SlackSectionBlockElement::MultiChannelsSelect(element)
642 }
643}
644
645impl From<SlackBlockMultiChannelsSelectElement> for SlackInputBlockElement {
646 fn from(element: SlackBlockMultiChannelsSelectElement) -> Self {
647 SlackInputBlockElement::MultiChannelsSelect(element)
648 }
649}
650
651#[skip_serializing_none]
652#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
653pub struct SlackBlockOverflowElement {
654 pub action_id: SlackActionId,
655 pub options: Vec<SlackBlockChoiceItem<SlackBlockPlainTextOnly>>,
656 pub confirm: Option<SlackBlockConfirmItem>,
657}
658
659impl From<SlackBlockOverflowElement> for SlackSectionBlockElement {
660 fn from(element: SlackBlockOverflowElement) -> Self {
661 SlackSectionBlockElement::Overflow(element)
662 }
663}
664
665impl From<SlackBlockOverflowElement> for SlackActionBlockElement {
666 fn from(element: SlackBlockOverflowElement) -> Self {
667 SlackActionBlockElement::Overflow(element)
668 }
669}
670
671#[skip_serializing_none]
672#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
673pub struct SlackBlockDatePickerElement {
674 pub action_id: SlackActionId,
675 pub placeholder: Option<SlackBlockPlainTextOnly>,
676 pub initial_date: Option<String>,
677 pub confirm: Option<SlackBlockConfirmItem>,
678 pub focus_on_load: Option<bool>,
679}
680
681impl From<SlackBlockDatePickerElement> for SlackSectionBlockElement {
682 fn from(element: SlackBlockDatePickerElement) -> Self {
683 SlackSectionBlockElement::DatePicker(element)
684 }
685}
686
687impl From<SlackBlockDatePickerElement> for SlackInputBlockElement {
688 fn from(element: SlackBlockDatePickerElement) -> Self {
689 SlackInputBlockElement::DatePicker(element)
690 }
691}
692
693impl From<SlackBlockDatePickerElement> for SlackActionBlockElement {
694 fn from(element: SlackBlockDatePickerElement) -> Self {
695 SlackActionBlockElement::DatePicker(element)
696 }
697}
698
699#[skip_serializing_none]
700#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
701pub struct SlackBlockTimePickerElement {
702 pub action_id: SlackActionId,
703 pub initial_time: Option<String>,
704 pub confirm: Option<SlackBlockConfirmItem>,
705 pub focus_on_load: Option<bool>,
706 pub placeholder: Option<SlackBlockPlainTextOnly>,
707 pub timezone: Option<String>,
708}
709
710impl From<SlackBlockTimePickerElement> for SlackSectionBlockElement {
711 fn from(element: SlackBlockTimePickerElement) -> Self {
712 SlackSectionBlockElement::TimePicker(element)
713 }
714}
715
716impl From<SlackBlockTimePickerElement> for SlackInputBlockElement {
717 fn from(element: SlackBlockTimePickerElement) -> Self {
718 SlackInputBlockElement::TimePicker(element)
719 }
720}
721
722impl From<SlackBlockTimePickerElement> for SlackActionBlockElement {
723 fn from(element: SlackBlockTimePickerElement) -> Self {
724 SlackActionBlockElement::TimePicker(element)
725 }
726}
727
728#[skip_serializing_none]
729#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
730pub struct SlackBlockDateTimePickerElement {
731 pub action_id: SlackActionId,
732 pub initial_date_time: Option<SlackDateTime>,
733 pub confirm: Option<SlackBlockConfirmItem>,
734 pub focus_on_load: Option<bool>,
735}
736
737impl From<SlackBlockDateTimePickerElement> for SlackInputBlockElement {
738 fn from(element: SlackBlockDateTimePickerElement) -> Self {
739 SlackInputBlockElement::DateTimePicker(element)
740 }
741}
742
743impl From<SlackBlockDateTimePickerElement> for SlackActionBlockElement {
744 fn from(element: SlackBlockDateTimePickerElement) -> Self {
745 SlackActionBlockElement::DateTimePicker(element)
746 }
747}
748
749#[skip_serializing_none]
750#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
751pub struct SlackBlockPlainTextInputElement {
752 pub action_id: SlackActionId,
753 pub placeholder: Option<SlackBlockPlainTextOnly>,
754 pub initial_value: Option<String>,
755 pub multiline: Option<bool>,
756 pub min_length: Option<u64>,
757 pub max_length: Option<u64>,
758 pub focus_on_load: Option<bool>,
759}
760
761impl From<SlackBlockPlainTextInputElement> for SlackSectionBlockElement {
762 fn from(element: SlackBlockPlainTextInputElement) -> Self {
763 SlackSectionBlockElement::PlainTextInput(element)
764 }
765}
766
767impl From<SlackBlockPlainTextInputElement> for SlackInputBlockElement {
768 fn from(element: SlackBlockPlainTextInputElement) -> Self {
769 SlackInputBlockElement::PlainTextInput(element)
770 }
771}
772
773impl From<SlackBlockPlainTextInputElement> for SlackActionBlockElement {
774 fn from(element: SlackBlockPlainTextInputElement) -> Self {
775 SlackActionBlockElement::PlainTextInput(element)
776 }
777}
778
779#[skip_serializing_none]
780#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
781pub struct SlackBlockNumberInputElement {
782 pub action_id: SlackActionId,
783 pub is_decimal_allowed: bool,
784 pub focus_on_load: Option<bool>,
785 pub placeholder: Option<SlackBlockPlainTextOnly>,
786 pub initial_value: Option<String>,
787 pub min_value: Option<String>,
788 pub max_value: Option<String>,
789}
790
791impl From<SlackBlockNumberInputElement> for SlackSectionBlockElement {
792 fn from(element: SlackBlockNumberInputElement) -> Self {
793 SlackSectionBlockElement::NumberInput(element)
794 }
795}
796
797impl From<SlackBlockNumberInputElement> for SlackInputBlockElement {
798 fn from(element: SlackBlockNumberInputElement) -> Self {
799 SlackInputBlockElement::NumberInput(element)
800 }
801}
802
803impl From<SlackBlockNumberInputElement> for SlackActionBlockElement {
804 fn from(element: SlackBlockNumberInputElement) -> Self {
805 SlackActionBlockElement::NumberInput(element)
806 }
807}
808
809#[skip_serializing_none]
810#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
811pub struct SlackBlockUrlInputElement {
812 pub action_id: SlackActionId,
813 pub placeholder: Option<SlackBlockPlainTextOnly>,
814 pub initial_value: Option<String>,
815}
816
817impl From<SlackBlockUrlInputElement> for SlackSectionBlockElement {
818 fn from(element: SlackBlockUrlInputElement) -> Self {
819 SlackSectionBlockElement::UrlInput(element)
820 }
821}
822
823impl From<SlackBlockUrlInputElement> for SlackInputBlockElement {
824 fn from(element: SlackBlockUrlInputElement) -> Self {
825 SlackInputBlockElement::UrlInput(element)
826 }
827}
828
829impl From<SlackBlockUrlInputElement> for SlackActionBlockElement {
830 fn from(element: SlackBlockUrlInputElement) -> Self {
831 SlackActionBlockElement::UrlInput(element)
832 }
833}
834
835#[skip_serializing_none]
836#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
837pub struct SlackBlockEmailInputElement {
838 pub action_id: SlackActionId,
839 pub focus_on_load: Option<bool>,
840 pub placeholder: Option<SlackBlockPlainTextOnly>,
841 pub initial_value: Option<EmailAddress>,
842}
843
844impl From<SlackBlockEmailInputElement> for SlackInputBlockElement {
845 fn from(element: SlackBlockEmailInputElement) -> Self {
846 SlackInputBlockElement::EmailInput(element)
847 }
848}
849
850#[skip_serializing_none]
851#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
852pub struct SlackBlockRadioButtonsElement {
853 pub action_id: SlackActionId,
854 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
855 pub initial_option: Option<SlackBlockChoiceItem<SlackBlockText>>,
856 pub confirm: Option<SlackBlockConfirmItem>,
857 pub focus_on_load: Option<bool>,
858}
859
860impl From<SlackBlockRadioButtonsElement> for SlackSectionBlockElement {
861 fn from(element: SlackBlockRadioButtonsElement) -> Self {
862 SlackSectionBlockElement::RadioButtons(element)
863 }
864}
865
866impl From<SlackBlockRadioButtonsElement> for SlackInputBlockElement {
867 fn from(element: SlackBlockRadioButtonsElement) -> Self {
868 SlackInputBlockElement::RadioButtons(element)
869 }
870}
871
872impl From<SlackBlockRadioButtonsElement> for SlackActionBlockElement {
873 fn from(element: SlackBlockRadioButtonsElement) -> Self {
874 SlackActionBlockElement::RadioButtons(element)
875 }
876}
877
878#[skip_serializing_none]
879#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
880pub struct SlackBlockCheckboxesElement {
881 pub action_id: SlackActionId,
882 pub options: Vec<SlackBlockChoiceItem<SlackBlockText>>,
883 pub initial_options: Option<Vec<SlackBlockChoiceItem<SlackBlockText>>>,
884 pub confirm: Option<SlackBlockConfirmItem>,
885 pub focus_on_load: Option<bool>,
886}
887
888impl From<SlackBlockCheckboxesElement> for SlackSectionBlockElement {
889 fn from(element: SlackBlockCheckboxesElement) -> Self {
890 SlackSectionBlockElement::Checkboxes(element)
891 }
892}
893
894impl From<SlackBlockCheckboxesElement> for SlackInputBlockElement {
895 fn from(element: SlackBlockCheckboxesElement) -> Self {
896 SlackInputBlockElement::Checkboxes(element)
897 }
898}
899
900impl From<SlackBlockCheckboxesElement> for SlackActionBlockElement {
901 fn from(element: SlackBlockCheckboxesElement) -> Self {
902 SlackActionBlockElement::Checkboxes(element)
903 }
904}
905
906#[skip_serializing_none]
910#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
911pub struct SlackBlockPlainText {
912 pub text: String,
913 pub emoji: Option<bool>,
914}
915
916#[skip_serializing_none]
920#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
921pub struct SlackBlockMarkDownText {
922 pub text: String,
923 pub verbatim: Option<bool>,
924}
925
926#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
930#[serde(tag = "type")]
931pub enum SlackBlockText {
932 #[serde(rename = "plain_text")]
933 Plain(SlackBlockPlainText),
934 #[serde(rename = "mrkdwn")]
935 MarkDown(SlackBlockMarkDownText),
936}
937
938#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
939#[serde(tag = "type", rename = "plain_text")]
940pub struct SlackBlockPlainTextOnly {
941 #[serde(flatten)]
942 value: SlackBlockPlainText,
943}
944
945impl SlackBlockPlainText {
946 pub fn as_block_text(&self) -> SlackBlockText {
947 SlackBlockText::Plain(self.clone())
948 }
949}
950
951impl From<String> for SlackBlockPlainText {
952 fn from(value: String) -> Self {
953 SlackBlockPlainText::new(value)
954 }
955}
956
957impl From<&str> for SlackBlockPlainText {
958 fn from(value: &str) -> Self {
959 SlackBlockPlainText::new(String::from(value))
960 }
961}
962
963impl SlackBlockMarkDownText {
964 pub fn as_block_text(&self) -> SlackBlockText {
965 SlackBlockText::MarkDown(self.clone())
966 }
967}
968
969impl From<String> for SlackBlockMarkDownText {
970 fn from(value: String) -> Self {
971 SlackBlockMarkDownText::new(value)
972 }
973}
974
975impl From<&str> for SlackBlockMarkDownText {
976 fn from(value: &str) -> Self {
977 SlackBlockMarkDownText::new(String::from(value))
978 }
979}
980
981impl From<SlackBlockPlainText> for SlackBlockPlainTextOnly {
982 fn from(pt: SlackBlockPlainText) -> Self {
983 SlackBlockPlainTextOnly { value: pt }
984 }
985}
986
987impl From<SlackBlockPlainText> for SlackBlockText {
988 fn from(text: SlackBlockPlainText) -> Self {
989 SlackBlockText::Plain(text)
990 }
991}
992
993impl From<SlackBlockMarkDownText> for SlackBlockText {
994 fn from(text: SlackBlockMarkDownText) -> Self {
995 SlackBlockText::MarkDown(text)
996 }
997}
998
999impl From<SlackBlockPlainText> for SlackContextBlockElement {
1000 fn from(text: SlackBlockPlainText) -> Self {
1001 SlackContextBlockElement::Plain(text)
1002 }
1003}
1004
1005impl From<SlackBlockMarkDownText> for SlackContextBlockElement {
1006 fn from(text: SlackBlockMarkDownText) -> Self {
1007 SlackContextBlockElement::MarkDown(text)
1008 }
1009}
1010
1011impl From<SlackBlockPlainTextOnly> for SlackBlockText {
1012 fn from(text: SlackBlockPlainTextOnly) -> Self {
1013 SlackBlockText::Plain(text.value)
1014 }
1015}
1016
1017impl From<String> for SlackBlockPlainTextOnly {
1018 fn from(value: String) -> Self {
1019 SlackBlockPlainTextOnly {
1020 value: value.into(),
1021 }
1022 }
1023}
1024
1025impl From<&str> for SlackBlockPlainTextOnly {
1026 fn from(value: &str) -> Self {
1027 SlackBlockPlainTextOnly {
1028 value: value.into(),
1029 }
1030 }
1031}
1032
1033#[skip_serializing_none]
1037#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1038pub struct SlackVideoBlock {
1039 pub alt_text: String,
1040 pub author_name: Option<String>,
1041 pub block_id: Option<SlackBlockId>,
1042 pub description: Option<SlackBlockPlainTextOnly>,
1043 pub provider_icon_url: Option<Url>,
1044 pub provider_name: Option<String>,
1045 pub title: SlackBlockPlainTextOnly,
1046 pub title_url: Option<Url>,
1047 pub thumbnail_url: Url,
1048 pub video_url: Url,
1049}
1050
1051impl From<SlackVideoBlock> for SlackBlock {
1052 fn from(block: SlackVideoBlock) -> Self {
1053 SlackBlock::Video(block)
1054 }
1055}
1056
1057#[skip_serializing_none]
1061#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1062pub struct SlackMarkdownBlock {
1063 pub block_id: Option<SlackBlockId>,
1064 pub text: String,
1065}
1066
1067impl From<SlackMarkdownBlock> for SlackBlock {
1068 fn from(block: SlackMarkdownBlock) -> Self {
1069 SlackBlock::Markdown(block)
1070 }
1071}
1072
1073#[skip_serializing_none]
1077#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1078pub struct SlackRichTextBlock {
1079 pub block_id: Option<SlackBlockId>,
1080 pub elements: Vec<SlackRichTextElement>,
1081}
1082
1083impl From<SlackRichTextBlock> for SlackBlock {
1084 fn from(block: SlackRichTextBlock) -> Self {
1085 SlackBlock::RichText(block)
1086 }
1087}
1088
1089#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1090#[serde(tag = "type")]
1091pub enum SlackRichTextElement {
1092 #[serde(rename = "rich_text_section")]
1093 Section(SlackRichTextSection),
1094 #[serde(rename = "rich_text_list")]
1095 List(SlackRichTextList),
1096 #[serde(rename = "rich_text_preformatted")]
1097 Preformatted(SlackRichTextPreformatted),
1098 #[serde(rename = "rich_text_quote")]
1099 Quote(SlackRichTextQuote),
1100}
1101
1102impl From<SlackRichTextSection> for SlackRichTextElement {
1103 fn from(element: SlackRichTextSection) -> Self {
1104 SlackRichTextElement::Section(element)
1105 }
1106}
1107
1108impl From<SlackRichTextList> for SlackRichTextElement {
1109 fn from(list: SlackRichTextList) -> Self {
1110 SlackRichTextElement::List(list)
1111 }
1112}
1113
1114impl From<SlackRichTextPreformatted> for SlackRichTextElement {
1115 fn from(element: SlackRichTextPreformatted) -> Self {
1116 SlackRichTextElement::Preformatted(element)
1117 }
1118}
1119
1120impl From<SlackRichTextQuote> for SlackRichTextElement {
1121 fn from(element: SlackRichTextQuote) -> Self {
1122 SlackRichTextElement::Quote(element)
1123 }
1124}
1125
1126#[skip_serializing_none]
1127#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1128pub struct SlackRichTextSection {
1129 pub elements: Vec<SlackRichTextInlineElement>,
1130}
1131
1132#[skip_serializing_none]
1133#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1134pub struct SlackRichTextList {
1135 pub style: SlackRichTextListStyle,
1136 pub elements: Vec<SlackRichTextSection>,
1137 pub indent: Option<u64>,
1138 pub offset: Option<u64>,
1139 pub border: Option<u64>,
1140}
1141
1142#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1143#[serde(rename_all = "snake_case")]
1144pub enum SlackRichTextListStyle {
1145 Bullet,
1146 Ordered,
1147}
1148
1149#[skip_serializing_none]
1150#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1151pub struct SlackRichTextPreformatted {
1152 pub elements: Vec<SlackRichTextInlineElement>,
1153 pub border: Option<u64>,
1154 pub language: Option<String>,
1155}
1156
1157#[skip_serializing_none]
1158#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1159pub struct SlackRichTextQuote {
1160 pub elements: Vec<SlackRichTextInlineElement>,
1161 pub border: Option<u64>,
1162}
1163
1164#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1165#[serde(tag = "type")]
1166pub enum SlackRichTextInlineElement {
1167 #[serde(rename = "text")]
1168 Text(SlackRichTextText),
1169 #[serde(rename = "link")]
1170 Link(SlackRichTextLink),
1171 #[serde(rename = "user")]
1172 User(SlackRichTextUser),
1173 #[serde(rename = "channel")]
1174 Channel(SlackRichTextChannel),
1175 #[serde(rename = "usergroup")]
1176 UserGroup(SlackRichTextUserGroup),
1177 #[serde(rename = "emoji")]
1178 Emoji(SlackRichTextEmoji),
1179 #[serde(rename = "date")]
1180 Date(SlackRichTextDate),
1181 #[serde(rename = "broadcast")]
1182 Broadcast(SlackRichTextBroadcast),
1183 #[serde(rename = "color")]
1184 Color(SlackRichTextColor),
1185}
1186
1187#[skip_serializing_none]
1188#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1189pub struct SlackRichTextStyle {
1190 pub bold: Option<bool>,
1191 pub italic: Option<bool>,
1192 pub strike: Option<bool>,
1193 pub code: Option<bool>,
1194 pub underline: Option<bool>,
1195 pub highlight: Option<bool>,
1196 pub client_highlight: Option<bool>,
1197 pub unlink: Option<bool>,
1198}
1199
1200#[skip_serializing_none]
1201#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1202pub struct SlackRichTextText {
1203 pub text: String,
1204 pub style: Option<SlackRichTextStyle>,
1205}
1206
1207#[skip_serializing_none]
1208#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1209pub struct SlackRichTextLink {
1210 pub url: Url,
1211 pub text: Option<String>,
1212 #[serde(rename = "unsafe")]
1213 pub unsafe_: Option<bool>,
1214 pub style: Option<SlackRichTextStyle>,
1215}
1216
1217#[skip_serializing_none]
1218#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1219pub struct SlackRichTextUser {
1220 pub user_id: SlackUserId,
1221 pub style: Option<SlackRichTextStyle>,
1222}
1223
1224#[skip_serializing_none]
1225#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1226pub struct SlackRichTextChannel {
1227 pub channel_id: SlackChannelId,
1228 pub style: Option<SlackRichTextStyle>,
1229}
1230
1231#[skip_serializing_none]
1232#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1233pub struct SlackRichTextUserGroup {
1234 pub usergroup_id: SlackUserGroupId,
1235 pub style: Option<SlackRichTextStyle>,
1236}
1237
1238#[skip_serializing_none]
1239#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1240pub struct SlackRichTextEmoji {
1241 pub name: SlackEmojiName,
1242 pub unicode: Option<String>,
1243}
1244
1245#[skip_serializing_none]
1246#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1247pub struct SlackRichTextDate {
1248 pub timestamp: SlackDateTime,
1249 pub format: String,
1250 pub fallback: Option<String>,
1251 pub style: Option<SlackRichTextStyle>,
1252}
1253
1254#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1255#[serde(rename_all = "snake_case")]
1256pub enum SlackRichTextBroadcastRange {
1257 Here,
1258 Channel,
1259 Everyone,
1260}
1261
1262#[skip_serializing_none]
1263#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1264pub struct SlackRichTextBroadcast {
1265 pub range: SlackRichTextBroadcastRange,
1266 pub style: Option<SlackRichTextStyle>,
1267}
1268
1269#[skip_serializing_none]
1270#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1271pub struct SlackRichTextColor {
1272 pub value: String,
1273}
1274
1275#[skip_serializing_none]
1279#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
1280pub struct SlackBlockRichTextInputElement {
1281 pub action_id: SlackActionId,
1282 pub initial_value: Option<SlackRichTextBlock>,
1283 pub focus_on_load: Option<bool>,
1284 pub placeholder: Option<SlackBlockPlainTextOnly>,
1285}
1286
1287impl From<SlackBlockRichTextInputElement> for SlackInputBlockElement {
1288 fn from(element: SlackBlockRichTextInputElement) -> Self {
1289 SlackInputBlockElement::RichTextInput(element)
1290 }
1291}
1292
1293#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1294#[serde(untagged)]
1295pub enum SlackImageUrlOrFile {
1296 ImageUrl { image_url: Url },
1297 SlackFile { slack_file: SlackFileIdOrUrl },
1298}
1299
1300impl SlackImageUrlOrFile {
1301 pub fn image_url(&self) -> Option<&Url> {
1302 match self {
1303 SlackImageUrlOrFile::ImageUrl { image_url } => Some(image_url),
1304 SlackImageUrlOrFile::SlackFile { slack_file } => match slack_file {
1305 SlackFileIdOrUrl::Url { url } => Some(url),
1306 _ => None,
1307 },
1308 }
1309 }
1310}
1311
1312impl From<Url> for SlackImageUrlOrFile {
1313 fn from(value: Url) -> Self {
1314 SlackImageUrlOrFile::ImageUrl { image_url: value }
1315 }
1316}
1317
1318#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
1319#[serde(untagged)]
1320pub enum SlackFileIdOrUrl {
1321 Id { id: SlackFileId },
1322 Url { url: Url },
1323}
1324
1325impl From<SlackFileId> for SlackFileIdOrUrl {
1326 fn from(value: SlackFileId) -> Self {
1327 SlackFileIdOrUrl::Id { id: value }
1328 }
1329}
1330
1331#[cfg(test)]
1332mod test {
1333 use super::*;
1334 use crate::blocks::SlackHomeView;
1335
1336 #[test]
1337 fn test_conversation_filter_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1338 let payload = include_str!("./fixtures/slack_conversations_select_with_filter.json");
1339 let block: SlackBlock = serde_json::from_str(payload)?;
1340 match block {
1341 SlackBlock::Section(section) => match section.accessory {
1342 Some(SlackSectionBlockElement::ConversationsSelect(elem)) => {
1343 let filter = elem.filter.expect("filter should be present");
1344 let include = filter.include.expect("include should be present");
1345 assert_eq!(include.len(), 2);
1346 assert_eq!(include[0], SlackConversationFilterInclude::Public);
1347 assert_eq!(include[1], SlackConversationFilterInclude::Private);
1348 assert_eq!(filter.exclude_external_shared_channels, Some(true));
1349 assert_eq!(filter.exclude_bot_users, Some(true));
1350 }
1351 _ => panic!("Expected ConversationsSelect accessory"),
1352 },
1353 _ => panic!("Expected Section block"),
1354 }
1355 Ok(())
1356 }
1357
1358 #[test]
1359 fn test_conversation_filter_serialize() -> Result<(), Box<dyn std::error::Error>> {
1360 let filter = SlackBlockConversationFilter::new()
1361 .with_include(vec![
1362 SlackConversationFilterInclude::Im,
1363 SlackConversationFilterInclude::Mpim,
1364 ])
1365 .with_exclude_bot_users(true);
1366
1367 let json = serde_json::to_value(&filter)?;
1368 assert_eq!(
1369 json,
1370 serde_json::json!({
1371 "include": ["im", "mpim"],
1372 "exclude_bot_users": true
1373 })
1374 );
1375 Ok(())
1376 }
1377
1378 #[test]
1379 fn test_conversation_filter_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1380 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("test_action".into()))
1381 .with_filter(
1382 SlackBlockConversationFilter::new()
1383 .with_include(vec![SlackConversationFilterInclude::Public])
1384 .with_exclude_external_shared_channels(true),
1385 );
1386
1387 let json = serde_json::to_string(&elem)?;
1388 let parsed: SlackBlockConversationsSelectElement = serde_json::from_str(&json)?;
1389 assert_eq!(elem, parsed);
1390 Ok(())
1391 }
1392
1393 #[test]
1394 fn test_multi_conversations_select_filter() -> Result<(), Box<dyn std::error::Error>> {
1395 let elem =
1396 SlackBlockMultiConversationsSelectElement::new(SlackActionId("multi_action".into()))
1397 .with_filter(
1398 SlackBlockConversationFilter::new()
1399 .with_include(vec![
1400 SlackConversationFilterInclude::Public,
1401 SlackConversationFilterInclude::Private,
1402 ])
1403 .with_exclude_bot_users(true),
1404 );
1405
1406 let json = serde_json::to_string(&elem)?;
1407 let parsed: SlackBlockMultiConversationsSelectElement = serde_json::from_str(&json)?;
1408 assert_eq!(elem, parsed);
1409 Ok(())
1410 }
1411
1412 #[test]
1413 fn test_conversation_filter_none_omitted() -> Result<(), Box<dyn std::error::Error>> {
1414 let elem = SlackBlockConversationsSelectElement::new(SlackActionId("no_filter".into()));
1415
1416 let json = serde_json::to_value(&elem)?;
1417 assert!(json.get("filter").is_none());
1418 Ok(())
1419 }
1420
1421 #[test]
1422 fn test_slack_image_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1423 let payload = include_str!("./fixtures/slack_image_blocks.json");
1424 let content: SlackMessageContent = serde_json::from_str(payload)?;
1425 let blocks = content.blocks.expect("Blocks should not be empty");
1426 match blocks.first() {
1427 Some(SlackBlock::Section(section)) => match §ion.accessory {
1428 Some(SlackSectionBlockElement::Image(image)) => {
1429 assert_eq!(image.alt_text, "alt text for image");
1430 match &image.image_url_or_file {
1431 SlackImageUrlOrFile::ImageUrl { image_url } => {
1432 assert_eq!(image_url.as_str(), "https://s3-media3.fl.yelpcdn.com/bphoto/c7ed05m9lC2EmA3Aruue7A/o.jpg");
1433 }
1434 SlackImageUrlOrFile::SlackFile { slack_file } => {
1435 panic!("Expected an image URL, not a Slack file: {:?}", slack_file);
1436 }
1437 }
1438 }
1439 _ => panic!("Expected a section block with an image accessory"),
1440 },
1441 _ => panic!("Expected a section block"),
1442 }
1443 Ok(())
1444 }
1445
1446 #[test]
1447 fn test_rich_text_block_deserialize() -> Result<(), Box<dyn std::error::Error>> {
1448 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1449 let block: SlackBlock = serde_json::from_str(payload)?;
1450
1451 let rich = match block {
1452 SlackBlock::RichText(r) => r,
1453 _ => panic!("Expected a RichText block"),
1454 };
1455
1456 assert_eq!(rich.block_id, Some(SlackBlockId("test_block".into())));
1457 assert_eq!(rich.elements.len(), 4);
1458
1459 let section = match &rich.elements[0] {
1461 SlackRichTextElement::Section(s) => s,
1462 _ => panic!("Expected a Section element"),
1463 };
1464 assert_eq!(section.elements.len(), 7);
1465
1466 let text = match §ion.elements[0] {
1468 SlackRichTextInlineElement::Text(t) => t,
1469 _ => panic!("Expected a Text element"),
1470 };
1471 assert_eq!(text.text, "Hello ");
1472 assert_eq!(text.style.as_ref().and_then(|s| s.bold), Some(true));
1473
1474 assert!(matches!(
1476 §ion.elements[1],
1477 SlackRichTextInlineElement::User(_)
1478 ));
1479
1480 let emoji = match §ion.elements[4] {
1482 SlackRichTextInlineElement::Emoji(e) => e,
1483 _ => panic!("Expected an Emoji element"),
1484 };
1485 assert_eq!(emoji.name, SlackEmojiName::new("wave".into()));
1486
1487 let list = match &rich.elements[1] {
1489 SlackRichTextElement::List(l) => l,
1490 _ => panic!("Expected a List element"),
1491 };
1492 assert_eq!(list.style, SlackRichTextListStyle::Bullet);
1493 assert_eq!(list.elements.len(), 2);
1494
1495 assert!(matches!(
1497 &rich.elements[2],
1498 SlackRichTextElement::Preformatted(_)
1499 ));
1500
1501 assert!(matches!(&rich.elements[3], SlackRichTextElement::Quote(_)));
1503
1504 Ok(())
1505 }
1506
1507 #[test]
1508 fn test_rich_text_block_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
1509 let payload = include_str!("./fixtures/slack_rich_text_block.json");
1510 let block: SlackBlock = serde_json::from_str(payload)?;
1511 let serialized = serde_json::to_string(&block)?;
1512 let block2: SlackBlock = serde_json::from_str(&serialized)?;
1513 assert_eq!(block, block2);
1514 Ok(())
1515 }
1516}