diff --git a/discord/ui/action_row.py b/discord/ui/action_row.py index 9b01cd3a0..9fa8541c7 100644 --- a/discord/ui/action_row.py +++ b/discord/ui/action_row.py @@ -75,8 +75,8 @@ __all__ = ('ActionRow',) class _ActionRowCallback: __slots__ = ('row', 'callback', 'item') - def __init__(self, callback: ItemCallbackType[Any, Any], row: ActionRow, item: Item[Any]) -> None: - self.callback: ItemCallbackType[Any, Any] = callback + def __init__(self, callback: ItemCallbackType[Any], row: ActionRow, item: Item[Any]) -> None: + self.callback: ItemCallbackType[Any] = callback self.row: ActionRow = row self.item: Item[Any] = item @@ -97,7 +97,7 @@ class ActionRow(Item[V]): The ID of this component. This must be unique across the view. """ - __action_row_children_items__: ClassVar[List[ItemCallbackType[Any, Any]]] = [] + __action_row_children_items__: ClassVar[List[ItemCallbackType[Any]]] = [] __discord_ui_action_row__: ClassVar[bool] = True __discord_ui_update_view__: ClassVar[bool] = True @@ -110,7 +110,7 @@ class ActionRow(Item[V]): def __init_subclass__(cls) -> None: super().__init_subclass__() - children: Dict[str, ItemCallbackType[Any, Any]] = {} + children: Dict[str, ItemCallbackType[Any]] = {} for base in reversed(cls.__mro__): for name, member in base.__dict__.items(): if hasattr(member, '__discord_ui_model_type__'): @@ -269,7 +269,7 @@ class ActionRow(Item[V]): disabled: bool = False, style: ButtonStyle = ButtonStyle.secondary, emoji: Optional[Union[str, Emoji, PartialEmoji]] = None, - ) -> Callable[[ItemCallbackType[V, Button[V]]], Button[V]]: + ) -> Callable[[ItemCallbackType[Button[V]]], Button[V]]: """A decorator that attaches a button to a component. The function being decorated should have three parameters, ``self`` representing @@ -302,7 +302,7 @@ class ActionRow(Item[V]): or a full :class:`.Emoji`. """ - def decorator(func: ItemCallbackType[V, Button[V]]) -> ItemCallbackType[V, Button[V]]: + def decorator(func: ItemCallbackType[Button[V]]) -> ItemCallbackType[Button[V]]: ret = _button( label=label, custom_id=custom_id, @@ -328,7 +328,7 @@ class ActionRow(Item[V]): min_values: int = ..., max_values: int = ..., disabled: bool = ..., - ) -> SelectCallbackDecorator[V, SelectT]: + ) -> SelectCallbackDecorator[SelectT]: ... @overload @@ -344,7 +344,7 @@ class ActionRow(Item[V]): max_values: int = ..., disabled: bool = ..., default_values: Sequence[ValidDefaultValues] = ..., - ) -> SelectCallbackDecorator[V, UserSelectT]: + ) -> SelectCallbackDecorator[UserSelectT]: ... @overload @@ -360,7 +360,7 @@ class ActionRow(Item[V]): max_values: int = ..., disabled: bool = ..., default_values: Sequence[ValidDefaultValues] = ..., - ) -> SelectCallbackDecorator[V, RoleSelectT]: + ) -> SelectCallbackDecorator[RoleSelectT]: ... @overload @@ -376,7 +376,7 @@ class ActionRow(Item[V]): max_values: int = ..., disabled: bool = ..., default_values: Sequence[ValidDefaultValues] = ..., - ) -> SelectCallbackDecorator[V, ChannelSelectT]: + ) -> SelectCallbackDecorator[ChannelSelectT]: ... @overload @@ -392,7 +392,7 @@ class ActionRow(Item[V]): max_values: int = ..., disabled: bool = ..., default_values: Sequence[ValidDefaultValues] = ..., - ) -> SelectCallbackDecorator[V, MentionableSelectT]: + ) -> SelectCallbackDecorator[MentionableSelectT]: ... def select( @@ -407,7 +407,7 @@ class ActionRow(Item[V]): max_values: int = 1, disabled: bool = False, default_values: Sequence[ValidDefaultValues] = MISSING, - ) -> SelectCallbackDecorator[V, BaseSelectT]: + ) -> SelectCallbackDecorator[BaseSelectT]: """A decorator that attaches a select menu to a component. The function being decorated should have three parameters, ``self`` representing @@ -477,7 +477,7 @@ class ActionRow(Item[V]): Number of items must be in range of ``min_values`` and ``max_values``. """ - def decorator(func: ItemCallbackType[V, BaseSelectT]) -> ItemCallbackType[V, BaseSelectT]: + def decorator(func: ItemCallbackType[BaseSelectT]) -> ItemCallbackType[BaseSelectT]: r = _select( # type: ignore cls=cls, # type: ignore placeholder=placeholder, diff --git a/discord/ui/button.py b/discord/ui/button.py index 7a60333db..46230d480 100644 --- a/discord/ui/button.py +++ b/discord/ui/button.py @@ -281,7 +281,8 @@ def button( style: ButtonStyle = ButtonStyle.secondary, emoji: Optional[Union[str, Emoji, PartialEmoji]] = None, row: Optional[int] = None, -) -> Callable[[ItemCallbackType[V, Button[V]]], Button[V]]: + id: Optional[int] = None, +) -> Callable[[ItemCallbackType[Button[V]]], Button[V]]: """A decorator that attaches a button to a component. The function being decorated should have three parameters, ``self`` representing @@ -318,9 +319,13 @@ def button( 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). + id: Optional[:class:`int`] + The ID of this component. This must be unique across the view. + + .. versionadded:: 2.6 """ - def decorator(func: ItemCallbackType[V, Button[V]]) -> ItemCallbackType[V, Button[V]]: + def decorator(func: ItemCallbackType[Button[V]]) -> ItemCallbackType[Button[V]]: if not inspect.iscoroutinefunction(func): raise TypeError('button function must be a coroutine function') @@ -334,6 +339,7 @@ def button( 'emoji': emoji, 'row': row, 'sku_id': None, + 'id': id, } return func diff --git a/discord/ui/container.py b/discord/ui/container.py index 96bafde0d..58176d0f5 100644 --- a/discord/ui/container.py +++ b/discord/ui/container.py @@ -46,8 +46,8 @@ __all__ = ('Container',) class _ContainerCallback: __slots__ = ('container', 'callback', 'item') - def __init__(self, callback: ItemCallbackType[Any, Any], container: Container, item: Item[Any]) -> None: - self.callback: ItemCallbackType[Any, Any] = callback + def __init__(self, callback: ItemCallbackType[Any], container: Container, item: Item[Any]) -> None: + self.callback: ItemCallbackType[Any] = callback self.container: Container = container self.item: Item[Any] = item @@ -63,7 +63,7 @@ class Container(Item[V]): Parameters ---------- children: List[:class:`Item`] - The initial children or :class:`View` s of this container. Can have up to 10 + The initial children of this container. Can have up to 10 items. accent_colour: Optional[:class:`.Colour`] The colour of the container. Defaults to ``None``. @@ -124,6 +124,7 @@ class Container(Item[V]): if getattr(raw, '__discord_ui_section__', False) and raw.accessory.is_dispatchable(): # type: ignore self.__dispatchable.append(raw.accessory) # type: ignore elif getattr(raw, '__discord_ui_action_row__', False) and raw.is_dispatchable(): + raw._parent = self # type: ignore self.__dispatchable.extend(raw._children) # type: ignore else: # action rows can be created inside containers, and then callbacks can exist here diff --git a/discord/ui/item.py b/discord/ui/item.py index d735641db..4206274c3 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -43,7 +43,7 @@ if TYPE_CHECKING: I = TypeVar('I', bound='Item[Any]') V = TypeVar('V', bound='BaseView', covariant=True) -ItemCallbackType = Callable[[V, Interaction[Any], I], Coroutine[Any, Any, Any]] +ItemCallbackType = Callable[[Any, Interaction[Any], I], Coroutine[Any, Any, Any]] class Item(Generic[V]): @@ -151,6 +151,17 @@ class Item(Generic[V]): def id(self, value: Optional[int]) -> None: self._id = value + async def _run_checks(self, interaction: Interaction[ClientT]) -> bool: + can_run = await self.interaction_check(interaction) + + if can_run: + parent = getattr(self, '_parent', None) + + if parent is not None: + can_run = await parent._run_checks(interaction) + + return can_run + async def callback(self, interaction: Interaction[ClientT]) -> Any: """|coro| diff --git a/discord/ui/select.py b/discord/ui/select.py index e2d3d34d2..40b8a26f3 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -109,7 +109,7 @@ UserSelectT = TypeVar('UserSelectT', bound='UserSelect[Any]') RoleSelectT = TypeVar('RoleSelectT', bound='RoleSelect[Any]') ChannelSelectT = TypeVar('ChannelSelectT', bound='ChannelSelect[Any]') MentionableSelectT = TypeVar('MentionableSelectT', bound='MentionableSelect[Any]') -SelectCallbackDecorator: TypeAlias = Callable[[ItemCallbackType[V, BaseSelectT]], BaseSelectT] +SelectCallbackDecorator: TypeAlias = Callable[[ItemCallbackType[BaseSelectT]], BaseSelectT] DefaultSelectComponentTypes = Literal[ ComponentType.user_select, ComponentType.role_select, @@ -936,7 +936,7 @@ def select( disabled: bool = ..., row: Optional[int] = ..., id: Optional[int] = ..., -) -> SelectCallbackDecorator[V, SelectT]: +) -> SelectCallbackDecorator[SelectT]: ... @@ -954,7 +954,7 @@ def select( default_values: Sequence[ValidDefaultValues] = ..., row: Optional[int] = ..., id: Optional[int] = ..., -) -> SelectCallbackDecorator[V, UserSelectT]: +) -> SelectCallbackDecorator[UserSelectT]: ... @@ -972,7 +972,7 @@ def select( default_values: Sequence[ValidDefaultValues] = ..., row: Optional[int] = ..., id: Optional[int] = ..., -) -> SelectCallbackDecorator[V, RoleSelectT]: +) -> SelectCallbackDecorator[RoleSelectT]: ... @@ -990,7 +990,7 @@ def select( default_values: Sequence[ValidDefaultValues] = ..., row: Optional[int] = ..., id: Optional[int] = ..., -) -> SelectCallbackDecorator[V, ChannelSelectT]: +) -> SelectCallbackDecorator[ChannelSelectT]: ... @@ -1008,7 +1008,7 @@ def select( default_values: Sequence[ValidDefaultValues] = ..., row: Optional[int] = ..., id: Optional[int] = ..., -) -> SelectCallbackDecorator[V, MentionableSelectT]: +) -> SelectCallbackDecorator[MentionableSelectT]: ... @@ -1025,7 +1025,7 @@ def select( default_values: Sequence[ValidDefaultValues] = MISSING, row: Optional[int] = None, id: Optional[int] = None, -) -> SelectCallbackDecorator[V, BaseSelectT]: +) -> SelectCallbackDecorator[BaseSelectT]: """A decorator that attaches a select menu to a component. The function being decorated should have three parameters, ``self`` representing @@ -1110,7 +1110,7 @@ def select( .. versionadded:: 2.6 """ - def decorator(func: ItemCallbackType[V, BaseSelectT]) -> ItemCallbackType[V, BaseSelectT]: + def decorator(func: ItemCallbackType[BaseSelectT]) -> ItemCallbackType[BaseSelectT]: if not inspect.iscoroutinefunction(func): raise TypeError('select function must be a coroutine function') callback_cls = getattr(cls, '__origin__', cls) diff --git a/discord/ui/view.py b/discord/ui/view.py index 5107716bd..0fb577871 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -84,7 +84,7 @@ if TYPE_CHECKING: from ..state import ConnectionState from .modal import Modal - ItemLike = Union[ItemCallbackType[Any, Any], Item[Any]] + ItemLike = Union[ItemCallbackType[Any], Item[Any]] _log = logging.getLogger(__name__) @@ -185,8 +185,8 @@ class _ViewWeights: class _ViewCallback: __slots__ = ('view', 'callback', 'item') - def __init__(self, callback: ItemCallbackType[Any, Any], view: BaseView, item: Item[BaseView]) -> None: - self.callback: ItemCallbackType[Any, Any] = callback + def __init__(self, callback: ItemCallbackType[Any], view: BaseView, item: Item[BaseView]) -> None: + self.callback: ItemCallbackType[Any] = callback self.view: BaseView = view self.item: Item[BaseView] = item @@ -452,7 +452,7 @@ class BaseView: try: item._refresh_state(interaction, interaction.data) # type: ignore - allow = await item.interaction_check(interaction) and await self.interaction_check(interaction) + allow = await item._run_checks(interaction) and await self.interaction_check(interaction) if not allow: return @@ -581,13 +581,13 @@ class View(BaseView): def __init_subclass__(cls) -> None: warnings.warn( - 'discord.ui.View and subclasses are deprecated and will be removed in' + 'discord.ui.View and subclasses are deprecated and will be removed in ' 'a future version, use discord.ui.LayoutView instead', DeprecationWarning, ) super().__init_subclass__() - children: Dict[str, ItemCallbackType[Any, Any]] = {} + children: Dict[str, ItemCallbackType[Any]] = {} for base in reversed(cls.__mro__): for name, member in base.__dict__.items(): if hasattr(member, '__discord_ui_model_type__'): @@ -716,7 +716,7 @@ class LayoutView(BaseView): def __init_subclass__(cls) -> None: children: Dict[str, Item[Any]] = {} - callback_children: Dict[str, ItemCallbackType[Any, Any]] = {} + callback_children: Dict[str, ItemCallbackType[Any]] = {} row = 0