Adding all files
This commit is contained in:
364
.local/lib/python3.14/site-packages/disnake/ui/button.py
Normal file
364
.local/lib/python3.14/site-packages/disnake/ui/button.py
Normal file
@@ -0,0 +1,364 @@
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Tuple, TypeVar, Union, overload
|
||||
|
||||
from ..components import Button as ButtonComponent
|
||||
from ..enums import ButtonStyle, ComponentType
|
||||
from ..partial_emoji import PartialEmoji, _EmojiTag
|
||||
from ..utils import MISSING, iscoroutinefunction
|
||||
from .item import DecoratedItem, Item
|
||||
|
||||
__all__ = (
|
||||
"Button",
|
||||
"button",
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import ParamSpec, Self
|
||||
|
||||
from ..emoji import Emoji
|
||||
from .item import ItemCallbackType
|
||||
from .view import View
|
||||
|
||||
else:
|
||||
ParamSpec = TypeVar
|
||||
|
||||
B = TypeVar("B", bound="Button")
|
||||
B_co = TypeVar("B_co", bound="Button", covariant=True)
|
||||
V_co = TypeVar("V_co", bound="Optional[View]", covariant=True)
|
||||
P = ParamSpec("P")
|
||||
|
||||
|
||||
class Button(Item[V_co]):
|
||||
"""Represents a UI button.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Parameters
|
||||
----------
|
||||
style: :class:`disnake.ButtonStyle`
|
||||
The style of the button.
|
||||
custom_id: Optional[:class:`str`]
|
||||
The ID of the button that gets received during an interaction.
|
||||
If this button is for a URL or an SKU, it does not have a custom ID.
|
||||
url: Optional[:class:`str`]
|
||||
The URL this button sends you to.
|
||||
disabled: :class:`bool`
|
||||
Whether the button is disabled.
|
||||
label: Optional[:class:`str`]
|
||||
The label of the button, if any.
|
||||
emoji: Optional[Union[:class:`.PartialEmoji`, :class:`.Emoji`, :class:`str`]]
|
||||
The emoji of the button, if available.
|
||||
sku_id: Optional[:class:`int`]
|
||||
The ID of a purchasable SKU, for premium buttons.
|
||||
Premium buttons additionally cannot have a ``label``, ``url``, or ``emoji``.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
id: :class:`int`
|
||||
The numeric identifier for the component. Must be unique within the message.
|
||||
If set to ``0`` (the default) when sending a component, the API will assign
|
||||
sequential identifiers to the components in the message.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
row: Optional[:class:`int`]
|
||||
The relative row this button 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).
|
||||
"""
|
||||
|
||||
__repr_attributes__: ClassVar[Tuple[str, ...]] = (
|
||||
"style",
|
||||
"url",
|
||||
"disabled",
|
||||
"label",
|
||||
"emoji",
|
||||
"sku_id",
|
||||
"row",
|
||||
)
|
||||
# We have to set this to MISSING in order to overwrite the abstract property from UIComponent
|
||||
_underlying: ButtonComponent = MISSING
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self: Button[None],
|
||||
*,
|
||||
style: ButtonStyle = ButtonStyle.secondary,
|
||||
label: Optional[str] = None,
|
||||
disabled: bool = False,
|
||||
custom_id: Optional[str] = None,
|
||||
url: Optional[str] = None,
|
||||
emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
|
||||
sku_id: Optional[int] = None,
|
||||
id: int = 0,
|
||||
row: Optional[int] = None,
|
||||
) -> None: ...
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self: Button[V_co],
|
||||
*,
|
||||
style: ButtonStyle = ButtonStyle.secondary,
|
||||
label: Optional[str] = None,
|
||||
disabled: bool = False,
|
||||
custom_id: Optional[str] = None,
|
||||
url: Optional[str] = None,
|
||||
emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
|
||||
sku_id: Optional[int] = None,
|
||||
id: int = 0,
|
||||
row: Optional[int] = None,
|
||||
) -> None: ...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
style: ButtonStyle = ButtonStyle.secondary,
|
||||
label: Optional[str] = None,
|
||||
disabled: bool = False,
|
||||
custom_id: Optional[str] = None,
|
||||
url: Optional[str] = None,
|
||||
emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
|
||||
sku_id: Optional[int] = None,
|
||||
id: int = 0,
|
||||
row: Optional[int] = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._provided_custom_id = custom_id is not None
|
||||
mutually_exclusive = 3 - (custom_id, url, sku_id).count(None)
|
||||
|
||||
if mutually_exclusive == 0:
|
||||
custom_id = os.urandom(16).hex()
|
||||
elif mutually_exclusive != 1:
|
||||
raise TypeError("cannot mix url, sku_id and custom_id with Button")
|
||||
|
||||
if url is not None:
|
||||
style = ButtonStyle.link
|
||||
if sku_id is not None:
|
||||
style = ButtonStyle.premium
|
||||
|
||||
if emoji is not None:
|
||||
if isinstance(emoji, str):
|
||||
emoji = PartialEmoji.from_str(emoji)
|
||||
elif isinstance(emoji, _EmojiTag):
|
||||
emoji = emoji._to_partial()
|
||||
else:
|
||||
raise TypeError(
|
||||
f"expected emoji to be str, Emoji, or PartialEmoji not {emoji.__class__}"
|
||||
)
|
||||
|
||||
self._underlying = ButtonComponent._raw_construct(
|
||||
type=ComponentType.button,
|
||||
id=id,
|
||||
custom_id=custom_id,
|
||||
url=url,
|
||||
disabled=disabled,
|
||||
label=label,
|
||||
style=style,
|
||||
emoji=emoji,
|
||||
sku_id=sku_id,
|
||||
)
|
||||
self.row = row
|
||||
|
||||
@property
|
||||
def width(self) -> int:
|
||||
return 1
|
||||
|
||||
@property
|
||||
def style(self) -> ButtonStyle:
|
||||
""":class:`disnake.ButtonStyle`: The style of the button."""
|
||||
return self._underlying.style
|
||||
|
||||
@style.setter
|
||||
def style(self, value: ButtonStyle) -> None:
|
||||
self._underlying.style = value
|
||||
|
||||
@property
|
||||
def custom_id(self) -> Optional[str]:
|
||||
"""Optional[:class:`str`]: The ID of the button that gets received during an interaction.
|
||||
|
||||
If this button is for a URL or an SKU, it does not have a custom ID.
|
||||
"""
|
||||
return self._underlying.custom_id
|
||||
|
||||
@custom_id.setter
|
||||
def custom_id(self, value: Optional[str]) -> None:
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("custom_id must be None or str")
|
||||
|
||||
self._underlying.custom_id = value
|
||||
|
||||
@property
|
||||
def url(self) -> Optional[str]:
|
||||
"""Optional[:class:`str`]: The URL this button sends you to."""
|
||||
return self._underlying.url
|
||||
|
||||
@url.setter
|
||||
def url(self, value: Optional[str]) -> None:
|
||||
if value is not None and not isinstance(value, str):
|
||||
raise TypeError("url must be None or str")
|
||||
self._underlying.url = value
|
||||
|
||||
@property
|
||||
def disabled(self) -> bool:
|
||||
""":class:`bool`: Whether the button is disabled."""
|
||||
return self._underlying.disabled
|
||||
|
||||
@disabled.setter
|
||||
def disabled(self, value: bool) -> None:
|
||||
self._underlying.disabled = bool(value)
|
||||
|
||||
@property
|
||||
def label(self) -> Optional[str]:
|
||||
"""Optional[:class:`str`]: The label of the button, if available."""
|
||||
return self._underlying.label
|
||||
|
||||
@label.setter
|
||||
def label(self, value: Optional[str]) -> None:
|
||||
self._underlying.label = str(value) if value is not None else value
|
||||
|
||||
@property
|
||||
def emoji(self) -> Optional[PartialEmoji]:
|
||||
"""Optional[:class:`.PartialEmoji`]: The emoji of the button, if available."""
|
||||
return self._underlying.emoji
|
||||
|
||||
@emoji.setter
|
||||
def emoji(self, value: Optional[Union[str, Emoji, PartialEmoji]]) -> None:
|
||||
if value is not None:
|
||||
if isinstance(value, str):
|
||||
self._underlying.emoji = PartialEmoji.from_str(value)
|
||||
elif isinstance(value, _EmojiTag):
|
||||
self._underlying.emoji = value._to_partial()
|
||||
else:
|
||||
raise TypeError(
|
||||
f"expected str, Emoji, or PartialEmoji, received {value.__class__} instead"
|
||||
)
|
||||
else:
|
||||
self._underlying.emoji = None
|
||||
|
||||
@property
|
||||
def sku_id(self) -> Optional[int]:
|
||||
"""Optional[:class:`int`]: The ID of a purchasable SKU, for premium buttons.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
"""
|
||||
return self._underlying.sku_id
|
||||
|
||||
@sku_id.setter
|
||||
def sku_id(self, value: Optional[int]) -> None:
|
||||
if value is not None and not isinstance(value, int):
|
||||
raise TypeError("sku_id must be None or int")
|
||||
self._underlying.sku_id = value
|
||||
|
||||
@classmethod
|
||||
def from_component(cls, button: ButtonComponent) -> Self:
|
||||
return cls(
|
||||
style=button.style,
|
||||
label=button.label,
|
||||
disabled=button.disabled,
|
||||
custom_id=button.custom_id,
|
||||
url=button.url,
|
||||
emoji=button.emoji,
|
||||
sku_id=button.sku_id,
|
||||
id=button.id,
|
||||
row=None,
|
||||
)
|
||||
|
||||
def is_dispatchable(self) -> bool:
|
||||
return self.custom_id is not None
|
||||
|
||||
def is_persistent(self) -> bool:
|
||||
if self.style is ButtonStyle.link:
|
||||
return self.url is not None
|
||||
elif self.style is ButtonStyle.premium:
|
||||
return self.sku_id is not None
|
||||
return super().is_persistent()
|
||||
|
||||
def refresh_component(self, button: ButtonComponent) -> None:
|
||||
self._underlying = button
|
||||
|
||||
|
||||
@overload
|
||||
def button(
|
||||
*,
|
||||
label: Optional[str] = None,
|
||||
custom_id: Optional[str] = None,
|
||||
disabled: bool = False,
|
||||
style: ButtonStyle = ButtonStyle.secondary,
|
||||
emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,
|
||||
id: int = 0,
|
||||
row: Optional[int] = None,
|
||||
) -> Callable[[ItemCallbackType[V_co, Button[V_co]]], DecoratedItem[Button[V_co]]]: ...
|
||||
|
||||
|
||||
@overload
|
||||
def button(
|
||||
cls: Callable[P, B_co], *_: P.args, **kwargs: P.kwargs
|
||||
) -> Callable[[ItemCallbackType[V_co, B_co]], DecoratedItem[B_co]]: ...
|
||||
|
||||
|
||||
def button(
|
||||
cls: Callable[..., B_co] = Button[Any], **kwargs: Any
|
||||
) -> Callable[[ItemCallbackType[V_co, B_co]], DecoratedItem[B_co]]:
|
||||
"""A decorator that attaches a button to a component.
|
||||
|
||||
The function being decorated should have three parameters, ``self`` representing
|
||||
the :class:`disnake.ui.View`, the :class:`disnake.ui.Button` that was
|
||||
interacted with, and the :class:`disnake.MessageInteraction`.
|
||||
|
||||
.. note::
|
||||
|
||||
Link/Premium buttons cannot be created with this function,
|
||||
since these buttons do not have a callback associated with them.
|
||||
Consider creating a :class:`Button` manually instead, and adding it
|
||||
using :meth:`View.add_item`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
cls: Callable[..., :class:`Button`]
|
||||
A callable (may be a :class:`Button` subclass) to create a new instance of this component.
|
||||
If provided, the other parameters described below do not apply.
|
||||
Instead, this decorator will accept the same keywords as the passed callable/class does.
|
||||
|
||||
.. versionadded:: 2.6
|
||||
label: Optional[:class:`str`]
|
||||
The label of the button, if any.
|
||||
custom_id: Optional[:class:`str`]
|
||||
The ID of the button that gets received during an interaction.
|
||||
It is recommended not to set this parameter to prevent conflicts.
|
||||
style: :class:`.ButtonStyle`
|
||||
The style of the button. Defaults to :attr:`.ButtonStyle.grey`.
|
||||
disabled: :class:`bool`
|
||||
Whether the button is disabled. Defaults to ``False``.
|
||||
emoji: Optional[Union[:class:`str`, :class:`.Emoji`, :class:`.PartialEmoji`]]
|
||||
The emoji of the button. This can be in string form or a :class:`.PartialEmoji`
|
||||
or a full :class:`.Emoji`.
|
||||
id: :class:`int`
|
||||
The numeric identifier for the component. Must be unique within the message.
|
||||
If set to ``0`` (the default) when sending a component, the API will assign
|
||||
sequential identifiers to the components in the message.
|
||||
|
||||
.. versionadded:: 2.11
|
||||
row: Optional[:class:`int`]
|
||||
The relative row this button 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).
|
||||
"""
|
||||
if not callable(cls):
|
||||
raise TypeError("cls argument must be callable")
|
||||
|
||||
def decorator(func: ItemCallbackType[V_co, B_co]) -> DecoratedItem[B_co]:
|
||||
if not iscoroutinefunction(func):
|
||||
raise TypeError("button function must be a coroutine function")
|
||||
|
||||
func.__discord_ui_model_type__ = cls
|
||||
func.__discord_ui_model_kwargs__ = kwargs
|
||||
return func # type: ignore
|
||||
|
||||
return decorator
|
||||
Reference in New Issue
Block a user