From 6dbe1184ad2d5c6eb8277362714b4051fb1423c8 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 00:03:16 +0200 Subject: [PATCH 01/24] Client & Bot --- discord/client.py | 31 +++++++++++++++++++++++++++---- discord/ext/commands/bot.py | 14 ++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/discord/client.py b/discord/client.py index b997bd96f..8e160755c 100644 --- a/discord/client.py +++ b/discord/client.py @@ -42,6 +42,7 @@ from typing import ( Tuple, Type, TypeVar, + TypedDict, Union, overload, ) @@ -82,7 +83,7 @@ from .soundboard import SoundboardDefaultSound, SoundboardSound if TYPE_CHECKING: from types import TracebackType - from typing_extensions import Self + from typing_extensions import Self, NotRequired, Unpack, Required from .abc import Messageable, PrivateChannel, Snowflake, SnowflakeTime from .app_commands import Command, ContextMenu @@ -120,7 +121,29 @@ if TYPE_CHECKING: from .audit_logs import AuditLogEntry from .poll import PollAnswer from .subscription import Subscription - + from .flags import MemberCacheFlags + + class _ClientOptions(TypedDict, total=False): + intents: Required[Intents] + max_messages: NotRequired[Optional[int]] + proxy: NotRequired[Optional[str]] + proxy_auth: NotRequired[Optional[aiohttp.BasicAuth]] + shard_id: NotRequired[Optional[int]] + shard_count: NotRequired[Optional[int]] + application_id: NotRequired[int] + member_cache_flags: NotRequired[MemberCacheFlags] + chunk_guilds_at_startup: NotRequired[bool] + status: NotRequired[Optional[Status]] + activity: NotRequired[Optional[BaseActivity]] + allowed_mentions: NotRequired[Optional[AllowedMentions]] + heartbeat_timeout: NotRequired[float] + guild_ready_timeout: NotRequired[float] + assume_unsync_clock: NotRequired[bool] + enable_debug_events: NotRequired[bool] + enable_raw_presences: NotRequired[bool] + http_trace: NotRequired[Optional[aiohttp.TraceConfig]] + max_ratelimit_timeout: NotRequired[Optional[float]] + connector: NotRequired[Optional[aiohttp.BaseConnector]] # fmt: off __all__ = ( @@ -272,7 +295,7 @@ class Client: The websocket gateway the client is currently connected to. Could be ``None``. """ - def __init__(self, *, intents: Intents, **options: Any) -> None: + def __init__(self, **options: Unpack[_ClientOptions]) -> None: self.loop: asyncio.AbstractEventLoop = _loop # self.ws is set in the connect method self.ws: DiscordWebSocket = None # type: ignore @@ -305,7 +328,7 @@ class Client: } self._enable_debug_events: bool = options.pop('enable_debug_events', False) - self._connection: ConnectionState[Self] = self._get_state(intents=intents, **options) + self._connection: ConnectionState[Self] = self._get_state(**options) self._connection.shard_count = self.shard_count self._closing_task: Optional[asyncio.Task[None]] = None self._ready: asyncio.Event = MISSING diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index 8ce872f1a..858d4f10d 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -64,7 +64,7 @@ from .cog import Cog from .hybrid import hybrid_command, hybrid_group, HybridCommand, HybridGroup if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack, NotRequired import importlib.machinery @@ -81,11 +81,18 @@ if TYPE_CHECKING: ) from .core import Command from .hybrid import CommandCallback, ContextT, P + from discord.client import _ClientOptions _Prefix = Union[Iterable[str], str] _PrefixCallable = MaybeAwaitableFunc[[BotT, Message], _Prefix] PrefixType = Union[_Prefix, _PrefixCallable[BotT]] + class _BotOptions(_ClientOptions): + owner_id: NotRequired[Optional[int]] + owner_ids: NotRequired[Optional[Collection[int]]] + strip_after_prefix: NotRequired[bool] + + __all__ = ( 'when_mentioned', 'when_mentioned_or', @@ -168,10 +175,9 @@ class BotBase(GroupMixin[None]): description: Optional[str] = None, allowed_contexts: app_commands.AppCommandContext = MISSING, allowed_installs: app_commands.AppInstallationType = MISSING, - intents: discord.Intents, - **options: Any, + **options: Unpack[_BotOptions], ) -> None: - super().__init__(intents=intents, **options) + super().__init__(**options) self.command_prefix: PrefixType[BotT] = command_prefix # type: ignore self.extra_events: Dict[str, List[CoroFunc]] = {} # Self doesn't have the ClientT bound, but since this is a mixin it technically does From b4a10c50fb117f8ff0d620600c81110d6ca3f3b1 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 00:06:42 +0200 Subject: [PATCH 02/24] fix: intents should actually be required --- discord/client.py | 5 ++--- discord/ext/commands/bot.py | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/discord/client.py b/discord/client.py index 8e160755c..8d0a4881d 100644 --- a/discord/client.py +++ b/discord/client.py @@ -124,7 +124,6 @@ if TYPE_CHECKING: from .flags import MemberCacheFlags class _ClientOptions(TypedDict, total=False): - intents: Required[Intents] max_messages: NotRequired[Optional[int]] proxy: NotRequired[Optional[str]] proxy_auth: NotRequired[Optional[aiohttp.BasicAuth]] @@ -295,7 +294,7 @@ class Client: The websocket gateway the client is currently connected to. Could be ``None``. """ - def __init__(self, **options: Unpack[_ClientOptions]) -> None: + def __init__(self, *, intents: Intents, **options: Unpack[_ClientOptions]) -> None: self.loop: asyncio.AbstractEventLoop = _loop # self.ws is set in the connect method self.ws: DiscordWebSocket = None # type: ignore @@ -328,7 +327,7 @@ class Client: } self._enable_debug_events: bool = options.pop('enable_debug_events', False) - self._connection: ConnectionState[Self] = self._get_state(**options) + self._connection: ConnectionState[Self] = self._get_state(intents=intents, **options) self._connection.shard_count = self.shard_count self._closing_task: Optional[asyncio.Task[None]] = None self._ready: asyncio.Event = MISSING diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index 858d4f10d..cefe837c0 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -175,9 +175,10 @@ class BotBase(GroupMixin[None]): description: Optional[str] = None, allowed_contexts: app_commands.AppCommandContext = MISSING, allowed_installs: app_commands.AppInstallationType = MISSING, + intents: discord.Intents, **options: Unpack[_BotOptions], ) -> None: - super().__init__(**options) + super().__init__(intents=intents, **options) self.command_prefix: PrefixType[BotT] = command_prefix # type: ignore self.extra_events: Dict[str, List[CoroFunc]] = {} # Self doesn't have the ClientT bound, but since this is a mixin it technically does From 38b72da47988fe57491c4f7462c9a2c8eb5adc24 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 00:22:19 +0200 Subject: [PATCH 03/24] Remove unused imports --- discord/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index 8d0a4881d..be5fb1736 100644 --- a/discord/client.py +++ b/discord/client.py @@ -83,7 +83,7 @@ from .soundboard import SoundboardDefaultSound, SoundboardSound if TYPE_CHECKING: from types import TracebackType - from typing_extensions import Self, NotRequired, Unpack, Required + from typing_extensions import Self, NotRequired, Unpack from .abc import Messageable, PrivateChannel, Snowflake, SnowflakeTime from .app_commands import Command, ContextMenu From 79e426f7ea68df718d178c059b5dcd9fdd636e22 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 15:28:27 +0200 Subject: [PATCH 04/24] flags: Intents --- discord/flags.py | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/discord/flags.py b/discord/flags.py index 20f8c5470..7a841a216 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -40,12 +40,44 @@ from typing import ( Type, TypeVar, overload, + TypedDict, ) from .enums import UserFlags if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack, NotRequired + + class _IntentsFlagsKwargs(TypedDict): + guilds: NotRequired[bool] + members: NotRequired[bool] + moderation: NotRequired[bool] + bans: NotRequired[bool] + emojis: NotRequired[bool] + emojis_and_stickers: NotRequired[bool] + expressions: NotRequired[bool] + integrations: NotRequired[bool] + webhooks: NotRequired[bool] + invites: NotRequired[bool] + voice_states: NotRequired[bool] + presences: NotRequired[bool] + messages: NotRequired[bool] + guild_messages: NotRequired[bool] + dm_messages: NotRequired[bool] + reactions: NotRequired[bool] + guild_reactions: NotRequired[bool] + dm_reactions: NotRequired[bool] + typing: NotRequired[bool] + guild_typing: NotRequired[bool] + dm_typing: NotRequired[bool] + message_content: NotRequired[bool] + guild_scheduled_events: NotRequired[bool] + auto_moderation: NotRequired[bool] + auto_moderation_configuration: NotRequired[bool] + auto_moderation_execution: NotRequired[bool] + polls: NotRequired[bool] + guild_polls: NotRequired[bool] + dm_polls: NotRequired[bool] __all__ = ( @@ -754,12 +786,12 @@ class Intents(BaseFlags): __slots__ = () - def __init__(self, value: int = 0, **kwargs: bool) -> None: + def __init__(self, value: int = 0, **kwargs: Unpack[_IntentsFlagsKwargs]) -> None: self.value: int = value - for key, value in kwargs.items(): + for key, kwvalue in kwargs.items(): if key not in self.VALID_FLAGS: raise TypeError(f'{key!r} is not a valid flag name.') - setattr(self, key, value) + setattr(self, key, kwvalue) @classmethod def all(cls: Type[Intents]) -> Intents: From 3ff66286cbb95b834ecc4ab2aea522390b599a67 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 16:18:39 +0200 Subject: [PATCH 05/24] ext.commands: cog, core --- discord/ext/commands/cog.py | 17 ++++++-- discord/ext/commands/core.py | 75 ++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/discord/ext/commands/cog.py b/discord/ext/commands/cog.py index 659d69ebb..f80cf0336 100644 --- a/discord/ext/commands/cog.py +++ b/discord/ext/commands/cog.py @@ -44,18 +44,29 @@ from typing import ( Tuple, TypeVar, Union, + TypedDict ) from ._types import _BaseCommand, BotT if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack, NotRequired from discord.abc import Snowflake from discord._types import ClientT from .bot import BotBase from .context import Context - from .core import Command + from .core import Command, _CommandDecoratorKwargs + + class _CogKwargs(TypedDict): + name: NotRequired[Optional[str]] + group_name: NotRequired[Optional[Union[str, app_commands.locale_str]]] + description: NotRequired[Optional[str]] + group_description: NotRequired[Optional[Union[str, app_commands.locale_str]]] + group_nsfw: NotRequired[bool] + group_auto_locale_strings: NotRequired[bool] + group_extras: NotRequired[Dict[Any, Any]] + command_attrs: NotRequired[_CommandDecoratorKwargs] __all__ = ( 'CogMeta', @@ -169,7 +180,7 @@ class CogMeta(type): __cog_app_commands__: List[Union[app_commands.Group, app_commands.Command[Any, ..., Any]]] __cog_listeners__: List[Tuple[str, str]] - def __new__(cls, *args: Any, **kwargs: Any) -> CogMeta: + def __new__(cls, *args: Any, **kwargs: Unpack[_CogKwargs]) -> CogMeta: name, bases, attrs = args if any(issubclass(base, app_commands.Group) for base in bases): raise TypeError( diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index 372fcbedf..ccd85a5ae 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -43,6 +43,7 @@ from typing import ( TypeVar, Union, overload, + TypedDict, ) import re @@ -58,10 +59,37 @@ from .parameters import Parameter, Signature from discord.app_commands.commands import NUMPY_DOCSTRING_ARG_REGEX if TYPE_CHECKING: - from typing_extensions import Concatenate, ParamSpec, Self + from typing_extensions import Concatenate, ParamSpec, Self, NotRequired, Unpack from ._types import BotT, Check, ContextT, Coro, CoroFunc, Error, Hook, UserCheck + class _CommandDecoratorKwargs(TypedDict): + enabled: NotRequired[bool] + help: NotRequired[str] + brief: NotRequired[str] + usage: NotRequired[str] + rest_is_raw: NotRequired[bool] + aliases: NotRequired[List[str]] + description: NotRequired[str] + hidden: NotRequired[bool] + checks: NotRequired[List[UserCheck[Context[Any]]]] + cooldown: NotRequired[CooldownMapping[Context[Any]]] + max_concurrency: NotRequired[MaxConcurrency] + require_var_positional: NotRequired[bool] + cooldown_after_parsing: NotRequired[bool] + ignore_extra: NotRequired[bool] + extras: NotRequired[Dict[Any, Any]] + + class _CommandKwargs(_CommandDecoratorKwargs): + name: NotRequired[str] + + class _GroupDecoratorKwargs(_CommandDecoratorKwargs): + invoke_without_command: NotRequired[bool] + case_insensitive: NotRequired[bool] + + class _GroupKwargs(_GroupDecoratorKwargs): + name: NotRequired[str] + __all__ = ( 'Command', @@ -368,9 +396,9 @@ class Command(_BaseCommand, Generic[CogT, P, T]): .. versionadded:: 2.0 """ - __original_kwargs__: Dict[str, Any] + __original_kwargs__: _CommandKwargs - def __new__(cls, *args: Any, **kwargs: Any) -> Self: + def __new__(cls, *args: Any, **kwargs: Unpack[_CommandKwargs]) -> Self: # if you're wondering why this is done, it's because we need to ensure # we have a complete original copy of **kwargs even for classes that # mess with it by popping before delegating to the subclass __init__. @@ -393,11 +421,11 @@ class Command(_BaseCommand, Generic[CogT, P, T]): Callable[Concatenate[Context[Any], P], Coro[T]], ], /, - **kwargs: Any, + **kwargs: Unpack[_CommandKwargs], ) -> None: if not asyncio.iscoroutinefunction(func): raise TypeError('Callback must be a coroutine.') - + name = kwargs.get('name') or func.__name__ if not isinstance(name, str): raise TypeError('Name of a command must be a string.') @@ -453,7 +481,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]): max_concurrency = kwargs.get('max_concurrency') self._max_concurrency: Optional[MaxConcurrency] = max_concurrency - + self.require_var_positional: bool = kwargs.get('require_var_positional', False) self.ignore_extra: bool = kwargs.get('ignore_extra', True) self.cooldown_after_parsing: bool = kwargs.get('cooldown_after_parsing', False) @@ -556,7 +584,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]): except ValueError: pass - def update(self, **kwargs: Any) -> None: + def update(self, **kwargs: Unpack[_CommandKwargs]) -> None: """Updates :class:`Command` instance with updated attribute. This works similarly to the :func:`~discord.ext.commands.command` decorator in terms @@ -564,7 +592,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]): subclass constructors, sans the name and callback. """ cog = self.cog - self.__init__(self.callback, **dict(self.__original_kwargs__, **kwargs)) + self.__init__(self.callback, **dict(self.__original_kwargs__, **kwargs)) # type: ignore # it's a typeddict self.cog = cog async def __call__(self, context: Context[BotT], /, *args: P.args, **kwargs: P.kwargs) -> T: @@ -1468,7 +1496,7 @@ class GroupMixin(Generic[CogT]): self: GroupMixin[CogT], name: str = ..., *args: Any, - **kwargs: Any, + **kwargs: Unpack[_CommandDecoratorKwargs], ) -> Callable[ [ Union[ @@ -1486,7 +1514,7 @@ class GroupMixin(Generic[CogT]): name: str = ..., cls: Type[CommandT] = ..., # type: ignore # previous overload handles case where cls is not set *args: Any, - **kwargs: Any, + **kwargs: Unpack[_CommandDecoratorKwargs], ) -> Callable[ [ Union[ @@ -1503,7 +1531,7 @@ class GroupMixin(Generic[CogT]): name: str = MISSING, cls: Type[Command[Any, ..., Any]] = MISSING, *args: Any, - **kwargs: Any, + **kwargs: Unpack[_CommandDecoratorKwargs], ) -> Any: """A shortcut decorator that invokes :func:`~discord.ext.commands.command` and adds it to the internal command list via :meth:`~.GroupMixin.add_command`. @@ -1515,8 +1543,7 @@ class GroupMixin(Generic[CogT]): """ def decorator(func): - - kwargs.setdefault('parent', self) + kwargs.setdefault('parent', self) # type: ignore # the parent kwarg is not for users to set. result = command(name=name, cls=cls, *args, **kwargs)(func) self.add_command(result) return result @@ -1528,7 +1555,7 @@ class GroupMixin(Generic[CogT]): self: GroupMixin[CogT], name: str = ..., *args: Any, - **kwargs: Any, + **kwargs: Unpack[_GroupDecoratorKwargs], ) -> Callable[ [ Union[ @@ -1546,7 +1573,7 @@ class GroupMixin(Generic[CogT]): name: str = ..., cls: Type[GroupT] = ..., # type: ignore # previous overload handles case where cls is not set *args: Any, - **kwargs: Any, + **kwargs: Unpack[_GroupDecoratorKwargs], ) -> Callable[ [ Union[ @@ -1563,7 +1590,7 @@ class GroupMixin(Generic[CogT]): name: str = MISSING, cls: Type[Group[Any, ..., Any]] = MISSING, *args: Any, - **kwargs: Any, + **kwargs: Unpack[_GroupDecoratorKwargs], ) -> Any: """A shortcut decorator that invokes :func:`.group` and adds it to the internal command list via :meth:`~.GroupMixin.add_command`. @@ -1575,7 +1602,7 @@ class GroupMixin(Generic[CogT]): """ def decorator(func): - kwargs.setdefault('parent', self) + kwargs.setdefault('parent', self) # type: ignore # the parent kwarg is not for users to set. result = group(name=name, cls=cls, *args, **kwargs)(func) self.add_command(result) return result @@ -1606,7 +1633,7 @@ class Group(GroupMixin[CogT], Command[CogT, P, T]): Defaults to ``False``. """ - def __init__(self, *args: Any, **attrs: Any) -> None: + def __init__(self, *args: Any, **attrs: Unpack[_GroupKwargs]) -> None: self.invoke_without_command: bool = attrs.pop('invoke_without_command', False) super().__init__(*args, **attrs) @@ -1728,7 +1755,7 @@ if TYPE_CHECKING: @overload def command( name: str = ..., - **attrs: Any, + **attrs: Unpack[_CommandDecoratorKwargs], ) -> _CommandDecorator: ... @@ -1737,7 +1764,7 @@ def command( def command( name: str = ..., cls: Type[CommandT] = ..., # type: ignore # previous overload handles case where cls is not set - **attrs: Any, + **attrs: Unpack[_CommandDecoratorKwargs], ) -> Callable[ [ Union[ @@ -1753,7 +1780,7 @@ def command( def command( name: str = MISSING, cls: Type[Command[Any, ..., Any]] = MISSING, - **attrs: Any, + **attrs: Unpack[_CommandDecoratorKwargs], ) -> Any: """A decorator that transforms a function into a :class:`.Command` or if called with :func:`.group`, :class:`.Group`. @@ -1798,7 +1825,7 @@ def command( @overload def group( name: str = ..., - **attrs: Any, + **attrs: Unpack[_GroupDecoratorKwargs], ) -> _GroupDecorator: ... @@ -1807,7 +1834,7 @@ def group( def group( name: str = ..., cls: Type[GroupT] = ..., # type: ignore # previous overload handles case where cls is not set - **attrs: Any, + **attrs: Unpack[_GroupDecoratorKwargs], ) -> Callable[ [ Union[ @@ -1823,7 +1850,7 @@ def group( def group( name: str = MISSING, cls: Type[Group[Any, ..., Any]] = MISSING, - **attrs: Any, + **attrs: Unpack[_GroupDecoratorKwargs], ) -> Any: """A decorator that transforms a function into a :class:`.Group`. From 13848f0e889d34e7a1470223a15a5c77fd40bfb0 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 16:25:12 +0200 Subject: [PATCH 06/24] Remove unneeded total=False --- discord/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/client.py b/discord/client.py index be5fb1736..d7f67580f 100644 --- a/discord/client.py +++ b/discord/client.py @@ -123,7 +123,7 @@ if TYPE_CHECKING: from .subscription import Subscription from .flags import MemberCacheFlags - class _ClientOptions(TypedDict, total=False): + class _ClientOptions(TypedDict): max_messages: NotRequired[Optional[int]] proxy: NotRequired[Optional[str]] proxy_auth: NotRequired[Optional[aiohttp.BasicAuth]] From edb32b6337a424a7cfea97ae6692ec2d4070fa7c Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 16:25:42 +0200 Subject: [PATCH 07/24] Run "black discord" --- discord/client.py | 1 + discord/ext/commands/cog.py | 3 ++- discord/ext/commands/core.py | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/discord/client.py b/discord/client.py index d7f67580f..adbfb2583 100644 --- a/discord/client.py +++ b/discord/client.py @@ -144,6 +144,7 @@ if TYPE_CHECKING: max_ratelimit_timeout: NotRequired[Optional[float]] connector: NotRequired[Optional[aiohttp.BaseConnector]] + # fmt: off __all__ = ( 'Client', diff --git a/discord/ext/commands/cog.py b/discord/ext/commands/cog.py index f80cf0336..48069dd85 100644 --- a/discord/ext/commands/cog.py +++ b/discord/ext/commands/cog.py @@ -44,7 +44,7 @@ from typing import ( Tuple, TypeVar, Union, - TypedDict + TypedDict, ) from ._types import _BaseCommand, BotT @@ -68,6 +68,7 @@ if TYPE_CHECKING: group_extras: NotRequired[Dict[Any, Any]] command_attrs: NotRequired[_CommandDecoratorKwargs] + __all__ = ( 'CogMeta', 'Cog', diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index ccd85a5ae..d9598786c 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -425,7 +425,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]): ) -> None: if not asyncio.iscoroutinefunction(func): raise TypeError('Callback must be a coroutine.') - + name = kwargs.get('name') or func.__name__ if not isinstance(name, str): raise TypeError('Name of a command must be a string.') @@ -481,7 +481,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]): max_concurrency = kwargs.get('max_concurrency') self._max_concurrency: Optional[MaxConcurrency] = max_concurrency - + self.require_var_positional: bool = kwargs.get('require_var_positional', False) self.ignore_extra: bool = kwargs.get('ignore_extra', True) self.cooldown_after_parsing: bool = kwargs.get('cooldown_after_parsing', False) From 7e8a254541bbfd3929733dd63853ae11b27a77cc Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 17:18:57 +0200 Subject: [PATCH 08/24] ext.commands: hybrid --- discord/ext/commands/hybrid.py | 65 ++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/discord/ext/commands/hybrid.py b/discord/ext/commands/hybrid.py index 0857003fa..6beef6980 100644 --- a/discord/ext/commands/hybrid.py +++ b/discord/ext/commands/hybrid.py @@ -24,19 +24,7 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations -from typing import ( - TYPE_CHECKING, - Any, - Callable, - ClassVar, - Dict, - List, - Tuple, - Type, - TypeVar, - Union, - Optional, -) +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Tuple, Type, TypeVar, Union, Optional, TypedDict import discord import inspect @@ -51,7 +39,7 @@ from .cog import Cog from .view import StringView if TYPE_CHECKING: - from typing_extensions import Self, ParamSpec, Concatenate + from typing_extensions import Self, ParamSpec, Concatenate, Unpack, NotRequired from ._types import ContextT, Coro, BotT from .bot import Bot from .context import Context @@ -60,6 +48,31 @@ if TYPE_CHECKING: AutocompleteCallback, ChoiceT, ) + from .core import _CommandKwargs + + class _HybridCommandKwargs( + _CommandKwargs, + ): + guild_ids: NotRequired[Optional[List[int]]] + guild_only: NotRequired[bool] + default_permissions: NotRequired[Optional[bool]] + nsfw: NotRequired[bool] + with_app_command: NotRequired[bool] + + class _HybridCommandDecoratorKwargs(_HybridCommandKwargs): + description: NotRequired[Union[str, app_commands.locale_str]] + + class _HybridGroupKwargs(_HybridCommandDecoratorKwargs): + with_app_command: NotRequired[bool] + guild_ids: NotRequired[Optional[List[int]]] + guild_only: NotRequired[bool] + default_permissions: NotRequired[Optional[bool]] + nsfw: NotRequired[bool] + description: NotRequired[str] + + class _HybridGroupDecoratorKwargs(_HybridGroupKwargs): + description: NotRequired[Union[str, app_commands.locale_str]] + fallback: NotRequired[Union[str, app_commands.locale_str]] __all__ = ( @@ -501,7 +514,7 @@ class HybridCommand(Command[CogT, P, T]): *, name: Union[str, app_commands.locale_str] = MISSING, description: Union[str, app_commands.locale_str] = MISSING, - **kwargs: Any, + **kwargs: Unpack[_HybridCommandKwargs], # type: ignore # name, description ) -> None: name, name_locale = (name.message, name) if isinstance(name, app_commands.locale_str) else (name, None) if name is not MISSING: @@ -621,7 +634,7 @@ class HybridGroup(Group[CogT, P, T]): name: Union[str, app_commands.locale_str] = MISSING, description: Union[str, app_commands.locale_str] = MISSING, fallback: Optional[Union[str, app_commands.locale_str]] = None, - **attrs: Any, + **attrs: Unpack[_HybridGroupKwargs], # type: ignore # name, description ) -> None: name, name_locale = (name.message, name) if isinstance(name, app_commands.locale_str) else (name, None) if name is not MISSING: @@ -825,7 +838,7 @@ class HybridGroup(Group[CogT, P, T]): name: Union[str, app_commands.locale_str] = MISSING, *args: Any, with_app_command: bool = True, - **kwargs: Any, + **kwargs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command ) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridCommand[CogT, P2, U]]: """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_command` and adds it to the internal command list via :meth:`add_command`. @@ -837,8 +850,8 @@ class HybridGroup(Group[CogT, P, T]): """ def decorator(func: CommandCallback[CogT, ContextT, P2, U]): - kwargs.setdefault('parent', self) - result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func) + kwargs.setdefault('parent', self) # type: ignore # parent is not for users to set + result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command self.add_command(result) return result @@ -849,7 +862,7 @@ class HybridGroup(Group[CogT, P, T]): name: Union[str, app_commands.locale_str] = MISSING, *args: Any, with_app_command: bool = True, - **kwargs: Any, + **kwargs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command ) -> Callable[[CommandCallback[CogT, ContextT, P2, U]], HybridGroup[CogT, P2, U]]: """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_group` and adds it to the internal command list via :meth:`~.GroupMixin.add_command`. @@ -861,8 +874,8 @@ class HybridGroup(Group[CogT, P, T]): """ def decorator(func: CommandCallback[CogT, ContextT, P2, U]): - kwargs.setdefault('parent', self) - result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func) + kwargs.setdefault('parent', self) # type: ignore # parent is not for users to set + result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command self.add_command(result) return result @@ -873,7 +886,7 @@ def hybrid_command( name: Union[str, app_commands.locale_str] = MISSING, *, with_app_command: bool = True, - **attrs: Any, + **attrs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command ) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridCommand[CogT, P, T]]: r"""A decorator that transforms a function into a :class:`.HybridCommand`. @@ -916,7 +929,7 @@ def hybrid_command( if isinstance(func, Command): raise TypeError('Callback is already a command.') # Pyright does not allow Command[Any] to be assigned to Command[CogT] despite it being okay here - return HybridCommand(func, name=name, with_app_command=with_app_command, **attrs) # type: ignore + return HybridCommand(func, name=name, with_app_command=with_app_command, **attrs) # type: ignore # name, with_app_command return decorator @@ -925,7 +938,7 @@ def hybrid_group( name: Union[str, app_commands.locale_str] = MISSING, *, with_app_command: bool = True, - **attrs: Any, + **attrs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command ) -> Callable[[CommandCallback[CogT, ContextT, P, T]], HybridGroup[CogT, P, T]]: """A decorator that transforms a function into a :class:`.HybridGroup`. @@ -949,6 +962,6 @@ def hybrid_group( def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> HybridGroup[CogT, P, T]: if isinstance(func, Command): raise TypeError('Callback is already a command.') - return HybridGroup(func, name=name, with_app_command=with_app_command, **attrs) + return HybridGroup(func, name=name, with_app_command=with_app_command, **attrs) # type: ignore # name, with_app_command return decorator From 89a210b55b3cc1770c95ffb4c166b774f2fbb0ae Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 17:20:44 +0200 Subject: [PATCH 09/24] Remove unusued imports --- discord/ext/commands/hybrid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/ext/commands/hybrid.py b/discord/ext/commands/hybrid.py index 6beef6980..6e6ba6702 100644 --- a/discord/ext/commands/hybrid.py +++ b/discord/ext/commands/hybrid.py @@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Tuple, Type, TypeVar, Union, Optional, TypedDict +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Tuple, Type, TypeVar, Union, Optional import discord import inspect From f2700a96e1d4410efdb320894bf87841a6d621b5 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 17:25:09 +0200 Subject: [PATCH 10/24] flags: MemberCacheFlags --- discord/flags.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/discord/flags.py b/discord/flags.py index 7a841a216..f3c57ba29 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -79,6 +79,10 @@ if TYPE_CHECKING: guild_polls: NotRequired[bool] dm_polls: NotRequired[bool] + class _MemberCacheFlagsKwargs(TypedDict): + voice: NotRequired[bool] + joined: NotRequired[bool] + __all__ = ( 'SystemChannelFlags', @@ -1447,7 +1451,7 @@ class MemberCacheFlags(BaseFlags): __slots__ = () - def __init__(self, **kwargs: bool): + def __init__(self, **kwargs: Unpack[_MemberCacheFlagsKwargs]) -> None: bits = max(self.VALID_FLAGS.values()).bit_length() self.value: int = (1 << bits) - 1 for key, value in kwargs.items(): From 624ce4ddd668302ba1294d2bfba1602c4ccddbcf Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 17:33:12 +0200 Subject: [PATCH 11/24] Permission[s](Overwrite) --- discord/permissions.py | 127 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 6 deletions(-) diff --git a/discord/permissions.py b/discord/permissions.py index b553e2578..491919872 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations -from typing import Callable, Any, ClassVar, Dict, Iterator, Set, TYPE_CHECKING, Tuple, Optional +from typing import Callable, Any, ClassVar, Dict, Iterator, Set, TYPE_CHECKING, Tuple, Optional, TypedDict from .flags import BaseFlags, flag_value, fill_with_flags, alias_flag_value __all__ = ( @@ -33,7 +33,122 @@ __all__ = ( ) if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack, NotRequired + + class _PermissionsKwargs(TypedDict): + create_instant_invite: NotRequired[bool] + kick_members: NotRequired[bool] + ban_members: NotRequired[bool] + administrator: NotRequired[bool] + manage_channels: NotRequired[bool] + manage_guild: NotRequired[bool] + add_reactions: NotRequired[bool] + view_audit_log: NotRequired[bool] + priority_speaker: NotRequired[bool] + stream: NotRequired[bool] + read_messages: NotRequired[bool] + view_channel: NotRequired[bool] + send_messages: NotRequired[bool] + send_tts_messages: NotRequired[bool] + manage_messages: NotRequired[bool] + embed_links: NotRequired[bool] + attach_files: NotRequired[bool] + read_message_history: NotRequired[bool] + mention_everyone: NotRequired[bool] + external_emojis: NotRequired[bool] + use_external_emojis: NotRequired[bool] + view_guild_insights: NotRequired[bool] + connect: NotRequired[bool] + speak: NotRequired[bool] + mute_members: NotRequired[bool] + deafen_members: NotRequired[bool] + move_members: NotRequired[bool] + use_voice_activation: NotRequired[bool] + change_nickname: NotRequired[bool] + manage_nicknames: NotRequired[bool] + manage_roles: NotRequired[bool] + manage_permissions: NotRequired[bool] + manage_webhooks: NotRequired[bool] + manage_expressions: NotRequired[bool] + manage_emojis: NotRequired[bool] + manage_emojis_and_stickers: NotRequired[bool] + use_application_commands: NotRequired[bool] + request_to_speak: NotRequired[bool] + manage_events: NotRequired[bool] + manage_threads: NotRequired[bool] + create_public_threads: NotRequired[bool] + create_private_threads: NotRequired[bool] + send_messages_in_threads: NotRequired[bool] + external_stickers: NotRequired[bool] + use_external_stickers: NotRequired[bool] + use_embedded_activities: NotRequired[bool] + moderate_members: NotRequired[bool] + use_soundboard: NotRequired[bool] + use_external_sounds: NotRequired[bool] + send_voice_messages: NotRequired[bool] + create_expressions: NotRequired[bool] + create_events: NotRequired[bool] + send_polls: NotRequired[bool] + create_polls: NotRequired[bool] + use_external_apps: NotRequired[bool] + + class _PermissionOverwriteKwargs(_PermissionsKwargs): + create_instant_invite: NotRequired[Optional[bool]] + kick_members: NotRequired[Optional[bool]] + ban_members: NotRequired[Optional[bool]] + administrator: NotRequired[Optional[bool]] + manage_channels: NotRequired[Optional[bool]] + manage_guild: NotRequired[Optional[bool]] + add_reactions: NotRequired[Optional[bool]] + view_audit_log: NotRequired[Optional[bool]] + priority_speaker: NotRequired[Optional[bool]] + stream: NotRequired[Optional[bool]] + read_messages: NotRequired[Optional[bool]] + view_channel: NotRequired[Optional[bool]] + send_messages: NotRequired[Optional[bool]] + send_tts_messages: NotRequired[Optional[bool]] + manage_messages: NotRequired[Optional[bool]] + embed_links: NotRequired[Optional[bool]] + attach_files: NotRequired[Optional[bool]] + read_message_history: NotRequired[Optional[bool]] + mention_everyone: NotRequired[Optional[bool]] + external_emojis: NotRequired[Optional[bool]] + use_external_emojis: NotRequired[Optional[bool]] + view_guild_insights: NotRequired[Optional[bool]] + connect: NotRequired[Optional[bool]] + speak: NotRequired[Optional[bool]] + mute_members: NotRequired[Optional[bool]] + deafen_members: NotRequired[Optional[bool]] + move_members: NotRequired[Optional[bool]] + use_voice_activation: NotRequired[Optional[bool]] + change_nickname: NotRequired[Optional[bool]] + manage_nicknames: NotRequired[Optional[bool]] + manage_roles: NotRequired[Optional[bool]] + manage_permissions: NotRequired[Optional[bool]] + manage_webhooks: NotRequired[Optional[bool]] + manage_expressions: NotRequired[Optional[bool]] + manage_emojis: NotRequired[Optional[bool]] + manage_emojis_and_stickers: NotRequired[Optional[bool]] + use_application_commands: NotRequired[Optional[bool]] + request_to_speak: NotRequired[Optional[bool]] + manage_events: NotRequired[Optional[bool]] + manage_threads: NotRequired[Optional[bool]] + create_public_threads: NotRequired[Optional[bool]] + create_private_threads: NotRequired[Optional[bool]] + send_messages_in_threads: NotRequired[Optional[bool]] + external_stickers: NotRequired[Optional[bool]] + use_external_stickers: NotRequired[Optional[bool]] + use_embedded_activities: NotRequired[Optional[bool]] + moderate_members: NotRequired[Optional[bool]] + use_soundboard: NotRequired[Optional[bool]] + use_external_sounds: NotRequired[Optional[bool]] + send_voice_messages: NotRequired[Optional[bool]] + create_expressions: NotRequired[Optional[bool]] + create_events: NotRequired[Optional[bool]] + send_polls: NotRequired[Optional[bool]] + create_polls: NotRequired[Optional[bool]] + use_external_apps: NotRequired[Optional[bool]] + # A permission alias works like a regular flag but is marked # So the PermissionOverwrite knows to work with it @@ -135,18 +250,18 @@ class Permissions(BaseFlags): __slots__ = () - def __init__(self, permissions: int = 0, **kwargs: bool): + def __init__(self, permissions: int = 0, **kwargs: Unpack[_PermissionsKwargs]): if not isinstance(permissions, int): raise TypeError(f'Expected int parameter, received {permissions.__class__.__name__} instead.') self.value = permissions - for key, value in kwargs.items(): + for key, kwvalue in kwargs.items(): try: flag = self.VALID_FLAGS[key] except KeyError: raise TypeError(f'{key!r} is not a valid permission name.') from None else: - self._set_flag(flag, value) + self._set_flag(flag, kwvalue) # type: ignore def is_subset(self, other: Permissions) -> bool: """Returns ``True`` if self has the same or fewer permissions as other.""" @@ -908,7 +1023,7 @@ class PermissionOverwrite: create_polls: Optional[bool] use_external_apps: Optional[bool] - def __init__(self, **kwargs: Optional[bool]): + def __init__(self, **kwargs: Unpack[_PermissionOverwriteKwargs]) -> None: self._values: Dict[str, Optional[bool]] = {} for key, value in kwargs.items(): From 2353a82b9b2bc3efbff0fe0bdd1ad0beb657066f Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 18:00:02 +0200 Subject: [PATCH 12/24] AutoSharded(Client/Bot) --- discord/ext/commands/bot.py | 15 ++++++++++++++- discord/shard.py | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index cefe837c0..2079fce7e 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -82,6 +82,7 @@ if TYPE_CHECKING: from .core import Command from .hybrid import CommandCallback, ContextT, P from discord.client import _ClientOptions + from discord.shard import _AutoShardedClientOptions _Prefix = Union[Iterable[str], str] _PrefixCallable = MaybeAwaitableFunc[[BotT, Message], _Prefix] @@ -92,6 +93,9 @@ if TYPE_CHECKING: owner_ids: NotRequired[Optional[Collection[int]]] strip_after_prefix: NotRequired[bool] + class _AutoShardedBotOptions(_AutoShardedClientOptions, _BotOptions): + ... + __all__ = ( 'when_mentioned', @@ -1534,4 +1538,13 @@ class AutoShardedBot(BotBase, discord.AutoShardedClient): .. versionadded:: 2.0 """ - pass + if TYPE_CHECKING: + + def __init__( + self, + command_prefix: PrefixType[BotT], + *, + intents: discord.Intents, + **kwargs: Unpack[_AutoShardedBotOptions], + ) -> None: + ... diff --git a/discord/shard.py b/discord/shard.py index 454fd5e28..89d22588a 100644 --- a/discord/shard.py +++ b/discord/shard.py @@ -47,11 +47,17 @@ from .enums import Status from typing import TYPE_CHECKING, Any, Callable, Tuple, Type, Optional, List, Dict if TYPE_CHECKING: - from typing_extensions import Unpack + from typing_extensions import Unpack, NotRequired from .gateway import DiscordWebSocket from .activity import BaseActivity from .flags import Intents from .types.gateway import SessionStartLimit + from .client import _ClientOptions + + class _AutoShardedClientOptions(_ClientOptions): + shard_ids: NotRequired[Optional[List[int]]] + shard_connect_timeout: NotRequired[Optional[float]] + __all__ = ( 'AutoShardedClient', @@ -365,10 +371,14 @@ class AutoShardedClient(Client): if TYPE_CHECKING: _connection: AutoShardedConnectionState - def __init__(self, *args: Any, intents: Intents, **kwargs: Any) -> None: + def __init__(self, *args: Any, intents: Intents, **kwargs: Unpack[_AutoShardedClientOptions]) -> None: kwargs.pop('shard_id', None) - self.shard_ids: Optional[List[int]] = kwargs.pop('shard_ids', None) - self.shard_connect_timeout: Optional[float] = kwargs.pop('shard_connect_timeout', 180.0) + self.shard_ids: Optional[List[int]] = kwargs.pop( + 'shard_ids', None + ) # pyright: ignore[reportAttributeAccessIssue] # it's fine + self.shard_connect_timeout: Optional[float] = kwargs.pop( + 'shard_connect_timeout', 180.0 + ) # pyright: ignore[reportAttributeAccessIssue] # it's fine super().__init__(*args, intents=intents, **kwargs) From 574235c63402c1a86bb47d9b391707aa89dd63c0 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:01:38 +0200 Subject: [PATCH 13/24] Remove unnecessary comments --- discord/shard.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/discord/shard.py b/discord/shard.py index 89d22588a..ecca84997 100644 --- a/discord/shard.py +++ b/discord/shard.py @@ -373,12 +373,8 @@ class AutoShardedClient(Client): def __init__(self, *args: Any, intents: Intents, **kwargs: Unpack[_AutoShardedClientOptions]) -> None: kwargs.pop('shard_id', None) - self.shard_ids: Optional[List[int]] = kwargs.pop( - 'shard_ids', None - ) # pyright: ignore[reportAttributeAccessIssue] # it's fine - self.shard_connect_timeout: Optional[float] = kwargs.pop( - 'shard_connect_timeout', 180.0 - ) # pyright: ignore[reportAttributeAccessIssue] # it's fine + self.shard_ids: Optional[List[int]] = kwargs.pop('shard_ids', None) + self.shard_connect_timeout: Optional[float] = kwargs.pop('shard_connect_timeout', 180.0) super().__init__(*args, intents=intents, **kwargs) From 0145ecb46c1a1be663c154dcd87e60de121cb5b9 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:01:54 +0200 Subject: [PATCH 14/24] permissions --- discord/permissions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/discord/permissions.py b/discord/permissions.py index 491919872..0619dac03 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -496,7 +496,7 @@ class Permissions(BaseFlags): """ return cls(1 << 3) - def update(self, **kwargs: bool) -> None: + def update(self, **kwargs: Unpack[_PermissionsKwargs]) -> None: r"""Bulk updates this permission object. Allows you to set multiple attributes by using keyword @@ -511,7 +511,7 @@ class Permissions(BaseFlags): for key, value in kwargs.items(): flag = self.VALID_FLAGS.get(key) if flag is not None: - self._set_flag(flag, value) + self._set_flag(flag, value) # type: ignore def handle_overwrite(self, allow: int, deny: int) -> None: # Basically this is what's happening here. @@ -1085,7 +1085,7 @@ class PermissionOverwrite: """ return len(self._values) == 0 - def update(self, **kwargs: Optional[bool]) -> None: + def update(self, **kwargs: Unpack[_PermissionOverwriteKwargs]) -> None: r"""Bulk updates this permission overwrite object. Allows you to set multiple attributes by using keyword From 62a59babacedbd41052bbe8f88bd792efc031de2 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:02:00 +0200 Subject: [PATCH 15/24] ext.commands: help --- discord/ext/commands/help.py | 51 ++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/discord/ext/commands/help.py b/discord/ext/commands/help.py index d06fbd8bf..06b497234 100644 --- a/discord/ext/commands/help.py +++ b/discord/ext/commands/help.py @@ -42,6 +42,7 @@ from typing import ( Iterable, Sequence, Mapping, + TypedDict, ) import discord.utils @@ -50,7 +51,7 @@ from .core import Group, Command, get_signature_parameters from .errors import CommandError if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack, NotRequired import discord.abc @@ -58,6 +59,7 @@ if TYPE_CHECKING: from .context import Context from .cog import Cog from .parameters import Parameter + from .core import _CommandKwargs from ._types import ( UserCheck, @@ -65,6 +67,30 @@ if TYPE_CHECKING: _Bot, ) + class _HelpCommandOptions(TypedDict): + show_hidden: NotRequired[bool] + verify_checks: NotRequired[bool] + command_attrs: NotRequired[_CommandKwargs] + + class _BaseHelpCommandOptions(_HelpCommandOptions): + sort_commands: NotRequired[bool] + dm_help: NotRequired[bool] + dm_help_threshold: NotRequired[int] + no_category: NotRequired[str] + paginator: NotRequired[Paginator] + commands_heading: NotRequired[str] + + class _DefaultHelpCommandOptions(_BaseHelpCommandOptions): + width: NotRequired[int] + indent: NotRequired[int] + arguments_heading: NotRequired[str] + default_argument_description: NotRequired[str] + show_parameter_descriptions: NotRequired[bool] + + class _MinimalHelpCommandOptions(_BaseHelpCommandOptions): + aliases_heading: NotRequired[str] + + __all__ = ( 'Paginator', 'HelpCommand', @@ -224,7 +250,7 @@ def _not_overridden(f: FuncT) -> FuncT: class _HelpCommandImpl(Command): - def __init__(self, inject: HelpCommand, *args: Any, **kwargs: Any) -> None: + def __init__(self, inject: HelpCommand, *args: Any, **kwargs: Unpack[_CommandKwargs]) -> None: super().__init__(inject.command_callback, *args, **kwargs) self._original: HelpCommand = inject self._injected: HelpCommand = inject @@ -299,7 +325,7 @@ class _HelpCommandImpl(Command): def update(self, **kwargs: Any) -> None: cog = self.cog - self.__init__(self._original, **dict(self.__original_kwargs__, **kwargs)) + self.__init__(self._original, **dict(self.__original_kwargs__, **kwargs)) # type: ignore self.cog = cog @@ -366,10 +392,9 @@ class HelpCommand: self.__original_args__ = deepcopy(args) return self - def __init__(self, **options: Any) -> None: + def __init__(self, **options: Unpack[_HelpCommandOptions]) -> None: self.show_hidden: bool = options.pop('show_hidden', False) self.verify_checks: bool = options.pop('verify_checks', True) - self.command_attrs: Dict[str, Any] self.command_attrs = attrs = options.pop('command_attrs', {}) attrs.setdefault('name', 'help') attrs.setdefault('help', 'Shows this message') @@ -1041,7 +1066,7 @@ class DefaultHelpCommand(HelpCommand): The paginator used to paginate the help command output. """ - def __init__(self, **options: Any) -> None: + def __init__(self, **options: Unpack[_DefaultHelpCommandOptions]) -> None: self.width: int = options.pop('width', 80) self.indent: int = options.pop('indent', 2) self.sort_commands: bool = options.pop('sort_commands', True) @@ -1051,11 +1076,13 @@ class DefaultHelpCommand(HelpCommand): self.commands_heading: str = options.pop('commands_heading', 'Commands:') self.default_argument_description: str = options.pop('default_argument_description', 'No description given') self.no_category: str = options.pop('no_category', 'No Category') - self.paginator: Paginator = options.pop('paginator', None) self.show_parameter_descriptions: bool = options.pop('show_parameter_descriptions', True) - if self.paginator is None: + paginator = options.pop('paginator', None) + if paginator is None: self.paginator: Paginator = Paginator() + else: + self.paginator: Paginator = paginator super().__init__(**options) @@ -1334,17 +1361,19 @@ class MinimalHelpCommand(HelpCommand): The paginator used to paginate the help command output. """ - def __init__(self, **options: Any) -> None: + def __init__(self, **options: Unpack[_MinimalHelpCommandOptions]) -> None: self.sort_commands: bool = options.pop('sort_commands', True) self.commands_heading: str = options.pop('commands_heading', 'Commands') self.dm_help: bool = options.pop('dm_help', False) self.dm_help_threshold: int = options.pop('dm_help_threshold', 1000) self.aliases_heading: str = options.pop('aliases_heading', 'Aliases:') self.no_category: str = options.pop('no_category', 'No Category') - self.paginator: Paginator = options.pop('paginator', None) - if self.paginator is None: + paginator = options.pop('paginator', None) + if paginator is None: self.paginator: Paginator = Paginator(suffix=None, prefix=None) + else: + self.paginator: Paginator = paginator super().__init__(**options) From 21b07a2a792a64e89e744d3c031c165073704a68 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:02:15 +0200 Subject: [PATCH 16/24] ext.commands: core --- discord/ext/commands/core.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index d9598786c..d0db462ac 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -63,6 +63,8 @@ if TYPE_CHECKING: from ._types import BotT, Check, ContextT, Coro, CoroFunc, Error, Hook, UserCheck + from discord.permissions import _PermissionsKwargs + class _CommandDecoratorKwargs(TypedDict): enabled: NotRequired[bool] help: NotRequired[str] @@ -2192,7 +2194,7 @@ def bot_has_any_role(*items: int) -> Callable[[T], T]: return check(predicate) -def has_permissions(**perms: bool) -> Check[Any]: +def has_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]: """A :func:`.check` that is added that checks if the member has all of the permissions necessary. @@ -2239,7 +2241,7 @@ def has_permissions(**perms: bool) -> Check[Any]: return check(predicate) -def bot_has_permissions(**perms: bool) -> Check[Any]: +def bot_has_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]: """Similar to :func:`.has_permissions` except checks if the bot itself has the permissions listed. @@ -2264,7 +2266,7 @@ def bot_has_permissions(**perms: bool) -> Check[Any]: return check(predicate) -def has_guild_permissions(**perms: bool) -> Check[Any]: +def has_guild_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]: """Similar to :func:`.has_permissions`, but operates on guild wide permissions instead of the current channel permissions. @@ -2293,7 +2295,7 @@ def has_guild_permissions(**perms: bool) -> Check[Any]: return check(predicate) -def bot_has_guild_permissions(**perms: bool) -> Check[Any]: +def bot_has_guild_permissions(**perms: Unpack[_PermissionsKwargs]) -> Check[Any]: """Similar to :func:`.has_guild_permissions`, but checks the bot members guild permissions. From 62e6b8e20f52470a38287851f09d40a3bfbf2a78 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:02:35 +0200 Subject: [PATCH 17/24] ext.commands: bot --- discord/ext/commands/bot.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index 2079fce7e..7951a7530 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -80,7 +80,7 @@ if TYPE_CHECKING: MaybeAwaitableFunc, ) from .core import Command - from .hybrid import CommandCallback, ContextT, P + from .hybrid import CommandCallback, ContextT, P, _HybridCommandDecoratorKwargs, _HybridGroupDecoratorKwargs from discord.client import _ClientOptions from discord.shard import _AutoShardedClientOptions @@ -292,7 +292,7 @@ class BotBase(GroupMixin[None]): name: Union[str, app_commands.locale_str] = MISSING, with_app_command: bool = True, *args: Any, - **kwargs: Any, + **kwargs: Unpack[_HybridCommandDecoratorKwargs], # type: ignore # name, with_app_command ) -> Callable[[CommandCallback[Any, ContextT, P, T]], HybridCommand[Any, P, T]]: """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_command` and adds it to the internal command list via :meth:`add_command`. @@ -304,8 +304,8 @@ class BotBase(GroupMixin[None]): """ def decorator(func: CommandCallback[Any, ContextT, P, T]): - kwargs.setdefault('parent', self) - result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func) + kwargs.setdefault('parent', self) # type: ignore # parent is not for the user to set + result = hybrid_command(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command self.add_command(result) return result @@ -316,7 +316,7 @@ class BotBase(GroupMixin[None]): name: Union[str, app_commands.locale_str] = MISSING, with_app_command: bool = True, *args: Any, - **kwargs: Any, + **kwargs: Unpack[_HybridGroupDecoratorKwargs], # type: ignore # name, with_app_command ) -> Callable[[CommandCallback[Any, ContextT, P, T]], HybridGroup[Any, P, T]]: """A shortcut decorator that invokes :func:`~discord.ext.commands.hybrid_group` and adds it to the internal command list via :meth:`add_command`. @@ -328,8 +328,8 @@ class BotBase(GroupMixin[None]): """ def decorator(func: CommandCallback[Any, ContextT, P, T]): - kwargs.setdefault('parent', self) - result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func) + kwargs.setdefault('parent', self) # type: ignore # parent is not for the user to set + result = hybrid_group(name=name, *args, with_app_command=with_app_command, **kwargs)(func) # type: ignore # name, with_app_command self.add_command(result) return result From b2173bf6ecc361fb80fc3b81304f1342d41ab921 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:03:39 +0200 Subject: [PATCH 18/24] ext.commands: checks --- discord/app_commands/checks.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/discord/app_commands/checks.py b/discord/app_commands/checks.py index 5c17b951c..3fbd677c3 100644 --- a/discord/app_commands/checks.py +++ b/discord/app_commands/checks.py @@ -55,8 +55,9 @@ from ..utils import get as utils_get, MISSING, maybe_coroutine T = TypeVar('T') if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack from ..interactions import Interaction + from ..permissions import _PermissionsKwargs CooldownFunction = Union[ Callable[[Interaction[Any]], Coroutine[Any, Any, T]], @@ -286,7 +287,7 @@ def has_any_role(*items: Union[int, str]) -> Callable[[T], T]: return check(predicate) -def has_permissions(**perms: bool) -> Callable[[T], T]: +def has_permissions(**perms: Unpack[_PermissionsKwargs]) -> Callable[[T], T]: r"""A :func:`~discord.app_commands.check` that is added that checks if the member has all of the permissions necessary. @@ -341,7 +342,7 @@ def has_permissions(**perms: bool) -> Callable[[T], T]: return check(predicate) -def bot_has_permissions(**perms: bool) -> Callable[[T], T]: +def bot_has_permissions(**perms: Unpack[_PermissionsKwargs]) -> Callable[[T], T]: """Similar to :func:`has_permissions` except checks if the bot itself has the permissions listed. This relies on :attr:`discord.Interaction.app_permissions`. From a4aeab9a599dcd812776ae0daccd12455195dfce Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:03:45 +0200 Subject: [PATCH 19/24] abc --- discord/abc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/discord/abc.py b/discord/abc.py index 692472f8f..b38f6ffac 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -73,7 +73,7 @@ __all__ = ( T = TypeVar('T', bound=VoiceProtocol) if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack from .client import Client from .user import ClientUser @@ -108,6 +108,7 @@ if TYPE_CHECKING: from .types.snowflake import ( SnowflakeList, ) + from .permissions import _PermissionOverwriteKwargs PartialMessageableChannel = Union[TextChannel, VoiceChannel, StageChannel, Thread, DMChannel, PartialMessageable] MessageableChannel = Union[PartialMessageableChannel, GroupChannel] @@ -886,7 +887,7 @@ class GuildChannel: target: Union[Member, Role], *, reason: Optional[str] = ..., - **permissions: Optional[bool], + **permissions: Unpack[_PermissionOverwriteKwargs], ) -> None: ... @@ -896,7 +897,7 @@ class GuildChannel: *, overwrite: Any = _undefined, reason: Optional[str] = None, - **permissions: Optional[bool], + **permissions: Unpack[_PermissionOverwriteKwargs], ) -> None: r"""|coro| From a79a69386824243751721c74f9d0cdd7e9577660 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sat, 17 May 2025 22:03:54 +0200 Subject: [PATCH 20/24] app_commands: commands --- discord/app_commands/commands.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/discord/app_commands/commands.py b/discord/app_commands/commands.py index d5b8d93b2..41cfd4172 100644 --- a/discord/app_commands/commands.py +++ b/discord/app_commands/commands.py @@ -61,7 +61,7 @@ from ..permissions import Permissions from ..utils import resolve_annotation, MISSING, is_inside_class, maybe_coroutine, async_all, _shorten, _to_kebab_case if TYPE_CHECKING: - from typing_extensions import ParamSpec, Concatenate + from typing_extensions import ParamSpec, Concatenate, Unpack from ..interactions import Interaction from ..abc import Snowflake from .namespace import Namespace @@ -73,6 +73,7 @@ if TYPE_CHECKING: # However, for type hinting purposes it's unfortunately necessary for one to # reference the other to prevent type checking errors in callbacks from discord.ext import commands + from discord.permissions import _PermissionsKwargs ErrorFunc = Callable[[Interaction, AppCommandError], Coroutine[Any, Any, None]] @@ -2821,7 +2822,7 @@ def allowed_installs( return inner -def default_permissions(perms_obj: Optional[Permissions] = None, /, **perms: bool) -> Callable[[T], T]: +def default_permissions(perms_obj: Optional[Permissions] = None, /, **perms: Unpack[_PermissionsKwargs]) -> Callable[[T], T]: r"""A decorator that sets the default permissions needed to execute this command. When this decorator is used, by default users must have these permissions to execute the command. From 621af5f654eedba83661587a3cbb191eb53bf3c3 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Sun, 18 May 2025 17:10:04 +0200 Subject: [PATCH 21/24] Add missing kwargs for (AutoSharded)Bot --- discord/ext/commands/bot.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index 7951a7530..005a25377 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -92,6 +92,7 @@ if TYPE_CHECKING: owner_id: NotRequired[Optional[int]] owner_ids: NotRequired[Optional[Collection[int]]] strip_after_prefix: NotRequired[bool] + case_insensitive: NotRequired[bool] class _AutoShardedBotOptions(_AutoShardedClientOptions, _BotOptions): ... @@ -1544,6 +1545,11 @@ class AutoShardedBot(BotBase, discord.AutoShardedClient): self, command_prefix: PrefixType[BotT], *, + help_command: Optional[HelpCommand] = _default, + tree_cls: Type[app_commands.CommandTree[Any]] = app_commands.CommandTree, + description: Optional[str] = None, + allowed_contexts: app_commands.AppCommandContext = MISSING, + allowed_installs: app_commands.AppInstallationType = MISSING, intents: discord.Intents, **kwargs: Unpack[_AutoShardedBotOptions], ) -> None: From b52ded68c8ce9f1722edd121c938adea6dda1cad Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Tue, 20 May 2025 19:02:43 +0200 Subject: [PATCH 22/24] Use total=False over NotRequired everything --- discord/client.py | 42 +++--- discord/ext/commands/bot.py | 12 +- discord/ext/commands/cog.py | 20 +-- discord/ext/commands/core.py | 54 ++++---- discord/ext/commands/help.py | 46 +++---- discord/ext/commands/hybrid.py | 46 ++++--- discord/flags.py | 72 +++++------ discord/permissions.py | 230 ++++++++++++++++----------------- discord/shard.py | 8 +- 9 files changed, 264 insertions(+), 266 deletions(-) diff --git a/discord/client.py b/discord/client.py index adbfb2583..67e42410b 100644 --- a/discord/client.py +++ b/discord/client.py @@ -83,7 +83,7 @@ from .soundboard import SoundboardDefaultSound, SoundboardSound if TYPE_CHECKING: from types import TracebackType - from typing_extensions import Self, NotRequired, Unpack + from typing_extensions import Self, Unpack from .abc import Messageable, PrivateChannel, Snowflake, SnowflakeTime from .app_commands import Command, ContextMenu @@ -123,26 +123,26 @@ if TYPE_CHECKING: from .subscription import Subscription from .flags import MemberCacheFlags - class _ClientOptions(TypedDict): - max_messages: NotRequired[Optional[int]] - proxy: NotRequired[Optional[str]] - proxy_auth: NotRequired[Optional[aiohttp.BasicAuth]] - shard_id: NotRequired[Optional[int]] - shard_count: NotRequired[Optional[int]] - application_id: NotRequired[int] - member_cache_flags: NotRequired[MemberCacheFlags] - chunk_guilds_at_startup: NotRequired[bool] - status: NotRequired[Optional[Status]] - activity: NotRequired[Optional[BaseActivity]] - allowed_mentions: NotRequired[Optional[AllowedMentions]] - heartbeat_timeout: NotRequired[float] - guild_ready_timeout: NotRequired[float] - assume_unsync_clock: NotRequired[bool] - enable_debug_events: NotRequired[bool] - enable_raw_presences: NotRequired[bool] - http_trace: NotRequired[Optional[aiohttp.TraceConfig]] - max_ratelimit_timeout: NotRequired[Optional[float]] - connector: NotRequired[Optional[aiohttp.BaseConnector]] + class _ClientOptions(TypedDict, total=False): + max_messages: int + proxy: str + proxy_auth: aiohttp.BasicAuth + shard_id: int + shard_count: int + application_id: int + member_cache_flags: MemberCacheFlags + chunk_guilds_at_startup: bool + status: Status + activity: BaseActivity + allowed_mentions: AllowedMentions + heartbeat_timeout: float + guild_ready_timeout: float + assume_unsync_clock: bool + enable_debug_events: bool + enable_raw_presences: bool + http_trace: aiohttp.TraceConfig + max_ratelimit_timeout: float + connector: aiohttp.BaseConnector # fmt: off diff --git a/discord/ext/commands/bot.py b/discord/ext/commands/bot.py index 005a25377..29b1f045e 100644 --- a/discord/ext/commands/bot.py +++ b/discord/ext/commands/bot.py @@ -64,7 +64,7 @@ from .cog import Cog from .hybrid import hybrid_command, hybrid_group, HybridCommand, HybridGroup if TYPE_CHECKING: - from typing_extensions import Self, Unpack, NotRequired + from typing_extensions import Self, Unpack import importlib.machinery @@ -88,11 +88,11 @@ if TYPE_CHECKING: _PrefixCallable = MaybeAwaitableFunc[[BotT, Message], _Prefix] PrefixType = Union[_Prefix, _PrefixCallable[BotT]] - class _BotOptions(_ClientOptions): - owner_id: NotRequired[Optional[int]] - owner_ids: NotRequired[Optional[Collection[int]]] - strip_after_prefix: NotRequired[bool] - case_insensitive: NotRequired[bool] + class _BotOptions(_ClientOptions, total=False): + owner_id: int + owner_ids: Collection[int] + strip_after_prefix: bool + case_insensitive: bool class _AutoShardedBotOptions(_AutoShardedClientOptions, _BotOptions): ... diff --git a/discord/ext/commands/cog.py b/discord/ext/commands/cog.py index 48069dd85..1b8a24e66 100644 --- a/discord/ext/commands/cog.py +++ b/discord/ext/commands/cog.py @@ -50,7 +50,7 @@ from typing import ( from ._types import _BaseCommand, BotT if TYPE_CHECKING: - from typing_extensions import Self, Unpack, NotRequired + from typing_extensions import Self, Unpack from discord.abc import Snowflake from discord._types import ClientT @@ -58,15 +58,15 @@ if TYPE_CHECKING: from .context import Context from .core import Command, _CommandDecoratorKwargs - class _CogKwargs(TypedDict): - name: NotRequired[Optional[str]] - group_name: NotRequired[Optional[Union[str, app_commands.locale_str]]] - description: NotRequired[Optional[str]] - group_description: NotRequired[Optional[Union[str, app_commands.locale_str]]] - group_nsfw: NotRequired[bool] - group_auto_locale_strings: NotRequired[bool] - group_extras: NotRequired[Dict[Any, Any]] - command_attrs: NotRequired[_CommandDecoratorKwargs] + class _CogKwargs(TypedDict, total=False): + name: str + group_name: Union[str, app_commands.locale_str] + description: str + group_description: Union[str, app_commands.locale_str] + group_nsfw: bool + group_auto_locale_strings: bool + group_extras: Dict[Any, Any] + command_attrs: _CommandDecoratorKwargs __all__ = ( diff --git a/discord/ext/commands/core.py b/discord/ext/commands/core.py index d0db462ac..9107a3d22 100644 --- a/discord/ext/commands/core.py +++ b/discord/ext/commands/core.py @@ -59,38 +59,38 @@ from .parameters import Parameter, Signature from discord.app_commands.commands import NUMPY_DOCSTRING_ARG_REGEX if TYPE_CHECKING: - from typing_extensions import Concatenate, ParamSpec, Self, NotRequired, Unpack + from typing_extensions import Concatenate, ParamSpec, Self, Unpack from ._types import BotT, Check, ContextT, Coro, CoroFunc, Error, Hook, UserCheck from discord.permissions import _PermissionsKwargs - class _CommandDecoratorKwargs(TypedDict): - enabled: NotRequired[bool] - help: NotRequired[str] - brief: NotRequired[str] - usage: NotRequired[str] - rest_is_raw: NotRequired[bool] - aliases: NotRequired[List[str]] - description: NotRequired[str] - hidden: NotRequired[bool] - checks: NotRequired[List[UserCheck[Context[Any]]]] - cooldown: NotRequired[CooldownMapping[Context[Any]]] - max_concurrency: NotRequired[MaxConcurrency] - require_var_positional: NotRequired[bool] - cooldown_after_parsing: NotRequired[bool] - ignore_extra: NotRequired[bool] - extras: NotRequired[Dict[Any, Any]] - - class _CommandKwargs(_CommandDecoratorKwargs): - name: NotRequired[str] - - class _GroupDecoratorKwargs(_CommandDecoratorKwargs): - invoke_without_command: NotRequired[bool] - case_insensitive: NotRequired[bool] - - class _GroupKwargs(_GroupDecoratorKwargs): - name: NotRequired[str] + class _CommandDecoratorKwargs(TypedDict, total=False): + enabled: bool + help: str + brief: str + usage: str + rest_is_raw: bool + aliases: List[str] + description: str + hidden: bool + checks: List[UserCheck[Context[Any]]] + cooldown: CooldownMapping[Context[Any]] + max_concurrency: MaxConcurrency + require_var_positional: bool + cooldown_after_parsing: bool + ignore_extra: bool + extras: Dict[Any, Any] + + class _CommandKwargs(_CommandDecoratorKwargs, total=False): + name: str + + class _GroupDecoratorKwargs(_CommandDecoratorKwargs, total=False): + invoke_without_command: bool + case_insensitive: bool + + class _GroupKwargs(_GroupDecoratorKwargs, total=False): + name: str __all__ = ( diff --git a/discord/ext/commands/help.py b/discord/ext/commands/help.py index 06b497234..90b44d16a 100644 --- a/discord/ext/commands/help.py +++ b/discord/ext/commands/help.py @@ -51,7 +51,7 @@ from .core import Group, Command, get_signature_parameters from .errors import CommandError if TYPE_CHECKING: - from typing_extensions import Self, Unpack, NotRequired + from typing_extensions import Self, Unpack import discord.abc @@ -67,28 +67,28 @@ if TYPE_CHECKING: _Bot, ) - class _HelpCommandOptions(TypedDict): - show_hidden: NotRequired[bool] - verify_checks: NotRequired[bool] - command_attrs: NotRequired[_CommandKwargs] - - class _BaseHelpCommandOptions(_HelpCommandOptions): - sort_commands: NotRequired[bool] - dm_help: NotRequired[bool] - dm_help_threshold: NotRequired[int] - no_category: NotRequired[str] - paginator: NotRequired[Paginator] - commands_heading: NotRequired[str] - - class _DefaultHelpCommandOptions(_BaseHelpCommandOptions): - width: NotRequired[int] - indent: NotRequired[int] - arguments_heading: NotRequired[str] - default_argument_description: NotRequired[str] - show_parameter_descriptions: NotRequired[bool] - - class _MinimalHelpCommandOptions(_BaseHelpCommandOptions): - aliases_heading: NotRequired[str] + class _HelpCommandOptions(TypedDict, total=False): + show_hidden: bool + verify_checks: bool + command_attrs: _CommandKwargs + + class _BaseHelpCommandOptions(_HelpCommandOptions, total=False): + sort_commands: bool + dm_help: bool + dm_help_threshold: int + no_category: str + paginator: Paginator + commands_heading: str + + class _DefaultHelpCommandOptions(_BaseHelpCommandOptions, total=False): + width: int + indent: int + arguments_heading: str + default_argument_description: str + show_parameter_descriptions: bool + + class _MinimalHelpCommandOptions(_BaseHelpCommandOptions, total=False): + aliases_heading: str __all__ = ( diff --git a/discord/ext/commands/hybrid.py b/discord/ext/commands/hybrid.py index 6e6ba6702..78e2f43bb 100644 --- a/discord/ext/commands/hybrid.py +++ b/discord/ext/commands/hybrid.py @@ -39,7 +39,7 @@ from .cog import Cog from .view import StringView if TYPE_CHECKING: - from typing_extensions import Self, ParamSpec, Concatenate, Unpack, NotRequired + from typing_extensions import Self, ParamSpec, Concatenate, Unpack from ._types import ContextT, Coro, BotT from .bot import Bot from .context import Context @@ -50,29 +50,27 @@ if TYPE_CHECKING: ) from .core import _CommandKwargs - class _HybridCommandKwargs( - _CommandKwargs, - ): - guild_ids: NotRequired[Optional[List[int]]] - guild_only: NotRequired[bool] - default_permissions: NotRequired[Optional[bool]] - nsfw: NotRequired[bool] - with_app_command: NotRequired[bool] - - class _HybridCommandDecoratorKwargs(_HybridCommandKwargs): - description: NotRequired[Union[str, app_commands.locale_str]] - - class _HybridGroupKwargs(_HybridCommandDecoratorKwargs): - with_app_command: NotRequired[bool] - guild_ids: NotRequired[Optional[List[int]]] - guild_only: NotRequired[bool] - default_permissions: NotRequired[Optional[bool]] - nsfw: NotRequired[bool] - description: NotRequired[str] - - class _HybridGroupDecoratorKwargs(_HybridGroupKwargs): - description: NotRequired[Union[str, app_commands.locale_str]] - fallback: NotRequired[Union[str, app_commands.locale_str]] + class _HybridCommandKwargs(_CommandKwargs, total=False): + guild_ids: list[int] + guild_only: bool + default_permissions: bool + nsfw: bool + with_app_command: bool + + class _HybridCommandDecoratorKwargs(_HybridCommandKwargs, total=False): + description: Union[str, app_commands.locale_str] + + class _HybridGroupKwargs(_HybridCommandDecoratorKwargs, total=False): + with_app_command: bool + guild_ids: list[int] + guild_only: bool + default_permissions: bool + nsfw: bool + description: str + + class _HybridGroupDecoratorKwargs(_HybridGroupKwargs, total=False): + description: Union[str, app_commands.locale_str] + fallback: Union[str, app_commands.locale_str] __all__ = ( diff --git a/discord/flags.py b/discord/flags.py index f3c57ba29..82a430482 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -46,42 +46,42 @@ from typing import ( from .enums import UserFlags if TYPE_CHECKING: - from typing_extensions import Self, Unpack, NotRequired - - class _IntentsFlagsKwargs(TypedDict): - guilds: NotRequired[bool] - members: NotRequired[bool] - moderation: NotRequired[bool] - bans: NotRequired[bool] - emojis: NotRequired[bool] - emojis_and_stickers: NotRequired[bool] - expressions: NotRequired[bool] - integrations: NotRequired[bool] - webhooks: NotRequired[bool] - invites: NotRequired[bool] - voice_states: NotRequired[bool] - presences: NotRequired[bool] - messages: NotRequired[bool] - guild_messages: NotRequired[bool] - dm_messages: NotRequired[bool] - reactions: NotRequired[bool] - guild_reactions: NotRequired[bool] - dm_reactions: NotRequired[bool] - typing: NotRequired[bool] - guild_typing: NotRequired[bool] - dm_typing: NotRequired[bool] - message_content: NotRequired[bool] - guild_scheduled_events: NotRequired[bool] - auto_moderation: NotRequired[bool] - auto_moderation_configuration: NotRequired[bool] - auto_moderation_execution: NotRequired[bool] - polls: NotRequired[bool] - guild_polls: NotRequired[bool] - dm_polls: NotRequired[bool] - - class _MemberCacheFlagsKwargs(TypedDict): - voice: NotRequired[bool] - joined: NotRequired[bool] + from typing_extensions import Self, Unpack + + class _IntentsFlagsKwargs(TypedDict, total=False): + guilds: bool + members: bool + moderation: bool + bans: bool + emojis: bool + emojis_and_stickers: bool + expressions: bool + integrations: bool + webhooks: bool + invites: bool + voice_states: bool + presences: bool + messages: bool + guild_messages: bool + dm_messages: bool + reactions: bool + guild_reactions: bool + dm_reactions: bool + typing: bool + guild_typing: bool + dm_typing: bool + message_content: bool + guild_scheduled_events: bool + auto_moderation: bool + auto_moderation_configuration: bool + auto_moderation_execution: bool + polls: bool + guild_polls: bool + dm_polls: bool + + class _MemberCacheFlagsKwargs(TypedDict, total=False): + voice: bool + joined: bool __all__ = ( diff --git a/discord/permissions.py b/discord/permissions.py index 0619dac03..e46f26805 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -33,121 +33,121 @@ __all__ = ( ) if TYPE_CHECKING: - from typing_extensions import Self, Unpack, NotRequired - - class _PermissionsKwargs(TypedDict): - create_instant_invite: NotRequired[bool] - kick_members: NotRequired[bool] - ban_members: NotRequired[bool] - administrator: NotRequired[bool] - manage_channels: NotRequired[bool] - manage_guild: NotRequired[bool] - add_reactions: NotRequired[bool] - view_audit_log: NotRequired[bool] - priority_speaker: NotRequired[bool] - stream: NotRequired[bool] - read_messages: NotRequired[bool] - view_channel: NotRequired[bool] - send_messages: NotRequired[bool] - send_tts_messages: NotRequired[bool] - manage_messages: NotRequired[bool] - embed_links: NotRequired[bool] - attach_files: NotRequired[bool] - read_message_history: NotRequired[bool] - mention_everyone: NotRequired[bool] - external_emojis: NotRequired[bool] - use_external_emojis: NotRequired[bool] - view_guild_insights: NotRequired[bool] - connect: NotRequired[bool] - speak: NotRequired[bool] - mute_members: NotRequired[bool] - deafen_members: NotRequired[bool] - move_members: NotRequired[bool] - use_voice_activation: NotRequired[bool] - change_nickname: NotRequired[bool] - manage_nicknames: NotRequired[bool] - manage_roles: NotRequired[bool] - manage_permissions: NotRequired[bool] - manage_webhooks: NotRequired[bool] - manage_expressions: NotRequired[bool] - manage_emojis: NotRequired[bool] - manage_emojis_and_stickers: NotRequired[bool] - use_application_commands: NotRequired[bool] - request_to_speak: NotRequired[bool] - manage_events: NotRequired[bool] - manage_threads: NotRequired[bool] - create_public_threads: NotRequired[bool] - create_private_threads: NotRequired[bool] - send_messages_in_threads: NotRequired[bool] - external_stickers: NotRequired[bool] - use_external_stickers: NotRequired[bool] - use_embedded_activities: NotRequired[bool] - moderate_members: NotRequired[bool] - use_soundboard: NotRequired[bool] - use_external_sounds: NotRequired[bool] - send_voice_messages: NotRequired[bool] - create_expressions: NotRequired[bool] - create_events: NotRequired[bool] - send_polls: NotRequired[bool] - create_polls: NotRequired[bool] - use_external_apps: NotRequired[bool] - - class _PermissionOverwriteKwargs(_PermissionsKwargs): - create_instant_invite: NotRequired[Optional[bool]] - kick_members: NotRequired[Optional[bool]] - ban_members: NotRequired[Optional[bool]] - administrator: NotRequired[Optional[bool]] - manage_channels: NotRequired[Optional[bool]] - manage_guild: NotRequired[Optional[bool]] - add_reactions: NotRequired[Optional[bool]] - view_audit_log: NotRequired[Optional[bool]] - priority_speaker: NotRequired[Optional[bool]] - stream: NotRequired[Optional[bool]] - read_messages: NotRequired[Optional[bool]] - view_channel: NotRequired[Optional[bool]] - send_messages: NotRequired[Optional[bool]] - send_tts_messages: NotRequired[Optional[bool]] - manage_messages: NotRequired[Optional[bool]] - embed_links: NotRequired[Optional[bool]] - attach_files: NotRequired[Optional[bool]] - read_message_history: NotRequired[Optional[bool]] - mention_everyone: NotRequired[Optional[bool]] - external_emojis: NotRequired[Optional[bool]] - use_external_emojis: NotRequired[Optional[bool]] - view_guild_insights: NotRequired[Optional[bool]] - connect: NotRequired[Optional[bool]] - speak: NotRequired[Optional[bool]] - mute_members: NotRequired[Optional[bool]] - deafen_members: NotRequired[Optional[bool]] - move_members: NotRequired[Optional[bool]] - use_voice_activation: NotRequired[Optional[bool]] - change_nickname: NotRequired[Optional[bool]] - manage_nicknames: NotRequired[Optional[bool]] - manage_roles: NotRequired[Optional[bool]] - manage_permissions: NotRequired[Optional[bool]] - manage_webhooks: NotRequired[Optional[bool]] - manage_expressions: NotRequired[Optional[bool]] - manage_emojis: NotRequired[Optional[bool]] - manage_emojis_and_stickers: NotRequired[Optional[bool]] - use_application_commands: NotRequired[Optional[bool]] - request_to_speak: NotRequired[Optional[bool]] - manage_events: NotRequired[Optional[bool]] - manage_threads: NotRequired[Optional[bool]] - create_public_threads: NotRequired[Optional[bool]] - create_private_threads: NotRequired[Optional[bool]] - send_messages_in_threads: NotRequired[Optional[bool]] - external_stickers: NotRequired[Optional[bool]] - use_external_stickers: NotRequired[Optional[bool]] - use_embedded_activities: NotRequired[Optional[bool]] - moderate_members: NotRequired[Optional[bool]] - use_soundboard: NotRequired[Optional[bool]] - use_external_sounds: NotRequired[Optional[bool]] - send_voice_messages: NotRequired[Optional[bool]] - create_expressions: NotRequired[Optional[bool]] - create_events: NotRequired[Optional[bool]] - send_polls: NotRequired[Optional[bool]] - create_polls: NotRequired[Optional[bool]] - use_external_apps: NotRequired[Optional[bool]] + from typing_extensions import Self, Unpack + + class _PermissionsKwargs(TypedDict, total=False): + create_instant_invite: bool + kick_members: bool + ban_members: bool + administrator: bool + manage_channels: bool + manage_guild: bool + add_reactions: bool + view_audit_log: bool + priority_speaker: bool + stream: bool + read_messages: bool + view_channel: bool + send_messages: bool + send_tts_messages: bool + manage_messages: bool + embed_links: bool + attach_files: bool + read_message_history: bool + mention_everyone: bool + external_emojis: bool + use_external_emojis: bool + view_guild_insights: bool + connect: bool + speak: bool + mute_members: bool + deafen_members: bool + move_members: bool + use_voice_activation: bool + change_nickname: bool + manage_nicknames: bool + manage_roles: bool + manage_permissions: bool + manage_webhooks: bool + manage_expressions: bool + manage_emojis: bool + manage_emojis_and_stickers: bool + use_application_commands: bool + request_to_speak: bool + manage_events: bool + manage_threads: bool + create_public_threads: bool + create_private_threads: bool + send_messages_in_threads: bool + external_stickers: bool + use_external_stickers: bool + use_embedded_activities: bool + moderate_members: bool + use_soundboard: bool + use_external_sounds: bool + send_voice_messages: bool + create_expressions: bool + create_events: bool + send_polls: bool + create_polls: bool + use_external_apps: bool + + class _PermissionOverwriteKwargs(_PermissionsKwargs, total=False): + create_instant_invite: Optional[bool] + kick_members: Optional[bool] + ban_members: Optional[bool] + administrator: Optional[bool] + manage_channels: Optional[bool] + manage_guild: Optional[bool] + add_reactions: Optional[bool] + view_audit_log: Optional[bool] + priority_speaker: Optional[bool] + stream: Optional[bool] + read_messages: Optional[bool] + view_channel: Optional[bool] + send_messages: Optional[bool] + send_tts_messages: Optional[bool] + manage_messages: Optional[bool] + embed_links: Optional[bool] + attach_files: Optional[bool] + read_message_history: Optional[bool] + mention_everyone: Optional[bool] + external_emojis: Optional[bool] + use_external_emojis: Optional[bool] + view_guild_insights: Optional[bool] + connect: Optional[bool] + speak: Optional[bool] + mute_members: Optional[bool] + deafen_members: Optional[bool] + move_members: Optional[bool] + use_voice_activation: Optional[bool] + change_nickname: Optional[bool] + manage_nicknames: Optional[bool] + manage_roles: Optional[bool] + manage_permissions: Optional[bool] + manage_webhooks: Optional[bool] + manage_expressions: Optional[bool] + manage_emojis: Optional[bool] + manage_emojis_and_stickers: Optional[bool] + use_application_commands: Optional[bool] + request_to_speak: Optional[bool] + manage_events: Optional[bool] + manage_threads: Optional[bool] + create_public_threads: Optional[bool] + create_private_threads: Optional[bool] + send_messages_in_threads: Optional[bool] + external_stickers: Optional[bool] + use_external_stickers: Optional[bool] + use_embedded_activities: Optional[bool] + moderate_members: Optional[bool] + use_soundboard: Optional[bool] + use_external_sounds: Optional[bool] + send_voice_messages: Optional[bool] + create_expressions: Optional[bool] + create_events: Optional[bool] + send_polls: Optional[bool] + create_polls: Optional[bool] + use_external_apps: Optional[bool] # A permission alias works like a regular flag but is marked diff --git a/discord/shard.py b/discord/shard.py index ecca84997..3725324d4 100644 --- a/discord/shard.py +++ b/discord/shard.py @@ -47,16 +47,16 @@ from .enums import Status from typing import TYPE_CHECKING, Any, Callable, Tuple, Type, Optional, List, Dict if TYPE_CHECKING: - from typing_extensions import Unpack, NotRequired + from typing_extensions import Unpack from .gateway import DiscordWebSocket from .activity import BaseActivity from .flags import Intents from .types.gateway import SessionStartLimit from .client import _ClientOptions - class _AutoShardedClientOptions(_ClientOptions): - shard_ids: NotRequired[Optional[List[int]]] - shard_connect_timeout: NotRequired[Optional[float]] + class _AutoShardedClientOptions(_ClientOptions, total=False): + shard_ids: List[int] + shard_connect_timeout: Optional[float] __all__ = ( From 6224fb5b8cce47ee36a8acaafd34281afb03fb67 Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Fri, 23 May 2025 09:04:40 +0200 Subject: [PATCH 23/24] Cleanup types for Permission(s)(Overwrite) --- discord/permissions.py | 179 +++++++++++++++-------------------------- 1 file changed, 65 insertions(+), 114 deletions(-) diff --git a/discord/permissions.py b/discord/permissions.py index e46f26805..6a28c6821 100644 --- a/discord/permissions.py +++ b/discord/permissions.py @@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE. from __future__ import annotations -from typing import Callable, Any, ClassVar, Dict, Iterator, Set, TYPE_CHECKING, Tuple, Optional, TypedDict +from typing import Callable, Any, ClassVar, Dict, Iterator, Set, TYPE_CHECKING, Tuple, Optional, TypedDict, Generic, TypeVar from .flags import BaseFlags, flag_value, fill_with_flags, alias_flag_value __all__ = ( @@ -35,119 +35,70 @@ __all__ = ( if TYPE_CHECKING: from typing_extensions import Self, Unpack - class _PermissionsKwargs(TypedDict, total=False): - create_instant_invite: bool - kick_members: bool - ban_members: bool - administrator: bool - manage_channels: bool - manage_guild: bool - add_reactions: bool - view_audit_log: bool - priority_speaker: bool - stream: bool - read_messages: bool - view_channel: bool - send_messages: bool - send_tts_messages: bool - manage_messages: bool - embed_links: bool - attach_files: bool - read_message_history: bool - mention_everyone: bool - external_emojis: bool - use_external_emojis: bool - view_guild_insights: bool - connect: bool - speak: bool - mute_members: bool - deafen_members: bool - move_members: bool - use_voice_activation: bool - change_nickname: bool - manage_nicknames: bool - manage_roles: bool - manage_permissions: bool - manage_webhooks: bool - manage_expressions: bool - manage_emojis: bool - manage_emojis_and_stickers: bool - use_application_commands: bool - request_to_speak: bool - manage_events: bool - manage_threads: bool - create_public_threads: bool - create_private_threads: bool - send_messages_in_threads: bool - external_stickers: bool - use_external_stickers: bool - use_embedded_activities: bool - moderate_members: bool - use_soundboard: bool - use_external_sounds: bool - send_voice_messages: bool - create_expressions: bool - create_events: bool - send_polls: bool - create_polls: bool - use_external_apps: bool - - class _PermissionOverwriteKwargs(_PermissionsKwargs, total=False): - create_instant_invite: Optional[bool] - kick_members: Optional[bool] - ban_members: Optional[bool] - administrator: Optional[bool] - manage_channels: Optional[bool] - manage_guild: Optional[bool] - add_reactions: Optional[bool] - view_audit_log: Optional[bool] - priority_speaker: Optional[bool] - stream: Optional[bool] - read_messages: Optional[bool] - view_channel: Optional[bool] - send_messages: Optional[bool] - send_tts_messages: Optional[bool] - manage_messages: Optional[bool] - embed_links: Optional[bool] - attach_files: Optional[bool] - read_message_history: Optional[bool] - mention_everyone: Optional[bool] - external_emojis: Optional[bool] - use_external_emojis: Optional[bool] - view_guild_insights: Optional[bool] - connect: Optional[bool] - speak: Optional[bool] - mute_members: Optional[bool] - deafen_members: Optional[bool] - move_members: Optional[bool] - use_voice_activation: Optional[bool] - change_nickname: Optional[bool] - manage_nicknames: Optional[bool] - manage_roles: Optional[bool] - manage_permissions: Optional[bool] - manage_webhooks: Optional[bool] - manage_expressions: Optional[bool] - manage_emojis: Optional[bool] - manage_emojis_and_stickers: Optional[bool] - use_application_commands: Optional[bool] - request_to_speak: Optional[bool] - manage_events: Optional[bool] - manage_threads: Optional[bool] - create_public_threads: Optional[bool] - create_private_threads: Optional[bool] - send_messages_in_threads: Optional[bool] - external_stickers: Optional[bool] - use_external_stickers: Optional[bool] - use_embedded_activities: Optional[bool] - moderate_members: Optional[bool] - use_soundboard: Optional[bool] - use_external_sounds: Optional[bool] - send_voice_messages: Optional[bool] - create_expressions: Optional[bool] - create_events: Optional[bool] - send_polls: Optional[bool] - create_polls: Optional[bool] - use_external_apps: Optional[bool] + BoolOrNoneT = TypeVar('BoolOrNoneT', bound=Optional[bool]) + + class _BasePermissionsKwargs(Generic[BoolOrNoneT], TypedDict, total=False): + create_instant_invite: BoolOrNoneT + kick_members: BoolOrNoneT + ban_members: BoolOrNoneT + administrator: BoolOrNoneT + manage_channels: BoolOrNoneT + manage_guild: BoolOrNoneT + add_reactions: BoolOrNoneT + view_audit_log: BoolOrNoneT + priority_speaker: BoolOrNoneT + stream: BoolOrNoneT + read_messages: BoolOrNoneT + view_channel: BoolOrNoneT + send_messages: BoolOrNoneT + send_tts_messages: BoolOrNoneT + manage_messages: BoolOrNoneT + embed_links: BoolOrNoneT + attach_files: BoolOrNoneT + read_message_history: BoolOrNoneT + mention_everyone: BoolOrNoneT + external_emojis: BoolOrNoneT + use_external_emojis: BoolOrNoneT + view_guild_insights: BoolOrNoneT + connect: BoolOrNoneT + speak: BoolOrNoneT + mute_members: BoolOrNoneT + deafen_members: BoolOrNoneT + move_members: BoolOrNoneT + use_voice_activation: BoolOrNoneT + change_nickname: BoolOrNoneT + manage_nicknames: BoolOrNoneT + manage_roles: BoolOrNoneT + manage_permissions: BoolOrNoneT + manage_webhooks: BoolOrNoneT + manage_expressions: BoolOrNoneT + manage_emojis: BoolOrNoneT + manage_emojis_and_stickers: BoolOrNoneT + use_application_commands: BoolOrNoneT + request_to_speak: BoolOrNoneT + manage_events: BoolOrNoneT + manage_threads: BoolOrNoneT + create_public_threads: BoolOrNoneT + create_private_threads: BoolOrNoneT + send_messages_in_threads: BoolOrNoneT + external_stickers: BoolOrNoneT + use_external_stickers: BoolOrNoneT + use_embedded_activities: BoolOrNoneT + moderate_members: BoolOrNoneT + use_soundboard: BoolOrNoneT + use_external_sounds: BoolOrNoneT + send_voice_messages: BoolOrNoneT + create_expressions: BoolOrNoneT + create_events: BoolOrNoneT + send_polls: BoolOrNoneT + create_polls: BoolOrNoneT + use_external_apps: BoolOrNoneT + + class _PermissionsKwargs(_BasePermissionsKwargs[bool]): + ... + + class _PermissionOverwriteKwargs(_BasePermissionsKwargs[Optional[bool]]): + ... # A permission alias works like a regular flag but is marked From 63e799d4014bb06552a15c5dbae71aedbbd64fdb Mon Sep 17 00:00:00 2001 From: Soheab_ <33902984+Soheab@users.noreply.github.com> Date: Fri, 23 May 2025 09:24:21 +0200 Subject: [PATCH 24/24] Category.create_text/voice/stage/forum(_channel) --- discord/channel.py | 55 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/discord/channel.py b/discord/channel.py index a306707d6..8f6f9546b 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -39,6 +39,7 @@ from typing import ( Sequence, Tuple, TypeVar, + TypedDict, Union, overload, ) @@ -85,7 +86,7 @@ __all__ = ( ) if TYPE_CHECKING: - from typing_extensions import Self + from typing_extensions import Self, Unpack from .types.threads import ThreadArchiveDuration from .role import Role @@ -120,6 +121,50 @@ if TYPE_CHECKING: OverwriteKeyT = TypeVar('OverwriteKeyT', Role, BaseUser, Object, Union[Role, Member, Object]) + class _CreateChannelWithCategory(TypedDict, total=False): + category: Optional[CategoryChannel] + + class _CreateNewsChannel(TypedDict, total=False): + news: bool + + class _BaseCreateChannelOptions(TypedDict, total=False): + reason: Optional[str] + position: int + + class _CreateTextChannelOptions(_BaseCreateChannelOptions, total=False): + topic: str + slowmode_delay: int + nsfw: bool + overwrites: Mapping[Union[Role, Member, Object], PermissionOverwrite] + default_auto_archive_duration: int + default_thread_slowmode_delay: int + + class _CreateVoiceChannelOptions(_BaseCreateChannelOptions, total=False): + bitrate: int + user_limit: int + rtc_region: Optional[str] + video_quality_mode: VideoQualityMode + overwrites: Mapping[Union[Role, Member, Object], PermissionOverwrite] + + class _CreateStageChannelOptions(_CreateVoiceChannelOptions, total=False): + bitrate: int + user_limit: int + rtc_region: Optional[str] + video_quality_mode: VideoQualityMode + overwrites: Mapping[Union[Role, Member, Object], PermissionOverwrite] + + class _CreateForumChannelOptions(_CreateTextChannelOptions, total=False): + topic: str + slowmode_delay: int + nsfw: bool + overwrites: Mapping[Union[Role, Member, Object], PermissionOverwrite] + default_auto_archive_duration: int + default_thread_slowmode_delay: int + default_sort_order: ForumOrderType + default_reaction_emoji: EmojiInputType + default_layout: ForumLayoutType + available_tags: Sequence[ForumTag] + class ThreadWithMessage(NamedTuple): thread: Thread @@ -2194,7 +2239,7 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable): r.sort(key=lambda c: (c.position, c.id)) return r - async def create_text_channel(self, name: str, **options: Any) -> TextChannel: + async def create_text_channel(self, name: str, **options: Unpack[_CreateTextChannelOptions]) -> TextChannel: """|coro| A shortcut method to :meth:`Guild.create_text_channel` to create a :class:`TextChannel` in the category. @@ -2206,7 +2251,7 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable): """ return await self.guild.create_text_channel(name, category=self, **options) - async def create_voice_channel(self, name: str, **options: Any) -> VoiceChannel: + async def create_voice_channel(self, name: str, **options: Unpack[_CreateVoiceChannelOptions]) -> VoiceChannel: """|coro| A shortcut method to :meth:`Guild.create_voice_channel` to create a :class:`VoiceChannel` in the category. @@ -2218,7 +2263,7 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable): """ return await self.guild.create_voice_channel(name, category=self, **options) - async def create_stage_channel(self, name: str, **options: Any) -> StageChannel: + async def create_stage_channel(self, name: str, **options: Unpack[_CreateStageChannelOptions]) -> StageChannel: """|coro| A shortcut method to :meth:`Guild.create_stage_channel` to create a :class:`StageChannel` in the category. @@ -2232,7 +2277,7 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable): """ return await self.guild.create_stage_channel(name, category=self, **options) - async def create_forum(self, name: str, **options: Any) -> ForumChannel: + async def create_forum(self, name: str, **options: Unpack[_CreateForumChannelOptions]) -> ForumChannel: """|coro| A shortcut method to :meth:`Guild.create_forum` to create a :class:`ForumChannel` in the category.