diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8ce8ea467a..034e3fba66 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [ '3.10' ] + python-version: [ 3.8 ] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -17,7 +17,7 @@ jobs: - name: Install dependencies run: | python -m pip install -U pip - pip install -U sphinx sphinxcontrib-trio aiohttp sphinxcontrib-websupport myst-parser typing-extensions + pip install -U sphinx sphinxcontrib-trio aiohttp sphinxcontrib-websupport myst-parser - name: Compile to html run: | cd docs diff --git a/discord/asset.py b/discord/asset.py index 035814c405..5b025ded5a 100644 --- a/discord/asset.py +++ b/discord/asset.py @@ -268,11 +268,11 @@ def __str__(self) -> str: def __len__(self) -> int: return len(self._url) - def __repr__(self) -> str: + def __repr__(self): shorten = self._url.replace(self.BASE, '') return f'' - def __eq__(self, other) -> bool: + def __eq__(self, other): return isinstance(other, Asset) and self._url == other._url def __hash__(self): diff --git a/discord/bot.py b/discord/bot.py index 8cd05cefe3..275ba9417a 100644 --- a/discord/bot.py +++ b/discord/bot.py @@ -40,7 +40,6 @@ Type, TypeVar, Union, - Type, ) from .client import Client diff --git a/discord/commands/_types.py b/discord/commands/_types.py deleted file mode 100644 index 8fb0803232..0000000000 --- a/discord/commands/_types.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import Callable, TYPE_CHECKING, Union, Coroutine, Any, TypeVar - -if TYPE_CHECKING: - from .. import Cog, ApplicationContext - -T = TypeVar('T') - -Coro = Coroutine[Any, Any, T] -MaybeCoro = Union[T, Coro[T]] -CoroFunc = Callable[..., Coro[Any]] - -Check = Union[Callable[["Cog", "ApplicationContext[Any]"], MaybeCoro[bool]], - Callable[["ApplicationContext[Any]"], MaybeCoro[bool]]] -Hook = Union[Callable[["Cog", "ApplicationContext[Any]"], Coro[Any]], Callable[["ApplicationContext[Any]"], Coro[Any]]] -Error = Union[Callable[["Cog", "ApplicationContext[Any]", "CommandError"], Coro[Any]], - Callable[["ApplicationContext[Any]", "CommandError"], Coro[Any]]] diff --git a/discord/commands/context.py b/discord/commands/context.py index 5e0ea30a06..2d057ab87b 100644 --- a/discord/commands/context.py +++ b/discord/commands/context.py @@ -24,27 +24,22 @@ """ from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Union, TypeVar, Generic, Callable, List, Any, Dict +from typing import TYPE_CHECKING, Optional, TypeVar, Union -import discord.utils +import discord.abc if TYPE_CHECKING: - from . import ApplicationCommand, Option - from ..cog import Cog - from ..embeds import Embed - from ..file import File - from ..guild import Guild - from ..interactions import Interaction, InteractionChannel, InteractionResponse, InteractionMessage - from ..member import Member - from ..mentions import AllowedMentions - from ..message import Message - from ..state import ConnectionState - from ..user import User - from ..ui import View - from ..voice_client import VoiceProtocol - from ..webhook import Webhook, WebhookMessage from typing_extensions import ParamSpec + import discord + from discord import Bot + from discord.state import ConnectionState + + from .core import ApplicationCommand, Option + from ..cog import Cog + from ..webhook import WebhookMessage + from typing import Callable + from ..guild import Guild from ..interactions import Interaction, InteractionResponse from ..member import Member @@ -63,18 +58,7 @@ __all__ = ("ApplicationContext", "AutocompleteContext") -MISSING: Any = discord.utils.MISSING - -T = TypeVar("T") -BotT = TypeVar("BotT", bound="Union[discord.Bot, discord.AutoShardedBot]") -CogT = TypeVar("CogT", bound="Cog") - -if TYPE_CHECKING: - P = ParamSpec('P') -else: - P = TypeVar('P') - -class ApplicationContext(discord.abc.Messageable, Generic[BotT]): +class ApplicationContext(discord.abc.Messageable): """Represents a Discord application command interaction context. This class is not created manually and is instead passed to application @@ -92,9 +76,9 @@ class ApplicationContext(discord.abc.Messageable, Generic[BotT]): The command that this context belongs to. """ - def __init__(self, bot: BotT, interaction: Interaction) -> None: - self.bot: BotT = bot - self.interaction: Interaction = interaction + def __init__(self, bot: Bot, interaction: Interaction): + self.bot = bot + self.interaction = interaction # below attributes will be set after initialization self.command: ApplicationCommand = None # type: ignore @@ -104,7 +88,7 @@ def __init__(self, bot: BotT, interaction: Interaction) -> None: self._state: ConnectionState = self.interaction._state - async def _get_channel(self) -> Optional[InteractionChannel]: + async def _get_channel(self) -> discord.abc.Messageable: return self.channel async def invoke(self, command: ApplicationCommand[CogT, P, T], /, *args: P.args, **kwargs: P.kwargs) -> T: @@ -134,7 +118,7 @@ async def invoke(self, command: ApplicationCommand[CogT, P, T], /, *args: P.args return await command(self, *args, **kwargs) @cached_property - def channel(self) -> Optional[InteractionChannel]: + def channel(self): return self.interaction.channel @cached_property @@ -149,6 +133,14 @@ def guild(self) -> Optional[Guild]: def guild_id(self) -> Optional[int]: return self.interaction.guild_id + @cached_property + def locale(self) -> Optional[str]: + return self.interaction.locale + + @cached_property + def guild_locale(self) -> Optional[str]: + return self.interaction.guild_locale + @cached_property def me(self) -> Union[Member, User]: return self.guild.me if self.guild is not None else self.bot.user @@ -176,14 +168,6 @@ def voice_client(self): def response(self) -> InteractionResponse: return self.interaction.response - @property - def cog(self) -> Optional[Cog]: - """Optional[:class:`.Cog`]: Returns the cog associated with this context's command. ``None`` if it does not exist.""" - if self.command is None: - return None - - return self.command.cog - @property def respond(self) -> Callable[..., Union[Interaction, WebhookMessage]]: """Callable[..., Union[:class:`~.Interaction`, :class:`~.Webhook`]]: Sends either a response @@ -211,15 +195,15 @@ def send_followup(self): f"Interaction was not yet issued a response. Try using {type(self).__name__}.respond() first." ) - @discord.utils.copy_doc(InteractionResponse.defer) - async def defer(self, *, ephemeral: bool = False) -> None: - return await self.interaction.response.defer(ephemeral=ephemeral) + @property + def defer(self): + return self.interaction.response.defer @property - def followup(self) -> Webhook: + def followup(self): return self.interaction.followup - async def delete(self) -> None: + async def delete(self): """Calls :attr:`~discord.commands.ApplicationContext.respond`. If the response is done, then calls :attr:`~discord.commands.ApplicationContext.respond` first.""" if not self.response.is_done(): @@ -227,26 +211,17 @@ async def delete(self) -> None: return await self.interaction.delete_original_message() - async def edit( - self, - *, - content: Optional[str] = MISSING, - embeds: List[Embed] = MISSING, - embed: Optional[Embed] = MISSING, - file: File = MISSING, - files: List[File] = MISSING, - view: Optional[View] = MISSING, - allowed_mentions: Optional[AllowedMentions] = None, - ) -> InteractionMessage: - return await self.interaction.edit_original_message( - content=content, - embeds=embeds, - embed=embed, - file=file, - files=files, - view=view, - allowed_mentions=allowed_mentions, - ) + @property + def edit(self): + return self.interaction.edit_original_message + + @property + def cog(self) -> Optional[Cog]: + """Optional[:class:`.Cog`]: Returns the cog associated with this context's command. ``None`` if it does not exist.""" + if self.command is None: + return None + + return self.command.cog class AutocompleteContext: @@ -273,24 +248,18 @@ class AutocompleteContext: """ __slots__ = ("bot", "interaction", "command", "focused", "value", "options") - - def __init__( - self, - interaction: Interaction, - *, - command: ApplicationCommand, - focused: Option, - value: str, - options: Dict[str, Any], - ) -> None: - self.interaction: Interaction = interaction - self.command: ApplicationCommand = command - self.focused: Option = focused - self.value: str = value - self.options: Dict[str, Any] = options + + def __init__(self, bot: Bot, interaction: Interaction) -> None: + self.bot = bot + self.interaction = interaction + + self.command: ApplicationCommand = None # type: ignore + self.focused: Option = None # type: ignore + self.value: str = None # type: ignore + self.options: dict = None # type: ignore @property - def cog(self) -> Optional[CogT]: + def cog(self) -> Optional[Cog]: """Optional[:class:`.Cog`]: Returns the cog associated with this context's command. ``None`` if it does not exist.""" if self.command is None: return None diff --git a/discord/commands/core.py b/discord/commands/core.py index 59bcae4062..a029301d06 100644 --- a/discord/commands/core.py +++ b/discord/commands/core.py @@ -32,25 +32,8 @@ import re import types from collections import OrderedDict -from typing_extensions import ParamSpec -from typing import ( - Any, - Callable, - Dict, - List, - Optional, - Union, - TYPE_CHECKING, - Awaitable, - overload, - TypeVar, - Generic, - Type, - Concatenate, - Generator, -) +from typing import Any, Callable, Dict, Generator, Generic, List, Optional, Type, TypeVar, Union, TYPE_CHECKING -from ..enums import SlashCommandOptionType, ChannelType from .context import ApplicationContext, AutocompleteContext from .errors import ApplicationCommandError, CheckFailure, ApplicationCommandInvokeError from .options import Option, OptionChoice @@ -58,34 +41,9 @@ from ..enums import SlashCommandOptionType, ChannelType from ..errors import ValidationError, ClientException from ..member import Member -from ..user import User from ..message import Message -from ..utils import find, async_all, get_or_fetch, utcnow -from ..errors import ValidationError, ClientException -from .context import AutocompleteContext -from .errors import ApplicationCommandError, CheckFailure, ApplicationCommandInvokeError -from .permissions import CommandPermission - -if TYPE_CHECKING: - from ..types.interactions import ( - CreateApplicationCommand, - ApplicationCommandOption as ApplicationCommandOptionData, - ApplicationCommandOptionChoice - ) - from ._types import ( - Coro, - CoroFunc, - Hook, - Error - ) - from .context import ApplicationContext - from ..cog import Cog - from .permissions import Permission - from ..interactions import Interaction - - P = ParamSpec('P') -else: - P = TypeVar('P') +from ..user import User +from ..utils import find, get_or_fetch, async_all, utcnow __all__ = ( "_BaseCommand", @@ -102,15 +60,20 @@ "MessageCommand", ) +if TYPE_CHECKING: + from typing_extensions import ParamSpec + + from ..cog import Cog + T = TypeVar('T') -CogT = TypeVar("CogT", bound="Cog") -ApplicationCommandT = TypeVar("ApplicationCommandT", bound="ApplicationCommand") -ApplicationContextT = TypeVar("ApplicationContextT", bound="ApplicationContext") -HookT = TypeVar("HookT", bound="Hook") -ErrorT = TypeVar('ErrorT', bound='Error') +CogT = TypeVar('CogT', bound='Cog') -def wrap_callback(coro): # TODO: Maybe typehint +if TYPE_CHECKING: + P = ParamSpec('P') +else: + P = TypeVar('P') +def wrap_callback(coro): @functools.wraps(coro) async def wrapped(*args, **kwargs): try: @@ -122,12 +85,9 @@ async def wrapped(*args, **kwargs): except Exception as exc: raise ApplicationCommandInvokeError(exc) from exc return ret - return wrapped - -def hooked_wrapped_callback(command: ApplicationCommandT, ctx: ApplicationContextT, coro): # TODO: Maybe Typehint coro & return type - +def hooked_wrapped_callback(command, ctx, coro): @functools.wraps(coro) async def wrapped(arg): try: @@ -143,26 +103,13 @@ async def wrapped(arg): await command._max_concurrency.release(ctx) await command.call_after_hooks(ctx) return ret - return wrapped - class _BaseCommand: __slots__ = () -# finished class ApplicationCommand(_BaseCommand, Generic[CogT, P, T]): - __original_kwargs__: Dict[str, Any] - cog: Optional[Cog] = None - name: str - description: str - callback: HookT - checks: List[Callable[[ApplicationContext], Awaitable[bool]]] - on_error: Callable[[ApplicationContext, Exception], Awaitable[None]] - _before_invoke: Optional[Callable[[ApplicationContext], Awaitable[None]]] - _after_invoke: Optional[Callable[[ApplicationContext], Awaitable[None]]] - default_permission: bool - permissions: List[Permission] + cog = None def __init__(self, func: Callable, **kwargs) -> None: from ..ext.commands.cooldowns import CooldownMapping, BucketType, MaxConcurrency @@ -187,10 +134,10 @@ def __init__(self, func: Callable, **kwargs) -> None: self._max_concurrency: Optional[MaxConcurrency] = max_concurrency - def __repr__(self) -> str: + def __repr__(self): return f"" - def __eq__(self, other: Any) -> bool: + def __eq__(self, other) -> bool: if hasattr(self, "id") and hasattr(other, "id"): check = self.id == other.id else: @@ -204,7 +151,7 @@ def __eq__(self, other: Any) -> bool: and check ) - async def __call__(self, ctx: ApplicationContext, *args: P.args, **kwargs: P.kwargs) -> T: + async def __call__(self, ctx, *args, **kwargs): """|coro| Calls the command's callback. @@ -212,7 +159,7 @@ async def __call__(self, ctx: ApplicationContext, *args: P.args, **kwargs: P.kwa convert the arguments beforehand, so take care to pass the correct arguments in. """ - await self.callback(ctx, *args, **kwargs) + return await self.callback(ctx, *args, **kwargs) def _prepare_cooldowns(self, ctx: ApplicationContext): if self._buckets.valid: @@ -348,10 +295,10 @@ async def dispatch_error(self, ctx: ApplicationContext, error: Exception) -> Non finally: ctx.bot.dispatch('application_command_error', ctx, error) - def _get_signature_parameters(self) -> OrderedDict[str, inspect.Parameter]: + def _get_signature_parameters(self): return OrderedDict(inspect.signature(self.callback).parameters) - def error(self, coro: ErrorT) -> ErrorT: + def error(self, coro): """A decorator that registers a coroutine as a local error handler. A local error handler is an :func:`.on_command_error` event limited to @@ -380,7 +327,7 @@ def has_error_handler(self) -> bool: """ return hasattr(self, 'on_error') - def before_invoke(self, coro: HookT) -> HookT: + def before_invoke(self, coro): """A decorator that registers a coroutine as a pre-invoke hook. A pre-invoke hook is called directly before the command is called. This makes it a useful function to set up database @@ -402,7 +349,7 @@ def before_invoke(self, coro: HookT) -> HookT: self._before_invoke = coro return coro - def after_invoke(self, coro: HookT) -> HookT: + def after_invoke(self, coro): """A decorator that registers a coroutine as a post-invoke hook. A post-invoke hook is called directly after the command is called. This makes it a useful function to clean-up database @@ -506,7 +453,6 @@ def qualified_name(self) -> str: def _set_cog(self, cog): self.cog = cog - class SlashCommand(ApplicationCommand): r"""A class that implements the protocol for a slash command. @@ -552,39 +498,16 @@ class SlashCommand(ApplicationCommand): .. versionadded:: 2.0 """ - type: int = 1 + type = 1 - def __new__(cls: Type[ApplicationCommandT], *args: Any, **kwargs: Any) -> ApplicationCommandT: + def __new__(cls, *args, **kwargs) -> SlashCommand: self = super().__new__(cls) self.__original_kwargs__ = kwargs.copy() return self - @overload - def __init__( - self, - func: Union[ - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]], - Callable[Concatenate[ApplicationContextT, P], Coro[T]] - ], - *, - name: Optional[str] = None, - description: Optional[str] = None, - guild_ids: Optional[List[int]] = None, - parent: Optional[SlashCommandGroup] = None, - checks: Optional[List[Callable[[ApplicationContext], Awaitable[bool]]]] = None, - default_permission: bool = None, - permissions: Optional[List[Permission]] = None, - ) -> None: - ... - - def __init__( - self, - func: Callable[[ApplicationContext, ...], Awaitable[None]], - **kwargs: Any - ) -> None: + def __init__(self, func: Callable, *args, **kwargs) -> None: super().__init__(func, **kwargs) - if not asyncio.iscoroutinefunction(func): raise TypeError("Callback must be a coroutine.") self.callback = func @@ -594,7 +517,7 @@ def __init__( name = kwargs.get("name") or func.__name__ validate_chat_input_name(name) self.name: str = name - self.id = None # TODO: typehint usage? + self.id = None description = kwargs.get("description") or ( inspect.cleandoc(func.__doc__).splitlines()[0] @@ -603,10 +526,10 @@ def __init__( ) validate_chat_input_description(description) self.description: str = description - self.parent: SlashCommandGroup = kwargs.get('parent') + self.parent = kwargs.get('parent') self.attached_to_group: bool = False - self.cog: Optional[Cog] = None + self.cog = None params = self._get_signature_parameters() if (kwop := kwargs.get('options', None)): @@ -631,7 +554,10 @@ def __init__( if self.permissions and self.default_permission: self.default_permission = False - def _parse_options(self, params: OrderedDict[str, inspect.Parameter]) -> List[Option]: + + def _parse_options(self, params) -> List[Option]: + final_options = [] + if list(params.items())[0][0] == "self": temp = list(params.items()) temp.pop(0) @@ -685,6 +611,7 @@ def _parse_options(self, params: OrderedDict[str, inspect.Parameter]) -> List[Op return final_options + def _match_option_param_names(self, params, options): if list(params.items())[0][0] == "self": temp = list(params.items()) @@ -730,20 +657,20 @@ def _match_option_param_names(self, params, options): return options - def _is_typing_union(self, annotation: Any) -> bool: # TODO: Typehint annotation + def _is_typing_union(self, annotation): return ( - getattr(annotation, '__origin__', None) is Union - or type(annotation) is getattr(types, "UnionType", Union) - ) # type: ignore + getattr(annotation, '__origin__', None) is Union + or type(annotation) is getattr(types, "UnionType", Union) + ) # type: ignore - def _is_typing_optional(self, annotation: Any) -> bool: # TODO: Typehint annotation + def _is_typing_optional(self, annotation): return self._is_typing_union(annotation) and type(None) in annotation.__args__ # type: ignore @property def is_subcommand(self) -> bool: return self.parent is not None - def to_dict(self) -> CreateApplicationCommand: + def to_dict(self) -> Dict: as_dict = { "name": self.name, "description": self.description, @@ -755,9 +682,6 @@ def to_dict(self) -> CreateApplicationCommand: return as_dict - # NOTE: while checking conflicts (for master -> core/typing) - # __eq__ seems to have been removed from master?? - async def _invoke(self, ctx: ApplicationContext) -> None: # TODO: Parse the args better kwargs = {} @@ -767,9 +691,9 @@ async def _invoke(self, ctx: ApplicationContext) -> None: # Checks if input_type is user, role or channel if ( - SlashCommandOptionType.user.value - <= op.input_type.value - <= SlashCommandOptionType.role.value + SlashCommandOptionType.user.value + <= op.input_type.value + <= SlashCommandOptionType.role.value ): if ctx.guild is None and op.input_type.name == "user": _data = ctx.interaction.data["resolved"]["users"][arg] @@ -831,7 +755,8 @@ async def invoke_autocomplete_callback(self, ctx: AutocompleteContext): ][:25] return await ctx.interaction.response.send_autocomplete_result(choices=choices) - def copy(self) -> "SlashCommand": + + def copy(self): """Creates a copy of this command. Returns @@ -842,14 +767,14 @@ def copy(self) -> "SlashCommand": ret = self.__class__(self.callback, **self.__original_kwargs__) return self._ensure_assignment_on_copy(ret) - def _ensure_assignment_on_copy(self, other: "SlashCommand") -> "SlashCommand": + def _ensure_assignment_on_copy(self, other): other._before_invoke = self._before_invoke other._after_invoke = self._after_invoke if self.checks != other.checks: other.checks = self.checks.copy() - # if self._buckets.valid and not other._buckets.valid: + #if self._buckets.valid and not other._buckets.valid: # other._buckets = self._buckets.copy() - # if self._max_concurrency != other._max_concurrency: + #if self._max_concurrency != other._max_concurrency: # # _max_concurrency won't be None at this point # other._max_concurrency = self._max_concurrency.copy() # type: ignore @@ -859,7 +784,7 @@ def _ensure_assignment_on_copy(self, other: "SlashCommand") -> "SlashCommand": pass return other - def _update_copy(self, kwargs: Dict[str, Any]) -> "SlashCommand": + def _update_copy(self, kwargs: Dict[str, Any]): if kwargs: kw = kwargs.copy() kw.update(self.__original_kwargs__) @@ -869,127 +794,7 @@ def _update_copy(self, kwargs: Dict[str, Any]) -> "SlashCommand": return self.copy() -channel_type_map = { - 'TextChannel': ChannelType.text, - 'VoiceChannel': ChannelType.voice, - 'StageChannel': ChannelType.stage_voice, - 'CategoryChannel': ChannelType.category -} - - -class Option: - - @overload - def __int__( - self, - input_type: Any, - /, - description: str, - *, - name: Optional[str] = None, - channel_type: Optional[ChannelType] = None, - required: bool = None, - default: Any = None, - min_value: Optional[int] = None, - max_value: Optional[int] = None, - autocomplete: Optional[Callable[[AutocompleteContext], Awaitable[Any]]] = None, - ) -> None: - ... - - def __init__( - self, input_type: Any, /, description: str = None, **kwargs - ) -> None: - self.name: Optional[str] = kwargs.pop("name", None) - self.description: str = description or "No description provided" - self._converter = None - self.channel_types: List[ChannelType] = kwargs.pop("channel_types", []) - if not isinstance(input_type, SlashCommandOptionType): - if hasattr(input_type, "convert"): - self._converter = input_type - input_type = SlashCommandOptionType.string - else: - _type = SlashCommandOptionType.from_datatype(input_type) - if _type == SlashCommandOptionType.channel: - if not isinstance(input_type, tuple): - input_type = (input_type,) - for i in input_type: - if i.__name__ == 'GuildChannel': - continue - - channel_type = channel_type_map[i.__name__] - self.channel_types.append(channel_type) - input_type = _type - self.input_type: Any = input_type - self.required: bool = kwargs.pop("required", True) - self.choices: List[OptionChoice] = [ - o if isinstance(o, OptionChoice) else OptionChoice(o) - for o in kwargs.pop("choices", list()) - ] - self.default: Any = kwargs.pop("default", None) - if self.input_type == SlashCommandOptionType.integer: - minmax_types = (int, type(None)) - elif self.input_type == SlashCommandOptionType.number: - minmax_types = (int, float, type(None)) - else: - minmax_types = (type(None),) - minmax_typehint = Optional[Union[minmax_types]] # type: ignore - - self.min_value: minmax_typehint = kwargs.pop("min_value", None) - self.max_value: minmax_typehint = kwargs.pop("max_value", None) - - if not (isinstance(self.min_value, minmax_types) or self.min_value is None): - raise TypeError(f"Expected {minmax_typehint} for min_value, got \"{type(self.min_value).__name__}\"") - if not (isinstance(self.max_value, minmax_types) or self.min_value is None): - raise TypeError(f"Expected {minmax_typehint} for max_value, got \"{type(self.max_value).__name__}\"") - - self.autocomplete: Callable[[AutocompleteContext], Awaitable[List[Union[OptionChoice, str]]]] \ - = kwargs.pop("autocomplete", None) - - def to_dict(self) -> ApplicationCommandOptionData: - as_dict = { - "type": self.input_type.value, - "name": self.name, - "description": self.description, - "required": self.required, - "choices": [c.to_dict() for c in self.choices], - "autocomplete": bool(self.autocomplete) - } - if self.channel_types: - as_dict["channel_types"] = [t.value for t in self.channel_types] - if self.min_value is not None: - as_dict["min_value"] = self.min_value - if self.max_value is not None: - as_dict["max_value"] = self.max_value - - return as_dict - - def __repr__(self) -> str: - return f"" - - -class OptionChoice: - def __init__(self, name: str, value: Optional[Union[str, int, float]] = None): - self.name: str = name - self.value: Union[str, int, float] = value or name - - def to_dict(self) -> ApplicationCommandOptionChoice: - return {"name": self.name, "value": self.value} - - -def option(name: str, option_type=None, **kwargs: Any): # TODO: Typehint everything - """A decorator that can be used instead of type hinting Option""" - - def decor(func): - nonlocal option_type - option_type = option_type or func.__annotations__.get(name, str) - func.__annotations__[name] = Option(type, **kwargs) - return func - - return decor - - -# finished -class SlashCommandGroup(ApplicationCommand, Option): +class SlashCommandGroup(ApplicationCommand): r"""A class that implements the protocol for a slash command group. These can be created manually, but they should be created via the @@ -1018,9 +823,9 @@ class SlashCommandGroup(ApplicationCommand, Option): :exc:`.CheckFailure` exception is raised to the :func:`.on_application_command_error` event. """ - type: int = 1 + type = 1 - def __new__(cls: ApplicationCommandT, *args: Any, **kwargs: Any) -> ApplicationCommandT: + def __new__(cls, *args, **kwargs) -> SlashCommandGroup: self = super().__new__(cls) self.__original_kwargs__ = kwargs.copy() @@ -1042,26 +847,13 @@ def __new__(cls: ApplicationCommandT, *args: Any, **kwargs: Any) -> ApplicationC return self - @overload def __init__( - self, - name: str, - description: str, - guild_ids: Optional[List[int]] = None, - parent: Optional[SlashCommandGroup] = None, - *, - default_permission: Optional[bool] = None, - permissions: Optional[List[Permission]] = None, - ): - ... - - def __init__( - self, - name: str, - description: str, - guild_ids: Optional[List[int]] = None, - parent: Optional[SlashCommandGroup] = None, - **kwargs + self, + name: str, + description: str, + guild_ids: Optional[List[int]] = None, + parent: Optional[SlashCommandGroup] = None, + **kwargs ) -> None: validate_chat_input_name(name) validate_chat_input_description(description) @@ -1069,8 +861,8 @@ def __init__( self.description = description self.input_type = SlashCommandOptionType.sub_command_group self.subcommands: List[Union[SlashCommand, SlashCommandGroup]] = self.__initial_commands__ - self.guild_ids: Optional[List[int]] = guild_ids - self.parent: Optional[SlashCommandGroup] = parent + self.guild_ids = guild_ids + self.parent = parent self.checks = [] self._before_invoke = None @@ -1078,12 +870,12 @@ def __init__( self.cog = None # Permissions - self.default_permission: bool = kwargs.get("default_permission", True) + self.default_permission = kwargs.get("default_permission", True) self.permissions: List[CommandPermission] = kwargs.get("permissions", []) if self.permissions and self.default_permission: self.default_permission = False - def to_dict(self) -> CreateApplicationCommand: + def to_dict(self) -> Dict: as_dict = { "name": self.name, "description": self.description, @@ -1223,7 +1015,6 @@ def _set_cog(self, cog): subcommand._set_cog(cog) -# finished class ContextMenuCommand(ApplicationCommand): r"""A class that implements the protocol for context menu commands. @@ -1261,14 +1052,13 @@ class ContextMenuCommand(ApplicationCommand): .. versionadded:: 2.0 """ - - def __new__(cls: ApplicationCommandT, *args: Any, **kwargs: Any) -> ApplicationCommandT: + def __new__(cls, *args, **kwargs) -> ContextMenuCommand: self = super().__new__(cls) self.__original_kwargs__ = kwargs.copy() return self - def __init__(self, func: Callable, *args: Any, **kwargs: Any) -> None: + def __init__(self, func: Callable, *args, **kwargs) -> None: super().__init__(func, **kwargs) if not asyncio.iscoroutinefunction(func): raise TypeError("Callback must be a coroutine.") @@ -1305,7 +1095,7 @@ def __init__(self, func: Callable, *args: Any, **kwargs: Any) -> None: # Context Menu commands can't have parents self.parent = None - def validate_parameters(self) -> None: + def validate_parameters(self): params = self._get_signature_parameters() if list(params.items())[0][0] == "self": temp = list(params.items()) @@ -1340,14 +1130,13 @@ def validate_parameters(self) -> None: pass @property - def qualified_name(self) -> str: + def qualified_name(self): return self.name - def to_dict(self) -> CreateApplicationCommand: + def to_dict(self) -> Dict[str, Union[str, int]]: return {"name": self.name, "description": self.description, "type": self.type, "default_permission": self.default_permission} -# finished class UserCommand(ContextMenuCommand): r"""A class that implements the protocol for user context menu commands. @@ -1374,7 +1163,7 @@ class UserCommand(ContextMenuCommand): """ type = 2 - def __new__(cls: Type[ApplicationCommandT], *args: Any, **kwargs: Any) -> ApplicationCommandT: + def __new__(cls, *args, **kwargs) -> UserCommand: self = super().__new__(cls) self.__original_kwargs__ = kwargs.copy() @@ -1408,7 +1197,7 @@ async def _invoke(self, ctx: ApplicationContext) -> None: else: await self.callback(ctx, target) - def copy(self: ApplicationCommandT) -> ApplicationCommandT: + def copy(self): """Creates a copy of this command. Returns @@ -1419,14 +1208,14 @@ def copy(self: ApplicationCommandT) -> ApplicationCommandT: ret = self.__class__(self.callback, **self.__original_kwargs__) return self._ensure_assignment_on_copy(ret) - def _ensure_assignment_on_copy(self, other: ApplicationCommandT) -> ApplicationCommandT: + def _ensure_assignment_on_copy(self, other): other._before_invoke = self._before_invoke other._after_invoke = self._after_invoke if self.checks != other.checks: other.checks = self.checks.copy() - # if self._buckets.valid and not other._buckets.valid: + #if self._buckets.valid and not other._buckets.valid: # other._buckets = self._buckets.copy() - # if self._max_concurrency != other._max_concurrency: + #if self._max_concurrency != other._max_concurrency: # # _max_concurrency won't be None at this point # other._max_concurrency = self._max_concurrency.copy() # type: ignore @@ -1436,7 +1225,7 @@ def _ensure_assignment_on_copy(self, other: ApplicationCommandT) -> ApplicationC pass return other - def _update_copy(self: ApplicationCommandT, kwargs: Dict[str, Any]) -> ApplicationCommandT: + def _update_copy(self, kwargs: Dict[str, Any]): if kwargs: kw = kwargs.copy() kw.update(self.__original_kwargs__) @@ -1446,7 +1235,6 @@ def _update_copy(self: ApplicationCommandT, kwargs: Dict[str, Any]) -> Applicati return self.copy() -# finished class MessageCommand(ContextMenuCommand): r"""A class that implements the protocol for message context menu commands. @@ -1473,13 +1261,13 @@ class MessageCommand(ContextMenuCommand): """ type = 3 - def __new__(cls: Type[ApplicationCommandT], *args: Any, **kwargs: Any) -> ApplicationCommandT: + def __new__(cls, *args, **kwargs) -> MessageCommand: self = super().__new__(cls) self.__original_kwargs__ = kwargs.copy() return self - async def _invoke(self, ctx: ApplicationContext) -> None: + async def _invoke(self, ctx: ApplicationContext): _data = ctx.interaction.data["resolved"]["messages"] for i, v in _data.items(): v["id"] = int(i) @@ -1498,7 +1286,7 @@ async def _invoke(self, ctx: ApplicationContext) -> None: else: await self.callback(ctx, target) - def copy(self: ApplicationCommandT) -> ApplicationCommandT: + def copy(self): """Creates a copy of this command. Returns @@ -1509,14 +1297,14 @@ def copy(self: ApplicationCommandT) -> ApplicationCommandT: ret = self.__class__(self.callback, **self.__original_kwargs__) return self._ensure_assignment_on_copy(ret) - def _ensure_assignment_on_copy(self, other: ApplicationCommandT) -> ApplicationCommandT: + def _ensure_assignment_on_copy(self, other): other._before_invoke = self._before_invoke other._after_invoke = self._after_invoke if self.checks != other.checks: other.checks = self.checks.copy() - # if self._buckets.valid and not other._buckets.valid: + #if self._buckets.valid and not other._buckets.valid: # other._buckets = self._buckets.copy() - # if self._max_concurrency != other._max_concurrency: + #if self._max_concurrency != other._max_concurrency: # # _max_concurrency won't be None at this point # other._max_concurrency = self._max_concurrency.copy() # type: ignore @@ -1526,7 +1314,7 @@ def _ensure_assignment_on_copy(self, other: ApplicationCommandT) -> ApplicationC pass return other - def _update_copy(self: ApplicationCommandT, kwargs: Dict[str, Any]) -> ApplicationCommandT: + def _update_copy(self, kwargs: Dict[str, Any]): if kwargs: kw = kwargs.copy() kw.update(self.__original_kwargs__) @@ -1535,42 +1323,7 @@ def _update_copy(self: ApplicationCommandT, kwargs: Dict[str, Any]) -> Applicati else: return self.copy() - -@overload -def slash_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommand[CogT, P, T]]: - ... - - -@overload -def slash_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommandT]: - ... - - -def slash_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[Any]] - ] - ], Union[ApplicationCommand[CogT, P, T], ApplicationCommandT]]: +def slash_command(**kwargs): """Decorator for slash commands that invokes :func:`application_command`. .. versionadded:: 2.0 Returns @@ -1580,42 +1333,7 @@ def slash_command( """ return application_command(cls=SlashCommand, **kwargs) - -@overload -def user_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommand[CogT, P, T]]: - ... - - -@overload -def user_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommandT]: - ... - - -def user_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[Any]] - ] - ], Union[ApplicationCommand[CogT, P, T], ApplicationCommandT]]: +def user_command(**kwargs): """Decorator for user commands that invokes :func:`application_command`. .. versionadded:: 2.0 Returns @@ -1625,42 +1343,7 @@ def user_command( """ return application_command(cls=UserCommand, **kwargs) - -@overload -def message_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommand[CogT, P, T]]: - ... - - -@overload -def message_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommandT]: - ... - - -def message_command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[Any]] - ] - ], Union[ApplicationCommand[CogT, P, T], ApplicationCommandT]]: +def message_command(**kwargs): """Decorator for message commands that invokes :func:`application_command`. .. versionadded:: 2.0 Returns @@ -1670,45 +1353,7 @@ def message_command( """ return application_command(cls=MessageCommand, **kwargs) - -@overload -def application_command( - cls: Type[ApplicationCommand] = SlashCommand, - **attrs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[Any]] - ] - ], ApplicationCommand[CogT, P, T]]: - ... - - -@overload -def application_command( - cls: Type[ApplicationCommand] = SlashCommand, - **attrs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[Any]] - ] - ], ApplicationCommandT]: - ... - - -def application_command( - cls: Type[ApplicationCommand] = SlashCommand, - **attrs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[Any]] - ] - ], Union[ApplicationCommand[CogT, P, T], ApplicationCommandT]]: +def application_command(cls=SlashCommand, **attrs): """A decorator that transforms a function into an :class:`.ApplicationCommand`. More specifically, usually one of :class:`.SlashCommand`, :class:`.UserCommand`, or :class:`.MessageCommand`. The exact class depends on the ``cls`` parameter. @@ -1732,12 +1377,7 @@ def application_command( If the function is not a coroutine or is already a command. """ - def decorator( - func: Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[Any]] - ] - ) -> ApplicationCommandT: + def decorator(func: Callable) -> cls: if isinstance(func, ApplicationCommand): func = func.callback elif not callable(func): @@ -1748,42 +1388,7 @@ def decorator( return decorator - -@overload -def command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommand[CogT, P, T]]: - ... - - -@overload -def command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], ApplicationCommandT]: - ... - - -def command( - **kwargs: Any -) -> Callable[ - [ - Union[ - Callable[Concatenate[ApplicationContextT, P], Coro[Any]], - Callable[Concatenate[CogT, ApplicationContextT, P], Coro[T]] - ] - ], Union[ApplicationCommand[CogT, P, T], ApplicationCommandT]]: +def command(**kwargs): """There is an alias for :meth:`application_command`. .. note:: This decorator is overridden by :func:`commands.command`. @@ -1797,10 +1402,10 @@ def command( docs = "https://discord.com/developers/docs" -# NOTE: what is this? + # Validation -def validate_chat_input_name(name: Any) -> None: +def validate_chat_input_name(name: Any): # Must meet the regex ^[\w-]{1,32}$ if not isinstance(name, str): raise TypeError(f"Chat input command names and options must be of type str. Received {name}") @@ -1818,7 +1423,7 @@ def validate_chat_input_name(name: Any) -> None: raise ValidationError(f"Chat input command names and options must be lowercase. Received {name}") -def validate_chat_input_description(description: Any) -> None: +def validate_chat_input_description(description: Any): if not isinstance(description, str): raise TypeError(f"Command description must be of type str. Received {description}") if not 1 <= len(description) <= 100: diff --git a/discord/commands/errors.py b/discord/commands/errors.py index 794c34d82b..5a4b47eec8 100644 --- a/discord/commands/errors.py +++ b/discord/commands/errors.py @@ -1,64 +1,64 @@ -""" -The MIT License (MIT) - -Copyright (c) 2021-present Pycord Development - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -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 -DEALINGS IN THE SOFTWARE. -""" - -from ..errors import DiscordException - -__all__ = ( - "ApplicationCommandError", - "CheckFailure", - "ApplicationCommandInvokeError", -) - -class ApplicationCommandError(DiscordException): - r"""The base exception type for all application command related errors. - - This inherits from :exc:`discord.DiscordException`. - - This exception and exceptions inherited from it are handled - in a special way as they are caught and passed into a special event - from :class:`.Bot`\, :func:`.on_command_error`. - """ - pass - -class CheckFailure(ApplicationCommandError): - """Exception raised when the predicates in :attr:`.Command.checks` have failed. - - This inherits from :exc:`ApplicationCommandError` - """ - pass - -class ApplicationCommandInvokeError(ApplicationCommandError): - """Exception raised when the command being invoked raised an exception. - - This inherits from :exc:`ApplicationCommandError` - - Attributes - ----------- - original: :exc:`Exception` - The original exception that was raised. You can also get this via - the ``__cause__`` attribute. - """ - def __init__(self, e: Exception) -> None: - self.original: Exception = e - super().__init__(f'Application Command raised an exception: {e.__class__.__name__}: {e}') +""" +The MIT License (MIT) + +Copyright (c) 2021-present Pycord Development + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +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 +DEALINGS IN THE SOFTWARE. +""" + +from ..errors import DiscordException + +__all__ = ( + "ApplicationCommandError", + "CheckFailure", + "ApplicationCommandInvokeError", +) + +class ApplicationCommandError(DiscordException): + r"""The base exception type for all application command related errors. + + This inherits from :exc:`discord.DiscordException`. + + This exception and exceptions inherited from it are handled + in a special way as they are caught and passed into a special event + from :class:`.Bot`\, :func:`.on_command_error`. + """ + pass + +class CheckFailure(ApplicationCommandError): + """Exception raised when the predicates in :attr:`.Command.checks` have failed. + + This inherits from :exc:`ApplicationCommandError` + """ + pass + +class ApplicationCommandInvokeError(ApplicationCommandError): + """Exception raised when the command being invoked raised an exception. + + This inherits from :exc:`ApplicationCommandError` + + Attributes + ----------- + original: :exc:`Exception` + The original exception that was raised. You can also get this via + the ``__cause__`` attribute. + """ + def __init__(self, e: Exception) -> None: + self.original: Exception = e + super().__init__(f'Application Command raised an exception: {e.__class__.__name__}: {e}') diff --git a/discord/commands/permissions.py b/discord/commands/permissions.py index d143f5b02b..7cc4f6b4da 100644 --- a/discord/commands/permissions.py +++ b/discord/commands/permissions.py @@ -1,248 +1,228 @@ -""" -The MIT License (MIT) - -Copyright (c) 2015-2021 Rapptz -Copyright (c) 2021-present Pycord Development - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -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 -DEALINGS IN THE SOFTWARE. -""" - -from __future__ import annotations - -from typing import Union, Callable, Optional, TYPE_CHECKING - -if TYPE_CHECKING: - from ..types.interactions import ( - ApplicationCommandPermissions, - ApplicationCommandPermissionType - ) - from ..types.snowflake import Snowflake - -__all__ = ( - "CommandPermission", - "has_role", - "has_any_role", - "is_user", - "is_owner", - "permission", -) - - -class CommandPermission: - """The class used in the application command decorators - to hash permission data into a dictionary using the - :meth:`to_dict` method to be sent to the discord API later on. - - .. versionadded:: 2.0 - - Attributes - ----------- - id: Union[:class:`int`, :class:`str`] - A string or integer that represents or helps get - the id of the user or role that the permission is tied to. - type: :class:`int` - An integer representing the type of the permission. - permission: :class:`bool` - A boolean representing the permission's value. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - def __init__(self, perm_id: Snowflake, perm_type: int, permission: bool = True, guild_id: Optional[int] = None): - self.id: Snowflake = perm_id - self.type: ApplicationCommandPermissionType = perm_type - self.permission: bool = permission - self.guild_id: Optional[int] = guild_id - - def to_dict(self) -> ApplicationCommandPermissions: - return {"id": self.id, "type": self.type, "permission": self.permission} - - -def permission(role_id: int = None, user_id: int = None, permission: bool = True, guild_id: int = None): - """The method used to specify application command permissions - for specific users or roles using their id. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 - - Parameters - ----------- - role_id: :class:`int` - An integer which represents the id of the role that the - permission may be tied to. - user_id: :class:`int` - An integer which represents the id of the user that the - permission may be tied to. - permission: :class:`bool` - A boolean representing the permission's value. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - if role_id is not None: - app_cmd_perm = CommandPermission(role_id, 1, permission, guild_id) - elif user_id is not None: - app_cmd_perm = CommandPermission(user_id, 2, permission, guild_id) - else: - raise ValueError("role_id or user_id must be specified!") - - # Create __app_cmd_perms__ - if not hasattr(func, '__app_cmd_perms__'): - func.__app_cmd_perms__ = [] - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func - - return decorator - - -def has_role(item: Union[int, str], guild_id: int = None): - """The method used to specify application command role restrictions. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 - - Parameters - ----------- - item: Union[:class:`int`, :class:`str`] - An integer or string that represent the id or name of the role - that the permission is tied to. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, '__app_cmd_perms__'): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - app_cmd_perm = CommandPermission(item, 1, True, guild_id) # {"id": item, "type": 1, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func - - return decorator - - -def has_any_role(*items: Union[int, str], guild_id: int = None): - """The method used to specify multiple application command role restrictions, - The application command runs if the invoker has **any** of the specified roles. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 - - Parameters - ----------- - *items: Union[:class:`int`, :class:`str`] - The integers or strings that represent the ids or names of the roles - that the permission is tied to. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, '__app_cmd_perms__'): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - for item in items: - app_cmd_perm = CommandPermission(item, 1, True, guild_id) # {"id": item, "type": 1, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func - - return decorator - - -def is_user(user: int, guild_id: int = None): - """The method used to specify application command user restrictions. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 - - Parameters - ----------- - user: :class:`int` - An integer that represent the id of the user that the permission is tied to. - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, '__app_cmd_perms__'): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - app_cmd_perm = CommandPermission(user, 2, True, guild_id) # {"id": user, "type": 2, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func - - return decorator - - -def is_owner(guild_id: int = None): - """The method used to limit application commands exclusively - to the owner of the bot. - - This method is meant to be used as a decorator. - - .. versionadded:: 2.0 - - Parameters - ----------- - guild_id: :class:`int` - The integer which represents the id of the guild that the - permission may be tied to. - """ - - def decorator(func: Callable): - # Create __app_cmd_perms__ - if not hasattr(func, '__app_cmd_perms__'): - func.__app_cmd_perms__ = [] - - # Permissions (Will Convert ID later in register_commands if needed) - app_cmd_perm = CommandPermission("owner", 2, True, guild_id) # {"id": "owner", "type": 2, "permission": True} - - # Append - func.__app_cmd_perms__.append(app_cmd_perm) - - return func - - return decorator +""" +The MIT License (MIT) + +Copyright (c) 2015-2021 Rapptz +Copyright (c) 2021-present Pycord Development + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +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 +DEALINGS IN THE SOFTWARE. +""" + +from typing import Union, Dict, Callable + +__all__ = ( + "CommandPermission", + "has_role", + "has_any_role", + "is_user", + "is_owner", + "permission", +) + +class CommandPermission: + """The class used in the application command decorators + to hash permission data into a dictionary using the + :meth:`to_dict` method to be sent to the discord API later on. + + .. versionadded:: 2.0 + + Attributes + ----------- + id: Union[:class:`int`, :class:`str`] + A string or integer that represents or helps get + the id of the user or role that the permission is tied to. + type: :class:`int` + An integer representing the type of the permission. + permission: :class:`bool` + A boolean representing the permission's value. + guild_id: :class:`int` + The integer which represents the id of the guild that the + permission may be tied to. + """ + def __init__(self, id: Union[int, str], type: int, permission: bool = True, guild_id: int = None): + self.id = id + self.type = type + self.permission = permission + self.guild_id = guild_id + + def to_dict(self) -> Dict[str, Union[int, bool]]: + return {"id": self.id, "type": self.type, "permission": self.permission} + +def permission(role_id: int = None, user_id: int = None, permission: bool = True, guild_id: int = None): + """The method used to specify application command permissions + for specific users or roles using their id. + + This method is meant to be used as a decorator. + + .. versionadded:: 2.0 + + Parameters + ----------- + role_id: :class:`int` + An integer which represents the id of the role that the + permission may be tied to. + user_id: :class:`int` + An integer which represents the id of the user that the + permission may be tied to. + permission: :class:`bool` + A boolean representing the permission's value. + guild_id: :class:`int` + The integer which represents the id of the guild that the + permission may be tied to. + """ + def decorator(func: Callable): + if not role_id is None: + app_cmd_perm = CommandPermission(role_id, 1, permission, guild_id) + elif not user_id is None: + app_cmd_perm = CommandPermission(user_id, 2, permission, guild_id) + else: + raise ValueError("role_id or user_id must be specified!") + + # Create __app_cmd_perms__ + if not hasattr(func, '__app_cmd_perms__'): + func.__app_cmd_perms__ = [] + + # Append + func.__app_cmd_perms__.append(app_cmd_perm) + + return func + + return decorator + +def has_role(item: Union[int, str], guild_id: int = None): + """The method used to specify application command role restrictions. + + This method is meant to be used as a decorator. + + .. versionadded:: 2.0 + + Parameters + ----------- + item: Union[:class:`int`, :class:`str`] + An integer or string that represent the id or name of the role + that the permission is tied to. + guild_id: :class:`int` + The integer which represents the id of the guild that the + permission may be tied to. + """ + def decorator(func: Callable): + # Create __app_cmd_perms__ + if not hasattr(func, '__app_cmd_perms__'): + func.__app_cmd_perms__ = [] + + # Permissions (Will Convert ID later in register_commands if needed) + app_cmd_perm = CommandPermission(item, 1, True, guild_id) #{"id": item, "type": 1, "permission": True} + + # Append + func.__app_cmd_perms__.append(app_cmd_perm) + + return func + + return decorator + +def has_any_role(*items: Union[int, str], guild_id: int = None): + """The method used to specify multiple application command role restrictions, + The application command runs if the invoker has **any** of the specified roles. + + This method is meant to be used as a decorator. + + .. versionadded:: 2.0 + + Parameters + ----------- + *items: Union[:class:`int`, :class:`str`] + The integers or strings that represent the ids or names of the roles + that the permission is tied to. + guild_id: :class:`int` + The integer which represents the id of the guild that the + permission may be tied to. + """ + def decorator(func: Callable): + # Create __app_cmd_perms__ + if not hasattr(func, '__app_cmd_perms__'): + func.__app_cmd_perms__ = [] + + # Permissions (Will Convert ID later in register_commands if needed) + for item in items: + app_cmd_perm = CommandPermission(item, 1, True, guild_id) #{"id": item, "type": 1, "permission": True} + + # Append + func.__app_cmd_perms__.append(app_cmd_perm) + + return func + + return decorator + +def is_user(user: int, guild_id: int = None): + """The method used to specify application command user restrictions. + + This method is meant to be used as a decorator. + + .. versionadded:: 2.0 + + Parameters + ----------- + user: :class:`int` + An integer that represent the id of the user that the permission is tied to. + guild_id: :class:`int` + The integer which represents the id of the guild that the + permission may be tied to. + """ + def decorator(func: Callable): + # Create __app_cmd_perms__ + if not hasattr(func, '__app_cmd_perms__'): + func.__app_cmd_perms__ = [] + + # Permissions (Will Convert ID later in register_commands if needed) + app_cmd_perm = CommandPermission(user, 2, True, guild_id) #{"id": user, "type": 2, "permission": True} + + # Append + func.__app_cmd_perms__.append(app_cmd_perm) + + return func + + return decorator + +def is_owner(guild_id: int = None): + """The method used to limit application commands exclusively + to the owner of the bot. + + This method is meant to be used as a decorator. + + .. versionadded:: 2.0 + + Parameters + ----------- + guild_id: :class:`int` + The integer which represents the id of the guild that the + permission may be tied to. + """ + def decorator(func: Callable): + # Create __app_cmd_perms__ + if not hasattr(func, '__app_cmd_perms__'): + func.__app_cmd_perms__ = [] + + # Permissions (Will Convert ID later in register_commands if needed) + app_cmd_perm = CommandPermission("owner", 2, True, guild_id) #{"id": "owner", "type": 2, "permission": True} + + # Append + func.__app_cmd_perms__.append(app_cmd_perm) + + return func + + return decorator diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index d49fd7c14d..57d5955be8 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -115,7 +115,7 @@ def _is_submodule(parent: str, child: str) -> bool: return parent == child or child.startswith(parent + ".") class _DefaultRepr: - def __repr__(self) -> str: + def __repr__(self): return '' _default = _DefaultRepr() diff --git a/discord/ext/commands/converter.py b/discord/ext/commands/converter.py index f1ac89003c..754e43267a 100644 --- a/discord/ext/commands/converter.py +++ b/discord/ext/commands/converter.py @@ -988,7 +988,7 @@ async def test(ctx, numbers: Greedy[int], reason: str): def __init__(self, *, converter: T): self.converter = converter - def __repr__(self) -> str: + def __repr__(self): converter = getattr(self.converter, '__name__', repr(self.converter)) return f'Greedy[{converter}]' diff --git a/discord/ext/commands/help.py b/discord/ext/commands/help.py index 458c0c55b8..163111f534 100644 --- a/discord/ext/commands/help.py +++ b/discord/ext/commands/help.py @@ -176,7 +176,7 @@ def pages(self): self.close_page() return self._pages - def __repr__(self) -> str: + def __repr__(self): fmt = '' return fmt.format(self) diff --git a/discord/ext/commands/view.py b/discord/ext/commands/view.py index 59076ca5bc..57d6439a5d 100644 --- a/discord/ext/commands/view.py +++ b/discord/ext/commands/view.py @@ -189,5 +189,5 @@ def get_quoted_word(self): result.append(current) - def __repr__(self) -> str: + def __repr__(self): return f'' diff --git a/discord/flags.py b/discord/flags.py index 10c3bedff9..40e527ce96 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -63,7 +63,7 @@ def __get__(self, instance: Optional[BF], owner: Type[BF]) -> Any: def __set__(self, instance: BF, value: bool) -> None: instance._set_flag(self.flag, value) - def __repr__(self) -> str: + def __repr__(self): return f'' diff --git a/discord/http.py b/discord/http.py index 99afd66986..cdefd1f5e4 100644 --- a/discord/http.py +++ b/discord/http.py @@ -1693,9 +1693,7 @@ def get_global_command( ) return self.request(r) - def upsert_global_command( - self, application_id: Snowflake, payload: interactions.CreateApplicationCommand - ) -> Response[interactions.ApplicationCommand]: + def upsert_global_command(self, application_id: Snowflake, payload) -> Response[interactions.ApplicationCommand]: r = Route('POST', '/applications/{application_id}/commands', application_id=application_id) return self.request(r, json=payload) @@ -1729,7 +1727,7 @@ def delete_global_command(self, application_id: Snowflake, command_id: Snowflake return self.request(r) def bulk_upsert_global_commands( - self, application_id: Snowflake, payload: List[interactions.CreateApplicationCommand] + self, application_id: Snowflake, payload ) -> Response[List[interactions.ApplicationCommand]]: r = Route('PUT', '/applications/{application_id}/commands', application_id=application_id) return self.request(r, json=payload) @@ -1766,7 +1764,7 @@ def upsert_guild_command( self, application_id: Snowflake, guild_id: Snowflake, - payload: interactions.CreateApplicationCommand, + payload: interactions.EditApplicationCommand, ) -> Response[interactions.ApplicationCommand]: r = Route( 'POST', @@ -1817,7 +1815,7 @@ def bulk_upsert_guild_commands( self, application_id: Snowflake, guild_id: Snowflake, - payload: List[interactions.CreateApplicationCommand], + payload: List[interactions.EditApplicationCommand], ) -> Response[List[interactions.ApplicationCommand]]: r = Route( 'PUT', @@ -1831,7 +1829,7 @@ def bulk_upsert_command_permissions( self, application_id: Snowflake, guild_id: Snowflake, - payload: List[interactions.BaseGuildApplicationCommandPermissions], + payload: List[interactions.EditApplicationCommand], ) -> Response[List[interactions.ApplicationCommand]]: r = Route( 'PUT', diff --git a/discord/integrations.py b/discord/integrations.py index 85de8df321..6a5f0f5929 100644 --- a/discord/integrations.py +++ b/discord/integrations.py @@ -115,7 +115,7 @@ def __init__(self, *, data: IntegrationPayload, guild: Guild) -> None: self._state = guild._state self._from_data(data) - def __repr__(self) -> str: + def __repr__(self): return f"<{self.__class__.__name__} id={self.id} name={self.name!r}>" def _from_data(self, data: IntegrationPayload) -> None: diff --git a/discord/mentions.py b/discord/mentions.py index 14156c064c..81f80f9223 100644 --- a/discord/mentions.py +++ b/discord/mentions.py @@ -36,10 +36,10 @@ class _FakeBool: - def __repr__(self) -> str: + def __repr__(self): return 'True' - def __eq__(self, other) -> bool: + def __eq__(self, other): return other is True def __bool__(self): diff --git a/discord/partial_emoji.py b/discord/partial_emoji.py index 4310e88635..2eef74d54c 100644 --- a/discord/partial_emoji.py +++ b/discord/partial_emoji.py @@ -173,7 +173,7 @@ def __str__(self) -> str: return f'' return f'<:{self.name}:{self.id}>' - def __repr__(self) -> str: + def __repr__(self): return f'<{self.__class__.__name__} animated={self.animated} name={self.name!r} id={self.id}>' def __eq__(self, other: Any) -> bool: diff --git a/discord/types/interactions.py b/discord/types/interactions.py index bce2797e72..716427494e 100644 --- a/discord/types/interactions.py +++ b/discord/types/interactions.py @@ -40,12 +40,9 @@ ApplicationCommandType = Literal[1, 2, 3] - class _ApplicationCommandOptional(TypedDict, total=False): - type: ApplicationCommandType - guild_id: Snowflake options: List[ApplicationCommandOption] - default_permission: bool + type: ApplicationCommandType class ApplicationCommand(_ApplicationCommandOptional): @@ -53,17 +50,11 @@ class ApplicationCommand(_ApplicationCommandOptional): application_id: Snowflake name: str description: str - version: Snowflake class _ApplicationCommandOptionOptional(TypedDict, total=False): - required: bool choices: List[ApplicationCommandOptionChoice] options: List[ApplicationCommandOption] - channel_types: List[ChannelType] - min_value: int - max_value: int - autocomplete: bool ApplicationCommandOptionType = Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] @@ -73,11 +64,12 @@ class ApplicationCommandOption(_ApplicationCommandOptionOptional): type: ApplicationCommandOptionType name: str description: str + required: bool class ApplicationCommandOptionChoice(TypedDict): name: str - value: Union[str, int, float] + value: Union[str, int] ApplicationCommandPermissionType = Literal[1, 2] @@ -102,7 +94,7 @@ class GuildApplicationCommandPermissions(PartialGuildApplicationCommandPermissio guild_id: Snowflake -InteractionType = Literal[1, 2, 3, 4] +InteractionType = Literal[1, 2, 3] class _ApplicationCommandInteractionDataOption(TypedDict): @@ -233,25 +225,12 @@ class MessageInteraction(TypedDict): user: User -class _CreateApplicationCommandOptional(TypedDict, total=False): - options: List[ApplicationCommandOption] - default_permission: bool - type: ApplicationCommandType - - -class CreateApplicationCommand(_CreateApplicationCommandOptional): - name: str - description: str - - class _EditApplicationCommandOptional(TypedDict, total=False): - name: str description: str - options: List[ApplicationCommandOption] + options: Optional[List[ApplicationCommandOption]] type: ApplicationCommandType - default_permission: bool class EditApplicationCommand(_EditApplicationCommandOptional): - pass - + name: str + default_permission: bool diff --git a/discord/ui/button.py b/discord/ui/button.py index 61efb61b27..032dd33344 100644 --- a/discord/ui/button.py +++ b/discord/ui/button.py @@ -25,7 +25,7 @@ from __future__ import annotations -from typing import Callable, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union, Dict +from typing import Callable, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union import inspect import os @@ -132,7 +132,7 @@ def style(self) -> ButtonStyle: return self._underlying.style @style.setter - def style(self, value: ButtonStyle) -> None: + def style(self, value: ButtonStyle): self._underlying.style = value @property @@ -144,7 +144,7 @@ def custom_id(self) -> Optional[str]: return self._underlying.custom_id @custom_id.setter - def custom_id(self, value: Optional[str]) -> None: + def custom_id(self, value: Optional[str]): if value is not None and not isinstance(value, str): raise TypeError('custom_id must be None or str') @@ -156,7 +156,7 @@ def url(self) -> Optional[str]: return self._underlying.url @url.setter - def url(self, value: Optional[str]) -> None: + def url(self, value: Optional[str]): if value is not None and not isinstance(value, str): raise TypeError('url must be None or str') self._underlying.url = value @@ -167,7 +167,7 @@ def disabled(self) -> bool: return self._underlying.disabled @disabled.setter - def disabled(self, value: bool) -> None: + def disabled(self, value: bool): self._underlying.disabled = bool(value) @property @@ -176,7 +176,7 @@ def label(self) -> Optional[str]: return self._underlying.label @label.setter - def label(self, value: Optional[str]) -> None: + def label(self, value: Optional[str]): self._underlying.label = str(value) if value is not None else value @property @@ -185,7 +185,7 @@ def emoji(self) -> Optional[PartialEmoji]: return self._underlying.emoji @emoji.setter - def emoji(self, value: Optional[Union[str, Emoji, PartialEmoji]]) -> None: + def emoji(self, value: Optional[Union[str, Emoji, PartialEmoji]]): # type: ignore if value is not None: if isinstance(value, str): self._underlying.emoji = PartialEmoji.from_str(value) @@ -212,8 +212,7 @@ def from_component(cls: Type[B], button: ButtonComponent) -> B: def type(self) -> ComponentType: return self._underlying.type - # TODO(ultrabear) What is the type signature of the dict returned? - def to_component_dict(self) -> Dict: + def to_component_dict(self): return self._underlying.to_dict() def is_dispatchable(self) -> bool: diff --git a/discord/ui/item.py b/discord/ui/item.py index c4cfa2c14a..1fb168a6f7 100644 --- a/discord/ui/item.py +++ b/discord/ui/item.py @@ -56,7 +56,7 @@ class Item(Generic[V]): __item_repr_attributes__: Tuple[str, ...] = ('row',) - def __init__(self) -> None: + def __init__(self): self._view: Optional[V] = None self._row: Optional[int] = None self._rendered_row: Optional[int] = None @@ -100,7 +100,7 @@ def row(self) -> Optional[int]: return self._row @row.setter - def row(self, value: Optional[int]) -> None: + def row(self, value: Optional[int]): if value is None: self._row = None elif 5 > value >= 0: @@ -117,7 +117,7 @@ def view(self) -> Optional[V]: """Optional[:class:`View`]: The underlying view for this item.""" return self._view - async def callback(self, interaction: Interaction) -> None: + async def callback(self, interaction: Interaction): """|coro| The callback associated with this UI item. diff --git a/discord/ui/select.py b/discord/ui/select.py index 7b8a3650af..0241a3ddde 100644 --- a/discord/ui/select.py +++ b/discord/ui/select.py @@ -130,7 +130,7 @@ def custom_id(self) -> str: return self._underlying.custom_id @custom_id.setter - def custom_id(self, value: str) -> None: + def custom_id(self, value: str): if not isinstance(value, str): raise TypeError('custom_id must be None or str') @@ -142,7 +142,7 @@ def placeholder(self) -> Optional[str]: return self._underlying.placeholder @placeholder.setter - def placeholder(self, value: Optional[str]) -> None: + def placeholder(self, value: Optional[str]): if value is not None and not isinstance(value, str): raise TypeError('placeholder must be None or str') @@ -154,7 +154,7 @@ def min_values(self) -> int: return self._underlying.min_values @min_values.setter - def min_values(self, value: int) -> None: + def min_values(self, value: int): self._underlying.min_values = int(value) @property @@ -163,7 +163,7 @@ def max_values(self) -> int: return self._underlying.max_values @max_values.setter - def max_values(self, value: int) -> None: + def max_values(self, value: int): self._underlying.max_values = int(value) @property @@ -172,7 +172,7 @@ def options(self) -> List[SelectOption]: return self._underlying.options @options.setter - def options(self, value: List[SelectOption]) -> None: + def options(self, value: List[SelectOption]): if not isinstance(value, list): raise TypeError('options must be a list of SelectOption') if not all(isinstance(obj, SelectOption) for obj in value): @@ -228,7 +228,7 @@ def add_option( self.append_option(option) - def append_option(self, option: SelectOption) -> None: + def append_option(self, option: SelectOption): """Appends an option to the select menu. Parameters @@ -253,7 +253,7 @@ def disabled(self) -> bool: return self._underlying.disabled @disabled.setter - def disabled(self, value: bool) -> None: + def disabled(self, value: bool): self._underlying.disabled = bool(value) @property diff --git a/discord/ui/view.py b/discord/ui/view.py index 0fc9ddbee6..8ec2ee46cb 100644 --- a/discord/ui/view.py +++ b/discord/ui/view.py @@ -79,7 +79,7 @@ class _ViewWeights: 'weights', ) - def __init__(self, children: List[Item]) -> None: + def __init__(self, children: List[Item]): self.weights: List[int] = [0, 0, 0, 0, 0] key = lambda i: sys.maxsize if i.row is None else i.row @@ -155,7 +155,7 @@ def __init_subclass__(cls) -> None: cls.__view_children_items__ = children - def __init__(self, *items: Item, timeout: Optional[float] = 180.0) -> None: + def __init__(self, *items: Item, timeout: Optional[float] = 180.0): self.timeout = timeout self.children: List[Item] = [] for func in self.__view_children_items__: @@ -366,8 +366,6 @@ async def _scheduled_task(self, item: Item, interaction: Interaction): if not interaction.response._responded: await interaction.response.defer() except Exception as e: - # TODO(ultrabear) How to define this return type as what on_error returns? - # (as on_error can be reimplemented it seems) return await self.on_error(e, item, interaction) def _start_listening_from_store(self, store: ViewStore) -> None: @@ -380,20 +378,20 @@ def _start_listening_from_store(self, store: ViewStore) -> None: self.__timeout_expiry = time.monotonic() + self.timeout self.__timeout_task = loop.create_task(self.__timeout_task_impl()) - def _dispatch_timeout(self) -> None: + def _dispatch_timeout(self): if self.__stopped.done(): return self.__stopped.set_result(True) asyncio.create_task(self.on_timeout(), name=f'discord-ui-view-timeout-{self.id}') - def _dispatch_item(self, item: Item, interaction: Interaction) -> None: + def _dispatch_item(self, item: Item, interaction: Interaction): if self.__stopped.done(): return asyncio.create_task(self._scheduled_task(item, interaction), name=f'discord-ui-view-dispatch-{self.id}') - def refresh(self, components: List[Component]) -> None: + def refresh(self, components: List[Component]): # This is pretty hacky at the moment # fmt: off old_state: Dict[Tuple[int, str], Item] = { @@ -466,7 +464,7 @@ async def wait(self) -> bool: class ViewStore: - def __init__(self, state: ConnectionState) -> None: + def __init__(self, state: ConnectionState): # (component_type, message_id, custom_id): (View, Item) self._views: Dict[Tuple[int, Optional[int], str], Tuple[View, Item]] = {} # message_id: View @@ -484,7 +482,7 @@ def persistent_views(self) -> Sequence[View]: # fmt: on return list(views.values()) - def __verify_integrity(self) -> None: + def __verify_integrity(self): to_remove: List[Tuple[int, Optional[int], str]] = [] for (k, (view, _)) in self._views.items(): if view.is_finished(): @@ -493,7 +491,7 @@ def __verify_integrity(self) -> None: for k in to_remove: del self._views[k] - def add_view(self, view: View, message_id: Optional[int] = None) -> None: + def add_view(self, view: View, message_id: Optional[int] = None): self.__verify_integrity() view._start_listening_from_store(self) @@ -504,7 +502,7 @@ def add_view(self, view: View, message_id: Optional[int] = None) -> None: if message_id is not None: self._synced_message_views[message_id] = view - def remove_view(self, view: View) -> None: + def remove_view(self, view: View): for item in view.children: if item.is_dispatchable(): self._views.pop((item.type.value, item.custom_id), None) # type: ignore @@ -514,7 +512,7 @@ def remove_view(self, view: View) -> None: del self._synced_message_views[key] break - def dispatch(self, component_type: int, custom_id: str, interaction: Interaction) -> None: + def dispatch(self, component_type: int, custom_id: str, interaction: Interaction): self.__verify_integrity() message_id: Optional[int] = interaction.message and interaction.message.id key = (component_type, message_id, custom_id) @@ -528,13 +526,13 @@ def dispatch(self, component_type: int, custom_id: str, interaction: Interaction item.refresh_state(interaction) view._dispatch_item(item, interaction) - def is_message_tracked(self, message_id: int) -> bool: + def is_message_tracked(self, message_id: int): return message_id in self._synced_message_views def remove_message_tracking(self, message_id: int) -> Optional[View]: return self._synced_message_views.pop(message_id, None) - def update_from_message(self, message_id: int, components: List[ComponentPayload]) -> None: + def update_from_message(self, message_id: int, components: List[ComponentPayload]): # pre-req: is_message_tracked == true view = self._synced_message_views[message_id] view.refresh([_component_factory(d) for d in components]) diff --git a/discord/utils.py b/discord/utils.py index 3f07b8c8c7..cd93543431 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -96,13 +96,13 @@ class _MissingSentinel: - def __eq__(self, other) -> bool: + def __eq__(self, other): return False def __bool__(self): return False - def __repr__(self) -> str: + def __repr__(self): return '...' diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index 0578d619b3..11cdf0c975 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -578,7 +578,7 @@ def __init__(self, *, data): self.id = int(data['id']) self.name = data['name'] - def __repr__(self) -> str: + def __repr__(self): return f'' @@ -605,7 +605,7 @@ def __init__(self, *, data, state): self.name = data['name'] self._icon = data['icon'] - def __repr__(self) -> str: + def __repr__(self): return f'' @property @@ -993,7 +993,7 @@ def __init__(self, data: WebhookPayload, session: aiohttp.ClientSession, token: super().__init__(data, token, state) self.session = session - def __repr__(self) -> str: + def __repr__(self): return f'' @property diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index 2e89e08b3c..699fae7576 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -554,7 +554,7 @@ def __init__(self, data: WebhookPayload, session: Session, token: Optional[str] super().__init__(data, token, state) self.session = session - def __repr__(self) -> str: + def __repr__(self): return f'' @property diff --git a/discord/welcome_screen.py b/discord/welcome_screen.py index 7ead156623..b97c6653e8 100644 --- a/discord/welcome_screen.py +++ b/discord/welcome_screen.py @@ -65,7 +65,7 @@ def __init__(self, channel: Snowflake, description: str, emoji: Union[Emoji, Par self.description = description self.emoji = emoji - def __repr__(self) -> str: + def __repr__(self): return f'WelcomeScreenChannel(channel={self.channel} description={self.description})' def to_dict(self) -> WelcomeScreenChannelPayload: @@ -123,7 +123,7 @@ def __init__(self, data: WelcomeScreenPayload, guild: Guild): self._guild = guild self._update(data) - def __repr__(self) -> str: + def __repr__(self): return f'=3.6.0,<3.9.0 -typing-extensions==4.0.1 +aiohttp>=3.6.0,<3.9.0 \ No newline at end of file