Skip to content

Commit d335831

Browse files
authored
feat(models): add feedback_buttons and icon_button blocks as context_actions block elements (#1750)
1 parent 551e379 commit d335831

File tree

7 files changed

+499
-142
lines changed

7 files changed

+499
-142
lines changed

slack_sdk/models/blocks/__init__.py

Lines changed: 71 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,67 +6,79 @@
66
* https://api.slack.com/reference/block-kit/blocks
77
* https://app.slack.com/block-kit-builder
88
"""
9-
from .basic_components import ButtonStyles
10-
from .basic_components import ConfirmObject
11-
from .basic_components import DynamicSelectElementTypes
12-
from .basic_components import MarkdownTextObject
13-
from .basic_components import Option
14-
from .basic_components import OptionGroup
15-
from .basic_components import PlainTextObject
16-
from .basic_components import TextObject
17-
from .block_elements import BlockElement
18-
from .block_elements import ButtonElement
19-
from .block_elements import ChannelMultiSelectElement
20-
from .block_elements import ChannelSelectElement
21-
from .block_elements import CheckboxesElement
22-
from .block_elements import ConversationFilter
23-
from .block_elements import ConversationMultiSelectElement
24-
from .block_elements import ConversationSelectElement
25-
from .block_elements import DatePickerElement
26-
from .block_elements import TimePickerElement
27-
from .block_elements import DateTimePickerElement
28-
from .block_elements import ExternalDataMultiSelectElement
29-
from .block_elements import ExternalDataSelectElement
30-
from .block_elements import ImageElement
31-
from .block_elements import InputInteractiveElement
32-
from .block_elements import InteractiveElement
33-
from .block_elements import LinkButtonElement
34-
from .block_elements import OverflowMenuElement
35-
from .block_elements import RichTextInputElement
36-
from .block_elements import PlainTextInputElement
37-
from .block_elements import EmailInputElement
38-
from .block_elements import UrlInputElement
39-
from .block_elements import NumberInputElement
40-
from .block_elements import RadioButtonsElement
41-
from .block_elements import SelectElement
42-
from .block_elements import StaticMultiSelectElement
43-
from .block_elements import StaticSelectElement
44-
from .block_elements import UserMultiSelectElement
45-
from .block_elements import UserSelectElement
46-
from .block_elements import RichTextElement
47-
from .block_elements import RichTextElementParts
48-
from .block_elements import RichTextListElement
49-
from .block_elements import RichTextPreformattedElement
50-
from .block_elements import RichTextQuoteElement
51-
from .block_elements import RichTextSectionElement
52-
from .blocks import ActionsBlock
53-
from .blocks import Block
54-
from .blocks import CallBlock
55-
from .blocks import ContextBlock
56-
from .blocks import DividerBlock
57-
from .blocks import FileBlock
58-
from .blocks import HeaderBlock
59-
from .blocks import ImageBlock
60-
from .blocks import InputBlock
61-
from .blocks import MarkdownBlock
62-
from .blocks import SectionBlock
63-
from .blocks import VideoBlock
64-
from .blocks import RichTextBlock
9+
10+
from .basic_components import (
11+
ButtonStyles,
12+
ConfirmObject,
13+
DynamicSelectElementTypes,
14+
FeedbackButtonObject,
15+
MarkdownTextObject,
16+
Option,
17+
OptionGroup,
18+
PlainTextObject,
19+
TextObject,
20+
)
21+
from .block_elements import (
22+
BlockElement,
23+
ButtonElement,
24+
ChannelMultiSelectElement,
25+
ChannelSelectElement,
26+
CheckboxesElement,
27+
ConversationFilter,
28+
ConversationMultiSelectElement,
29+
ConversationSelectElement,
30+
DatePickerElement,
31+
DateTimePickerElement,
32+
EmailInputElement,
33+
ExternalDataMultiSelectElement,
34+
ExternalDataSelectElement,
35+
FeedbackButtonsElement,
36+
IconButtonElement,
37+
ImageElement,
38+
InputInteractiveElement,
39+
InteractiveElement,
40+
LinkButtonElement,
41+
NumberInputElement,
42+
OverflowMenuElement,
43+
PlainTextInputElement,
44+
RadioButtonsElement,
45+
RichTextElement,
46+
RichTextElementParts,
47+
RichTextInputElement,
48+
RichTextListElement,
49+
RichTextPreformattedElement,
50+
RichTextQuoteElement,
51+
RichTextSectionElement,
52+
SelectElement,
53+
StaticMultiSelectElement,
54+
StaticSelectElement,
55+
TimePickerElement,
56+
UrlInputElement,
57+
UserMultiSelectElement,
58+
UserSelectElement,
59+
)
60+
from .blocks import (
61+
ActionsBlock,
62+
Block,
63+
CallBlock,
64+
ContextActionsBlock,
65+
ContextBlock,
66+
DividerBlock,
67+
FileBlock,
68+
HeaderBlock,
69+
ImageBlock,
70+
InputBlock,
71+
MarkdownBlock,
72+
RichTextBlock,
73+
SectionBlock,
74+
VideoBlock,
75+
)
6576

6677
__all__ = [
6778
"ButtonStyles",
6879
"ConfirmObject",
6980
"DynamicSelectElementTypes",
81+
"FeedbackButtonObject",
7082
"MarkdownTextObject",
7183
"Option",
7284
"OptionGroup",
@@ -85,6 +97,8 @@
8597
"DateTimePickerElement",
8698
"ExternalDataMultiSelectElement",
8799
"ExternalDataSelectElement",
100+
"FeedbackButtonsElement",
101+
"IconButtonElement",
88102
"ImageElement",
89103
"InputInteractiveElement",
90104
"InteractiveElement",
@@ -110,6 +124,7 @@
110124
"ActionsBlock",
111125
"Block",
112126
"CallBlock",
127+
"ContextActionsBlock",
113128
"ContextBlock",
114129
"DividerBlock",
115130
"FileBlock",

slack_sdk/models/blocks/basic_components.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
import copy
22
import logging
33
import warnings
4-
from typing import List, Optional, Set, Union, Sequence, Dict, Any
4+
from typing import Any, Dict, List, Optional, Sequence, Set, Union
55

66
from slack_sdk.models import show_unknown_key_warning
7-
from slack_sdk.models.basic_objects import (
8-
JsonObject,
9-
JsonValidator,
10-
)
7+
from slack_sdk.models.basic_objects import JsonObject, JsonValidator
118
from slack_sdk.models.messages import Link
129

1310
ButtonStyles = {"danger", "primary"}
@@ -526,6 +523,66 @@ def to_dict(self) -> Dict[str, Any]:
526523
return json
527524

528525

526+
class FeedbackButtonObject(JsonObject):
527+
attributes: Set[str] = set()
528+
529+
text_max_length = 75
530+
value_max_length = 2000
531+
532+
@classmethod
533+
def parse(cls, feedback_button: Union["FeedbackButtonObject", Dict[str, Any]]):
534+
if feedback_button:
535+
if isinstance(feedback_button, FeedbackButtonObject):
536+
return feedback_button
537+
elif isinstance(feedback_button, dict):
538+
return FeedbackButtonObject(**feedback_button)
539+
else:
540+
# Not yet implemented: show some warning here
541+
return None
542+
return None
543+
544+
def __init__(
545+
self,
546+
*,
547+
text: Union[str, Dict[str, Any], PlainTextObject],
548+
accessibility_label: Optional[str] = None,
549+
value: str,
550+
**others: Dict[str, Any],
551+
):
552+
"""
553+
A feedback button element object for either positive or negative feedback.
554+
555+
Args:
556+
text (required): An object containing some text. Maximum length for this field is 75 characters.
557+
accessibility_label: A label for longer descriptive text about a button element. This label will be read out by
558+
screen readers instead of the button `text` object.
559+
value (required): The button value. Maximum length for this field is 2000 characters.
560+
"""
561+
self._text: Optional[TextObject] = PlainTextObject.parse(text, default_type=PlainTextObject.type)
562+
self._accessibility_label: Optional[str] = accessibility_label
563+
self._value: Optional[str] = value
564+
show_unknown_key_warning(self, others)
565+
566+
@JsonValidator(f"text attribute cannot exceed {text_max_length} characters")
567+
def text_length(self) -> bool:
568+
return self._text is None or len(self._text.text) <= self.text_max_length
569+
570+
@JsonValidator(f"value attribute cannot exceed {value_max_length} characters")
571+
def value_length(self) -> bool:
572+
return self._value is None or len(self._value) <= self.value_max_length
573+
574+
def to_dict(self) -> Dict[str, Any]:
575+
self.validate_json()
576+
json: Dict[str, Union[str, dict]] = {}
577+
if self._text:
578+
json["text"] = self._text.to_dict()
579+
if self._accessibility_label:
580+
json["accessibility_label"] = self._accessibility_label
581+
if self._value:
582+
json["value"] = self._value
583+
return json
584+
585+
529586
class WorkflowTrigger(JsonObject):
530587
attributes = {"trigger"}
531588

slack_sdk/models/blocks/block_elements.py

Lines changed: 105 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,24 @@
33
import re
44
import warnings
55
from abc import ABCMeta
6-
from typing import Iterator, List, Optional, Set, Type, Union, Sequence, Dict, Any
6+
from typing import Any, Dict, Iterator, List, Optional, Sequence, Set, Type, Union
77

88
from slack_sdk.models import show_unknown_key_warning
9-
from slack_sdk.models.basic_objects import (
10-
JsonObject,
11-
JsonValidator,
12-
EnumValidator,
9+
from slack_sdk.models.basic_objects import EnumValidator, JsonObject, JsonValidator
10+
11+
from .basic_components import (
12+
ButtonStyles,
13+
ConfirmObject,
14+
DispatchActionConfig,
15+
FeedbackButtonObject,
16+
MarkdownTextObject,
17+
Option,
18+
OptionGroup,
19+
PlainTextObject,
20+
SlackFile,
21+
TextObject,
22+
Workflow,
1323
)
14-
from .basic_components import ButtonStyles, Workflow, SlackFile
15-
from .basic_components import ConfirmObject
16-
from .basic_components import DispatchActionConfig
17-
from .basic_components import MarkdownTextObject
18-
from .basic_components import Option
19-
from .basic_components import OptionGroup
20-
from .basic_components import PlainTextObject
21-
from .basic_components import TextObject
22-
2324

2425
# -------------------------------------------------
2526
# Block Elements
@@ -539,6 +540,43 @@ def _validate_initial_date_time_valid(self) -> bool:
539540
return self.initial_date_time is None or (0 <= self.initial_date_time <= 9999999999)
540541

541542

543+
# -------------------------------------------------
544+
# Feedback Buttons Element
545+
# -------------------------------------------------
546+
547+
548+
class FeedbackButtonsElement(InteractiveElement):
549+
type = "feedback_buttons"
550+
551+
@property
552+
def attributes(self) -> Set[str]: # type: ignore[override]
553+
return super().attributes.union({"positive_button", "negative_button"})
554+
555+
def __init__(
556+
self,
557+
*,
558+
action_id: Optional[str] = None,
559+
positive_button: Union[dict, FeedbackButtonObject],
560+
negative_button: Union[dict, FeedbackButtonObject],
561+
**others: dict,
562+
):
563+
"""Buttons to indicate positive or negative feedback.
564+
565+
Args:
566+
action_id (required): An identifier for this action.
567+
You can use this when you receive an interaction payload to identify the source of the action.
568+
Should be unique among all other action_ids in the containing block.
569+
Maximum length for this field is 255 characters.
570+
positive_button (required): A button to indicate positive feedback.
571+
negative_button (required): A button to indicate negative feedback.
572+
"""
573+
super().__init__(action_id=action_id, type=self.type)
574+
show_unknown_key_warning(self, others)
575+
576+
self.positive_button = FeedbackButtonObject.parse(positive_button)
577+
self.negative_button = FeedbackButtonObject.parse(negative_button)
578+
579+
542580
# -------------------------------------------------
543581
# Image
544582
# -------------------------------------------------
@@ -587,6 +625,59 @@ def _validate_alt_text_length(self) -> bool:
587625
return len(self.alt_text) <= self.alt_text_max_length # type: ignore[arg-type]
588626

589627

628+
# -------------------------------------------------
629+
# Icon Button Element
630+
# -------------------------------------------------
631+
632+
633+
class IconButtonElement(InteractiveElement):
634+
type = "icon_button"
635+
636+
@property
637+
def attributes(self) -> Set[str]: # type: ignore[override]
638+
return super().attributes.union({"icon", "text", "accessibility_label", "value", "visible_to_user_ids", "confirm"})
639+
640+
def __init__(
641+
self,
642+
*,
643+
action_id: Optional[str] = None,
644+
icon: str,
645+
text: Union[str, dict, TextObject],
646+
accessibility_label: Optional[str] = None,
647+
value: Optional[str] = None,
648+
visible_to_user_ids: Optional[List[str]] = None,
649+
confirm: Optional[Union[dict, ConfirmObject]] = None,
650+
**others: dict,
651+
):
652+
"""An icon button to perform actions.
653+
654+
Args:
655+
action_id: An identifier for this action.
656+
You can use this when you receive an interaction payload to identify the source of the action.
657+
Should be unique among all other action_ids in the containing block.
658+
Maximum length for this field is 255 characters.
659+
icon (required): The icon to show (e.g., 'trash').
660+
text (required): Defines an object containing some text.
661+
accessibility_label: A label for longer descriptive text about a button element.
662+
This label will be read out by screen readers instead of the button text object.
663+
Maximum length for this field is 75 characters.
664+
value: The button value.
665+
Maximum length for this field is 2000 characters.
666+
visible_to_user_ids: User IDs for which the icon appears.
667+
Maximum length for this field is 10 user IDs.
668+
confirm: A confirm object that defines an optional confirmation dialog after the button is clicked.
669+
"""
670+
super().__init__(action_id=action_id, type=self.type)
671+
show_unknown_key_warning(self, others)
672+
673+
self.icon = icon
674+
self.text = TextObject.parse(text, PlainTextObject.type)
675+
self.accessibility_label = accessibility_label
676+
self.value = value
677+
self.visible_to_user_ids = visible_to_user_ids
678+
self.confirm = ConfirmObject.parse(confirm) if confirm else None
679+
680+
590681
# -------------------------------------------------
591682
# Static Select
592683
# -------------------------------------------------

0 commit comments

Comments
 (0)