Module slack_sdk.models.blocks.basic_components
Expand source code
import copy
import logging
import warnings
from typing import List, Optional, Set, Union, Sequence
from slack_sdk.models import show_unknown_key_warning
from slack_sdk.models.basic_objects import (
JsonObject,
JsonValidator,
)
from slack_sdk.models.messages import Link
ButtonStyles = {"danger", "primary"}
DynamicSelectElementTypes = {"channels", "conversations", "users"}
class TextObject(JsonObject):
"""The interface for text objects (types: plain_text, mrkdwn)"""
attributes = {"text", "type", "emoji"}
logger = logging.getLogger(__name__)
def _subtype_warning(self): # skipcq: PYL-R0201
warnings.warn(
"subtype is deprecated since slackclient 2.6.0, use type instead",
DeprecationWarning,
)
@property
def subtype(self) -> Optional[str]:
return self.type
@classmethod
def parse(
cls, text: Union[str, dict, "TextObject"], default_type: str = "mrkdwn"
) -> Optional["TextObject"]:
if not text: # skipcq: PYL-R1705
return None
elif isinstance(text, str):
if default_type == PlainTextObject.type: # skipcq: PYL-R1705
return PlainTextObject.from_str(text)
else:
return MarkdownTextObject.from_str(text)
elif isinstance(text, dict):
d = copy.copy(text)
t = d.pop("type")
if t == PlainTextObject.type: # skipcq: PYL-R1705
return PlainTextObject(**d)
else:
return MarkdownTextObject(**d)
elif isinstance(text, TextObject):
return text
else:
cls.logger.warning(
f"Unknown type ({type(text)}) detected when parsing a TextObject"
)
return None
def __init__(
self,
text: str,
type: Optional[str] = None, # skipcq: PYL-W0622
subtype: Optional[str] = None,
emoji: Optional[bool] = None,
**kwargs,
):
"""Super class for new text "objects" used in Block kit"""
if subtype:
self._subtype_warning()
self.text = text
self.type = type if type else subtype
self.emoji = emoji
class PlainTextObject(TextObject):
"""plain_text typed text object"""
type = "plain_text"
@property
def attributes(self) -> Set[str]:
return super().attributes.union({"emoji"})
def __init__(self, *, text: str, emoji: Optional[bool] = None):
"""A plain text object, meaning markdown characters will not be parsed as
formatting information.
https://api.slack.com/reference/block-kit/composition-objects#text
"""
super().__init__(text=text, type=self.type)
self.emoji = emoji
@staticmethod
def from_str(text: str) -> "PlainTextObject":
return PlainTextObject(text=text, emoji=True)
@staticmethod
def direct_from_string(text: str) -> dict:
"""Transforms a string into the required object shape to act as a PlainTextObject"""
return PlainTextObject.from_str(text).to_dict()
class MarkdownTextObject(TextObject):
"""mrkdwn typed text object"""
type = "mrkdwn"
@property
def attributes(self) -> Set[str]:
return super().attributes.union({"verbatim"})
def __init__(self, *, text: str, verbatim: Optional[bool] = None):
"""A Markdown text object, meaning markdown characters will be parsed as
formatting information.
https://api.slack.com/reference/block-kit/composition-objects#text
"""
super().__init__(text=text, type=self.type)
self.verbatim = verbatim
@staticmethod
def from_str(text: str) -> "MarkdownTextObject":
"""Transforms a string into the required object shape to act as a MarkdownTextObject"""
return MarkdownTextObject(text=text)
@staticmethod
def direct_from_string(text: str) -> dict:
"""Transforms a string into the required object shape to act as a MarkdownTextObject"""
return MarkdownTextObject.from_str(text).to_dict()
@staticmethod
def from_link(link: Link, title: str = "") -> "MarkdownTextObject":
"""
Transform a Link object directly into the required object shape
to act as a MarkdownTextObject
"""
if title:
title = f": {title}"
return MarkdownTextObject(text=f"{link}{title}")
@staticmethod
def direct_from_link(link: Link, title: str = "") -> dict:
"""
Transform a Link object directly into the required object shape
to act as a MarkdownTextObject
"""
return MarkdownTextObject.from_link(link, title).to_dict()
class Option(JsonObject):
"""Option object used in dialogs, legacy message actions (interactivity in attachments),
and blocks. JSON must be retrieved with an explicit option_type - the Slack API has
different required formats in different situations
"""
attributes = {} # no attributes because to_dict has unique implementations
logger = logging.getLogger(__name__)
label_max_length = 75
value_max_length = 75
def __init__(
self,
*,
value: str,
label: Optional[str] = None,
text: Optional[Union[str, dict, TextObject]] = None, # Block Kit
description: Optional[Union[str, dict, TextObject]] = None,
url: Optional[str] = None,
**others: dict,
):
"""
An object that represents a single selectable item in a block element (
SelectElement, OverflowMenuElement) or dialog element
(StaticDialogSelectElement)
Blocks:
https://api.slack.com/reference/block-kit/composition-objects#option
Dialogs:
https://api.slack.com/dialogs#select_elements
Legacy interactive attachments:
https://api.slack.com/legacy/interactive-message-field-guide#option_fields
Args:
label: A short, user-facing string to label this option to users.
Cannot exceed 75 characters.
value: A short string that identifies this particular option to your
application. It will be part of the payload when this option is selected
. Cannot exceed 75 characters.
description: A user-facing string that provides more details about
this option. Only supported in legacy message actions, not in blocks or
dialogs.
"""
if text:
self._text: Optional[TextObject] = TextObject.parse(text)
self._label: Optional[str] = None
else:
self._text: Optional[TextObject] = None
self._label: Optional[str] = label
# for backward-compatibility with version 2.0-2.5, the following fields return str values
self.text: Optional[str] = self._text.text if self._text else None
self.label: Optional[str] = self._label
self.value: str = value
# for backward-compatibility with version 2.0-2.5, the following fields return str values
if isinstance(description, str):
self.description = description
self._block_description = PlainTextObject.from_str(description)
elif isinstance(description, dict):
self.description = description["text"]
self._block_description = TextObject.parse(description)
elif isinstance(description, TextObject):
self.description = description.text
self._block_description = description
else:
self.description = None
self._block_description = None
# A URL to load in the user's browser when the option is clicked.
# The url attribute is only available in overflow menus.
# Maximum length for this field is 3000 characters.
# If you're using url, you'll still receive an interaction payload
# and will need to send an acknowledgement response.
self.url: Optional[str] = url
show_unknown_key_warning(self, others)
@JsonValidator(f"label attribute cannot exceed {label_max_length} characters")
def _validate_label_length(self):
return self._label is None or len(self._label) <= self.label_max_length
@JsonValidator(f"text attribute cannot exceed {label_max_length} characters")
def _validate_text_length(self):
return (
self._text is None
or self._text.text is None
or len(self._text.text) <= self.label_max_length
)
@JsonValidator(f"value attribute cannot exceed {value_max_length} characters")
def _validate_value_length(self):
return len(self.value) <= self.value_max_length
@classmethod
def parse_all(
cls, options: Optional[Sequence[Union[dict, "Option"]]]
) -> Optional[List["Option"]]:
if options is None:
return None
option_objects: List[Option] = []
for o in options:
if isinstance(o, dict):
d = copy.copy(o)
option_objects.append(Option(**d))
elif isinstance(o, Option):
option_objects.append(o)
else:
cls.logger.warning(f"Unknown option object detected and skipped ({o})")
return option_objects
def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221
"""
Different parent classes must call this with a valid value from OptionTypes -
either "dialog", "action", or "block", so that JSON is returned in the
correct shape.
"""
self.validate_json()
if option_type == "dialog": # skipcq: PYL-R1705
return {"label": self.label, "value": self.value}
elif option_type == "action" or option_type == "attachment":
# "action" can be confusing but it means a legacy message action in attachments
# we don't remove the type name for backward compatibility though
json = {"text": self.label, "value": self.value}
if self.description is not None:
json["description"] = self.description
return json
else: # if option_type == "block"; this should be the most common case
text: TextObject = self._text or PlainTextObject.from_str(self.label)
json: dict = {
"text": text.to_dict(),
"value": self.value,
}
if self._block_description:
json["description"] = self._block_description.to_dict()
if self.url:
json["url"] = self.url
return json
@staticmethod
def from_single_value(value_and_label: str):
"""Creates a simple Option instance with the same value and label"""
return Option(value=value_and_label, label=value_and_label)
class OptionGroup(JsonObject):
"""
JSON must be retrieved with an explicit option_type - the Slack API has
different required formats in different situations
"""
attributes = {} # no attributes because to_dict has unique implementations
label_max_length = 75
options_max_length = 100
logger = logging.getLogger(__name__)
def __init__(
self,
*,
label: Optional[Union[str, dict, TextObject]] = None,
options: Sequence[Union[dict, Option]],
**others: dict,
):
"""
Create a group of Option objects - pass in a label (that will be part of the
UI) and a list of Option objects.
Blocks:
https://api.slack.com/reference/block-kit/composition-objects#option-group
Dialogs:
https://api.slack.com/dialogs#select_elements
Legacy interactive attachments:
https://api.slack.com/legacy/interactive-message-field-guide#option_groups_to_place_within_message_menu_actions
Args:
label: Text to display at the top of this group of options.
options: A list of no more than 100 Option objects.
""" # noqa prevent flake8 blowing up on the long URL
# default_type=PlainTextObject.type is for backward-compatibility
self._label: Optional[TextObject] = TextObject.parse(
label, default_type=PlainTextObject.type
)
self.label: Optional[str] = self._label.text if self._label else None
self.options = Option.parse_all(options) # compatible with version 2.5
show_unknown_key_warning(self, others)
@JsonValidator(f"label attribute cannot exceed {label_max_length} characters")
def _validate_label_length(self):
return self.label is None or len(self.label) <= self.label_max_length
@JsonValidator(f"options attribute cannot exceed {options_max_length} elements")
def _validate_options_length(self):
return self.options is None or len(self.options) <= self.options_max_length
@classmethod
def parse_all(
cls, option_groups: Optional[Sequence[Union[dict, "OptionGroup"]]]
) -> Optional[List["OptionGroup"]]:
if option_groups is None:
return None
option_group_objects = []
for o in option_groups:
if isinstance(o, dict):
d = copy.copy(o)
option_group_objects.append(OptionGroup(**d))
elif isinstance(o, OptionGroup):
option_group_objects.append(o)
else:
cls.logger.warning(
f"Unknown option group object detected and skipped ({o})"
)
return option_group_objects
def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221
self.validate_json()
dict_options = [o.to_dict(option_type) for o in self.options]
if option_type == "dialog": # skipcq: PYL-R1705
return {
"label": self.label,
"options": dict_options,
}
elif option_type == "action":
return {
"text": self.label,
"options": dict_options,
}
else: # if option_type == "block"; this should be the most common case
dict_label: dict = self._label.to_dict()
return {
"label": dict_label,
"options": dict_options,
}
class ConfirmObject(JsonObject):
attributes = {} # no attributes because to_dict has unique implementations
title_max_length = 100
text_max_length = 300
confirm_max_length = 30
deny_max_length = 30
@classmethod
def parse(cls, confirm: Union["ConfirmObject", dict]):
if confirm:
if isinstance(confirm, ConfirmObject): # skipcq: PYL-R1705
return confirm
elif isinstance(confirm, dict):
return ConfirmObject(**confirm)
else:
# Not yet implemented: show some warning here
return None
return None
def __init__(
self,
*,
title: Union[str, dict, PlainTextObject],
text: Union[str, dict, TextObject],
confirm: Union[str, dict, PlainTextObject] = "Yes",
deny: Union[str, dict, PlainTextObject] = "No",
style: str = None,
):
"""
An object that defines a dialog that provides a confirmation step to any
interactive element. This dialog will ask the user to confirm their action by
offering a confirm and deny button.
https://api.slack.com/reference/block-kit/composition-objects#confirm
"""
self._title = TextObject.parse(title, default_type=PlainTextObject.type)
self._text = TextObject.parse(text, default_type=MarkdownTextObject.type)
self._confirm = TextObject.parse(confirm, default_type=PlainTextObject.type)
self._deny = TextObject.parse(deny, default_type=PlainTextObject.type)
self._style = style
# for backward-compatibility with version 2.0-2.5, the following fields return str values
self.title = self._title.text if self._title else None
self.text = self._text.text if self._text else None
self.confirm = self._confirm.text if self._confirm else None
self.deny = self._deny.text if self._deny else None
self.style = self._style
@JsonValidator(f"title attribute cannot exceed {title_max_length} characters")
def title_length(self):
return self._title is None or len(self._title.text) <= self.title_max_length
@JsonValidator(f"text attribute cannot exceed {text_max_length} characters")
def text_length(self):
return self._text is None or len(self._text.text) <= self.text_max_length
@JsonValidator(f"confirm attribute cannot exceed {confirm_max_length} characters")
def confirm_length(self):
return (
self._confirm is None or len(self._confirm.text) <= self.confirm_max_length
)
@JsonValidator(f"deny attribute cannot exceed {deny_max_length} characters")
def deny_length(self):
return self._deny is None or len(self._deny.text) <= self.deny_max_length
@JsonValidator('style for confirm must be either "primary" or "danger"')
def _validate_confirm_style(self):
return self._style is None or self._style in ["primary", "danger"]
def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221
if option_type == "action": # skipcq: PYL-R1705
# deliberately skipping JSON validators here - can't find documentation
# on actual limits here
json = {
"ok_text": self._confirm.text
if self._confirm and self._confirm.text != "Yes"
else "Okay",
"dismiss_text": self._deny.text
if self._deny and self._deny.text != "No"
else "Cancel",
}
if self._title:
json["title"] = self._title.text
if self._text:
json["text"] = self._text.text
return json
else:
self.validate_json()
json = {}
if self._title:
json["title"] = self._title.to_dict()
if self._text:
json["text"] = self._text.to_dict()
if self._confirm:
json["confirm"] = self._confirm.to_dict()
if self._deny:
json["deny"] = self._deny.to_dict()
if self._style:
json["style"] = self._style
return json
class DispatchActionConfig(JsonObject):
attributes = {"trigger_actions_on"}
@classmethod
def parse(cls, config: Union["DispatchActionConfig", dict]):
if config:
if isinstance(config, DispatchActionConfig): # skipcq: PYL-R1705
return config
elif isinstance(config, dict):
return DispatchActionConfig(**config)
else:
# Not yet implemented: show some warning here
return None
return None
def __init__(
self,
*,
trigger_actions_on: Optional[list] = None,
):
"""
Determines when a plain-text input element will return a block_actions interaction payload.
https://api.slack.com/reference/block-kit/composition-objects#dispatch_action_config
"""
self._trigger_actions_on = trigger_actions_on or []
def to_dict(self) -> dict: # skipcq: PYL-W0221
self.validate_json()
json = {}
if self._trigger_actions_on:
json["trigger_actions_on"] = self._trigger_actions_on
return json
Classes
class ConfirmObject (*, title: Union[str, dict, PlainTextObject], text: Union[str, dict, TextObject], confirm: Union[str, dict, PlainTextObject] = 'Yes', deny: Union[str, dict, PlainTextObject] = 'No', style: str = None)
-
The base class for JSON serializable class objects
An object that defines a dialog that provides a confirmation step to any interactive element. This dialog will ask the user to confirm their action by offering a confirm and deny button. https://api.slack.com/reference/block-kit/composition-objects#confirm
Expand source code
class ConfirmObject(JsonObject): attributes = {} # no attributes because to_dict has unique implementations title_max_length = 100 text_max_length = 300 confirm_max_length = 30 deny_max_length = 30 @classmethod def parse(cls, confirm: Union["ConfirmObject", dict]): if confirm: if isinstance(confirm, ConfirmObject): # skipcq: PYL-R1705 return confirm elif isinstance(confirm, dict): return ConfirmObject(**confirm) else: # Not yet implemented: show some warning here return None return None def __init__( self, *, title: Union[str, dict, PlainTextObject], text: Union[str, dict, TextObject], confirm: Union[str, dict, PlainTextObject] = "Yes", deny: Union[str, dict, PlainTextObject] = "No", style: str = None, ): """ An object that defines a dialog that provides a confirmation step to any interactive element. This dialog will ask the user to confirm their action by offering a confirm and deny button. https://api.slack.com/reference/block-kit/composition-objects#confirm """ self._title = TextObject.parse(title, default_type=PlainTextObject.type) self._text = TextObject.parse(text, default_type=MarkdownTextObject.type) self._confirm = TextObject.parse(confirm, default_type=PlainTextObject.type) self._deny = TextObject.parse(deny, default_type=PlainTextObject.type) self._style = style # for backward-compatibility with version 2.0-2.5, the following fields return str values self.title = self._title.text if self._title else None self.text = self._text.text if self._text else None self.confirm = self._confirm.text if self._confirm else None self.deny = self._deny.text if self._deny else None self.style = self._style @JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def title_length(self): return self._title is None or len(self._title.text) <= self.title_max_length @JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def text_length(self): return self._text is None or len(self._text.text) <= self.text_max_length @JsonValidator(f"confirm attribute cannot exceed {confirm_max_length} characters") def confirm_length(self): return ( self._confirm is None or len(self._confirm.text) <= self.confirm_max_length ) @JsonValidator(f"deny attribute cannot exceed {deny_max_length} characters") def deny_length(self): return self._deny is None or len(self._deny.text) <= self.deny_max_length @JsonValidator('style for confirm must be either "primary" or "danger"') def _validate_confirm_style(self): return self._style is None or self._style in ["primary", "danger"] def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221 if option_type == "action": # skipcq: PYL-R1705 # deliberately skipping JSON validators here - can't find documentation # on actual limits here json = { "ok_text": self._confirm.text if self._confirm and self._confirm.text != "Yes" else "Okay", "dismiss_text": self._deny.text if self._deny and self._deny.text != "No" else "Cancel", } if self._title: json["title"] = self._title.text if self._text: json["text"] = self._text.text return json else: self.validate_json() json = {} if self._title: json["title"] = self._title.to_dict() if self._text: json["text"] = self._text.to_dict() if self._confirm: json["confirm"] = self._confirm.to_dict() if self._deny: json["deny"] = self._deny.to_dict() if self._style: json["style"] = self._style return json
Ancestors
Class variables
var confirm_max_length
var deny_max_length
var text_max_length
var title_max_length
Static methods
def parse(confirm: Union[ForwardRef('ConfirmObject'), dict])
-
Expand source code
@classmethod def parse(cls, confirm: Union["ConfirmObject", dict]): if confirm: if isinstance(confirm, ConfirmObject): # skipcq: PYL-R1705 return confirm elif isinstance(confirm, dict): return ConfirmObject(**confirm) else: # Not yet implemented: show some warning here return None return None
Methods
def confirm_length(self)
-
Expand source code
@JsonValidator(f"confirm attribute cannot exceed {confirm_max_length} characters") def confirm_length(self): return ( self._confirm is None or len(self._confirm.text) <= self.confirm_max_length )
def deny_length(self)
-
Expand source code
@JsonValidator(f"deny attribute cannot exceed {deny_max_length} characters") def deny_length(self): return self._deny is None or len(self._deny.text) <= self.deny_max_length
def text_length(self)
-
Expand source code
@JsonValidator(f"text attribute cannot exceed {text_max_length} characters") def text_length(self): return self._text is None or len(self._text.text) <= self.text_max_length
def title_length(self)
-
Expand source code
@JsonValidator(f"title attribute cannot exceed {title_max_length} characters") def title_length(self): return self._title is None or len(self._title.text) <= self.title_max_length
Inherited members
class DispatchActionConfig (*, trigger_actions_on: Optional[list] = None)
-
The base class for JSON serializable class objects
Determines when a plain-text input element will return a block_actions interaction payload. https://api.slack.com/reference/block-kit/composition-objects#dispatch_action_config
Expand source code
class DispatchActionConfig(JsonObject): attributes = {"trigger_actions_on"} @classmethod def parse(cls, config: Union["DispatchActionConfig", dict]): if config: if isinstance(config, DispatchActionConfig): # skipcq: PYL-R1705 return config elif isinstance(config, dict): return DispatchActionConfig(**config) else: # Not yet implemented: show some warning here return None return None def __init__( self, *, trigger_actions_on: Optional[list] = None, ): """ Determines when a plain-text input element will return a block_actions interaction payload. https://api.slack.com/reference/block-kit/composition-objects#dispatch_action_config """ self._trigger_actions_on = trigger_actions_on or [] def to_dict(self) -> dict: # skipcq: PYL-W0221 self.validate_json() json = {} if self._trigger_actions_on: json["trigger_actions_on"] = self._trigger_actions_on return json
Ancestors
Static methods
def parse(config: Union[ForwardRef('DispatchActionConfig'), dict])
-
Expand source code
@classmethod def parse(cls, config: Union["DispatchActionConfig", dict]): if config: if isinstance(config, DispatchActionConfig): # skipcq: PYL-R1705 return config elif isinstance(config, dict): return DispatchActionConfig(**config) else: # Not yet implemented: show some warning here return None return None
Inherited members
class MarkdownTextObject (*, text: str, verbatim: Optional[bool] = None)
-
mrkdwn typed text object
A Markdown text object, meaning markdown characters will be parsed as formatting information. https://api.slack.com/reference/block-kit/composition-objects#text
Expand source code
class MarkdownTextObject(TextObject): """mrkdwn typed text object""" type = "mrkdwn" @property def attributes(self) -> Set[str]: return super().attributes.union({"verbatim"}) def __init__(self, *, text: str, verbatim: Optional[bool] = None): """A Markdown text object, meaning markdown characters will be parsed as formatting information. https://api.slack.com/reference/block-kit/composition-objects#text """ super().__init__(text=text, type=self.type) self.verbatim = verbatim @staticmethod def from_str(text: str) -> "MarkdownTextObject": """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject(text=text) @staticmethod def direct_from_string(text: str) -> dict: """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject.from_str(text).to_dict() @staticmethod def from_link(link: Link, title: str = "") -> "MarkdownTextObject": """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ if title: title = f": {title}" return MarkdownTextObject(text=f"{link}{title}") @staticmethod def direct_from_link(link: Link, title: str = "") -> dict: """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ return MarkdownTextObject.from_link(link, title).to_dict()
Ancestors
Class variables
var type
Static methods
def direct_from_link(link: Link, title: str = '') ‑> dict
-
Transform a Link object directly into the required object shape to act as a MarkdownTextObject
Expand source code
@staticmethod def direct_from_link(link: Link, title: str = "") -> dict: """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ return MarkdownTextObject.from_link(link, title).to_dict()
def direct_from_string(text: str) ‑> dict
-
Transforms a string into the required object shape to act as a MarkdownTextObject
Expand source code
@staticmethod def direct_from_string(text: str) -> dict: """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject.from_str(text).to_dict()
def from_link(link: Link, title: str = '') ‑> MarkdownTextObject
-
Transform a Link object directly into the required object shape to act as a MarkdownTextObject
Expand source code
@staticmethod def from_link(link: Link, title: str = "") -> "MarkdownTextObject": """ Transform a Link object directly into the required object shape to act as a MarkdownTextObject """ if title: title = f": {title}" return MarkdownTextObject(text=f"{link}{title}")
def from_str(text: str) ‑> MarkdownTextObject
-
Transforms a string into the required object shape to act as a MarkdownTextObject
Expand source code
@staticmethod def from_str(text: str) -> "MarkdownTextObject": """Transforms a string into the required object shape to act as a MarkdownTextObject""" return MarkdownTextObject(text=text)
Instance variables
var attributes : Set[str]
-
set() -> new empty set object set(iterable) -> new set object
Build an unordered collection of unique elements.
Expand source code
@property def attributes(self) -> Set[str]: return super().attributes.union({"verbatim"})
Inherited members
class Option (*, value: str, label: Optional[str] = None, text: Union[str, dict, TextObject, NoneType] = None, description: Union[str, dict, TextObject, NoneType] = None, url: Optional[str] = None, **others: dict)
-
Option object used in dialogs, legacy message actions (interactivity in attachments), and blocks. JSON must be retrieved with an explicit option_type - the Slack API has different required formats in different situations
An object that represents a single selectable item in a block element ( SelectElement, OverflowMenuElement) or dialog element (StaticDialogSelectElement)
Blocks: https://api.slack.com/reference/block-kit/composition-objects#option
Dialogs: https://api.slack.com/dialogs#select_elements
Legacy interactive attachments: https://api.slack.com/legacy/interactive-message-field-guide#option_fields
Args
label
- A short, user-facing string to label this option to users. Cannot exceed 75 characters.
value
- A short string that identifies this particular option to your application. It will be part of the payload when this option is selected . Cannot exceed 75 characters.
description
- A user-facing string that provides more details about this option. Only supported in legacy message actions, not in blocks or dialogs.
Expand source code
class Option(JsonObject): """Option object used in dialogs, legacy message actions (interactivity in attachments), and blocks. JSON must be retrieved with an explicit option_type - the Slack API has different required formats in different situations """ attributes = {} # no attributes because to_dict has unique implementations logger = logging.getLogger(__name__) label_max_length = 75 value_max_length = 75 def __init__( self, *, value: str, label: Optional[str] = None, text: Optional[Union[str, dict, TextObject]] = None, # Block Kit description: Optional[Union[str, dict, TextObject]] = None, url: Optional[str] = None, **others: dict, ): """ An object that represents a single selectable item in a block element ( SelectElement, OverflowMenuElement) or dialog element (StaticDialogSelectElement) Blocks: https://api.slack.com/reference/block-kit/composition-objects#option Dialogs: https://api.slack.com/dialogs#select_elements Legacy interactive attachments: https://api.slack.com/legacy/interactive-message-field-guide#option_fields Args: label: A short, user-facing string to label this option to users. Cannot exceed 75 characters. value: A short string that identifies this particular option to your application. It will be part of the payload when this option is selected . Cannot exceed 75 characters. description: A user-facing string that provides more details about this option. Only supported in legacy message actions, not in blocks or dialogs. """ if text: self._text: Optional[TextObject] = TextObject.parse(text) self._label: Optional[str] = None else: self._text: Optional[TextObject] = None self._label: Optional[str] = label # for backward-compatibility with version 2.0-2.5, the following fields return str values self.text: Optional[str] = self._text.text if self._text else None self.label: Optional[str] = self._label self.value: str = value # for backward-compatibility with version 2.0-2.5, the following fields return str values if isinstance(description, str): self.description = description self._block_description = PlainTextObject.from_str(description) elif isinstance(description, dict): self.description = description["text"] self._block_description = TextObject.parse(description) elif isinstance(description, TextObject): self.description = description.text self._block_description = description else: self.description = None self._block_description = None # A URL to load in the user's browser when the option is clicked. # The url attribute is only available in overflow menus. # Maximum length for this field is 3000 characters. # If you're using url, you'll still receive an interaction payload # and will need to send an acknowledgement response. self.url: Optional[str] = url show_unknown_key_warning(self, others) @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def _validate_label_length(self): return self._label is None or len(self._label) <= self.label_max_length @JsonValidator(f"text attribute cannot exceed {label_max_length} characters") def _validate_text_length(self): return ( self._text is None or self._text.text is None or len(self._text.text) <= self.label_max_length ) @JsonValidator(f"value attribute cannot exceed {value_max_length} characters") def _validate_value_length(self): return len(self.value) <= self.value_max_length @classmethod def parse_all( cls, options: Optional[Sequence[Union[dict, "Option"]]] ) -> Optional[List["Option"]]: if options is None: return None option_objects: List[Option] = [] for o in options: if isinstance(o, dict): d = copy.copy(o) option_objects.append(Option(**d)) elif isinstance(o, Option): option_objects.append(o) else: cls.logger.warning(f"Unknown option object detected and skipped ({o})") return option_objects def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221 """ Different parent classes must call this with a valid value from OptionTypes - either "dialog", "action", or "block", so that JSON is returned in the correct shape. """ self.validate_json() if option_type == "dialog": # skipcq: PYL-R1705 return {"label": self.label, "value": self.value} elif option_type == "action" or option_type == "attachment": # "action" can be confusing but it means a legacy message action in attachments # we don't remove the type name for backward compatibility though json = {"text": self.label, "value": self.value} if self.description is not None: json["description"] = self.description return json else: # if option_type == "block"; this should be the most common case text: TextObject = self._text or PlainTextObject.from_str(self.label) json: dict = { "text": text.to_dict(), "value": self.value, } if self._block_description: json["description"] = self._block_description.to_dict() if self.url: json["url"] = self.url return json @staticmethod def from_single_value(value_and_label: str): """Creates a simple Option instance with the same value and label""" return Option(value=value_and_label, label=value_and_label)
Ancestors
Class variables
var label_max_length
var logger
var value_max_length
Static methods
def from_single_value(value_and_label: str)
-
Creates a simple Option instance with the same value and label
Expand source code
@staticmethod def from_single_value(value_and_label: str): """Creates a simple Option instance with the same value and label""" return Option(value=value_and_label, label=value_and_label)
def parse_all(options: Optional[Sequence[Union[dict, ForwardRef('Option')]]]) ‑> Optional[List[Option]]
-
Expand source code
@classmethod def parse_all( cls, options: Optional[Sequence[Union[dict, "Option"]]] ) -> Optional[List["Option"]]: if options is None: return None option_objects: List[Option] = [] for o in options: if isinstance(o, dict): d = copy.copy(o) option_objects.append(Option(**d)) elif isinstance(o, Option): option_objects.append(o) else: cls.logger.warning(f"Unknown option object detected and skipped ({o})") return option_objects
Methods
def to_dict(self, option_type: str = 'block') ‑> dict
-
Different parent classes must call this with a valid value from OptionTypes - either "dialog", "action", or "block", so that JSON is returned in the correct shape.
Expand source code
def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221 """ Different parent classes must call this with a valid value from OptionTypes - either "dialog", "action", or "block", so that JSON is returned in the correct shape. """ self.validate_json() if option_type == "dialog": # skipcq: PYL-R1705 return {"label": self.label, "value": self.value} elif option_type == "action" or option_type == "attachment": # "action" can be confusing but it means a legacy message action in attachments # we don't remove the type name for backward compatibility though json = {"text": self.label, "value": self.value} if self.description is not None: json["description"] = self.description return json else: # if option_type == "block"; this should be the most common case text: TextObject = self._text or PlainTextObject.from_str(self.label) json: dict = { "text": text.to_dict(), "value": self.value, } if self._block_description: json["description"] = self._block_description.to_dict() if self.url: json["url"] = self.url return json
Inherited members
class OptionGroup (*, label: Union[str, dict, TextObject, NoneType] = None, options: Sequence[Union[dict, Option]], **others: dict)
-
JSON must be retrieved with an explicit option_type - the Slack API has different required formats in different situations
Create a group of Option objects - pass in a label (that will be part of the UI) and a list of Option objects.
Blocks: https://api.slack.com/reference/block-kit/composition-objects#option-group
Dialogs: https://api.slack.com/dialogs#select_elements
Legacy interactive attachments: https://api.slack.com/legacy/interactive-message-field-guide#option_groups_to_place_within_message_menu_actions
Args
label
- Text to display at the top of this group of options.
options
- A list of no more than 100 Option objects.
Expand source code
class OptionGroup(JsonObject): """ JSON must be retrieved with an explicit option_type - the Slack API has different required formats in different situations """ attributes = {} # no attributes because to_dict has unique implementations label_max_length = 75 options_max_length = 100 logger = logging.getLogger(__name__) def __init__( self, *, label: Optional[Union[str, dict, TextObject]] = None, options: Sequence[Union[dict, Option]], **others: dict, ): """ Create a group of Option objects - pass in a label (that will be part of the UI) and a list of Option objects. Blocks: https://api.slack.com/reference/block-kit/composition-objects#option-group Dialogs: https://api.slack.com/dialogs#select_elements Legacy interactive attachments: https://api.slack.com/legacy/interactive-message-field-guide#option_groups_to_place_within_message_menu_actions Args: label: Text to display at the top of this group of options. options: A list of no more than 100 Option objects. """ # noqa prevent flake8 blowing up on the long URL # default_type=PlainTextObject.type is for backward-compatibility self._label: Optional[TextObject] = TextObject.parse( label, default_type=PlainTextObject.type ) self.label: Optional[str] = self._label.text if self._label else None self.options = Option.parse_all(options) # compatible with version 2.5 show_unknown_key_warning(self, others) @JsonValidator(f"label attribute cannot exceed {label_max_length} characters") def _validate_label_length(self): return self.label is None or len(self.label) <= self.label_max_length @JsonValidator(f"options attribute cannot exceed {options_max_length} elements") def _validate_options_length(self): return self.options is None or len(self.options) <= self.options_max_length @classmethod def parse_all( cls, option_groups: Optional[Sequence[Union[dict, "OptionGroup"]]] ) -> Optional[List["OptionGroup"]]: if option_groups is None: return None option_group_objects = [] for o in option_groups: if isinstance(o, dict): d = copy.copy(o) option_group_objects.append(OptionGroup(**d)) elif isinstance(o, OptionGroup): option_group_objects.append(o) else: cls.logger.warning( f"Unknown option group object detected and skipped ({o})" ) return option_group_objects def to_dict(self, option_type: str = "block") -> dict: # skipcq: PYL-W0221 self.validate_json() dict_options = [o.to_dict(option_type) for o in self.options] if option_type == "dialog": # skipcq: PYL-R1705 return { "label": self.label, "options": dict_options, } elif option_type == "action": return { "text": self.label, "options": dict_options, } else: # if option_type == "block"; this should be the most common case dict_label: dict = self._label.to_dict() return { "label": dict_label, "options": dict_options, }
Ancestors
Class variables
var label_max_length
var logger
var options_max_length
Static methods
def parse_all(option_groups: Optional[Sequence[Union[dict, ForwardRef('OptionGroup')]]]) ‑> Optional[List[OptionGroup]]
-
Expand source code
@classmethod def parse_all( cls, option_groups: Optional[Sequence[Union[dict, "OptionGroup"]]] ) -> Optional[List["OptionGroup"]]: if option_groups is None: return None option_group_objects = [] for o in option_groups: if isinstance(o, dict): d = copy.copy(o) option_group_objects.append(OptionGroup(**d)) elif isinstance(o, OptionGroup): option_group_objects.append(o) else: cls.logger.warning( f"Unknown option group object detected and skipped ({o})" ) return option_group_objects
Inherited members
class PlainTextObject (*, text: str, emoji: Optional[bool] = None)
-
plain_text typed text object
A plain text object, meaning markdown characters will not be parsed as formatting information. https://api.slack.com/reference/block-kit/composition-objects#text
Expand source code
class PlainTextObject(TextObject): """plain_text typed text object""" type = "plain_text" @property def attributes(self) -> Set[str]: return super().attributes.union({"emoji"}) def __init__(self, *, text: str, emoji: Optional[bool] = None): """A plain text object, meaning markdown characters will not be parsed as formatting information. https://api.slack.com/reference/block-kit/composition-objects#text """ super().__init__(text=text, type=self.type) self.emoji = emoji @staticmethod def from_str(text: str) -> "PlainTextObject": return PlainTextObject(text=text, emoji=True) @staticmethod def direct_from_string(text: str) -> dict: """Transforms a string into the required object shape to act as a PlainTextObject""" return PlainTextObject.from_str(text).to_dict()
Ancestors
Class variables
var type
Static methods
def direct_from_string(text: str) ‑> dict
-
Transforms a string into the required object shape to act as a PlainTextObject
Expand source code
@staticmethod def direct_from_string(text: str) -> dict: """Transforms a string into the required object shape to act as a PlainTextObject""" return PlainTextObject.from_str(text).to_dict()
def from_str(text: str) ‑> PlainTextObject
-
Expand source code
@staticmethod def from_str(text: str) -> "PlainTextObject": return PlainTextObject(text=text, emoji=True)
Instance variables
var attributes : Set[str]
-
set() -> new empty set object set(iterable) -> new set object
Build an unordered collection of unique elements.
Expand source code
@property def attributes(self) -> Set[str]: return super().attributes.union({"emoji"})
Inherited members
class TextObject (text: str, type: Optional[str] = None, subtype: Optional[str] = None, emoji: Optional[bool] = None, **kwargs)
-
The interface for text objects (types: plain_text, mrkdwn)
Super class for new text "objects" used in Block kit
Expand source code
class TextObject(JsonObject): """The interface for text objects (types: plain_text, mrkdwn)""" attributes = {"text", "type", "emoji"} logger = logging.getLogger(__name__) def _subtype_warning(self): # skipcq: PYL-R0201 warnings.warn( "subtype is deprecated since slackclient 2.6.0, use type instead", DeprecationWarning, ) @property def subtype(self) -> Optional[str]: return self.type @classmethod def parse( cls, text: Union[str, dict, "TextObject"], default_type: str = "mrkdwn" ) -> Optional["TextObject"]: if not text: # skipcq: PYL-R1705 return None elif isinstance(text, str): if default_type == PlainTextObject.type: # skipcq: PYL-R1705 return PlainTextObject.from_str(text) else: return MarkdownTextObject.from_str(text) elif isinstance(text, dict): d = copy.copy(text) t = d.pop("type") if t == PlainTextObject.type: # skipcq: PYL-R1705 return PlainTextObject(**d) else: return MarkdownTextObject(**d) elif isinstance(text, TextObject): return text else: cls.logger.warning( f"Unknown type ({type(text)}) detected when parsing a TextObject" ) return None def __init__( self, text: str, type: Optional[str] = None, # skipcq: PYL-W0622 subtype: Optional[str] = None, emoji: Optional[bool] = None, **kwargs, ): """Super class for new text "objects" used in Block kit""" if subtype: self._subtype_warning() self.text = text self.type = type if type else subtype self.emoji = emoji
Ancestors
Subclasses
Class variables
var logger
Static methods
def parse(text: Union[str, dict, ForwardRef('TextObject')], default_type: str = 'mrkdwn') ‑> Optional[TextObject]
-
Expand source code
@classmethod def parse( cls, text: Union[str, dict, "TextObject"], default_type: str = "mrkdwn" ) -> Optional["TextObject"]: if not text: # skipcq: PYL-R1705 return None elif isinstance(text, str): if default_type == PlainTextObject.type: # skipcq: PYL-R1705 return PlainTextObject.from_str(text) else: return MarkdownTextObject.from_str(text) elif isinstance(text, dict): d = copy.copy(text) t = d.pop("type") if t == PlainTextObject.type: # skipcq: PYL-R1705 return PlainTextObject(**d) else: return MarkdownTextObject(**d) elif isinstance(text, TextObject): return text else: cls.logger.warning( f"Unknown type ({type(text)}) detected when parsing a TextObject" ) return None
Instance variables
var subtype : Optional[str]
-
Expand source code
@property def subtype(self) -> Optional[str]: return self.type
Inherited members