mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-08 04:38:42 +00:00
Implement New Select Types
Co-authored-by: Soheab_ <33902984+Soheab@users.noreply.github.com> Co-authored-by: rdrescher909 <51489753+rdrescher909@users.noreply.github.com> Co-authored-by: Danny <1695103+Rapptz@users.noreply.github.com>
This commit is contained in:
parent
4c8ba635db
commit
5009c83bc9
@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import ClassVar, List, Literal, Optional, TYPE_CHECKING, Tuple, Union, overload
|
from typing import ClassVar, List, Literal, Optional, TYPE_CHECKING, Tuple, Union, overload
|
||||||
from .enums import try_enum, ComponentType, ButtonStyle, TextStyle
|
from .enums import try_enum, ComponentType, ButtonStyle, TextStyle, ChannelType
|
||||||
from .utils import get_slots, MISSING
|
from .utils import get_slots, MISSING
|
||||||
from .partial_emoji import PartialEmoji, _EmojiTag
|
from .partial_emoji import PartialEmoji, _EmojiTag
|
||||||
|
|
||||||
@ -234,6 +234,8 @@ class SelectMenu(Component):
|
|||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
------------
|
------------
|
||||||
|
type: :class:`ComponentType`
|
||||||
|
The type of component.
|
||||||
custom_id: Optional[:class:`str`]
|
custom_id: Optional[:class:`str`]
|
||||||
The ID of the select menu that gets received during an interaction.
|
The ID of the select menu that gets received during an interaction.
|
||||||
placeholder: Optional[:class:`str`]
|
placeholder: Optional[:class:`str`]
|
||||||
@ -248,31 +250,32 @@ class SelectMenu(Component):
|
|||||||
A list of options that can be selected in this menu.
|
A list of options that can be selected in this menu.
|
||||||
disabled: :class:`bool`
|
disabled: :class:`bool`
|
||||||
Whether the select is disabled or not.
|
Whether the select is disabled or not.
|
||||||
|
channel_types: List[:class:`.ChannelType`]
|
||||||
|
A list of channel types that are allowed to be chosen in this select menu.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__: Tuple[str, ...] = (
|
__slots__: Tuple[str, ...] = (
|
||||||
|
'type',
|
||||||
'custom_id',
|
'custom_id',
|
||||||
'placeholder',
|
'placeholder',
|
||||||
'min_values',
|
'min_values',
|
||||||
'max_values',
|
'max_values',
|
||||||
'options',
|
'options',
|
||||||
'disabled',
|
'disabled',
|
||||||
|
'channel_types',
|
||||||
)
|
)
|
||||||
|
|
||||||
__repr_info__: ClassVar[Tuple[str, ...]] = __slots__
|
__repr_info__: ClassVar[Tuple[str, ...]] = __slots__
|
||||||
|
|
||||||
def __init__(self, data: SelectMenuPayload, /) -> None:
|
def __init__(self, data: SelectMenuPayload, /) -> None:
|
||||||
|
self.type: ComponentType = try_enum(ComponentType, data['type'])
|
||||||
self.custom_id: str = data['custom_id']
|
self.custom_id: str = data['custom_id']
|
||||||
self.placeholder: Optional[str] = data.get('placeholder')
|
self.placeholder: Optional[str] = data.get('placeholder')
|
||||||
self.min_values: int = data.get('min_values', 1)
|
self.min_values: int = data.get('min_values', 1)
|
||||||
self.max_values: int = data.get('max_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', [])]
|
self.options: List[SelectOption] = [SelectOption.from_dict(option) for option in data.get('options', [])]
|
||||||
self.disabled: bool = data.get('disabled', False)
|
self.disabled: bool = data.get('disabled', False)
|
||||||
|
self.channel_types: List[ChannelType] = [try_enum(ChannelType, t) for t in data.get('channel_types', [])]
|
||||||
@property
|
|
||||||
def type(self) -> Literal[ComponentType.select]:
|
|
||||||
""":class:`ComponentType`: The type of component."""
|
|
||||||
return ComponentType.select
|
|
||||||
|
|
||||||
def to_dict(self) -> SelectMenuPayload:
|
def to_dict(self) -> SelectMenuPayload:
|
||||||
payload: SelectMenuPayload = {
|
payload: SelectMenuPayload = {
|
||||||
@ -280,12 +283,14 @@ class SelectMenu(Component):
|
|||||||
'custom_id': self.custom_id,
|
'custom_id': self.custom_id,
|
||||||
'min_values': self.min_values,
|
'min_values': self.min_values,
|
||||||
'max_values': self.max_values,
|
'max_values': self.max_values,
|
||||||
'options': [op.to_dict() for op in self.options],
|
|
||||||
'disabled': self.disabled,
|
'disabled': self.disabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.placeholder:
|
if self.placeholder:
|
||||||
payload['placeholder'] = self.placeholder
|
payload['placeholder'] = self.placeholder
|
||||||
|
if self.options:
|
||||||
|
payload['options'] = [op.to_dict() for op in self.options]
|
||||||
|
if self.channel_types:
|
||||||
|
payload['channel_types'] = [t.value for t in self.channel_types]
|
||||||
|
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
@ -576,7 +576,12 @@ class ComponentType(Enum):
|
|||||||
action_row = 1
|
action_row = 1
|
||||||
button = 2
|
button = 2
|
||||||
select = 3
|
select = 3
|
||||||
|
string_select = 3
|
||||||
text_input = 4
|
text_input = 4
|
||||||
|
user_select = 5
|
||||||
|
role_select = 6
|
||||||
|
mentionable_select = 7
|
||||||
|
channel_select = 8
|
||||||
|
|
||||||
def __int__(self) -> int:
|
def __int__(self) -> int:
|
||||||
return self.value
|
return self.value
|
||||||
|
@ -28,6 +28,7 @@ from typing import List, Literal, TypedDict, Union
|
|||||||
from typing_extensions import NotRequired
|
from typing_extensions import NotRequired
|
||||||
|
|
||||||
from .emoji import PartialEmoji
|
from .emoji import PartialEmoji
|
||||||
|
from .channel import ChannelType
|
||||||
|
|
||||||
ComponentType = Literal[1, 2, 3, 4]
|
ComponentType = Literal[1, 2, 3, 4]
|
||||||
ButtonStyle = Literal[1, 2, 3, 4, 5]
|
ButtonStyle = Literal[1, 2, 3, 4, 5]
|
||||||
@ -57,16 +58,36 @@ class SelectOption(TypedDict):
|
|||||||
emoji: NotRequired[PartialEmoji]
|
emoji: NotRequired[PartialEmoji]
|
||||||
|
|
||||||
|
|
||||||
class SelectMenu(TypedDict):
|
class SelectComponent(TypedDict):
|
||||||
type: Literal[3]
|
|
||||||
custom_id: str
|
custom_id: str
|
||||||
options: List[SelectOption]
|
|
||||||
placeholder: NotRequired[str]
|
placeholder: NotRequired[str]
|
||||||
min_values: NotRequired[int]
|
min_values: NotRequired[int]
|
||||||
max_values: NotRequired[int]
|
max_values: NotRequired[int]
|
||||||
disabled: NotRequired[bool]
|
disabled: NotRequired[bool]
|
||||||
|
|
||||||
|
|
||||||
|
class StringSelectComponent(SelectComponent):
|
||||||
|
type: Literal[3]
|
||||||
|
options: NotRequired[List[SelectOption]]
|
||||||
|
|
||||||
|
|
||||||
|
class UserSelectComponent(SelectComponent):
|
||||||
|
type: Literal[5]
|
||||||
|
|
||||||
|
|
||||||
|
class RoleSelectComponent(SelectComponent):
|
||||||
|
type: Literal[6]
|
||||||
|
|
||||||
|
|
||||||
|
class MentionableSelectComponent(SelectComponent):
|
||||||
|
type: Literal[7]
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelSelectComponent(SelectComponent):
|
||||||
|
type: Literal[8]
|
||||||
|
channel_types: NotRequired[List[ChannelType]]
|
||||||
|
|
||||||
|
|
||||||
class TextInput(TypedDict):
|
class TextInput(TypedDict):
|
||||||
type: Literal[4]
|
type: Literal[4]
|
||||||
custom_id: str
|
custom_id: str
|
||||||
@ -79,5 +100,11 @@ class TextInput(TypedDict):
|
|||||||
max_length: NotRequired[int]
|
max_length: NotRequired[int]
|
||||||
|
|
||||||
|
|
||||||
|
class SelectMenu(SelectComponent):
|
||||||
|
type: Literal[3, 5, 6, 7, 8]
|
||||||
|
options: NotRequired[List[SelectOption]]
|
||||||
|
channel_types: NotRequired[List[ChannelType]]
|
||||||
|
|
||||||
|
|
||||||
ActionRowChildComponent = Union[ButtonComponent, SelectMenu, TextInput]
|
ActionRowChildComponent = Union[ButtonComponent, SelectMenu, TextInput]
|
||||||
Component = Union[ActionRow, ActionRowChildComponent]
|
Component = Union[ActionRow, ActionRowChildComponent]
|
||||||
|
@ -160,8 +160,9 @@ class ButtonMessageComponentInteractionData(_BaseMessageComponentInteractionData
|
|||||||
|
|
||||||
|
|
||||||
class SelectMessageComponentInteractionData(_BaseMessageComponentInteractionData):
|
class SelectMessageComponentInteractionData(_BaseMessageComponentInteractionData):
|
||||||
component_type: Literal[3]
|
component_type: Literal[3, 5, 6, 7, 8]
|
||||||
values: List[str]
|
values: List[str]
|
||||||
|
resolved: NotRequired[ResolvedData]
|
||||||
|
|
||||||
|
|
||||||
MessageComponentInteractionData = Union[ButtonMessageComponentInteractionData, SelectMessageComponentInteractionData]
|
MessageComponentInteractionData = Union[ButtonMessageComponentInteractionData, SelectMessageComponentInteractionData]
|
||||||
|
@ -76,7 +76,7 @@ class Item(Generic[V]):
|
|||||||
def _refresh_component(self, component: Component) -> None:
|
def _refresh_component(self, component: Component) -> None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _refresh_state(self, data: Dict[str, Any]) -> None:
|
def _refresh_state(self, interaction: Interaction, data: Dict[str, Any]) -> None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -163,21 +163,21 @@ class Modal(View):
|
|||||||
"""
|
"""
|
||||||
_log.error('Ignoring exception in modal %r:', self, exc_info=error)
|
_log.error('Ignoring exception in modal %r:', self, exc_info=error)
|
||||||
|
|
||||||
def _refresh(self, components: Sequence[ModalSubmitComponentInteractionDataPayload]) -> None:
|
def _refresh(self, interaction: Interaction, components: Sequence[ModalSubmitComponentInteractionDataPayload]) -> None:
|
||||||
for component in components:
|
for component in components:
|
||||||
if component['type'] == 1:
|
if component['type'] == 1:
|
||||||
self._refresh(component['components'])
|
self._refresh(interaction, component['components'])
|
||||||
else:
|
else:
|
||||||
item = find(lambda i: i.custom_id == component['custom_id'], self._children) # type: ignore
|
item = find(lambda i: i.custom_id == component['custom_id'], self._children) # type: ignore
|
||||||
if item is None:
|
if item is None:
|
||||||
_log.debug("Modal interaction referencing unknown item custom_id %s. Discarding", component['custom_id'])
|
_log.debug("Modal interaction referencing unknown item custom_id %s. Discarding", component['custom_id'])
|
||||||
continue
|
continue
|
||||||
item._refresh_state(component) # type: ignore
|
item._refresh_state(interaction, component) # type: ignore
|
||||||
|
|
||||||
async def _scheduled_task(self, interaction: Interaction, components: List[ModalSubmitComponentInteractionDataPayload]):
|
async def _scheduled_task(self, interaction: Interaction, components: List[ModalSubmitComponentInteractionDataPayload]):
|
||||||
try:
|
try:
|
||||||
self._refresh_timeout()
|
self._refresh_timeout()
|
||||||
self._refresh(components)
|
self._refresh(interaction, components)
|
||||||
|
|
||||||
allow = await self.interaction_check(interaction)
|
allow = await self.interaction_check(interaction)
|
||||||
if not allow:
|
if not allow:
|
||||||
|
@ -21,15 +21,14 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
DEALINGS IN THE SOFTWARE.
|
DEALINGS IN THE SOFTWARE.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from typing import List, Literal, Optional, TYPE_CHECKING, Tuple, TypeVar, Callable, Union, Dict
|
from typing import List, Literal, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Callable, Union, Dict, overload
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from .item import Item, ItemCallbackType
|
from .item import Item, ItemCallbackType
|
||||||
from ..enums import ComponentType
|
from ..enums import ChannelType, ComponentType
|
||||||
from ..partial_emoji import PartialEmoji
|
from ..partial_emoji import PartialEmoji
|
||||||
from ..emoji import Emoji
|
from ..emoji import Emoji
|
||||||
from ..utils import MISSING
|
from ..utils import MISSING
|
||||||
@ -37,52 +36,69 @@ from ..components import (
|
|||||||
SelectOption,
|
SelectOption,
|
||||||
SelectMenu,
|
SelectMenu,
|
||||||
)
|
)
|
||||||
|
from ..app_commands.namespace import Namespace
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Select',
|
'Select',
|
||||||
|
'UserSelect',
|
||||||
|
'RoleSelect',
|
||||||
|
'MentionableSelect',
|
||||||
|
'ChannelSelect',
|
||||||
'select',
|
'select',
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing_extensions import Self
|
from typing_extensions import TypeAlias, Self
|
||||||
|
|
||||||
from .view import View
|
from .view import View
|
||||||
from ..types.components import SelectMenu as SelectMenuPayload
|
from ..types.components import SelectMenu as SelectMenuPayload
|
||||||
from ..types.interactions import (
|
from ..types.interactions import SelectMessageComponentInteractionData
|
||||||
MessageComponentInteractionData,
|
from ..app_commands import AppCommandChannel, AppCommandThread
|
||||||
)
|
from ..member import Member
|
||||||
|
from ..role import Role
|
||||||
|
from ..user import User
|
||||||
|
from ..interactions import Interaction
|
||||||
|
|
||||||
|
ValidSelectType: TypeAlias = Literal[
|
||||||
|
ComponentType.string_select,
|
||||||
|
ComponentType.user_select,
|
||||||
|
ComponentType.role_select,
|
||||||
|
ComponentType.channel_select,
|
||||||
|
ComponentType.mentionable_select,
|
||||||
|
]
|
||||||
|
PossibleValue: TypeAlias = Union[
|
||||||
|
str, User, Member, Role, AppCommandChannel, AppCommandThread, Union[Role, Member], Union[Role, User]
|
||||||
|
]
|
||||||
|
|
||||||
V = TypeVar('V', bound='View', covariant=True)
|
V = TypeVar('V', bound='View', covariant=True)
|
||||||
|
BaseSelectT = TypeVar('BaseSelectT', bound='BaseSelect')
|
||||||
|
SelectT = TypeVar('SelectT', bound='Select')
|
||||||
|
UserSelectT = TypeVar('UserSelectT', bound='UserSelect')
|
||||||
|
RoleSelectT = TypeVar('RoleSelectT', bound='RoleSelect')
|
||||||
|
ChannelSelectT = TypeVar('ChannelSelectT', bound='ChannelSelect')
|
||||||
|
MentionableSelectT = TypeVar('MentionableSelectT', bound='MentionableSelect')
|
||||||
|
SelectCallbackDecorator: TypeAlias = Callable[[ItemCallbackType[V, BaseSelectT]], BaseSelectT]
|
||||||
|
|
||||||
selected_values: ContextVar[Dict[str, List[str]]] = ContextVar('selected_values')
|
selected_values: ContextVar[Dict[str, List[PossibleValue]]] = ContextVar('selected_values')
|
||||||
|
|
||||||
|
|
||||||
class Select(Item[V]):
|
class BaseSelect(Item[V]):
|
||||||
"""Represents a UI select menu.
|
"""The base Select model that all other Select models inherit from.
|
||||||
|
|
||||||
This is usually represented as a drop down menu.
|
This class inherits from :class:`Item` and implements the common attributes.
|
||||||
|
|
||||||
In order to get the selected items that the user has chosen, use :attr:`Select.values`.
|
The following implement this class:
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
- :class:`~discord.ui.Select`
|
||||||
|
- :class:`~discord.ui.ChannelSelect`
|
||||||
|
- :class:`~discord.ui.RoleSelect`
|
||||||
|
- :class:`~discord.ui.MentionableSelect`
|
||||||
|
- :class:`~discord.ui.UserSelect`
|
||||||
|
|
||||||
Parameters
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
Attributes
|
||||||
------------
|
------------
|
||||||
custom_id: :class:`str`
|
|
||||||
The ID of the select menu that gets received during an interaction.
|
|
||||||
If not given then one is generated for you.
|
|
||||||
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 0 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:`discord.SelectOption`]
|
|
||||||
A list of options that can be selected in this menu.
|
|
||||||
disabled: :class:`bool`
|
|
||||||
Whether the select is disabled or not.
|
|
||||||
row: Optional[:class:`int`]
|
row: Optional[:class:`int`]
|
||||||
The relative row this select menu belongs to. A Discord component can only have 5
|
The relative row this select menu belongs to. A Discord component can only have 5
|
||||||
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
||||||
@ -91,24 +107,27 @@ class Select(Item[V]):
|
|||||||
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
|
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__slots__ = ('_provided_custom_id', '_underlying', 'row', '_values')
|
||||||
|
|
||||||
__item_repr_attributes__: Tuple[str, ...] = (
|
__item_repr_attributes__: Tuple[str, ...] = (
|
||||||
'placeholder',
|
'placeholder',
|
||||||
'min_values',
|
'min_values',
|
||||||
'max_values',
|
'max_values',
|
||||||
'options',
|
|
||||||
'disabled',
|
'disabled',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
type: ValidSelectType,
|
||||||
*,
|
*,
|
||||||
custom_id: str = MISSING,
|
custom_id: str = MISSING,
|
||||||
placeholder: Optional[str] = None,
|
|
||||||
min_values: int = 1,
|
|
||||||
max_values: int = 1,
|
|
||||||
options: List[SelectOption] = MISSING,
|
|
||||||
disabled: bool = False,
|
|
||||||
row: Optional[int] = None,
|
row: Optional[int] = None,
|
||||||
|
placeholder: Optional[str] = None,
|
||||||
|
min_values: Optional[int] = None,
|
||||||
|
max_values: Optional[int] = None,
|
||||||
|
disabled: bool = False,
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
channel_types: List[ChannelType] = MISSING,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._provided_custom_id = custom_id is not MISSING
|
self._provided_custom_id = custom_id is not MISSING
|
||||||
@ -116,17 +135,24 @@ class Select(Item[V]):
|
|||||||
if not isinstance(custom_id, str):
|
if not isinstance(custom_id, str):
|
||||||
raise TypeError(f'expected custom_id to be str not {custom_id.__class__.__name__}')
|
raise TypeError(f'expected custom_id to be str not {custom_id.__class__.__name__}')
|
||||||
|
|
||||||
options = [] if options is MISSING else options
|
|
||||||
self._underlying = SelectMenu._raw_construct(
|
self._underlying = SelectMenu._raw_construct(
|
||||||
|
type=type,
|
||||||
custom_id=custom_id,
|
custom_id=custom_id,
|
||||||
placeholder=placeholder,
|
placeholder=placeholder,
|
||||||
min_values=min_values,
|
min_values=min_values,
|
||||||
max_values=max_values,
|
max_values=max_values,
|
||||||
options=options,
|
|
||||||
disabled=disabled,
|
disabled=disabled,
|
||||||
|
channel_types=[] if channel_types is MISSING else channel_types,
|
||||||
|
options=[] if options is MISSING else options,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.row = row
|
self.row = row
|
||||||
self._values: List[str] = []
|
self._values: List[PossibleValue] = []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> List[PossibleValue]:
|
||||||
|
values = selected_values.get({})
|
||||||
|
return values.get(self.custom_id, self._values)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def custom_id(self) -> str:
|
def custom_id(self) -> str:
|
||||||
@ -164,13 +190,120 @@ class Select(Item[V]):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def max_values(self) -> int:
|
def max_values(self) -> int:
|
||||||
""":class:`int`: The maximum number of items that must be chosen for this select menu."""
|
""":class:`int`: The maximum number of items that can be chosen for this select menu."""
|
||||||
return self._underlying.max_values
|
return self._underlying.max_values
|
||||||
|
|
||||||
@max_values.setter
|
@max_values.setter
|
||||||
def max_values(self, value: int) -> None:
|
def max_values(self, value: int) -> None:
|
||||||
self._underlying.max_values = int(value)
|
self._underlying.max_values = int(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def disabled(self) -> bool:
|
||||||
|
""":class:`bool`: Whether the select is disabled or not."""
|
||||||
|
return self._underlying.disabled
|
||||||
|
|
||||||
|
@disabled.setter
|
||||||
|
def disabled(self, value: bool) -> None:
|
||||||
|
self._underlying.disabled = bool(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self) -> int:
|
||||||
|
return 5
|
||||||
|
|
||||||
|
def to_component_dict(self) -> SelectMenuPayload:
|
||||||
|
return self._underlying.to_dict()
|
||||||
|
|
||||||
|
def _refresh_component(self, component: SelectMenu) -> None:
|
||||||
|
self._underlying = component
|
||||||
|
|
||||||
|
def _refresh_state(self, interaction: Interaction, data: SelectMessageComponentInteractionData) -> None:
|
||||||
|
values = selected_values.get({})
|
||||||
|
payload: List[PossibleValue]
|
||||||
|
try:
|
||||||
|
resolved = Namespace._get_resolved_items(interaction, data['resolved'])
|
||||||
|
payload = list(resolved.values())
|
||||||
|
except KeyError:
|
||||||
|
payload = data.get("values", []) # type: ignore
|
||||||
|
|
||||||
|
self._values = values[self.custom_id] = payload
|
||||||
|
selected_values.set(values)
|
||||||
|
|
||||||
|
def is_dispatchable(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_component(cls, component: SelectMenu) -> Self:
|
||||||
|
return cls(
|
||||||
|
**{k: getattr(component, k) for k in cls.__item_repr_attributes__},
|
||||||
|
row=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Select(BaseSelect[V]):
|
||||||
|
"""Represents a UI select menu with a list of custom options. This is represented
|
||||||
|
to the user as a dropdown menu.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
custom_id: :class:`str`
|
||||||
|
The ID of the select menu that gets received during an interaction.
|
||||||
|
If not given then one is generated for you.
|
||||||
|
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 0 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:`discord.SelectOption`]
|
||||||
|
A list of options that can be selected in this menu.
|
||||||
|
disabled: :class:`bool`
|
||||||
|
Whether the select is disabled or not.
|
||||||
|
row: Optional[:class:`int`]
|
||||||
|
The relative row this select menu belongs to. A Discord component can only have 5
|
||||||
|
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
||||||
|
like to control the relative positioning of the row then passing an index is advised.
|
||||||
|
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
|
||||||
|
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
|
||||||
|
"""
|
||||||
|
|
||||||
|
__item_repr_attributes__ = BaseSelect.__item_repr_attributes__ + ('options',)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
custom_id: str = MISSING,
|
||||||
|
placeholder: Optional[str] = None,
|
||||||
|
min_values: int = 1,
|
||||||
|
max_values: int = 1,
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
disabled: bool = False,
|
||||||
|
row: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(
|
||||||
|
self.type,
|
||||||
|
custom_id=custom_id,
|
||||||
|
placeholder=placeholder,
|
||||||
|
min_values=min_values,
|
||||||
|
max_values=max_values,
|
||||||
|
disabled=disabled,
|
||||||
|
options=options,
|
||||||
|
row=row,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> List[str]:
|
||||||
|
"""List[:class:`str`]: A list of values that have been selected by the user."""
|
||||||
|
return super().values # type: ignore
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Literal[ComponentType.string_select]:
|
||||||
|
""":class:`.ComponentType`: The type of this component."""
|
||||||
|
return ComponentType.string_select
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def options(self) -> List[SelectOption]:
|
def options(self) -> List[SelectOption]:
|
||||||
"""List[:class:`discord.SelectOption`]: A list of options that can be selected in this menu."""
|
"""List[:class:`discord.SelectOption`]: A list of options that can be selected in this menu."""
|
||||||
@ -251,77 +384,419 @@ class Select(Item[V]):
|
|||||||
|
|
||||||
self._underlying.options.append(option)
|
self._underlying.options.append(option)
|
||||||
|
|
||||||
@property
|
|
||||||
def disabled(self) -> bool:
|
|
||||||
""":class:`bool`: Whether the select is disabled or not."""
|
|
||||||
return self._underlying.disabled
|
|
||||||
|
|
||||||
@disabled.setter
|
class UserSelect(BaseSelect[V]):
|
||||||
def disabled(self, value: bool) -> None:
|
"""Represents a UI select menu with a list of predefined options with the current members of the guild.
|
||||||
self._underlying.disabled = bool(value)
|
|
||||||
|
|
||||||
@property
|
If this is sent a private message, it will only allow the user to select the client
|
||||||
def values(self) -> List[str]:
|
or themselves. Every selected option in a private message will resolve to
|
||||||
"""List[:class:`str`]: A list of values that have been selected by the user."""
|
a :class:`discord.User`.
|
||||||
values = selected_values.get({})
|
|
||||||
return values.get(self.custom_id, self._values)
|
|
||||||
|
|
||||||
@property
|
.. versionadded:: 2.1
|
||||||
def width(self) -> int:
|
|
||||||
return 5
|
|
||||||
|
|
||||||
def to_component_dict(self) -> SelectMenuPayload:
|
Parameters
|
||||||
return self._underlying.to_dict()
|
------------
|
||||||
|
custom_id: :class:`str`
|
||||||
|
The ID of the select menu that gets received during an interaction.
|
||||||
|
If not given then one is generated for you.
|
||||||
|
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 0 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.
|
||||||
|
disabled: :class:`bool`
|
||||||
|
Whether the select is disabled or not.
|
||||||
|
row: Optional[:class:`int`]
|
||||||
|
The relative row this select menu belongs to. A Discord component can only have 5
|
||||||
|
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
||||||
|
like to control the relative positioning of the row then passing an index is advised.
|
||||||
|
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
|
||||||
|
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
|
||||||
|
"""
|
||||||
|
|
||||||
def _refresh_component(self, component: SelectMenu) -> None:
|
def __init__(
|
||||||
self._underlying = component
|
self,
|
||||||
|
*,
|
||||||
def _refresh_state(self, data: MessageComponentInteractionData) -> None:
|
custom_id: str = MISSING,
|
||||||
values = selected_values.get({})
|
placeholder: Optional[str] = None,
|
||||||
self._values = values[self.custom_id] = data.get('values', [])
|
min_values: int = 1,
|
||||||
selected_values.set(values)
|
max_values: int = 1,
|
||||||
|
disabled: bool = False,
|
||||||
@classmethod
|
row: Optional[int] = None,
|
||||||
def from_component(cls, component: SelectMenu) -> Self:
|
) -> None:
|
||||||
return cls(
|
super().__init__(
|
||||||
custom_id=component.custom_id,
|
self.type,
|
||||||
placeholder=component.placeholder,
|
custom_id=custom_id,
|
||||||
min_values=component.min_values,
|
placeholder=placeholder,
|
||||||
max_values=component.max_values,
|
min_values=min_values,
|
||||||
options=component.options,
|
max_values=max_values,
|
||||||
disabled=component.disabled,
|
disabled=disabled,
|
||||||
row=None,
|
row=row,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> Literal[ComponentType.select]:
|
def type(self) -> Literal[ComponentType.user_select]:
|
||||||
return self._underlying.type
|
""":class:`.ComponentType`: The type of this component."""
|
||||||
|
return ComponentType.user_select
|
||||||
|
|
||||||
def is_dispatchable(self) -> bool:
|
@property
|
||||||
return True
|
def values(self) -> List[Union[Member, User]]:
|
||||||
|
"""List[Union[:class:`discord.Member`, :class:`discord.User`]]: A list of members
|
||||||
|
and users that have been selected by the user.
|
||||||
|
|
||||||
|
If this is sent a private message, it will only allow
|
||||||
|
the user to select the client or themselves. Every selected option in a private
|
||||||
|
message will resolve to a :class:`discord.User`.
|
||||||
|
|
||||||
|
If invoked in a guild, the values will always resolve to :class:`discord.Member`.
|
||||||
|
"""
|
||||||
|
return super().values # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class RoleSelect(BaseSelect[V]):
|
||||||
|
"""Represents a UI select menu with a list of predefined options with the current roles of the guild.
|
||||||
|
|
||||||
|
Please note that if you use this in a private message with a user, no roles will be displayed to the user.
|
||||||
|
|
||||||
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
custom_id: :class:`str`
|
||||||
|
The ID of the select menu that gets received during an interaction.
|
||||||
|
If not given then one is generated for you.
|
||||||
|
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 0 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.
|
||||||
|
disabled: :class:`bool`
|
||||||
|
Whether the select is disabled or not.
|
||||||
|
row: Optional[:class:`int`]
|
||||||
|
The relative row this select menu belongs to. A Discord component can only have 5
|
||||||
|
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
||||||
|
like to control the relative positioning of the row then passing an index is advised.
|
||||||
|
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
|
||||||
|
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
custom_id: str = MISSING,
|
||||||
|
placeholder: Optional[str] = None,
|
||||||
|
min_values: int = 1,
|
||||||
|
max_values: int = 1,
|
||||||
|
disabled: bool = False,
|
||||||
|
row: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(
|
||||||
|
self.type,
|
||||||
|
custom_id=custom_id,
|
||||||
|
placeholder=placeholder,
|
||||||
|
min_values=min_values,
|
||||||
|
max_values=max_values,
|
||||||
|
disabled=disabled,
|
||||||
|
row=row,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Literal[ComponentType.role_select]:
|
||||||
|
""":class:`.ComponentType`: The type of this component."""
|
||||||
|
return ComponentType.role_select
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> List[Role]:
|
||||||
|
"""List[:class:`discord.Role`]: A list of roles that have been selected by the user."""
|
||||||
|
return super().values # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class MentionableSelect(BaseSelect[V]):
|
||||||
|
"""Represents a UI select menu with a list of predefined options with the current members and roles in the guild.
|
||||||
|
|
||||||
|
If this is sent in a private message, it will only allow the user to select
|
||||||
|
the client or themselves. Every selected option in a private message
|
||||||
|
will resolve to a :class:`discord.User`. It will not give the user any roles
|
||||||
|
to select.
|
||||||
|
|
||||||
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
custom_id: :class:`str`
|
||||||
|
The ID of the select menu that gets received during an interaction.
|
||||||
|
If not given then one is generated for you.
|
||||||
|
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 0 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.
|
||||||
|
disabled: :class:`bool`
|
||||||
|
Whether the select is disabled or not.
|
||||||
|
row: Optional[:class:`int`]
|
||||||
|
The relative row this select menu belongs to. A Discord component can only have 5
|
||||||
|
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
||||||
|
like to control the relative positioning of the row then passing an index is advised.
|
||||||
|
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
|
||||||
|
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
custom_id: str = MISSING,
|
||||||
|
placeholder: Optional[str] = None,
|
||||||
|
min_values: int = 1,
|
||||||
|
max_values: int = 1,
|
||||||
|
disabled: bool = False,
|
||||||
|
row: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(
|
||||||
|
self.type,
|
||||||
|
custom_id=custom_id,
|
||||||
|
placeholder=placeholder,
|
||||||
|
min_values=min_values,
|
||||||
|
max_values=max_values,
|
||||||
|
disabled=disabled,
|
||||||
|
row=row,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Literal[ComponentType.mentionable_select]:
|
||||||
|
""":class:`.ComponentType`: The type of this component."""
|
||||||
|
return ComponentType.mentionable_select
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> List[Union[Member, User, Role]]:
|
||||||
|
"""List[Union[:class:`discord.Role`, :class:`discord.Member`, :class:`discord.User`]]: A list of roles, members,
|
||||||
|
and users that have been selected by the user.
|
||||||
|
|
||||||
|
If this is sent a private message, it will only allow
|
||||||
|
the user to select the client or themselves. Every selected option in a private
|
||||||
|
message will resolve to a :class:`discord.User`.
|
||||||
|
|
||||||
|
If invoked in a guild, the values will always resolve to :class:`discord.Member`.
|
||||||
|
"""
|
||||||
|
return super().values # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class ChannelSelect(BaseSelect[V]):
|
||||||
|
"""Represents a UI select menu with a list of predefined options with the current channels in the guild.
|
||||||
|
|
||||||
|
Please note that if you use this in a private message with a user, no channels will be displayed to the user.
|
||||||
|
|
||||||
|
.. versionadded:: 2.1
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
custom_id: :class:`str`
|
||||||
|
The ID of the select menu that gets received during an interaction.
|
||||||
|
If not given then one is generated for you.
|
||||||
|
channel_types: List[:class:`~discord.ChannelType`]
|
||||||
|
The types of channels to show in the select menu. Defaults to all channels.
|
||||||
|
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 0 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.
|
||||||
|
disabled: :class:`bool`
|
||||||
|
Whether the select is disabled or not.
|
||||||
|
row: Optional[:class:`int`]
|
||||||
|
The relative row this select menu belongs to. A Discord component can only have 5
|
||||||
|
rows. By default, items are arranged automatically into those 5 rows. If you'd
|
||||||
|
like to control the relative positioning of the row then passing an index is advised.
|
||||||
|
For example, row=1 will show up before row=2. Defaults to ``None``, which is automatic
|
||||||
|
ordering. The row number must be between 0 and 4 (i.e. zero indexed).
|
||||||
|
"""
|
||||||
|
|
||||||
|
__item_repr_attributes__ = BaseSelect.__item_repr_attributes__ + ('channel_types',)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
custom_id: str = MISSING,
|
||||||
|
channel_types: List[ChannelType] = MISSING,
|
||||||
|
placeholder: Optional[str] = None,
|
||||||
|
min_values: int = 1,
|
||||||
|
max_values: int = 1,
|
||||||
|
disabled: bool = False,
|
||||||
|
row: Optional[int] = None,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(
|
||||||
|
self.type,
|
||||||
|
custom_id=custom_id,
|
||||||
|
placeholder=placeholder,
|
||||||
|
min_values=min_values,
|
||||||
|
max_values=max_values,
|
||||||
|
disabled=disabled,
|
||||||
|
row=row,
|
||||||
|
channel_types=channel_types,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Literal[ComponentType.channel_select]:
|
||||||
|
""":class:`.ComponentType`: The type of this component."""
|
||||||
|
return ComponentType.channel_select
|
||||||
|
|
||||||
|
@property
|
||||||
|
def channel_types(self) -> List[ChannelType]:
|
||||||
|
"""List[:class:`~discord.ChannelType`]: A list of channel types that can be selected."""
|
||||||
|
return self._underlying.channel_types
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self) -> List[Union[AppCommandChannel, AppCommandThread]]:
|
||||||
|
"""List[Union[:class:`~discord.app_commands.AppCommandChannel`, :class:`~discord.app_commands.AppCommandThread`]]: A list of channels selected by the user."""
|
||||||
|
return super().values # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def select(
|
||||||
|
*,
|
||||||
|
cls: Type[SelectT] = Select,
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
channel_types: List[ChannelType] = ...,
|
||||||
|
placeholder: Optional[str] = ...,
|
||||||
|
custom_id: str = ...,
|
||||||
|
min_values: int = ...,
|
||||||
|
max_values: int = ...,
|
||||||
|
disabled: bool = ...,
|
||||||
|
row: Optional[int] = ...,
|
||||||
|
) -> SelectCallbackDecorator[V, SelectT]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def select(
|
||||||
|
*,
|
||||||
|
cls: Type[UserSelectT],
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
channel_types: List[ChannelType] = ...,
|
||||||
|
placeholder: Optional[str] = ...,
|
||||||
|
custom_id: str = ...,
|
||||||
|
min_values: int = ...,
|
||||||
|
max_values: int = ...,
|
||||||
|
disabled: bool = ...,
|
||||||
|
row: Optional[int] = ...,
|
||||||
|
) -> SelectCallbackDecorator[V, UserSelectT]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def select(
|
||||||
|
*,
|
||||||
|
cls: Type[RoleSelectT],
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
channel_types: List[ChannelType] = ...,
|
||||||
|
placeholder: Optional[str] = ...,
|
||||||
|
custom_id: str = ...,
|
||||||
|
min_values: int = ...,
|
||||||
|
max_values: int = ...,
|
||||||
|
disabled: bool = ...,
|
||||||
|
row: Optional[int] = ...,
|
||||||
|
) -> SelectCallbackDecorator[V, RoleSelectT]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def select(
|
||||||
|
*,
|
||||||
|
cls: Type[ChannelSelectT],
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
channel_types: List[ChannelType] = ...,
|
||||||
|
placeholder: Optional[str] = ...,
|
||||||
|
custom_id: str = ...,
|
||||||
|
min_values: int = ...,
|
||||||
|
max_values: int = ...,
|
||||||
|
disabled: bool = ...,
|
||||||
|
row: Optional[int] = ...,
|
||||||
|
) -> SelectCallbackDecorator[V, ChannelSelectT]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def select(
|
||||||
|
*,
|
||||||
|
cls: Type[MentionableSelectT],
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
channel_types: List[ChannelType] = MISSING,
|
||||||
|
placeholder: Optional[str] = ...,
|
||||||
|
custom_id: str = ...,
|
||||||
|
min_values: int = ...,
|
||||||
|
max_values: int = ...,
|
||||||
|
disabled: bool = ...,
|
||||||
|
row: Optional[int] = ...,
|
||||||
|
) -> SelectCallbackDecorator[V, MentionableSelectT]:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
def select(
|
def select(
|
||||||
*,
|
*,
|
||||||
|
cls: Type[BaseSelectT] = Select,
|
||||||
|
options: List[SelectOption] = MISSING,
|
||||||
|
channel_types: List[ChannelType] = MISSING,
|
||||||
placeholder: Optional[str] = None,
|
placeholder: Optional[str] = None,
|
||||||
custom_id: str = MISSING,
|
custom_id: str = MISSING,
|
||||||
min_values: int = 1,
|
min_values: int = 1,
|
||||||
max_values: int = 1,
|
max_values: int = 1,
|
||||||
options: List[SelectOption] = MISSING,
|
|
||||||
disabled: bool = False,
|
disabled: bool = False,
|
||||||
row: Optional[int] = None,
|
row: Optional[int] = None,
|
||||||
) -> Callable[[ItemCallbackType[V, Select[V]]], Select[V]]:
|
) -> SelectCallbackDecorator[V, BaseSelectT]:
|
||||||
"""A decorator that attaches a select menu to a component.
|
"""A decorator that attaches a select menu to a component.
|
||||||
|
|
||||||
The function being decorated should have three parameters, ``self`` representing
|
The function being decorated should have three parameters, ``self`` representing
|
||||||
the :class:`discord.ui.View`, the :class:`discord.Interaction` you receive and
|
the :class:`discord.ui.View`, the :class:`discord.Interaction` you receive and
|
||||||
the :class:`discord.ui.Select` being used.
|
the chosen select class.
|
||||||
|
|
||||||
In order to get the selected items that the user has chosen within the callback
|
To obtain the selected values inside the callback, you can use the ``values`` attribute of the chosen class in the callback. The list of values
|
||||||
use :attr:`Select.values`.
|
will depend on the type of select menu used. View the table below for more information.
|
||||||
|
|
||||||
|
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||||
|
| Select Type | Resolved Values |
|
||||||
|
+========================================+=================================================================================================================+
|
||||||
|
| :class:`discord.ui.Select` | List[:class:`str`] |
|
||||||
|
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||||
|
| :class:`discord.ui.UserSelect` | List[Union[:class:`discord.Member`, :class:`discord.User`]] |
|
||||||
|
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||||
|
| :class:`discord.ui.RoleSelect` | List[:class:`discord.Role`] |
|
||||||
|
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||||
|
| :class:`discord.ui.MentionableSelect` | List[Union[:class:`discord.Role`, :class:`discord.Member`, :class:`discord.User`]] |
|
||||||
|
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||||
|
| :class:`discord.ui.ChannelSelect` | List[Union[:class:`~discord.app_commands.AppCommandChannel`, :class:`~discord.app_commands.AppCommandThread`]] |
|
||||||
|
+----------------------------------------+-----------------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
.. versionchanged:: 2.1
|
||||||
|
Added the following keyword-arguments: ``cls``, ``channel_types``
|
||||||
|
|
||||||
|
Example
|
||||||
|
---------
|
||||||
|
.. code-block:: python3
|
||||||
|
|
||||||
|
class View(discord.ui.View):
|
||||||
|
|
||||||
|
@discord.ui.select(cls=ChannelSelect, channel_types=[discord.ChannelType.text])
|
||||||
|
async def select_channels(self, interaction: discord.Interaction, select: ChannelSelect):
|
||||||
|
return await interaction.response.send_message(f'You selected {select.values[0].mention}')
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
------------
|
------------
|
||||||
|
cls: Union[Type[:class:`discord.ui.Select`], Type[:class:`discord.ui.UserSelect`], Type[:class:`discord.ui.RoleSelect`], \
|
||||||
|
Type[:class:`discord.ui.MentionableSelect`], Type[:class:`discord.ui.ChannelSelect`]]
|
||||||
|
The class to use for the select menu. Defaults to :class:`discord.ui.Select`. You can use other
|
||||||
|
select types to display different select menus to the user. See the table above for the different
|
||||||
|
values you can get from each select type. Subclasses work as well, however the callback in the subclass will
|
||||||
|
get overridden.
|
||||||
placeholder: Optional[:class:`str`]
|
placeholder: Optional[:class:`str`]
|
||||||
The placeholder text that is shown if nothing is selected, if any.
|
The placeholder text that is shown if nothing is selected, if any.
|
||||||
custom_id: :class:`str`
|
custom_id: :class:`str`
|
||||||
@ -340,25 +815,36 @@ def select(
|
|||||||
The maximum number of items that must be chosen for this select menu.
|
The maximum number of items that must be chosen for this select menu.
|
||||||
Defaults to 1 and must be between 1 and 25.
|
Defaults to 1 and must be between 1 and 25.
|
||||||
options: List[:class:`discord.SelectOption`]
|
options: List[:class:`discord.SelectOption`]
|
||||||
A list of options that can be selected in this menu.
|
A list of options that can be selected in this menu. This can only be used with
|
||||||
|
:class:`Select` instances.
|
||||||
|
channel_types: List[:class:`~discord.ChannelType`]
|
||||||
|
The types of channels to show in the select menu. Defaults to all channels. This can only be used
|
||||||
|
with :class:`ChannelSelect` instances.
|
||||||
disabled: :class:`bool`
|
disabled: :class:`bool`
|
||||||
Whether the select is disabled or not. Defaults to ``False``.
|
Whether the select is disabled or not. Defaults to ``False``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func: ItemCallbackType[V, Select[V]]) -> ItemCallbackType[V, Select[V]]:
|
def decorator(func: ItemCallbackType[V, BaseSelectT]) -> ItemCallbackType[V, BaseSelectT]:
|
||||||
if not inspect.iscoroutinefunction(func):
|
if not inspect.iscoroutinefunction(func):
|
||||||
raise TypeError('select function must be a coroutine function')
|
raise TypeError('select function must be a coroutine function')
|
||||||
|
if not issubclass(cls, BaseSelect):
|
||||||
|
supported_classes = ", ".join(["ChannelSelect", "MentionableSelect", "RoleSelect", "Select", "UserSelect"])
|
||||||
|
raise TypeError(f'cls must be one of {supported_classes} or a subclass of one of them, not {cls!r}.')
|
||||||
|
|
||||||
func.__discord_ui_model_type__ = Select
|
func.__discord_ui_model_type__ = cls
|
||||||
func.__discord_ui_model_kwargs__ = {
|
func.__discord_ui_model_kwargs__ = {
|
||||||
'placeholder': placeholder,
|
'placeholder': placeholder,
|
||||||
'custom_id': custom_id,
|
'custom_id': custom_id,
|
||||||
'row': row,
|
'row': row,
|
||||||
'min_values': min_values,
|
'min_values': min_values,
|
||||||
'max_values': max_values,
|
'max_values': max_values,
|
||||||
'options': options,
|
|
||||||
'disabled': disabled,
|
'disabled': disabled,
|
||||||
}
|
}
|
||||||
|
if issubclass(cls, Select):
|
||||||
|
func.__discord_ui_model_kwargs__['options'] = options
|
||||||
|
if issubclass(cls, ChannelSelect):
|
||||||
|
func.__discord_ui_model_kwargs__['channel_types'] = channel_types
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return decorator # type: ignore
|
return decorator # type: ignore
|
||||||
|
@ -38,6 +38,7 @@ if TYPE_CHECKING:
|
|||||||
from ..types.components import TextInput as TextInputPayload
|
from ..types.components import TextInput as TextInputPayload
|
||||||
from ..types.interactions import ModalSubmitTextInputInteractionData as ModalSubmitTextInputInteractionDataPayload
|
from ..types.interactions import ModalSubmitTextInputInteractionData as ModalSubmitTextInputInteractionDataPayload
|
||||||
from .view import View
|
from .view import View
|
||||||
|
from ..interactions import Interaction
|
||||||
|
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
@ -218,7 +219,7 @@ class TextInput(Item[V]):
|
|||||||
def _refresh_component(self, component: TextInputComponent) -> None:
|
def _refresh_component(self, component: TextInputComponent) -> None:
|
||||||
self._underlying = component
|
self._underlying = component
|
||||||
|
|
||||||
def _refresh_state(self, data: ModalSubmitTextInputInteractionDataPayload) -> None:
|
def _refresh_state(self, interaction: Interaction, data: ModalSubmitTextInputInteractionDataPayload) -> None:
|
||||||
self._value = data.get('value', None)
|
self._value = data.get('value', None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -413,7 +413,7 @@ class View:
|
|||||||
|
|
||||||
async def _scheduled_task(self, item: Item, interaction: Interaction):
|
async def _scheduled_task(self, item: Item, interaction: Interaction):
|
||||||
try:
|
try:
|
||||||
item._refresh_state(interaction.data) # type: ignore
|
item._refresh_state(interaction, interaction.data) # type: ignore
|
||||||
|
|
||||||
allow = await self.interaction_check(interaction)
|
allow = await self.interaction_check(interaction)
|
||||||
if not allow:
|
if not allow:
|
||||||
|
@ -252,17 +252,35 @@ Enumerations
|
|||||||
.. attribute:: action_row
|
.. attribute:: action_row
|
||||||
|
|
||||||
Represents the group component which holds different components in a row.
|
Represents the group component which holds different components in a row.
|
||||||
|
|
||||||
.. attribute:: button
|
.. attribute:: button
|
||||||
|
|
||||||
Represents a button component.
|
Represents a button component.
|
||||||
.. attribute:: select
|
|
||||||
|
|
||||||
Represents a select component.
|
|
||||||
|
|
||||||
.. attribute:: text_input
|
.. attribute:: text_input
|
||||||
|
|
||||||
Represents a text box component.
|
Represents a text box component.
|
||||||
|
|
||||||
|
.. attribute:: select
|
||||||
|
|
||||||
|
Represents a select component.
|
||||||
|
|
||||||
|
.. attribute:: string_select
|
||||||
|
|
||||||
|
An alias to :attr:`select`. Represents a default select component.
|
||||||
|
|
||||||
|
.. attribute:: user_select
|
||||||
|
|
||||||
|
Represents a user select component.
|
||||||
|
|
||||||
|
.. attribute:: role_select
|
||||||
|
|
||||||
|
Represents a role select component.
|
||||||
|
|
||||||
|
.. attribute:: mentionable_select
|
||||||
|
|
||||||
|
Represents a select in which both users and roles can be selected.
|
||||||
|
|
||||||
.. class:: ButtonStyle
|
.. class:: ButtonStyle
|
||||||
|
|
||||||
Represents the style of the button component.
|
Represents the style of the button component.
|
||||||
@ -437,8 +455,13 @@ Button
|
|||||||
.. autofunction:: discord.ui.button
|
.. autofunction:: discord.ui.button
|
||||||
:decorator:
|
:decorator:
|
||||||
|
|
||||||
|
Select Menus
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The library provides classes to help create the different types of select menus.
|
||||||
|
|
||||||
Select
|
Select
|
||||||
~~~~~~~
|
+++++++
|
||||||
|
|
||||||
.. attributetable:: discord.ui.Select
|
.. attributetable:: discord.ui.Select
|
||||||
|
|
||||||
@ -446,11 +469,50 @@ Select
|
|||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
|
|
||||||
|
ChannelSelect
|
||||||
|
++++++++++++++
|
||||||
|
|
||||||
|
.. attributetable:: discord.ui.ChannelSelect
|
||||||
|
|
||||||
|
.. autoclass:: discord.ui.ChannelSelect
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
RoleSelect
|
||||||
|
++++++++++
|
||||||
|
|
||||||
|
.. attributetable:: discord.ui.RoleSelect
|
||||||
|
|
||||||
|
.. autoclass:: discord.ui.RoleSelect
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
MentionableSelect
|
||||||
|
++++++++++++++++++
|
||||||
|
|
||||||
|
.. attributetable:: discord.ui.MentionableSelect
|
||||||
|
|
||||||
|
.. autoclass:: discord.ui.MentionableSelect
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
UserSelect
|
||||||
|
+++++++++++
|
||||||
|
|
||||||
|
.. attributetable:: discord.ui.UserSelect
|
||||||
|
|
||||||
|
.. autoclass:: discord.ui.UserSelect
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
|
select
|
||||||
|
+++++++
|
||||||
.. autofunction:: discord.ui.select
|
.. autofunction:: discord.ui.select
|
||||||
:decorator:
|
:decorator:
|
||||||
|
|
||||||
|
|
||||||
TextInput
|
TextInput
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|
||||||
.. attributetable:: discord.ui.TextInput
|
.. attributetable:: discord.ui.TextInput
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user