Browse Source

Add support for reading SelectMenu components from messages

pull/6978/head
Rapptz 4 years ago
parent
commit
ff36aedf7b
  1. 152
      discord/components.py
  2. 1
      discord/enums.py
  3. 28
      discord/types/components.py
  4. 21
      docs/api.rst

152
discord/components.py

@ -33,6 +33,8 @@ if TYPE_CHECKING:
from .types.components import (
Component as ComponentPayload,
ButtonComponent as ButtonComponentPayload,
SelectMenu as SelectMenuPayload,
SelectOption as SelectOptionPayload,
ActionRow as ActionRowPayload,
)
@ -41,6 +43,8 @@ __all__ = (
'Component',
'ActionRow',
'Button',
'SelectMenu',
'SelectOption',
)
C = TypeVar('C', bound='Component')
@ -53,6 +57,7 @@ class Component:
- :class:`ActionRow`
- :class:`Button`
- :class:`SelectMenu`
This class is abstract and cannot be instantiated.
@ -71,7 +76,7 @@ class Component:
def __repr__(self) -> str:
attrs = ' '.join(f'{key}={getattr(self, key)!r}' for key in self.__repr_info__)
return f'<{self.__class__.__name__} type={self.type!r} {attrs}>'
return f'<{self.__class__.__name__} {attrs}>'
@classmethod
def _raw_construct(cls: Type[C], **kwargs) -> C:
@ -94,6 +99,8 @@ class ActionRow(Component):
This is a component that holds up to 5 children components in a row.
This inherits from :class:`Component`.
.. versionadded:: 2.0
Attributes
@ -186,12 +193,155 @@ class Button(Component):
return payload # type: ignore
class SelectMenu(Component):
"""Represents a select menu from the Discord Bot UI Kit.
A select menu is functionally the same as a dropdown, however
on mobile it renders a bit differently.
.. versionadded:: 2.0
Attributes
------------
custom_id: Optional[:class:`str`]
The ID of the select menu that gets received during an interaction.
placeholder: Optional[:class:`str`]
The placeholder text that is shown if nothing is selected, if any.
min_values: :class:`int`
The minimum number of items that must be chosen for this select menu.
Defaults to 1 and must be between 1 and 25.
max_values: :class:`int`
The maximum number of items that must be chosen for this select menu.
Defaults to 1 and must be between 1 and 25.
options: List[:class:`SelectOption`]
A list of options that can be selected in this menu.
"""
__slots__: Tuple[str, ...] = (
'custom_id',
'placeholder',
'min_values',
'max_values',
'options',
)
__repr_info__: ClassVar[Tuple[str, ...]] = __slots__
def __init__(self, data: SelectMenuPayload):
self.type = ComponentType.select
self.custom_id: str = data['custom_id']
self.placeholder: Optional[str] = data.get('placeholder')
self.min_values: int = data.get('min_values', 1)
self.max_values: int = data.get('max_values', 1)
self.options: List[SelectOption] = [SelectOption.from_dict(option) for option in data.get('options', [])]
def to_dict(self) -> SelectMenuPayload:
payload: SelectMenuPayload = {
'type': self.type.value,
'custom_id': self.custom_id,
'min_values': self.min_values,
'max_values': self.max_values,
'options': [op.to_dict() for op in self.options],
}
if self.placeholder:
payload['placeholder'] = self.placeholder
return payload
class SelectOption:
"""Represents a select menu's option.
These can be created by users.
.. versionadded:: 2.0
Attributes
-----------
label: :class:`str`
The label of the option. This is displayed to users.
Can only be up to 25 characters.
value: :class:`str`
The value of the option. This is not displayed to users.
Can only be up to 100 characters.
description: Optional[:class:`str`]
An additional description of the option, if any.
Can only be up to 50 characters.
emoji: Optional[:class:`PartialEmoji`]
The emoji of the option, if available.
default: :class:`bool`
Whether this option is selected by default.
"""
__slots__: Tuple[str, ...] = (
'label',
'value',
'description',
'emoji',
'default',
)
def __init__(
self,
*,
label: str,
value: str,
description: Optional[str] = None,
emoji: Optional[PartialEmoji] = None,
default: bool = False,
) -> None:
self.label = label
self.value = value
self.description = description
self.emoji = emoji
self.default = default
def __repr__(self) -> str:
return (
f'<SelectOption label={self.label!r} value={self.value!r} description={self.description!r} '
f'emoji={self.emoji!r} default={self.default!r}>'
)
@classmethod
def from_dict(cls, data: SelectOptionPayload) -> SelectOption:
try:
emoji = PartialEmoji.from_dict(data['emoji'])
except KeyError:
emoji = None
return cls(
label=data['label'],
value=data['value'],
description=data.get('description'),
emoji=emoji,
default=data.get('default', False),
)
def to_dict(self) -> SelectOptionPayload:
payload: SelectOptionPayload = {
'label': self.label,
'value': self.value,
'default': self.default,
}
if self.emoji:
payload['emoji'] = self.emoji.to_dict() # type: ignore
if self.description:
payload['description'] = self.description
return payload
def _component_factory(data: ComponentPayload) -> Component:
component_type = data['type']
if component_type == 1:
return ActionRow(data)
elif component_type == 2:
return Button(data) # type: ignore
elif component_type == 3:
return SelectMenu(data) # type: ignore
else:
as_enum = try_enum(ComponentType, component_type)
return Component._raw_construct(type=as_enum)

1
discord/enums.py

@ -458,6 +458,7 @@ class VideoQualityMode(Enum):
class ComponentType(Enum):
action_row = 1
button = 2
select = 3
def __int__(self):
return self.value

28
discord/types/components.py

@ -27,7 +27,7 @@ from __future__ import annotations
from typing import List, Literal, TypedDict, Union
from .emoji import PartialEmoji
ComponentType = Literal[1, 2]
ComponentType = Literal[1, 2, 3]
ButtonStyle = Literal[1, 2, 3, 4, 5]
@ -43,9 +43,33 @@ class _ButtonComponentOptional(TypedDict, total=False):
emoji: PartialEmoji
label: str
class ButtonComponent(_ButtonComponentOptional):
type: Literal[2]
style: ButtonStyle
Component = Union[ActionRow, ButtonComponent]
class _SelectMenuOptional(TypedDict, total=False):
placeholder: str
min_values: int
max_values: int
class _SelectOptionsOptional(TypedDict, total=False):
description: str
emoji: PartialEmoji
class SelectOption(_SelectOptionsOptional):
label: str
value: str
default: bool
class SelectMenu(_SelectMenuOptional):
type: Literal[3]
custom_id: str
options: List[SelectOption]
Component = Union[ActionRow, ButtonComponent, SelectMenu]

21
docs/api.rst

@ -1224,6 +1224,10 @@ of :class:`enum.Enum`.
.. attribute:: button
Represents a button component.
.. attribute:: select
Represents a select component.
.. class:: ButtonStyle
@ -2902,6 +2906,15 @@ Button
:members:
:inherited-members:
SelectMenu
~~~~~~~~~~~
.. attributetable:: SelectMenu
.. autoclass:: SelectMenu()
:members:
:inherited-members:
DeletedReferencedMessage
~~~~~~~~~~~~~~~~~~~~~~~~~
@ -3316,6 +3329,14 @@ PartialMessage
.. autoclass:: PartialMessage
:members:
SelectOption
~~~~~~~~~~~~~
.. attributetable:: SelectOption
.. autoclass:: SelectOption
:members:
Intents
~~~~~~~~~~

Loading…
Cancel
Save