Browse Source

Merge 63e799d401 into 26855160f8

pull/10189/merge
Soheab 6 days ago
committed by GitHub
parent
commit
b388571010
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 7
      discord/abc.py
  2. 7
      discord/app_commands/checks.py
  3. 5
      discord/app_commands/commands.py
  4. 55
      discord/channel.py
  5. 27
      discord/client.py
  6. 46
      discord/ext/commands/bot.py
  7. 18
      discord/ext/commands/cog.py
  8. 81
      discord/ext/commands/core.py
  9. 51
      discord/ext/commands/help.py
  10. 63
      discord/ext/commands/hybrid.py
  11. 46
      discord/flags.py
  12. 84
      discord/permissions.py
  13. 8
      discord/shard.py

7
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|

7
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`.

5
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.

55
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.

27
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, Unpack
from .abc import Messageable, PrivateChannel, Snowflake, SnowflakeTime
from .app_commands import Command, ContextMenu
@ -120,6 +121,28 @@ 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):
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
@ -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, *, 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

46
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
import importlib.machinery
@ -80,12 +80,24 @@ 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
_Prefix = Union[Iterable[str], str]
_PrefixCallable = MaybeAwaitableFunc[[BotT, Message], _Prefix]
PrefixType = Union[_Prefix, _PrefixCallable[BotT]]
class _BotOptions(_ClientOptions, total=False):
owner_id: int
owner_ids: Collection[int]
strip_after_prefix: bool
case_insensitive: bool
class _AutoShardedBotOptions(_AutoShardedClientOptions, _BotOptions):
...
__all__ = (
'when_mentioned',
'when_mentioned_or',
@ -169,7 +181,7 @@ class BotBase(GroupMixin[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)
self.command_prefix: PrefixType[BotT] = command_prefix # type: ignore
@ -281,7 +293,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`.
@ -293,8 +305,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
@ -305,7 +317,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`.
@ -317,8 +329,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
@ -1527,4 +1539,18 @@ class AutoShardedBot(BotBase, discord.AutoShardedClient):
.. versionadded:: 2.0
"""
pass
if TYPE_CHECKING:
def __init__(
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:
...

18
discord/ext/commands/cog.py

@ -44,18 +44,30 @@ 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
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, 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__ = (
'CogMeta',
@ -169,7 +181,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(

81
discord/ext/commands/core.py

@ -43,6 +43,7 @@ from typing import (
TypeVar,
Union,
overload,
TypedDict,
)
import re
@ -58,10 +59,39 @@ 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, Unpack
from ._types import BotT, Check, ContextT, Coro, CoroFunc, Error, Hook, UserCheck
from discord.permissions import _PermissionsKwargs
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__ = (
'Command',
@ -368,9 +398,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,7 +423,7 @@ 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.')
@ -556,7 +586,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 +594,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 +1498,7 @@ class GroupMixin(Generic[CogT]):
self: GroupMixin[CogT],
name: str = ...,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_CommandDecoratorKwargs],
) -> Callable[
[
Union[
@ -1486,7 +1516,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 +1533,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 +1545,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 +1557,7 @@ class GroupMixin(Generic[CogT]):
self: GroupMixin[CogT],
name: str = ...,
*args: Any,
**kwargs: Any,
**kwargs: Unpack[_GroupDecoratorKwargs],
) -> Callable[
[
Union[
@ -1546,7 +1575,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 +1592,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 +1604,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 +1635,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 +1757,7 @@ if TYPE_CHECKING:
@overload
def command(
name: str = ...,
**attrs: Any,
**attrs: Unpack[_CommandDecoratorKwargs],
) -> _CommandDecorator:
...
@ -1737,7 +1766,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 +1782,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 +1827,7 @@ def command(
@overload
def group(
name: str = ...,
**attrs: Any,
**attrs: Unpack[_GroupDecoratorKwargs],
) -> _GroupDecorator:
...
@ -1807,7 +1836,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 +1852,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`.
@ -2165,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.
@ -2212,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.
@ -2237,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.
@ -2266,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.

51
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
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, 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__ = (
'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)

63
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
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
from ._types import ContextT, Coro, BotT
from .bot import Bot
from .context import Context
@ -60,6 +48,29 @@ if TYPE_CHECKING:
AutocompleteCallback,
ChoiceT,
)
from .core import _CommandKwargs
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__ = (
@ -501,7 +512,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 +632,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 +836,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 +848,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 +860,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 +872,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 +884,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 +927,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 +936,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 +960,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

46
discord/flags.py

@ -40,12 +40,48 @@ 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
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__ = (
@ -754,12 +790,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:
@ -1415,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():

84
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, Generic, TypeVar
from .flags import BaseFlags, flag_value, fill_with_flags, alias_flag_value
__all__ = (
@ -33,7 +33,73 @@ __all__ = (
)
if TYPE_CHECKING:
from typing_extensions import Self
from typing_extensions import Self, Unpack
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
# So the PermissionOverwrite knows to work with it
@ -135,18 +201,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."""
@ -381,7 +447,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
@ -396,7 +462,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.
@ -908,7 +974,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():
@ -970,7 +1036,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

8
discord/shard.py

@ -52,6 +52,12 @@ if TYPE_CHECKING:
from .activity import BaseActivity
from .flags import Intents
from .types.gateway import SessionStartLimit
from .client import _ClientOptions
class _AutoShardedClientOptions(_ClientOptions, total=False):
shard_ids: List[int]
shard_connect_timeout: Optional[float]
__all__ = (
'AutoShardedClient',
@ -365,7 +371,7 @@ 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)

Loading…
Cancel
Save