Browse Source

Merge branch 'master' of https://github.com/rapptz/discord.py

pull/10166/head
DA-344 4 months ago
parent
commit
b2f77b9498
  1. 2
      .github/workflows/lint.yml
  2. 4
      discord/__init__.py
  3. 10
      discord/__main__.py
  4. 7
      discord/abc.py
  5. 10
      discord/activity.py
  6. 8
      discord/app_commands/commands.py
  7. 2
      discord/app_commands/transformers.py
  8. 2
      discord/app_commands/tree.py
  9. 129
      discord/appinfo.py
  10. 8
      discord/audit_logs.py
  11. 254
      discord/client.py
  12. 6
      discord/components.py
  13. 21
      discord/embeds.py
  14. 12
      discord/enums.py
  15. 4
      discord/ext/commands/bot.py
  16. 9
      discord/ext/commands/context.py
  17. 6
      discord/ext/commands/converter.py
  18. 2
      discord/ext/commands/core.py
  19. 7
      discord/ext/commands/errors.py
  20. 2
      discord/ext/commands/flags.py
  21. 13
      discord/ext/commands/hybrid.py
  22. 89
      discord/flags.py
  23. 4
      discord/gateway.py
  24. 5
      discord/guild.py
  25. 1
      discord/http.py
  26. 214
      discord/interactions.py
  27. 2
      discord/invite.py
  28. 10
      discord/member.py
  29. 23
      discord/message.py
  30. 9
      discord/poll.py
  31. 14
      discord/raw_models.py
  32. 110
      discord/role.py
  33. 8
      discord/state.py
  34. 2
      discord/threads.py
  35. 7
      discord/types/appinfo.py
  36. 2
      discord/types/embed.py
  37. 4
      discord/types/guild.py
  38. 34
      discord/types/interactions.py
  39. 5
      discord/ui/select.py
  40. 7
      discord/ui/view.py
  41. 6
      discord/utils.py
  42. 2
      discord/voice_state.py
  43. 45
      discord/webhook/async_.py
  44. 26
      discord/webhook/sync.py
  45. 2
      discord/widget.py
  46. 16
      docs/api.rst
  47. 5
      docs/ext/commands/api.rst
  48. 16
      docs/interactions/api.rst
  49. 154
      docs/whats_new.rst

2
.github/workflows/lint.yml

@ -38,7 +38,7 @@ jobs:
- name: Run Pyright - name: Run Pyright
uses: jakebailey/pyright-action@v1 uses: jakebailey/pyright-action@v1
with: with:
version: '1.1.351' version: '1.1.394'
warnings: false warnings: false
no-comments: ${{ matrix.python-version != '3.x' }} no-comments: ${{ matrix.python-version != '3.x' }}

4
discord/__init__.py

@ -13,7 +13,7 @@ __title__ = 'discord'
__author__ = 'Rapptz' __author__ = 'Rapptz'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright 2015-present Rapptz' __copyright__ = 'Copyright 2015-present Rapptz'
__version__ = '2.5.0a' __version__ = '2.6.0a'
__path__ = __import__('pkgutil').extend_path(__path__, __name__) __path__ = __import__('pkgutil').extend_path(__path__, __name__)
@ -83,7 +83,7 @@ class VersionInfo(NamedTuple):
serial: int serial: int
version_info: VersionInfo = VersionInfo(major=2, minor=5, micro=0, releaselevel='alpha', serial=0) version_info: VersionInfo = VersionInfo(major=2, minor=6, micro=0, releaselevel='alpha', serial=0)
logging.getLogger(__name__).addHandler(logging.NullHandler()) logging.getLogger(__name__).addHandler(logging.NullHandler())

10
discord/__main__.py

@ -28,7 +28,7 @@ from typing import Optional, Tuple, Dict
import argparse import argparse
import sys import sys
from pathlib import Path from pathlib import Path, PurePath, PureWindowsPath
import discord import discord
import importlib.metadata import importlib.metadata
@ -225,8 +225,14 @@ def to_path(parser: argparse.ArgumentParser, name: str, *, replace_spaces: bool
) )
if len(name) <= 4 and name.upper() in forbidden: if len(name) <= 4 and name.upper() in forbidden:
parser.error('invalid directory name given, use a different one') parser.error('invalid directory name given, use a different one')
path = PurePath(name)
if isinstance(path, PureWindowsPath) and path.drive:
drive, rest = path.parts[0], path.parts[1:]
transformed = tuple(map(lambda p: p.translate(_translation_table), rest))
name = drive + '\\'.join(transformed)
name = name.translate(_translation_table) else:
name = name.translate(_translation_table)
if replace_spaces: if replace_spaces:
name = name.replace(' ', '-') name = name.replace(' ', '-')
return Path(name) return Path(name)

7
discord/abc.py

@ -102,6 +102,9 @@ if TYPE_CHECKING:
GuildChannel as GuildChannelPayload, GuildChannel as GuildChannelPayload,
OverwriteType, OverwriteType,
) )
from .types.guild import (
ChannelPositionUpdate,
)
from .types.snowflake import ( from .types.snowflake import (
SnowflakeList, SnowflakeList,
) )
@ -1232,11 +1235,11 @@ class GuildChannel:
raise ValueError('Could not resolve appropriate move position') raise ValueError('Could not resolve appropriate move position')
channels.insert(max((index + offset), 0), self) channels.insert(max((index + offset), 0), self)
payload = [] payload: List[ChannelPositionUpdate] = []
lock_permissions = kwargs.get('sync_permissions', False) lock_permissions = kwargs.get('sync_permissions', False)
reason = kwargs.get('reason') reason = kwargs.get('reason')
for index, channel in enumerate(channels): for index, channel in enumerate(channels):
d = {'id': channel.id, 'position': index} d: ChannelPositionUpdate = {'id': channel.id, 'position': index}
if parent_id is not MISSING and channel.id == self.id: if parent_id is not MISSING and channel.id == self.id:
d.update(parent_id=parent_id, lock_permissions=lock_permissions) d.update(parent_id=parent_id, lock_permissions=lock_permissions)
payload.append(d) payload.append(d)

10
discord/activity.py

@ -273,7 +273,7 @@ class Activity(BaseActivity):
def start(self) -> Optional[datetime.datetime]: def start(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user started doing this activity in UTC, if applicable.""" """Optional[:class:`datetime.datetime`]: When the user started doing this activity in UTC, if applicable."""
try: try:
timestamp = self.timestamps['start'] / 1000 timestamp = self.timestamps['start'] / 1000 # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
return None return None
else: else:
@ -283,7 +283,7 @@ class Activity(BaseActivity):
def end(self) -> Optional[datetime.datetime]: def end(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: When the user will stop doing this activity in UTC, if applicable.""" """Optional[:class:`datetime.datetime`]: When the user will stop doing this activity in UTC, if applicable."""
try: try:
timestamp = self.timestamps['end'] / 1000 timestamp = self.timestamps['end'] / 1000 # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
return None return None
else: else:
@ -293,7 +293,7 @@ class Activity(BaseActivity):
def large_image_url(self) -> Optional[str]: def large_image_url(self) -> Optional[str]:
"""Optional[:class:`str`]: Returns a URL pointing to the large image asset of this activity, if applicable.""" """Optional[:class:`str`]: Returns a URL pointing to the large image asset of this activity, if applicable."""
try: try:
large_image = self.assets['large_image'] large_image = self.assets['large_image'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
return None return None
else: else:
@ -303,7 +303,7 @@ class Activity(BaseActivity):
def small_image_url(self) -> Optional[str]: def small_image_url(self) -> Optional[str]:
"""Optional[:class:`str`]: Returns a URL pointing to the small image asset of this activity, if applicable.""" """Optional[:class:`str`]: Returns a URL pointing to the small image asset of this activity, if applicable."""
try: try:
small_image = self.assets['small_image'] small_image = self.assets['small_image'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
return None return None
else: else:
@ -525,7 +525,7 @@ class Streaming(BaseActivity):
""" """
try: try:
name = self.assets['large_image'] name = self.assets['large_image'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
return None return None
else: else:

8
discord/app_commands/commands.py

@ -903,7 +903,7 @@ class Command(Generic[GroupT, P, T]):
predicates = getattr(param.autocomplete, '__discord_app_commands_checks__', []) predicates = getattr(param.autocomplete, '__discord_app_commands_checks__', [])
if predicates: if predicates:
try: try:
passed = await async_all(f(interaction) for f in predicates) passed = await async_all(f(interaction) for f in predicates) # type: ignore
except Exception: except Exception:
passed = False passed = False
@ -1014,7 +1014,7 @@ class Command(Generic[GroupT, P, T]):
if not predicates: if not predicates:
return True return True
return await async_all(f(interaction) for f in predicates) return await async_all(f(interaction) for f in predicates) # type: ignore
def error(self, coro: Error[GroupT]) -> Error[GroupT]: def error(self, coro: Error[GroupT]) -> Error[GroupT]:
"""A decorator that registers a coroutine as a local error handler. """A decorator that registers a coroutine as a local error handler.
@ -1308,7 +1308,7 @@ class ContextMenu:
if not predicates: if not predicates:
return True return True
return await async_all(f(interaction) for f in predicates) return await async_all(f(interaction) for f in predicates) # type: ignore
def _has_any_error_handlers(self) -> bool: def _has_any_error_handlers(self) -> bool:
return self.on_error is not None return self.on_error is not None
@ -1842,7 +1842,7 @@ class Group:
if len(params) != 2: if len(params) != 2:
raise TypeError('The error handler must have 2 parameters.') raise TypeError('The error handler must have 2 parameters.')
self.on_error = coro self.on_error = coro # type: ignore
return coro return coro
async def interaction_check(self, interaction: Interaction, /) -> bool: async def interaction_check(self, interaction: Interaction, /) -> bool:

2
discord/app_commands/transformers.py

@ -235,7 +235,7 @@ class Transformer(Generic[ClientT]):
pass pass
def __or__(self, rhs: Any) -> Any: def __or__(self, rhs: Any) -> Any:
return Union[self, rhs] # type: ignore return Union[self, rhs]
@property @property
def type(self) -> AppCommandOptionType: def type(self) -> AppCommandOptionType:

2
discord/app_commands/tree.py

@ -859,7 +859,7 @@ class CommandTree(Generic[ClientT]):
if len(params) != 2: if len(params) != 2:
raise TypeError('error handler must have 2 parameters') raise TypeError('error handler must have 2 parameters')
self.on_error = coro self.on_error = coro # type: ignore
return coro return coro
def command( def command(

129
discord/appinfo.py

@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
from typing import List, TYPE_CHECKING, Optional from typing import List, TYPE_CHECKING, Literal, Optional
from . import utils from . import utils
from .asset import Asset from .asset import Asset
@ -41,6 +41,7 @@ if TYPE_CHECKING:
PartialAppInfo as PartialAppInfoPayload, PartialAppInfo as PartialAppInfoPayload,
Team as TeamPayload, Team as TeamPayload,
InstallParams as InstallParamsPayload, InstallParams as InstallParamsPayload,
AppIntegrationTypeConfig as AppIntegrationTypeConfigPayload,
) )
from .user import User from .user import User
from .state import ConnectionState from .state import ConnectionState
@ -49,6 +50,7 @@ __all__ = (
'AppInfo', 'AppInfo',
'PartialAppInfo', 'PartialAppInfo',
'AppInstallParams', 'AppInstallParams',
'IntegrationTypeConfig',
) )
@ -180,6 +182,7 @@ class AppInfo:
'redirect_uris', 'redirect_uris',
'approximate_guild_count', 'approximate_guild_count',
'approximate_user_install_count', 'approximate_user_install_count',
'_integration_types_config',
) )
def __init__(self, state: ConnectionState, data: AppInfoPayload): def __init__(self, state: ConnectionState, data: AppInfoPayload):
@ -218,6 +221,9 @@ class AppInfo:
self.redirect_uris: List[str] = data.get('redirect_uris', []) self.redirect_uris: List[str] = data.get('redirect_uris', [])
self.approximate_guild_count: int = data.get('approximate_guild_count', 0) self.approximate_guild_count: int = data.get('approximate_guild_count', 0)
self.approximate_user_install_count: Optional[int] = data.get('approximate_user_install_count') self.approximate_user_install_count: Optional[int] = data.get('approximate_user_install_count')
self._integration_types_config: Dict[Literal['0', '1'], AppIntegrationTypeConfigPayload] = data.get(
'integration_types_config', {}
)
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (
@ -260,6 +266,36 @@ class AppInfo:
""" """
return ApplicationFlags._from_value(self._flags) return ApplicationFlags._from_value(self._flags)
@property
def guild_integration_config(self) -> Optional[IntegrationTypeConfig]:
"""Optional[:class:`IntegrationTypeConfig`]: The default settings for the
application's installation context in a guild.
.. versionadded:: 2.5
"""
if not self._integration_types_config:
return None
try:
return IntegrationTypeConfig(self._integration_types_config['0'])
except KeyError:
return None
@property
def user_integration_config(self) -> Optional[IntegrationTypeConfig]:
"""Optional[:class:`IntegrationTypeConfig`]: The default settings for the
application's installation context as a user.
.. versionadded:: 2.5
"""
if not self._integration_types_config:
return None
try:
return IntegrationTypeConfig(self._integration_types_config['1'])
except KeyError:
return None
async def edit( async def edit(
self, self,
*, *,
@ -274,6 +310,10 @@ class AppInfo:
cover_image: Optional[bytes] = MISSING, cover_image: Optional[bytes] = MISSING,
interactions_endpoint_url: Optional[str] = MISSING, interactions_endpoint_url: Optional[str] = MISSING,
tags: Optional[List[str]] = MISSING, tags: Optional[List[str]] = MISSING,
guild_install_scopes: Optional[List[str]] = MISSING,
guild_install_permissions: Optional[Permissions] = MISSING,
user_install_scopes: Optional[List[str]] = MISSING,
user_install_permissions: Optional[Permissions] = MISSING,
) -> AppInfo: ) -> AppInfo:
r"""|coro| r"""|coro|
@ -315,6 +355,24 @@ class AppInfo:
over the gateway. Can be ``None`` to remove the URL. over the gateway. Can be ``None`` to remove the URL.
tags: Optional[List[:class:`str`]] tags: Optional[List[:class:`str`]]
The new list of tags describing the functionality of the application. Can be ``None`` to remove the tags. The new list of tags describing the functionality of the application. Can be ``None`` to remove the tags.
guild_install_scopes: Optional[List[:class:`str`]]
The new list of :ddocs:`OAuth2 scopes <topics/oauth2#shared-resources-oauth2-scopes>` of
the default guild installation context. Can be ``None`` to remove the scopes.
.. versionadded: 2.5
guild_install_permissions: Optional[:class:`Permissions`]
The new permissions of the default guild installation context. Can be ``None`` to remove the permissions.
.. versionadded: 2.5
user_install_scopes: Optional[List[:class:`str`]]
The new list of :ddocs:`OAuth2 scopes <topics/oauth2#shared-resources-oauth2-scopes>` of
the default user installation context. Can be ``None`` to remove the scopes.
.. versionadded: 2.5
user_install_permissions: Optional[:class:`Permissions`]
The new permissions of the default user installation context. Can be ``None`` to remove the permissions.
.. versionadded: 2.5
reason: Optional[:class:`str`] reason: Optional[:class:`str`]
The reason for editing the application. Shows up on the audit log. The reason for editing the application. Shows up on the audit log.
@ -324,7 +382,8 @@ class AppInfo:
Editing the application failed Editing the application failed
ValueError ValueError
The image format passed in to ``icon`` or ``cover_image`` is invalid. This is also raised The image format passed in to ``icon`` or ``cover_image`` is invalid. This is also raised
when ``install_params_scopes`` and ``install_params_permissions`` are incompatible with each other. when ``install_params_scopes`` and ``install_params_permissions`` are incompatible with each other,
or when ``guild_install_scopes`` and ``guild_install_permissions`` are incompatible with each other.
Returns Returns
------- -------
@ -364,7 +423,7 @@ class AppInfo:
else: else:
if install_params_permissions is not MISSING: if install_params_permissions is not MISSING:
raise ValueError("install_params_scopes must be set if install_params_permissions is set") raise ValueError('install_params_scopes must be set if install_params_permissions is set')
if flags is not MISSING: if flags is not MISSING:
if flags is None: if flags is None:
@ -389,6 +448,51 @@ class AppInfo:
if tags is not MISSING: if tags is not MISSING:
payload['tags'] = tags payload['tags'] = tags
integration_types_config: Dict[str, Any] = {}
if guild_install_scopes is not MISSING or guild_install_permissions is not MISSING:
guild_install_params: Optional[Dict[str, Any]] = {}
if guild_install_scopes in (None, MISSING):
guild_install_scopes = []
if 'bot' not in guild_install_scopes and guild_install_permissions is not MISSING:
raise ValueError("'bot' must be in guild_install_scopes if guild_install_permissions is set")
if guild_install_permissions in (None, MISSING):
guild_install_params['permissions'] = 0
else:
guild_install_params['permissions'] = guild_install_permissions.value
guild_install_params['scopes'] = guild_install_scopes
integration_types_config['0'] = {'oauth2_install_params': guild_install_params or None}
else:
if guild_install_permissions is not MISSING:
raise ValueError('guild_install_scopes must be set if guild_install_permissions is set')
if user_install_scopes is not MISSING or user_install_permissions is not MISSING:
user_install_params: Optional[Dict[str, Any]] = {}
if user_install_scopes in (None, MISSING):
user_install_scopes = []
if 'bot' not in user_install_scopes and user_install_permissions is not MISSING:
raise ValueError("'bot' must be in user_install_scopes if user_install_permissions is set")
if user_install_permissions in (None, MISSING):
user_install_params['permissions'] = 0
else:
user_install_params['permissions'] = user_install_permissions.value
user_install_params['scopes'] = user_install_scopes
integration_types_config['1'] = {'oauth2_install_params': user_install_params or None}
else:
if user_install_permissions is not MISSING:
raise ValueError('user_install_scopes must be set if user_install_permissions is set')
if integration_types_config:
payload['integration_types_config'] = integration_types_config
data = await self._state.http.edit_application_info(reason=reason, payload=payload) data = await self._state.http.edit_application_info(reason=reason, payload=payload)
return AppInfo(data=data, state=self._state) return AppInfo(data=data, state=self._state)
@ -520,3 +624,22 @@ class AppInstallParams:
def __init__(self, data: InstallParamsPayload) -> None: def __init__(self, data: InstallParamsPayload) -> None:
self.scopes: List[str] = data.get('scopes', []) self.scopes: List[str] = data.get('scopes', [])
self.permissions: Permissions = Permissions(int(data['permissions'])) self.permissions: Permissions = Permissions(int(data['permissions']))
class IntegrationTypeConfig:
"""Represents the default settings for the application's installation context.
.. versionadded:: 2.5
Attributes
----------
oauth2_install_params: Optional[:class:`AppInstallParams`]
The install params for this installation context's default in-app authorization link.
"""
def __init__(self, data: AppIntegrationTypeConfigPayload) -> None:
self.oauth2_install_params: Optional[AppInstallParams] = None
try:
self.oauth2_install_params = AppInstallParams(data['oauth2_install_params']) # type: ignore # EAFP
except KeyError:
pass

8
discord/audit_logs.py

@ -874,7 +874,13 @@ class AuditLogEntry(Hashable):
def _convert_target_emoji(self, target_id: int) -> Union[Emoji, Object]: def _convert_target_emoji(self, target_id: int) -> Union[Emoji, Object]:
return self._state.get_emoji(target_id) or Object(id=target_id, type=Emoji) return self._state.get_emoji(target_id) or Object(id=target_id, type=Emoji)
def _convert_target_message(self, target_id: int) -> Union[Member, User, Object]: def _convert_target_message(self, target_id: Optional[int]) -> Optional[Union[Member, User, Object]]:
# The message_pin and message_unpin action types do not have a
# non-null target_id so safeguard against that
if target_id is None:
return None
return self._get_member(target_id) or Object(id=target_id, type=Member) return self._get_member(target_id) or Object(id=target_id, type=Member)
def _convert_target_stage_instance(self, target_id: int) -> Union[StageInstance, Object]: def _convert_target_stage_instance(self, target_id: int) -> Union[StageInstance, Object]:

254
discord/client.py

@ -1213,8 +1213,8 @@ class Client:
event: Literal['raw_app_command_permissions_update'], event: Literal['raw_app_command_permissions_update'],
/, /,
*, *,
check: Optional[Callable[[RawAppCommandPermissionsUpdateEvent], bool]], check: Optional[Callable[[RawAppCommandPermissionsUpdateEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawAppCommandPermissionsUpdateEvent: ) -> RawAppCommandPermissionsUpdateEvent:
... ...
@ -1224,8 +1224,8 @@ class Client:
event: Literal['app_command_completion'], event: Literal['app_command_completion'],
/, /,
*, *,
check: Optional[Callable[[Interaction[Self], Union[Command[Any, ..., Any], ContextMenu]], bool]], check: Optional[Callable[[Interaction[Self], Union[Command[Any, ..., Any], ContextMenu]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Interaction[Self], Union[Command[Any, ..., Any], ContextMenu]]: ) -> Tuple[Interaction[Self], Union[Command[Any, ..., Any], ContextMenu]]:
... ...
@ -1237,8 +1237,8 @@ class Client:
event: Literal['automod_rule_create', 'automod_rule_update', 'automod_rule_delete'], event: Literal['automod_rule_create', 'automod_rule_update', 'automod_rule_delete'],
/, /,
*, *,
check: Optional[Callable[[AutoModRule], bool]], check: Optional[Callable[[AutoModRule], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> AutoModRule: ) -> AutoModRule:
... ...
@ -1248,8 +1248,8 @@ class Client:
event: Literal['automod_action'], event: Literal['automod_action'],
/, /,
*, *,
check: Optional[Callable[[AutoModAction], bool]], check: Optional[Callable[[AutoModAction], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> AutoModAction: ) -> AutoModAction:
... ...
@ -1261,8 +1261,8 @@ class Client:
event: Literal['private_channel_update'], event: Literal['private_channel_update'],
/, /,
*, *,
check: Optional[Callable[[GroupChannel, GroupChannel], bool]], check: Optional[Callable[[GroupChannel, GroupChannel], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[GroupChannel, GroupChannel]: ) -> Tuple[GroupChannel, GroupChannel]:
... ...
@ -1272,8 +1272,8 @@ class Client:
event: Literal['private_channel_pins_update'], event: Literal['private_channel_pins_update'],
/, /,
*, *,
check: Optional[Callable[[PrivateChannel, datetime.datetime], bool]], check: Optional[Callable[[PrivateChannel, datetime.datetime], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[PrivateChannel, datetime.datetime]: ) -> Tuple[PrivateChannel, datetime.datetime]:
... ...
@ -1283,8 +1283,8 @@ class Client:
event: Literal['guild_channel_delete', 'guild_channel_create'], event: Literal['guild_channel_delete', 'guild_channel_create'],
/, /,
*, *,
check: Optional[Callable[[GuildChannel], bool]], check: Optional[Callable[[GuildChannel], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> GuildChannel: ) -> GuildChannel:
... ...
@ -1294,8 +1294,8 @@ class Client:
event: Literal['guild_channel_update'], event: Literal['guild_channel_update'],
/, /,
*, *,
check: Optional[Callable[[GuildChannel, GuildChannel], bool]], check: Optional[Callable[[GuildChannel, GuildChannel], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[GuildChannel, GuildChannel]: ) -> Tuple[GuildChannel, GuildChannel]:
... ...
@ -1311,7 +1311,7 @@ class Client:
bool, bool,
] ]
], ],
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Union[GuildChannel, Thread], Optional[datetime.datetime]]: ) -> Tuple[Union[GuildChannel, Thread], Optional[datetime.datetime]]:
... ...
@ -1321,8 +1321,8 @@ class Client:
event: Literal['typing'], event: Literal['typing'],
/, /,
*, *,
check: Optional[Callable[[Messageable, Union[User, Member], datetime.datetime], bool]], check: Optional[Callable[[Messageable, Union[User, Member], datetime.datetime], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Messageable, Union[User, Member], datetime.datetime]: ) -> Tuple[Messageable, Union[User, Member], datetime.datetime]:
... ...
@ -1332,8 +1332,8 @@ class Client:
event: Literal['raw_typing'], event: Literal['raw_typing'],
/, /,
*, *,
check: Optional[Callable[[RawTypingEvent], bool]], check: Optional[Callable[[RawTypingEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawTypingEvent: ) -> RawTypingEvent:
... ...
@ -1345,8 +1345,8 @@ class Client:
event: Literal['connect', 'disconnect', 'ready', 'resumed'], event: Literal['connect', 'disconnect', 'ready', 'resumed'],
/, /,
*, *,
check: Optional[Callable[[], bool]], check: Optional[Callable[[], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> None: ) -> None:
... ...
@ -1356,8 +1356,8 @@ class Client:
event: Literal['shard_connect', 'shard_disconnect', 'shard_ready', 'shard_resumed'], event: Literal['shard_connect', 'shard_disconnect', 'shard_ready', 'shard_resumed'],
/, /,
*, *,
check: Optional[Callable[[int], bool]], check: Optional[Callable[[int], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> int: ) -> int:
... ...
@ -1367,8 +1367,8 @@ class Client:
event: Literal['socket_event_type', 'socket_raw_receive'], event: Literal['socket_event_type', 'socket_raw_receive'],
/, /,
*, *,
check: Optional[Callable[[str], bool]], check: Optional[Callable[[str], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> str: ) -> str:
... ...
@ -1378,8 +1378,8 @@ class Client:
event: Literal['socket_raw_send'], event: Literal['socket_raw_send'],
/, /,
*, *,
check: Optional[Callable[[Union[str, bytes]], bool]], check: Optional[Callable[[Union[str, bytes]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Union[str, bytes]: ) -> Union[str, bytes]:
... ...
@ -1390,8 +1390,8 @@ class Client:
event: Literal['entitlement_create', 'entitlement_update', 'entitlement_delete'], event: Literal['entitlement_create', 'entitlement_update', 'entitlement_delete'],
/, /,
*, *,
check: Optional[Callable[[Entitlement], bool]], check: Optional[Callable[[Entitlement], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Entitlement: ) -> Entitlement:
... ...
@ -1408,8 +1408,8 @@ class Client:
], ],
/, /,
*, *,
check: Optional[Callable[[Guild], bool]], check: Optional[Callable[[Guild], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Guild: ) -> Guild:
... ...
@ -1419,8 +1419,8 @@ class Client:
event: Literal['guild_update'], event: Literal['guild_update'],
/, /,
*, *,
check: Optional[Callable[[Guild, Guild], bool]], check: Optional[Callable[[Guild, Guild], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Guild, Guild]: ) -> Tuple[Guild, Guild]:
... ...
@ -1430,8 +1430,8 @@ class Client:
event: Literal['guild_emojis_update'], event: Literal['guild_emojis_update'],
/, /,
*, *,
check: Optional[Callable[[Guild, Sequence[Emoji], Sequence[Emoji]], bool]], check: Optional[Callable[[Guild, Sequence[Emoji], Sequence[Emoji]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Guild, Sequence[Emoji], Sequence[Emoji]]: ) -> Tuple[Guild, Sequence[Emoji], Sequence[Emoji]]:
... ...
@ -1441,8 +1441,8 @@ class Client:
event: Literal['guild_stickers_update'], event: Literal['guild_stickers_update'],
/, /,
*, *,
check: Optional[Callable[[Guild, Sequence[GuildSticker], Sequence[GuildSticker]], bool]], check: Optional[Callable[[Guild, Sequence[GuildSticker], Sequence[GuildSticker]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Guild, Sequence[GuildSticker], Sequence[GuildSticker]]: ) -> Tuple[Guild, Sequence[GuildSticker], Sequence[GuildSticker]]:
... ...
@ -1452,8 +1452,8 @@ class Client:
event: Literal['invite_create', 'invite_delete'], event: Literal['invite_create', 'invite_delete'],
/, /,
*, *,
check: Optional[Callable[[Invite], bool]], check: Optional[Callable[[Invite], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Invite: ) -> Invite:
... ...
@ -1463,8 +1463,8 @@ class Client:
event: Literal['audit_log_entry_create'], event: Literal['audit_log_entry_create'],
/, /,
*, *,
check: Optional[Callable[[AuditLogEntry], bool]], check: Optional[Callable[[AuditLogEntry], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> AuditLogEntry: ) -> AuditLogEntry:
... ...
@ -1476,8 +1476,8 @@ class Client:
event: Literal['integration_create', 'integration_update'], event: Literal['integration_create', 'integration_update'],
/, /,
*, *,
check: Optional[Callable[[Integration], bool]], check: Optional[Callable[[Integration], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Integration: ) -> Integration:
... ...
@ -1487,8 +1487,8 @@ class Client:
event: Literal['guild_integrations_update'], event: Literal['guild_integrations_update'],
/, /,
*, *,
check: Optional[Callable[[Guild], bool]], check: Optional[Callable[[Guild], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Guild: ) -> Guild:
... ...
@ -1498,8 +1498,8 @@ class Client:
event: Literal['webhooks_update'], event: Literal['webhooks_update'],
/, /,
*, *,
check: Optional[Callable[[GuildChannel], bool]], check: Optional[Callable[[GuildChannel], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> GuildChannel: ) -> GuildChannel:
... ...
@ -1509,8 +1509,8 @@ class Client:
event: Literal['raw_integration_delete'], event: Literal['raw_integration_delete'],
/, /,
*, *,
check: Optional[Callable[[RawIntegrationDeleteEvent], bool]], check: Optional[Callable[[RawIntegrationDeleteEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawIntegrationDeleteEvent: ) -> RawIntegrationDeleteEvent:
... ...
@ -1522,8 +1522,8 @@ class Client:
event: Literal['interaction'], event: Literal['interaction'],
/, /,
*, *,
check: Optional[Callable[[Interaction[Self]], bool]], check: Optional[Callable[[Interaction[Self]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Interaction[Self]: ) -> Interaction[Self]:
... ...
@ -1535,8 +1535,8 @@ class Client:
event: Literal['member_join', 'member_remove'], event: Literal['member_join', 'member_remove'],
/, /,
*, *,
check: Optional[Callable[[Member], bool]], check: Optional[Callable[[Member], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Member: ) -> Member:
... ...
@ -1546,8 +1546,8 @@ class Client:
event: Literal['raw_member_remove'], event: Literal['raw_member_remove'],
/, /,
*, *,
check: Optional[Callable[[RawMemberRemoveEvent], bool]], check: Optional[Callable[[RawMemberRemoveEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawMemberRemoveEvent: ) -> RawMemberRemoveEvent:
... ...
@ -1557,8 +1557,8 @@ class Client:
event: Literal['member_update', 'presence_update'], event: Literal['member_update', 'presence_update'],
/, /,
*, *,
check: Optional[Callable[[Member, Member], bool]], check: Optional[Callable[[Member, Member], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Member, Member]: ) -> Tuple[Member, Member]:
... ...
@ -1568,8 +1568,8 @@ class Client:
event: Literal['user_update'], event: Literal['user_update'],
/, /,
*, *,
check: Optional[Callable[[User, User], bool]], check: Optional[Callable[[User, User], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[User, User]: ) -> Tuple[User, User]:
... ...
@ -1579,8 +1579,8 @@ class Client:
event: Literal['member_ban'], event: Literal['member_ban'],
/, /,
*, *,
check: Optional[Callable[[Guild, Union[User, Member]], bool]], check: Optional[Callable[[Guild, Union[User, Member]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Guild, Union[User, Member]]: ) -> Tuple[Guild, Union[User, Member]]:
... ...
@ -1590,8 +1590,8 @@ class Client:
event: Literal['member_unban'], event: Literal['member_unban'],
/, /,
*, *,
check: Optional[Callable[[Guild, User], bool]], check: Optional[Callable[[Guild, User], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Guild, User]: ) -> Tuple[Guild, User]:
... ...
@ -1603,8 +1603,8 @@ class Client:
event: Literal['message', 'message_delete'], event: Literal['message', 'message_delete'],
/, /,
*, *,
check: Optional[Callable[[Message], bool]], check: Optional[Callable[[Message], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Message: ) -> Message:
... ...
@ -1614,8 +1614,8 @@ class Client:
event: Literal['message_edit'], event: Literal['message_edit'],
/, /,
*, *,
check: Optional[Callable[[Message, Message], bool]], check: Optional[Callable[[Message, Message], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Message, Message]: ) -> Tuple[Message, Message]:
... ...
@ -1625,8 +1625,8 @@ class Client:
event: Literal['bulk_message_delete'], event: Literal['bulk_message_delete'],
/, /,
*, *,
check: Optional[Callable[[List[Message]], bool]], check: Optional[Callable[[List[Message]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> List[Message]: ) -> List[Message]:
... ...
@ -1636,8 +1636,8 @@ class Client:
event: Literal['raw_message_edit'], event: Literal['raw_message_edit'],
/, /,
*, *,
check: Optional[Callable[[RawMessageUpdateEvent], bool]], check: Optional[Callable[[RawMessageUpdateEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawMessageUpdateEvent: ) -> RawMessageUpdateEvent:
... ...
@ -1647,8 +1647,8 @@ class Client:
event: Literal['raw_message_delete'], event: Literal['raw_message_delete'],
/, /,
*, *,
check: Optional[Callable[[RawMessageDeleteEvent], bool]], check: Optional[Callable[[RawMessageDeleteEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawMessageDeleteEvent: ) -> RawMessageDeleteEvent:
... ...
@ -1658,8 +1658,8 @@ class Client:
event: Literal['raw_bulk_message_delete'], event: Literal['raw_bulk_message_delete'],
/, /,
*, *,
check: Optional[Callable[[RawBulkMessageDeleteEvent], bool]], check: Optional[Callable[[RawBulkMessageDeleteEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawBulkMessageDeleteEvent: ) -> RawBulkMessageDeleteEvent:
... ...
@ -1671,8 +1671,8 @@ class Client:
event: Literal['reaction_add', 'reaction_remove'], event: Literal['reaction_add', 'reaction_remove'],
/, /,
*, *,
check: Optional[Callable[[Reaction, Union[Member, User]], bool]], check: Optional[Callable[[Reaction, Union[Member, User]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Reaction, Union[Member, User]]: ) -> Tuple[Reaction, Union[Member, User]]:
... ...
@ -1682,8 +1682,8 @@ class Client:
event: Literal['reaction_clear'], event: Literal['reaction_clear'],
/, /,
*, *,
check: Optional[Callable[[Message, List[Reaction]], bool]], check: Optional[Callable[[Message, List[Reaction]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Message, List[Reaction]]: ) -> Tuple[Message, List[Reaction]]:
... ...
@ -1693,8 +1693,8 @@ class Client:
event: Literal['reaction_clear_emoji'], event: Literal['reaction_clear_emoji'],
/, /,
*, *,
check: Optional[Callable[[Reaction], bool]], check: Optional[Callable[[Reaction], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Reaction: ) -> Reaction:
... ...
@ -1704,8 +1704,8 @@ class Client:
event: Literal['raw_reaction_add', 'raw_reaction_remove'], event: Literal['raw_reaction_add', 'raw_reaction_remove'],
/, /,
*, *,
check: Optional[Callable[[RawReactionActionEvent], bool]], check: Optional[Callable[[RawReactionActionEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawReactionActionEvent: ) -> RawReactionActionEvent:
... ...
@ -1715,8 +1715,8 @@ class Client:
event: Literal['raw_reaction_clear'], event: Literal['raw_reaction_clear'],
/, /,
*, *,
check: Optional[Callable[[RawReactionClearEvent], bool]], check: Optional[Callable[[RawReactionClearEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawReactionClearEvent: ) -> RawReactionClearEvent:
... ...
@ -1726,8 +1726,8 @@ class Client:
event: Literal['raw_reaction_clear_emoji'], event: Literal['raw_reaction_clear_emoji'],
/, /,
*, *,
check: Optional[Callable[[RawReactionClearEmojiEvent], bool]], check: Optional[Callable[[RawReactionClearEmojiEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawReactionClearEmojiEvent: ) -> RawReactionClearEmojiEvent:
... ...
@ -1739,8 +1739,8 @@ class Client:
event: Literal['guild_role_create', 'guild_role_delete'], event: Literal['guild_role_create', 'guild_role_delete'],
/, /,
*, *,
check: Optional[Callable[[Role], bool]], check: Optional[Callable[[Role], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Role: ) -> Role:
... ...
@ -1750,8 +1750,8 @@ class Client:
event: Literal['guild_role_update'], event: Literal['guild_role_update'],
/, /,
*, *,
check: Optional[Callable[[Role, Role], bool]], check: Optional[Callable[[Role, Role], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Role, Role]: ) -> Tuple[Role, Role]:
... ...
@ -1763,8 +1763,8 @@ class Client:
event: Literal['scheduled_event_create', 'scheduled_event_delete'], event: Literal['scheduled_event_create', 'scheduled_event_delete'],
/, /,
*, *,
check: Optional[Callable[[ScheduledEvent], bool]], check: Optional[Callable[[ScheduledEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> ScheduledEvent: ) -> ScheduledEvent:
... ...
@ -1774,8 +1774,8 @@ class Client:
event: Literal['scheduled_event_user_add', 'scheduled_event_user_remove'], event: Literal['scheduled_event_user_add', 'scheduled_event_user_remove'],
/, /,
*, *,
check: Optional[Callable[[ScheduledEvent, User], bool]], check: Optional[Callable[[ScheduledEvent, User], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[ScheduledEvent, User]: ) -> Tuple[ScheduledEvent, User]:
... ...
@ -1787,8 +1787,8 @@ class Client:
event: Literal['stage_instance_create', 'stage_instance_delete'], event: Literal['stage_instance_create', 'stage_instance_delete'],
/, /,
*, *,
check: Optional[Callable[[StageInstance], bool]], check: Optional[Callable[[StageInstance], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> StageInstance: ) -> StageInstance:
... ...
@ -1798,8 +1798,8 @@ class Client:
event: Literal['stage_instance_update'], event: Literal['stage_instance_update'],
/, /,
*, *,
check: Optional[Callable[[StageInstance, StageInstance], bool]], check: Optional[Callable[[StageInstance, StageInstance], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Coroutine[Any, Any, Tuple[StageInstance, StageInstance]]: ) -> Coroutine[Any, Any, Tuple[StageInstance, StageInstance]]:
... ...
@ -1810,8 +1810,8 @@ class Client:
event: Literal['subscription_create', 'subscription_update', 'subscription_delete'], event: Literal['subscription_create', 'subscription_update', 'subscription_delete'],
/, /,
*, *,
check: Optional[Callable[[Subscription], bool]], check: Optional[Callable[[Subscription], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Subscription: ) -> Subscription:
... ...
@ -1822,8 +1822,8 @@ class Client:
event: Literal['thread_create', 'thread_join', 'thread_remove', 'thread_delete'], event: Literal['thread_create', 'thread_join', 'thread_remove', 'thread_delete'],
/, /,
*, *,
check: Optional[Callable[[Thread], bool]], check: Optional[Callable[[Thread], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Thread: ) -> Thread:
... ...
@ -1833,8 +1833,8 @@ class Client:
event: Literal['thread_update'], event: Literal['thread_update'],
/, /,
*, *,
check: Optional[Callable[[Thread, Thread], bool]], check: Optional[Callable[[Thread, Thread], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Thread, Thread]: ) -> Tuple[Thread, Thread]:
... ...
@ -1844,8 +1844,8 @@ class Client:
event: Literal['raw_thread_update'], event: Literal['raw_thread_update'],
/, /,
*, *,
check: Optional[Callable[[RawThreadUpdateEvent], bool]], check: Optional[Callable[[RawThreadUpdateEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawThreadUpdateEvent: ) -> RawThreadUpdateEvent:
... ...
@ -1855,8 +1855,8 @@ class Client:
event: Literal['raw_thread_delete'], event: Literal['raw_thread_delete'],
/, /,
*, *,
check: Optional[Callable[[RawThreadDeleteEvent], bool]], check: Optional[Callable[[RawThreadDeleteEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawThreadDeleteEvent: ) -> RawThreadDeleteEvent:
... ...
@ -1866,8 +1866,8 @@ class Client:
event: Literal['thread_member_join', 'thread_member_remove'], event: Literal['thread_member_join', 'thread_member_remove'],
/, /,
*, *,
check: Optional[Callable[[ThreadMember], bool]], check: Optional[Callable[[ThreadMember], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> ThreadMember: ) -> ThreadMember:
... ...
@ -1877,8 +1877,8 @@ class Client:
event: Literal['raw_thread_member_remove'], event: Literal['raw_thread_member_remove'],
/, /,
*, *,
check: Optional[Callable[[RawThreadMembersUpdate], bool]], check: Optional[Callable[[RawThreadMembersUpdate], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawThreadMembersUpdate: ) -> RawThreadMembersUpdate:
... ...
@ -1890,8 +1890,8 @@ class Client:
event: Literal['voice_state_update'], event: Literal['voice_state_update'],
/, /,
*, *,
check: Optional[Callable[[Member, VoiceState, VoiceState], bool]], check: Optional[Callable[[Member, VoiceState, VoiceState], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Member, VoiceState, VoiceState]: ) -> Tuple[Member, VoiceState, VoiceState]:
... ...
@ -1903,8 +1903,8 @@ class Client:
event: Literal['poll_vote_add', 'poll_vote_remove'], event: Literal['poll_vote_add', 'poll_vote_remove'],
/, /,
*, *,
check: Optional[Callable[[Union[User, Member], PollAnswer], bool]] = None, check: Optional[Callable[[Union[User, Member], PollAnswer], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Union[User, Member], PollAnswer]: ) -> Tuple[Union[User, Member], PollAnswer]:
... ...
@ -1914,8 +1914,8 @@ class Client:
event: Literal['raw_poll_vote_add', 'raw_poll_vote_remove'], event: Literal['raw_poll_vote_add', 'raw_poll_vote_remove'],
/, /,
*, *,
check: Optional[Callable[[RawPollVoteActionEvent], bool]] = None, check: Optional[Callable[[RawPollVoteActionEvent], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> RawPollVoteActionEvent: ) -> RawPollVoteActionEvent:
... ...
@ -1927,8 +1927,8 @@ class Client:
event: Literal["command", "command_completion"], event: Literal["command", "command_completion"],
/, /,
*, *,
check: Optional[Callable[[Context[Any]], bool]] = None, check: Optional[Callable[[Context[Any]], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Context[Any]: ) -> Context[Any]:
... ...
@ -1938,8 +1938,8 @@ class Client:
event: Literal["command_error"], event: Literal["command_error"],
/, /,
*, *,
check: Optional[Callable[[Context[Any], CommandError], bool]] = None, check: Optional[Callable[[Context[Any], CommandError], bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Tuple[Context[Any], CommandError]: ) -> Tuple[Context[Any], CommandError]:
... ...
@ -1949,8 +1949,8 @@ class Client:
event: str, event: str,
/, /,
*, *,
check: Optional[Callable[..., bool]] = None, check: Optional[Callable[..., bool]] = ...,
timeout: Optional[float] = None, timeout: Optional[float] = ...,
) -> Any: ) -> Any:
... ...

6
discord/components.py

@ -226,12 +226,12 @@ class Button(Component):
self.label: Optional[str] = data.get('label') self.label: Optional[str] = data.get('label')
self.emoji: Optional[PartialEmoji] self.emoji: Optional[PartialEmoji]
try: try:
self.emoji = PartialEmoji.from_dict(data['emoji']) self.emoji = PartialEmoji.from_dict(data['emoji']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.emoji = None self.emoji = None
try: try:
self.sku_id: Optional[int] = int(data['sku_id']) self.sku_id: Optional[int] = int(data['sku_id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.sku_id = None self.sku_id = None
@ -451,7 +451,7 @@ class SelectOption:
@classmethod @classmethod
def from_dict(cls, data: SelectOptionPayload) -> SelectOption: def from_dict(cls, data: SelectOptionPayload) -> SelectOption:
try: try:
emoji = PartialEmoji.from_dict(data['emoji']) emoji = PartialEmoji.from_dict(data['emoji']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
emoji = None emoji = None

21
discord/embeds.py

@ -29,6 +29,7 @@ from typing import Any, Dict, List, Mapping, Optional, Protocol, TYPE_CHECKING,
from . import utils from . import utils
from .colour import Colour from .colour import Colour
from .flags import AttachmentFlags, EmbedFlags
# fmt: off # fmt: off
__all__ = ( __all__ = (
@ -76,6 +77,7 @@ if TYPE_CHECKING:
proxy_url: Optional[str] proxy_url: Optional[str]
height: Optional[int] height: Optional[int]
width: Optional[int] width: Optional[int]
flags: Optional[AttachmentFlags]
class _EmbedVideoProxy(Protocol): class _EmbedVideoProxy(Protocol):
url: Optional[str] url: Optional[str]
@ -131,7 +133,7 @@ class Embed:
The type of embed. Usually "rich". The type of embed. Usually "rich".
This can be set during initialisation. This can be set during initialisation.
Possible strings for embed types can be found on discord's Possible strings for embed types can be found on discord's
:ddocs:`api docs <resources/channel#embed-object-embed-types>` :ddocs:`api docs <resources/message#embed-object-embed-types>`
description: Optional[:class:`str`] description: Optional[:class:`str`]
The description of the embed. The description of the embed.
This can be set during initialisation. This can be set during initialisation.
@ -146,6 +148,10 @@ class Embed:
colour: Optional[Union[:class:`Colour`, :class:`int`]] colour: Optional[Union[:class:`Colour`, :class:`int`]]
The colour code of the embed. Aliased to ``color`` as well. The colour code of the embed. Aliased to ``color`` as well.
This can be set during initialisation. This can be set during initialisation.
flags: Optional[:class:`EmbedFlags`]
The flags of this embed.
.. versionadded:: 2.5
""" """
__slots__ = ( __slots__ = (
@ -162,6 +168,7 @@ class Embed:
'_author', '_author',
'_fields', '_fields',
'description', 'description',
'flags',
) )
def __init__( def __init__(
@ -181,6 +188,7 @@ class Embed:
self.type: EmbedType = type self.type: EmbedType = type
self.url: Optional[str] = url self.url: Optional[str] = url
self.description: Optional[str] = description self.description: Optional[str] = description
self.flags: Optional[EmbedFlags] = None
if self.title is not None: if self.title is not None:
self.title = str(self.title) self.title = str(self.title)
@ -245,6 +253,11 @@ class Embed:
else: else:
setattr(self, '_' + attr, value) setattr(self, '_' + attr, value)
try:
self.flags = EmbedFlags._from_value(data['flags'])
except KeyError:
pass
return self return self
def copy(self) -> Self: def copy(self) -> Self:
@ -399,11 +412,15 @@ class Embed:
- ``proxy_url`` - ``proxy_url``
- ``width`` - ``width``
- ``height`` - ``height``
- ``flags``
If the attribute has no value then ``None`` is returned. If the attribute has no value then ``None`` is returned.
""" """
# Lying to the type checker for better developer UX. # Lying to the type checker for better developer UX.
return EmbedProxy(getattr(self, '_image', {})) # type: ignore data = getattr(self, '_image', {})
if 'flags' in data:
data['flags'] = AttachmentFlags._from_value(data['flags'])
return EmbedProxy(data) # type: ignore
def set_image(self, *, url: Optional[Any]) -> Self: def set_image(self, *, url: Optional[Any]) -> Self:
"""Sets the image for the embed content. """Sets the image for the embed content.

12
discord/enums.py

@ -86,13 +86,13 @@ def _create_value_cls(name: str, comparable: bool):
# All the type ignores here are due to the type checker being unable to recognise # All the type ignores here are due to the type checker being unable to recognise
# Runtime type creation without exploding. # Runtime type creation without exploding.
cls = namedtuple('_EnumValue_' + name, 'name value') cls = namedtuple('_EnumValue_' + name, 'name value')
cls.__repr__ = lambda self: f'<{name}.{self.name}: {self.value!r}>' # type: ignore cls.__repr__ = lambda self: f'<{name}.{self.name}: {self.value!r}>'
cls.__str__ = lambda self: f'{name}.{self.name}' # type: ignore cls.__str__ = lambda self: f'{name}.{self.name}'
if comparable: if comparable:
cls.__le__ = lambda self, other: isinstance(other, self.__class__) and self.value <= other.value # type: ignore cls.__le__ = lambda self, other: isinstance(other, self.__class__) and self.value <= other.value
cls.__ge__ = lambda self, other: isinstance(other, self.__class__) and self.value >= other.value # type: ignore cls.__ge__ = lambda self, other: isinstance(other, self.__class__) and self.value >= other.value
cls.__lt__ = lambda self, other: isinstance(other, self.__class__) and self.value < other.value # type: ignore cls.__lt__ = lambda self, other: isinstance(other, self.__class__) and self.value < other.value
cls.__gt__ = lambda self, other: isinstance(other, self.__class__) and self.value > other.value # type: ignore cls.__gt__ = lambda self, other: isinstance(other, self.__class__) and self.value > other.value
return cls return cls

4
discord/ext/commands/bot.py

@ -172,7 +172,7 @@ class BotBase(GroupMixin[None]):
**options: Any, **options: Any,
) -> None: ) -> None:
super().__init__(intents=intents, **options) super().__init__(intents=intents, **options)
self.command_prefix: PrefixType[BotT] = command_prefix self.command_prefix: PrefixType[BotT] = command_prefix # type: ignore
self.extra_events: Dict[str, List[CoroFunc]] = {} self.extra_events: Dict[str, List[CoroFunc]] = {}
# Self doesn't have the ClientT bound, but since this is a mixin it technically does # Self doesn't have the ClientT bound, but since this is a mixin it technically does
self.__tree: app_commands.CommandTree[Self] = tree_cls(self) # type: ignore self.__tree: app_commands.CommandTree[Self] = tree_cls(self) # type: ignore
@ -487,7 +487,7 @@ class BotBase(GroupMixin[None]):
if len(data) == 0: if len(data) == 0:
return True return True
return await discord.utils.async_all(f(ctx) for f in data) return await discord.utils.async_all(f(ctx) for f in data) # type: ignore
async def is_owner(self, user: User, /) -> bool: async def is_owner(self, user: User, /) -> bool:
"""|coro| """|coro|

9
discord/ext/commands/context.py

@ -82,7 +82,7 @@ def is_cog(obj: Any) -> TypeGuard[Cog]:
return hasattr(obj, '__cog_commands__') return hasattr(obj, '__cog_commands__')
class DeferTyping: class DeferTyping(Generic[BotT]):
def __init__(self, ctx: Context[BotT], *, ephemeral: bool): def __init__(self, ctx: Context[BotT], *, ephemeral: bool):
self.ctx: Context[BotT] = ctx self.ctx: Context[BotT] = ctx
self.ephemeral: bool = ephemeral self.ephemeral: bool = ephemeral
@ -1078,8 +1078,11 @@ class Context(discord.abc.Messageable, Generic[BotT]):
if self.interaction.response.is_done(): if self.interaction.response.is_done():
msg = await self.interaction.followup.send(**kwargs, wait=True) msg = await self.interaction.followup.send(**kwargs, wait=True)
else: else:
await self.interaction.response.send_message(**kwargs) response = await self.interaction.response.send_message(**kwargs)
msg = await self.interaction.original_response() if not isinstance(response.resource, discord.InteractionMessage):
msg = await self.interaction.original_response()
else:
msg = response.resource
if delete_after is not None: if delete_after is not None:
await msg.delete(delay=delete_after) await msg.delete(delay=delete_after)

6
discord/ext/commands/converter.py

@ -1125,7 +1125,7 @@ class Greedy(List[T]):
args = getattr(converter, '__args__', ()) args = getattr(converter, '__args__', ())
if discord.utils.PY_310 and converter.__class__ is types.UnionType: # type: ignore if discord.utils.PY_310 and converter.__class__ is types.UnionType: # type: ignore
converter = Union[args] # type: ignore converter = Union[args]
origin = getattr(converter, '__origin__', None) origin = getattr(converter, '__origin__', None)
@ -1138,7 +1138,7 @@ class Greedy(List[T]):
if origin is Union and type(None) in args: if origin is Union and type(None) in args:
raise TypeError(f'Greedy[{converter!r}] is invalid.') raise TypeError(f'Greedy[{converter!r}] is invalid.')
return cls(converter=converter) return cls(converter=converter) # type: ignore
@property @property
def constructed_converter(self) -> Any: def constructed_converter(self) -> Any:
@ -1325,7 +1325,7 @@ async def _actual_conversion(ctx: Context[BotT], converter: Any, argument: str,
else: else:
return await converter().convert(ctx, argument) return await converter().convert(ctx, argument)
elif isinstance(converter, Converter): elif isinstance(converter, Converter):
return await converter.convert(ctx, argument) # type: ignore return await converter.convert(ctx, argument)
except CommandError: except CommandError:
raise raise
except Exception as exc: except Exception as exc:

2
discord/ext/commands/core.py

@ -1285,7 +1285,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
# since we have no checks, then we just return True. # since we have no checks, then we just return True.
return True return True
return await discord.utils.async_all(predicate(ctx) for predicate in predicates) return await discord.utils.async_all(predicate(ctx) for predicate in predicates) # type: ignore
finally: finally:
ctx.command = original ctx.command = original

7
discord/ext/commands/errors.py

@ -24,18 +24,19 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Union from typing import TYPE_CHECKING, Any, Callable, List, Optional, Tuple, Union, Generic
from discord.errors import ClientException, DiscordException from discord.errors import ClientException, DiscordException
from discord.utils import _human_join from discord.utils import _human_join
from ._types import BotT
if TYPE_CHECKING: if TYPE_CHECKING:
from discord.abc import GuildChannel from discord.abc import GuildChannel
from discord.threads import Thread from discord.threads import Thread
from discord.types.snowflake import Snowflake, SnowflakeList from discord.types.snowflake import Snowflake, SnowflakeList
from discord.app_commands import AppCommandError from discord.app_commands import AppCommandError
from ._types import BotT
from .context import Context from .context import Context
from .converter import Converter from .converter import Converter
from .cooldowns import BucketType, Cooldown from .cooldowns import BucketType, Cooldown
@ -235,7 +236,7 @@ class CheckFailure(CommandError):
pass pass
class CheckAnyFailure(CheckFailure): class CheckAnyFailure(Generic[BotT], CheckFailure):
"""Exception raised when all predicates in :func:`check_any` fail. """Exception raised when all predicates in :func:`check_any` fail.
This inherits from :exc:`CheckFailure`. This inherits from :exc:`CheckFailure`.

2
discord/ext/commands/flags.py

@ -443,7 +443,7 @@ async def convert_flag(ctx: Context[BotT], argument: str, flag: Flag, annotation
return await convert_flag(ctx, argument, flag, annotation) return await convert_flag(ctx, argument, flag, annotation)
elif origin is Union and type(None) in annotation.__args__: elif origin is Union and type(None) in annotation.__args__:
# typing.Optional[x] # typing.Optional[x]
annotation = Union[tuple(arg for arg in annotation.__args__ if arg is not type(None))] # type: ignore annotation = Union[tuple(arg for arg in annotation.__args__ if arg is not type(None))]
return await run_converters(ctx, annotation, argument, param) return await run_converters(ctx, annotation, argument, param)
elif origin is dict: elif origin is dict:
# typing.Dict[K, V] -> typing.Tuple[K, V] # typing.Dict[K, V] -> typing.Tuple[K, V]

13
discord/ext/commands/hybrid.py

@ -203,9 +203,9 @@ def replace_parameter(
# Fallback to see if the behaviour needs changing # Fallback to see if the behaviour needs changing
origin = getattr(converter, '__origin__', None) origin = getattr(converter, '__origin__', None)
args = getattr(converter, '__args__', []) args = getattr(converter, '__args__', [])
if isinstance(converter, Range): if isinstance(converter, Range): # type: ignore # Range is not an Annotation at runtime
r = converter r = converter
param = param.replace(annotation=app_commands.Range[r.annotation, r.min, r.max]) param = param.replace(annotation=app_commands.Range[r.annotation, r.min, r.max]) # type: ignore
elif isinstance(converter, Greedy): elif isinstance(converter, Greedy):
# Greedy is "optional" in ext.commands # Greedy is "optional" in ext.commands
# However, in here, it probably makes sense to make it required. # However, in here, it probably makes sense to make it required.
@ -257,7 +257,7 @@ def replace_parameter(
inner = args[0] inner = args[0]
is_inner_transformer = is_transformer(inner) is_inner_transformer = is_transformer(inner)
if is_converter(inner) and not is_inner_transformer: if is_converter(inner) and not is_inner_transformer:
param = param.replace(annotation=Optional[ConverterTransformer(inner, original)]) # type: ignore param = param.replace(annotation=Optional[ConverterTransformer(inner, original)])
else: else:
raise raise
elif origin: elif origin:
@ -424,10 +424,10 @@ class HybridAppCommand(discord.app_commands.Command[CogT, P, T]):
if not ret: if not ret:
return False return False
if self.checks and not await async_all(f(interaction) for f in self.checks): if self.checks and not await async_all(f(interaction) for f in self.checks): # type: ignore
return False return False
if self.wrapped.checks and not await async_all(f(ctx) for f in self.wrapped.checks): if self.wrapped.checks and not await async_all(f(ctx) for f in self.wrapped.checks): # type: ignore
return False return False
return True return True
@ -915,7 +915,8 @@ def hybrid_command(
def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> HybridCommand[CogT, P, T]: def decorator(func: CommandCallback[CogT, ContextT, P, T]) -> HybridCommand[CogT, P, T]:
if isinstance(func, Command): if isinstance(func, Command):
raise TypeError('Callback is already a command.') raise TypeError('Callback is already a command.')
return HybridCommand(func, name=name, with_app_command=with_app_command, **attrs) # 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 decorator return decorator

89
discord/flags.py

@ -63,6 +63,7 @@ __all__ = (
'RoleFlags', 'RoleFlags',
'AppInstallationType', 'AppInstallationType',
'SKUFlags', 'SKUFlags',
'EmbedFlags',
) )
BF = TypeVar('BF', bound='BaseFlags') BF = TypeVar('BF', bound='BaseFlags')
@ -2181,6 +2182,30 @@ class AttachmentFlags(BaseFlags):
""":class:`bool`: Returns ``True`` if the attachment has been edited using the remix feature.""" """:class:`bool`: Returns ``True`` if the attachment has been edited using the remix feature."""
return 1 << 2 return 1 << 2
@flag_value
def spoiler(self):
""":class:`bool`: Returns ``True`` if the attachment was marked as a spoiler.
.. versionadded:: 2.5
"""
return 1 << 3
@flag_value
def contains_explicit_media(self):
""":class:`bool`: Returns ``True`` if the attachment was flagged as sensitive content.
.. versionadded:: 2.5
"""
return 1 << 4
@flag_value
def animated(self):
""":class:`bool`: Returns ``True`` if the attachment is an animated image.
.. versionadded:: 2.5
"""
return 1 << 5
@fill_with_flags() @fill_with_flags()
class RoleFlags(BaseFlags): class RoleFlags(BaseFlags):
@ -2316,3 +2341,67 @@ class SKUFlags(BaseFlags):
def user_subscription(self): def user_subscription(self):
""":class:`bool`: Returns ``True`` if the SKU is a user subscription.""" """:class:`bool`: Returns ``True`` if the SKU is a user subscription."""
return 1 << 8 return 1 << 8
@fill_with_flags()
class EmbedFlags(BaseFlags):
r"""Wraps up the Discord Embed flags
.. versionadded:: 2.5
.. container:: operations
.. describe:: x == y
Checks if two EmbedFlags are equal.
.. describe:: x != y
Checks if two EmbedFlags are not equal.
.. describe:: x | y, x |= y
Returns an EmbedFlags instance with all enabled flags from
both x and y.
.. describe:: x ^ y, x ^= y
Returns an EmbedFlags instance with only flags enabled on
only one of x or y, not on both.
.. describe:: ~x
Returns an EmbedFlags instance with all flags inverted from x.
.. describe:: hash(x)
Returns the flag's hash.
.. describe:: iter(x)
Returns an iterator of ``(name, value)`` pairs. This allows it
to be, for example, constructed as a dict or a list of pairs.
Note that aliases are not shown.
.. describe:: bool(b)
Returns whether any flag is set to ``True``.
Attributes
----------
value: :class:`int`
The raw value. You should query flags via the properties
rather than using this raw value.
"""
@flag_value
def contains_explicit_media(self):
""":class:`bool`: Returns ``True`` if the embed was flagged as sensitive content."""
return 1 << 4
@flag_value
def content_inventory_entry(self):
""":class:`bool`: Returns ``True`` if the embed is a reply to an activity card, and is no
longer displayed.
"""
return 1 << 5

4
discord/gateway.py

@ -831,7 +831,7 @@ class DiscordVoiceWebSocket:
self._close_code: Optional[int] = None self._close_code: Optional[int] = None
self.secret_key: Optional[List[int]] = None self.secret_key: Optional[List[int]] = None
if hook: if hook:
self._hook = hook self._hook = hook # type: ignore
async def _hook(self, *args: Any) -> None: async def _hook(self, *args: Any) -> None:
pass pass
@ -893,7 +893,7 @@ class DiscordVoiceWebSocket:
return ws return ws
async def select_protocol(self, ip: str, port: int, mode: int) -> None: async def select_protocol(self, ip: str, port: int, mode: str) -> None:
payload = { payload = {
'op': self.SELECT_PROTOCOL, 'op': self.SELECT_PROTOCOL,
'd': { 'd': {

5
discord/guild.py

@ -551,7 +551,8 @@ class Guild(Hashable):
member = self.get_member(user_id) member = self.get_member(user_id)
if member is None: if member is None:
try: try:
member = Member(data=data['member'], state=self._state, guild=self) member_data = data['member'] # pyright: ignore[reportTypedDictNotRequiredAccess]
member = Member(data=member_data, state=self._state, guild=self)
except KeyError: except KeyError:
member = None member = None
@ -573,7 +574,7 @@ class Guild(Hashable):
def _from_data(self, guild: GuildPayload) -> None: def _from_data(self, guild: GuildPayload) -> None:
try: try:
self._member_count = guild['member_count'] self._member_count = guild['member_count'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass

1
discord/http.py

@ -2620,6 +2620,7 @@ class HTTPClient:
'cover_image', 'cover_image',
'interactions_endpoint_url ', 'interactions_endpoint_url ',
'tags', 'tags',
'integration_types_config',
) )
payload = {k: v for k, v in payload.items() if k in valid_keys} payload = {k: v for k, v in payload.items() if k in valid_keys}

214
discord/interactions.py

@ -54,6 +54,8 @@ __all__ = (
'Interaction', 'Interaction',
'InteractionMessage', 'InteractionMessage',
'InteractionResponse', 'InteractionResponse',
'InteractionCallbackResponse',
'InteractionCallbackActivityInstance',
) )
if TYPE_CHECKING: if TYPE_CHECKING:
@ -61,6 +63,8 @@ if TYPE_CHECKING:
Interaction as InteractionPayload, Interaction as InteractionPayload,
InteractionData, InteractionData,
ApplicationCommandInteractionData, ApplicationCommandInteractionData,
InteractionCallback as InteractionCallbackPayload,
InteractionCallbackActivity as InteractionCallbackActivityPayload,
) )
from .types.webhook import ( from .types.webhook import (
Webhook as WebhookPayload, Webhook as WebhookPayload,
@ -90,6 +94,10 @@ if TYPE_CHECKING:
DMChannel, DMChannel,
GroupChannel, GroupChannel,
] ]
InteractionCallbackResource = Union[
"InteractionMessage",
"InteractionCallbackActivityInstance",
]
MISSING: Any = utils.MISSING MISSING: Any = utils.MISSING
@ -211,14 +219,15 @@ class Interaction(Generic[ClientT]):
int(k): int(v) for k, v in data.get('authorizing_integration_owners', {}).items() int(k): int(v) for k, v in data.get('authorizing_integration_owners', {}).items()
} }
try: try:
self.context = AppCommandContext._from_value([data['context']]) value = data['context'] # pyright: ignore[reportTypedDictNotRequiredAccess]
self.context = AppCommandContext._from_value([value])
except KeyError: except KeyError:
self.context = AppCommandContext() self.context = AppCommandContext()
self.locale: Locale = try_enum(Locale, data.get('locale', 'en-US')) self.locale: Locale = try_enum(Locale, data.get('locale', 'en-US'))
self.guild_locale: Optional[Locale] self.guild_locale: Optional[Locale]
try: try:
self.guild_locale = try_enum(Locale, data['guild_locale']) self.guild_locale = try_enum(Locale, data['guild_locale']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.guild_locale = None self.guild_locale = None
@ -469,6 +478,7 @@ class Interaction(Generic[ClientT]):
attachments: Sequence[Union[Attachment, File]] = MISSING, attachments: Sequence[Union[Attachment, File]] = MISSING,
view: Optional[View] = MISSING, view: Optional[View] = MISSING,
allowed_mentions: Optional[AllowedMentions] = None, allowed_mentions: Optional[AllowedMentions] = None,
poll: Poll = MISSING,
) -> InteractionMessage: ) -> InteractionMessage:
"""|coro| """|coro|
@ -503,6 +513,14 @@ class Interaction(Generic[ClientT]):
view: Optional[:class:`~discord.ui.View`] view: Optional[:class:`~discord.ui.View`]
The updated view to update this message with. If ``None`` is passed then The updated view to update this message with. If ``None`` is passed then
the view is removed. the view is removed.
poll: :class:`Poll`
The poll to create when editing the message.
.. versionadded:: 2.5
.. note::
This is only accepted when the response type is :attr:`InteractionResponseType.deferred_channel_message`.
Raises Raises
------- -------
@ -532,6 +550,7 @@ class Interaction(Generic[ClientT]):
view=view, view=view,
allowed_mentions=allowed_mentions, allowed_mentions=allowed_mentions,
previous_allowed_mentions=previous_mentions, previous_allowed_mentions=previous_mentions,
poll=poll,
) as params: ) as params:
adapter = async_context.get() adapter = async_context.get()
http = self._state.http http = self._state.http
@ -624,6 +643,106 @@ class Interaction(Generic[ClientT]):
return await translator.translate(string, locale=locale, context=context) return await translator.translate(string, locale=locale, context=context)
class InteractionCallbackActivityInstance:
"""Represents an activity instance launched as an interaction response.
.. versionadded:: 2.5
Attributes
----------
id: :class:`str`
The activity instance ID.
"""
__slots__ = ('id',)
def __init__(self, data: InteractionCallbackActivityPayload) -> None:
self.id: str = data['id']
class InteractionCallbackResponse(Generic[ClientT]):
"""Represents an interaction response callback.
.. versionadded:: 2.5
Attributes
----------
id: :class:`int`
The interaction ID.
type: :class:`InteractionResponseType`
The interaction callback response type.
resource: Optional[Union[:class:`InteractionMessage`, :class:`InteractionCallbackActivityInstance`]]
The resource that the interaction response created. If a message was sent, this will be
a :class:`InteractionMessage`. If an activity was launched this will be a
:class:`InteractionCallbackActivityInstance`. In any other case, this will be ``None``.
message_id: Optional[:class:`int`]
The message ID of the resource. Only available if the resource is a :class:`InteractionMessage`.
activity_id: Optional[:class:`str`]
The activity ID of the resource. Only available if the resource is a :class:`InteractionCallbackActivityInstance`.
"""
__slots__ = (
'_state',
'_parent',
'type',
'id',
'_thinking',
'_ephemeral',
'message_id',
'activity_id',
'resource',
)
def __init__(
self,
*,
data: InteractionCallbackPayload,
parent: Interaction[ClientT],
state: ConnectionState,
type: InteractionResponseType,
) -> None:
self._state: ConnectionState = state
self._parent: Interaction[ClientT] = parent
self.type: InteractionResponseType = type
self._update(data)
def _update(self, data: InteractionCallbackPayload) -> None:
interaction = data['interaction']
self.id: int = int(interaction['id'])
self._thinking: bool = interaction.get('response_message_loading', False)
self._ephemeral: bool = interaction.get('response_message_ephemeral', False)
self.message_id: Optional[int] = utils._get_as_snowflake(interaction, 'response_message_id')
self.activity_id: Optional[str] = interaction.get('activity_instance_id')
self.resource: Optional[InteractionCallbackResource] = None
resource = data.get('resource')
if resource is not None:
self.type = try_enum(InteractionResponseType, resource['type'])
message = resource.get('message')
activity_instance = resource.get('activity_instance')
if message is not None:
self.resource = InteractionMessage(
state=self._state,
channel=self._parent.channel, # type: ignore # channel should be the correct type here
data=message,
)
elif activity_instance is not None:
self.resource = InteractionCallbackActivityInstance(activity_instance)
def is_thinking(self) -> bool:
""":class:`bool`: Whether the response was a thinking defer."""
return self._thinking
def is_ephemeral(self) -> bool:
""":class:`bool`: Whether the response was ephemeral."""
return self._ephemeral
class InteractionResponse(Generic[ClientT]): class InteractionResponse(Generic[ClientT]):
"""Represents a Discord interaction response. """Represents a Discord interaction response.
@ -653,7 +772,12 @@ class InteractionResponse(Generic[ClientT]):
""":class:`InteractionResponseType`: The type of response that was sent, ``None`` if response is not done.""" """:class:`InteractionResponseType`: The type of response that was sent, ``None`` if response is not done."""
return self._response_type return self._response_type
async def defer(self, *, ephemeral: bool = False, thinking: bool = False) -> None: async def defer(
self,
*,
ephemeral: bool = False,
thinking: bool = False,
) -> Optional[InteractionCallbackResponse[ClientT]]:
"""|coro| """|coro|
Defers the interaction response. Defers the interaction response.
@ -667,6 +791,9 @@ class InteractionResponse(Generic[ClientT]):
- :attr:`InteractionType.component` - :attr:`InteractionType.component`
- :attr:`InteractionType.modal_submit` - :attr:`InteractionType.modal_submit`
.. versionchanged:: 2.5
This now returns a :class:`InteractionCallbackResponse` instance.
Parameters Parameters
----------- -----------
ephemeral: :class:`bool` ephemeral: :class:`bool`
@ -685,6 +812,11 @@ class InteractionResponse(Generic[ClientT]):
Deferring the interaction failed. Deferring the interaction failed.
InteractionResponded InteractionResponded
This interaction has already been responded to before. This interaction has already been responded to before.
Returns
-------
Optional[:class:`InteractionCallbackResponse`]
The interaction callback resource, or ``None``.
""" """
if self._response_type: if self._response_type:
raise InteractionResponded(self._parent) raise InteractionResponded(self._parent)
@ -709,7 +841,7 @@ class InteractionResponse(Generic[ClientT]):
adapter = async_context.get() adapter = async_context.get()
params = interaction_response_params(type=defer_type, data=data) params = interaction_response_params(type=defer_type, data=data)
http = parent._state.http http = parent._state.http
await adapter.create_interaction_response( response = await adapter.create_interaction_response(
parent.id, parent.id,
parent.token, parent.token,
session=parent._session, session=parent._session,
@ -718,6 +850,12 @@ class InteractionResponse(Generic[ClientT]):
params=params, params=params,
) )
self._response_type = InteractionResponseType(defer_type) self._response_type = InteractionResponseType(defer_type)
return InteractionCallbackResponse(
data=response,
parent=self._parent,
state=self._parent._state,
type=self._response_type,
)
async def pong(self) -> None: async def pong(self) -> None:
"""|coro| """|coro|
@ -767,11 +905,14 @@ class InteractionResponse(Generic[ClientT]):
silent: bool = False, silent: bool = False,
delete_after: Optional[float] = None, delete_after: Optional[float] = None,
poll: Poll = MISSING, poll: Poll = MISSING,
) -> None: ) -> InteractionCallbackResponse[ClientT]:
"""|coro| """|coro|
Responds to this interaction by sending a message. Responds to this interaction by sending a message.
.. versionchanged:: 2.5
This now returns a :class:`InteractionCallbackResponse` instance.
Parameters Parameters
----------- -----------
content: Optional[:class:`str`] content: Optional[:class:`str`]
@ -825,6 +966,11 @@ class InteractionResponse(Generic[ClientT]):
The length of ``embeds`` was invalid. The length of ``embeds`` was invalid.
InteractionResponded InteractionResponded
This interaction has already been responded to before. This interaction has already been responded to before.
Returns
-------
:class:`InteractionCallbackResponse`
The interaction callback data.
""" """
if self._response_type: if self._response_type:
raise InteractionResponded(self._parent) raise InteractionResponded(self._parent)
@ -855,7 +1001,7 @@ class InteractionResponse(Generic[ClientT]):
) )
http = parent._state.http http = parent._state.http
await adapter.create_interaction_response( response = await adapter.create_interaction_response(
parent.id, parent.id,
parent.token, parent.token,
session=parent._session, session=parent._session,
@ -886,6 +1032,13 @@ class InteractionResponse(Generic[ClientT]):
asyncio.create_task(inner_call()) asyncio.create_task(inner_call())
return InteractionCallbackResponse(
data=response,
parent=self._parent,
state=self._parent._state,
type=self._response_type,
)
async def edit_message( async def edit_message(
self, self,
*, *,
@ -897,12 +1050,15 @@ class InteractionResponse(Generic[ClientT]):
allowed_mentions: Optional[AllowedMentions] = MISSING, allowed_mentions: Optional[AllowedMentions] = MISSING,
delete_after: Optional[float] = None, delete_after: Optional[float] = None,
suppress_embeds: bool = MISSING, suppress_embeds: bool = MISSING,
) -> None: ) -> Optional[InteractionCallbackResponse[ClientT]]:
"""|coro| """|coro|
Responds to this interaction by editing the original message of Responds to this interaction by editing the original message of
a component or modal interaction. a component or modal interaction.
.. versionchanged:: 2.5
This now returns a :class:`InteractionCallbackResponse` instance.
Parameters Parameters
----------- -----------
content: Optional[:class:`str`] content: Optional[:class:`str`]
@ -948,6 +1104,11 @@ class InteractionResponse(Generic[ClientT]):
You specified both ``embed`` and ``embeds``. You specified both ``embed`` and ``embeds``.
InteractionResponded InteractionResponded
This interaction has already been responded to before. This interaction has already been responded to before.
Returns
-------
Optional[:class:`InteractionCallbackResponse`]
The interaction callback data, or ``None`` if editing the message was not possible.
""" """
if self._response_type: if self._response_type:
raise InteractionResponded(self._parent) raise InteractionResponded(self._parent)
@ -990,7 +1151,7 @@ class InteractionResponse(Generic[ClientT]):
) )
http = parent._state.http http = parent._state.http
await adapter.create_interaction_response( response = await adapter.create_interaction_response(
parent.id, parent.id,
parent.token, parent.token,
session=parent._session, session=parent._session,
@ -1015,11 +1176,21 @@ class InteractionResponse(Generic[ClientT]):
asyncio.create_task(inner_call()) asyncio.create_task(inner_call())
async def send_modal(self, modal: Modal, /) -> None: return InteractionCallbackResponse(
data=response,
parent=self._parent,
state=self._parent._state,
type=self._response_type,
)
async def send_modal(self, modal: Modal, /) -> InteractionCallbackResponse[ClientT]:
"""|coro| """|coro|
Responds to this interaction by sending a modal. Responds to this interaction by sending a modal.
.. versionchanged:: 2.5
This now returns a :class:`InteractionCallbackResponse` instance.
Parameters Parameters
----------- -----------
modal: :class:`~discord.ui.Modal` modal: :class:`~discord.ui.Modal`
@ -1031,6 +1202,11 @@ class InteractionResponse(Generic[ClientT]):
Sending the modal failed. Sending the modal failed.
InteractionResponded InteractionResponded
This interaction has already been responded to before. This interaction has already been responded to before.
Returns
-------
:class:`InteractionCallbackResponse`
The interaction callback data.
""" """
if self._response_type: if self._response_type:
raise InteractionResponded(self._parent) raise InteractionResponded(self._parent)
@ -1041,7 +1217,7 @@ class InteractionResponse(Generic[ClientT]):
http = parent._state.http http = parent._state.http
params = interaction_response_params(InteractionResponseType.modal.value, modal.to_dict()) params = interaction_response_params(InteractionResponseType.modal.value, modal.to_dict())
await adapter.create_interaction_response( response = await adapter.create_interaction_response(
parent.id, parent.id,
parent.token, parent.token,
session=parent._session, session=parent._session,
@ -1053,6 +1229,13 @@ class InteractionResponse(Generic[ClientT]):
self._parent._state.store_view(modal) self._parent._state.store_view(modal)
self._response_type = InteractionResponseType.modal self._response_type = InteractionResponseType.modal
return InteractionCallbackResponse(
data=response,
parent=self._parent,
state=self._parent._state,
type=self._response_type,
)
async def autocomplete(self, choices: Sequence[Choice[ChoiceT]]) -> None: async def autocomplete(self, choices: Sequence[Choice[ChoiceT]]) -> None:
"""|coro| """|coro|
@ -1154,6 +1337,7 @@ class InteractionMessage(Message):
view: Optional[View] = MISSING, view: Optional[View] = MISSING,
allowed_mentions: Optional[AllowedMentions] = None, allowed_mentions: Optional[AllowedMentions] = None,
delete_after: Optional[float] = None, delete_after: Optional[float] = None,
poll: Poll = MISSING,
) -> InteractionMessage: ) -> InteractionMessage:
"""|coro| """|coro|
@ -1188,6 +1372,15 @@ class InteractionMessage(Message):
then it is silently ignored. then it is silently ignored.
.. versionadded:: 2.2 .. versionadded:: 2.2
poll: :class:`~discord.Poll`
The poll to create when editing the message.
.. versionadded:: 2.5
.. note::
This is only accepted if the interaction response's :attr:`InteractionResponse.type`
attribute is :attr:`InteractionResponseType.deferred_channel_message`.
Raises Raises
------- -------
@ -1212,6 +1405,7 @@ class InteractionMessage(Message):
attachments=attachments, attachments=attachments,
view=view, view=view,
allowed_mentions=allowed_mentions, allowed_mentions=allowed_mentions,
poll=poll,
) )
if delete_after is not None: if delete_after is not None:
await self.delete(delay=delete_after) await self.delete(delay=delete_after)

2
discord/invite.py

@ -437,7 +437,7 @@ class Invite(Hashable):
def from_incomplete(cls, *, state: ConnectionState, data: InvitePayload) -> Self: def from_incomplete(cls, *, state: ConnectionState, data: InvitePayload) -> Self:
guild: Optional[Union[Guild, PartialInviteGuild]] guild: Optional[Union[Guild, PartialInviteGuild]]
try: try:
guild_data = data['guild'] guild_data = data['guild'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
# If we're here, then this is a group DM # If we're here, then this is a group DM
guild = None guild = None

10
discord/member.py

@ -326,7 +326,7 @@ class Member(discord.abc.Messageable, _UserTag):
self._flags: int = data['flags'] self._flags: int = data['flags']
self._avatar_decoration_data: Optional[AvatarDecorationData] = data.get('avatar_decoration_data') self._avatar_decoration_data: Optional[AvatarDecorationData] = data.get('avatar_decoration_data')
try: try:
self._permissions = int(data['permissions']) self._permissions = int(data['permissions']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self._permissions = None self._permissions = None
@ -418,12 +418,12 @@ class Member(discord.abc.Messageable, _UserTag):
# the nickname change is optional, # the nickname change is optional,
# if it isn't in the payload then it didn't change # if it isn't in the payload then it didn't change
try: try:
self.nick = data['nick'] self.nick = data['nick'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
try: try:
self.pending = data['pending'] self.pending = data['pending'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
@ -561,7 +561,9 @@ class Member(discord.abc.Messageable, _UserTag):
role = g.get_role(role_id) role = g.get_role(role_id)
if role: if role:
result.append(role) result.append(role)
result.append(g.default_role) default_role = g.default_role
if default_role:
result.append(default_role)
result.sort() result.sort()
return result return result

23
discord/message.py

@ -479,7 +479,7 @@ class MessageInteraction(Hashable):
self.user: Union[User, Member] = MISSING self.user: Union[User, Member] = MISSING
try: try:
payload = data['member'] payload = data['member'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.user = state.create_user(data['user']) self.user = state.create_user(data['user'])
else: else:
@ -1906,7 +1906,8 @@ class Message(PartialMessage, Hashable):
self.poll: Optional[Poll] = None self.poll: Optional[Poll] = None
try: try:
self.poll = Poll._from_data(data=data['poll'], message=self, state=state) poll = data['poll'] # pyright: ignore[reportTypedDictNotRequiredAccess]
self.poll = Poll._from_data(data=poll, message=self, state=state)
except KeyError: except KeyError:
pass pass
@ -1920,7 +1921,7 @@ class Message(PartialMessage, Hashable):
if self.guild is not None: if self.guild is not None:
try: try:
thread = data['thread'] thread = data['thread'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
else: else:
@ -1935,7 +1936,7 @@ class Message(PartialMessage, Hashable):
# deprecated # deprecated
try: try:
interaction = data['interaction'] interaction = data['interaction'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
else: else:
@ -1943,20 +1944,20 @@ class Message(PartialMessage, Hashable):
self.interaction_metadata: Optional[MessageInteractionMetadata] = None self.interaction_metadata: Optional[MessageInteractionMetadata] = None
try: try:
interaction_metadata = data['interaction_metadata'] interaction_metadata = data['interaction_metadata'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
else: else:
self.interaction_metadata = MessageInteractionMetadata(state=state, guild=self.guild, data=interaction_metadata) self.interaction_metadata = MessageInteractionMetadata(state=state, guild=self.guild, data=interaction_metadata)
try: try:
ref = data['message_reference'] ref = data['message_reference'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.reference = None self.reference = None
else: else:
self.reference = ref = MessageReference.with_state(state, ref) self.reference = ref = MessageReference.with_state(state, ref)
try: try:
resolved = data['referenced_message'] resolved = data['referenced_message'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
else: else:
@ -1983,7 +1984,7 @@ class Message(PartialMessage, Hashable):
self.application: Optional[MessageApplication] = None self.application: Optional[MessageApplication] = None
try: try:
application = data['application'] application = data['application'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
else: else:
@ -1991,7 +1992,7 @@ class Message(PartialMessage, Hashable):
self.role_subscription: Optional[RoleSubscriptionInfo] = None self.role_subscription: Optional[RoleSubscriptionInfo] = None
try: try:
role_subscription = data['role_subscription_data'] role_subscription = data['role_subscription_data'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
else: else:
@ -1999,7 +2000,7 @@ class Message(PartialMessage, Hashable):
self.purchase_notification: Optional[PurchaseNotification] = None self.purchase_notification: Optional[PurchaseNotification] = None
try: try:
purchase_notification = data['purchase_notification'] purchase_notification = data['purchase_notification'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
pass pass
else: else:
@ -2007,7 +2008,7 @@ class Message(PartialMessage, Hashable):
for handler in ('author', 'member', 'mentions', 'mention_roles', 'components', 'call'): for handler in ('author', 'member', 'mentions', 'mention_roles', 'components', 'call'):
try: try:
getattr(self, f'_handle_{handler}')(data[handler]) getattr(self, f'_handle_{handler}')(data[handler]) # type: ignore
except KeyError: except KeyError:
continue continue

9
discord/poll.py

@ -336,6 +336,15 @@ class Poll:
Defaults to ``False``. Defaults to ``False``.
layout_type: :class:`PollLayoutType` layout_type: :class:`PollLayoutType`
The layout type of the poll. Defaults to :attr:`PollLayoutType.default`. The layout type of the poll. Defaults to :attr:`PollLayoutType.default`.
Attributes
-----------
duration: :class:`datetime.timedelta`
The duration of the poll.
multiple: :class:`bool`
Whether users are allowed to select more than one answer.
layout_type: :class:`PollLayoutType`
The layout type of the poll.
""" """
__slots__ = ( __slots__ = (

14
discord/raw_models.py

@ -104,7 +104,7 @@ class RawMessageDeleteEvent(_RawReprMixin):
self.channel_id: int = int(data['channel_id']) self.channel_id: int = int(data['channel_id'])
self.cached_message: Optional[Message] = None self.cached_message: Optional[Message] = None
try: try:
self.guild_id: Optional[int] = int(data['guild_id']) self.guild_id: Optional[int] = int(data['guild_id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.guild_id: Optional[int] = None self.guild_id: Optional[int] = None
@ -132,7 +132,7 @@ class RawBulkMessageDeleteEvent(_RawReprMixin):
self.cached_messages: List[Message] = [] self.cached_messages: List[Message] = []
try: try:
self.guild_id: Optional[int] = int(data['guild_id']) self.guild_id: Optional[int] = int(data['guild_id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.guild_id: Optional[int] = None self.guild_id: Optional[int] = None
@ -248,7 +248,7 @@ class RawReactionActionEvent(_RawReprMixin):
self.type: ReactionType = try_enum(ReactionType, data['type']) self.type: ReactionType = try_enum(ReactionType, data['type'])
try: try:
self.guild_id: Optional[int] = int(data['guild_id']) self.guild_id: Optional[int] = int(data['guild_id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.guild_id: Optional[int] = None self.guild_id: Optional[int] = None
@ -281,7 +281,7 @@ class RawReactionClearEvent(_RawReprMixin):
self.channel_id: int = int(data['channel_id']) self.channel_id: int = int(data['channel_id'])
try: try:
self.guild_id: Optional[int] = int(data['guild_id']) self.guild_id: Optional[int] = int(data['guild_id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.guild_id: Optional[int] = None self.guild_id: Optional[int] = None
@ -311,7 +311,7 @@ class RawReactionClearEmojiEvent(_RawReprMixin):
self.channel_id: int = int(data['channel_id']) self.channel_id: int = int(data['channel_id'])
try: try:
self.guild_id: Optional[int] = int(data['guild_id']) self.guild_id: Optional[int] = int(data['guild_id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.guild_id: Optional[int] = None self.guild_id: Optional[int] = None
@ -338,7 +338,9 @@ class RawIntegrationDeleteEvent(_RawReprMixin):
self.guild_id: int = int(data['guild_id']) self.guild_id: int = int(data['guild_id'])
try: try:
self.application_id: Optional[int] = int(data['application_id']) self.application_id: Optional[int] = int(
data['application_id'] # pyright: ignore[reportTypedDictNotRequiredAccess]
)
except KeyError: except KeyError:
self.application_id: Optional[int] = None self.application_id: Optional[int] = None

110
discord/role.py

@ -23,7 +23,7 @@ DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations from __future__ import annotations
from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING from typing import Any, Dict, List, Optional, Union, overload, TYPE_CHECKING
from .asset import Asset from .asset import Asset
from .permissions import Permissions from .permissions import Permissions
@ -286,7 +286,7 @@ class Role(Hashable):
self._flags: int = data.get('flags', 0) self._flags: int = data.get('flags', 0)
try: try:
self.tags = RoleTags(data['tags']) self.tags = RoleTags(data['tags']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.tags = None self.tags = None
@ -522,6 +522,112 @@ class Role(Hashable):
data = await self._state.http.edit_role(self.guild.id, self.id, reason=reason, **payload) data = await self._state.http.edit_role(self.guild.id, self.id, reason=reason, **payload)
return Role(guild=self.guild, data=data, state=self._state) return Role(guild=self.guild, data=data, state=self._state)
@overload
async def move(self, *, beginning: bool, offset: int = ..., reason: Optional[str] = ...):
...
@overload
async def move(self, *, end: bool, offset: int = ..., reason: Optional[str] = ...):
...
@overload
async def move(self, *, above: Role, offset: int = ..., reason: Optional[str] = ...):
...
@overload
async def move(self, *, below: Role, offset: int = ..., reason: Optional[str] = ...):
...
async def move(
self,
*,
beginning: bool = MISSING,
end: bool = MISSING,
above: Role = MISSING,
below: Role = MISSING,
offset: int = 0,
reason: Optional[str] = None,
):
"""|coro|
A rich interface to help move a role relative to other roles.
You must have :attr:`~discord.Permissions.manage_roles` to do this,
and you cannot move roles above the client's top role in the guild.
.. versionadded:: 2.5
Parameters
-----------
beginning: :class:`bool`
Whether to move this at the beginning of the role list, above the default role.
This is mutually exclusive with `end`, `above`, and `below`.
end: :class:`bool`
Whether to move this at the end of the role list.
This is mutually exclusive with `beginning`, `above`, and `below`.
above: :class:`Role`
The role that should be above our current role.
This mutually exclusive with `beginning`, `end`, and `below`.
below: :class:`Role`
The role that should be below our current role.
This mutually exclusive with `beginning`, `end`, and `above`.
offset: :class:`int`
The number of roles to offset the move by. For example,
an offset of ``2`` with ``beginning=True`` would move
it 2 above the beginning. A positive number moves it above
while a negative number moves it below. Note that this
number is relative and computed after the ``beginning``,
``end``, ``before``, and ``after`` parameters.
reason: Optional[:class:`str`]
The reason for editing this role. Shows up on the audit log.
Raises
-------
Forbidden
You cannot move the role there, or lack permissions to do so.
HTTPException
Moving the role failed.
TypeError
A bad mix of arguments were passed.
ValueError
An invalid role was passed.
Returns
--------
List[:class:`Role`]
A list of all the roles in the guild.
"""
if sum(bool(a) for a in (beginning, end, above, below)) > 1:
raise TypeError('Only one of [beginning, end, above, below] can be used.')
target = above or below
guild = self.guild
guild_roles = guild.roles
if target:
if target not in guild_roles:
raise ValueError('Target role is from a different guild')
if above == guild.default_role:
raise ValueError('Role cannot be moved below the default role')
if self == target:
raise ValueError('Target role cannot be itself')
roles = [r for r in guild_roles if r != self]
if beginning:
index = 1
elif end:
index = len(roles)
elif above in roles:
index = roles.index(above)
elif below in roles:
index = roles.index(below) + 1
else:
index = guild_roles.index(self)
roles.insert(max((index + offset), 1), self)
payload: List[RolePositionUpdate] = [{'id': role.id, 'position': idx} for idx, role in enumerate(roles)]
await self._state.http.move_role_position(guild.id, payload, reason=reason)
async def delete(self, *, reason: Optional[str] = None) -> None: async def delete(self, *, reason: Optional[str] = None) -> None:
"""|coro| """|coro|

8
discord/state.py

@ -540,7 +540,7 @@ class ConnectionState(Generic[ClientT]):
) -> Tuple[Union[Channel, Thread], Optional[Guild]]: ) -> Tuple[Union[Channel, Thread], Optional[Guild]]:
channel_id = int(data['channel_id']) channel_id = int(data['channel_id'])
try: try:
guild_id = guild_id or int(data['guild_id']) guild_id = guild_id or int(data['guild_id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
guild = self._get_guild(guild_id) guild = self._get_guild(guild_id)
except KeyError: except KeyError:
channel = DMChannel._from_message(self, channel_id) channel = DMChannel._from_message(self, channel_id)
@ -736,7 +736,7 @@ class ConnectionState(Generic[ClientT]):
if 'components' in data: if 'components' in data:
try: try:
entity_id = int(data['interaction']['id']) entity_id = int(data['interaction']['id']) # pyright: ignore[reportTypedDictNotRequiredAccess]
except (KeyError, ValueError): except (KeyError, ValueError):
entity_id = raw.message_id entity_id = raw.message_id
@ -935,7 +935,7 @@ class ConnectionState(Generic[ClientT]):
def parse_channel_pins_update(self, data: gw.ChannelPinsUpdateEvent) -> None: def parse_channel_pins_update(self, data: gw.ChannelPinsUpdateEvent) -> None:
channel_id = int(data['channel_id']) channel_id = int(data['channel_id'])
try: try:
guild = self._get_guild(int(data['guild_id'])) guild = self._get_guild(int(data['guild_id'])) # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
guild = None guild = None
channel = self._get_private_channel(channel_id) channel = self._get_private_channel(channel_id)
@ -1017,7 +1017,7 @@ class ConnectionState(Generic[ClientT]):
return return
try: try:
channel_ids = {int(i) for i in data['channel_ids']} channel_ids = {int(i) for i in data['channel_ids']} # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
# If not provided, then the entire guild is being synced # If not provided, then the entire guild is being synced
# So all previous thread data should be overwritten # So all previous thread data should be overwritten

2
discord/threads.py

@ -192,7 +192,7 @@ class Thread(Messageable, Hashable):
self.me: Optional[ThreadMember] self.me: Optional[ThreadMember]
try: try:
member = data['member'] member = data['member'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
self.me = None self.me = None
else: else:

7
discord/types/appinfo.py

@ -24,7 +24,7 @@ DEALINGS IN THE SOFTWARE.
from __future__ import annotations from __future__ import annotations
from typing import TypedDict, List, Optional from typing import Literal, Dict, TypedDict, List, Optional
from typing_extensions import NotRequired from typing_extensions import NotRequired
from .user import User from .user import User
@ -38,6 +38,10 @@ class InstallParams(TypedDict):
permissions: str permissions: str
class AppIntegrationTypeConfig(TypedDict):
oauth2_install_params: NotRequired[InstallParams]
class BaseAppInfo(TypedDict): class BaseAppInfo(TypedDict):
id: Snowflake id: Snowflake
name: str name: str
@ -69,6 +73,7 @@ class AppInfo(BaseAppInfo):
tags: NotRequired[List[str]] tags: NotRequired[List[str]]
install_params: NotRequired[InstallParams] install_params: NotRequired[InstallParams]
custom_install_url: NotRequired[str] custom_install_url: NotRequired[str]
integration_types_config: NotRequired[Dict[Literal['0', '1'], AppIntegrationTypeConfig]]
class PartialAppInfo(BaseAppInfo, total=False): class PartialAppInfo(BaseAppInfo, total=False):

2
discord/types/embed.py

@ -50,6 +50,7 @@ class EmbedVideo(TypedDict, total=False):
proxy_url: str proxy_url: str
height: int height: int
width: int width: int
flags: int
class EmbedImage(TypedDict, total=False): class EmbedImage(TypedDict, total=False):
@ -88,3 +89,4 @@ class Embed(TypedDict, total=False):
provider: EmbedProvider provider: EmbedProvider
author: EmbedAuthor author: EmbedAuthor
fields: List[EmbedField] fields: List[EmbedField]
flags: int

4
discord/types/guild.py

@ -179,8 +179,8 @@ class GuildMFALevel(TypedDict):
class ChannelPositionUpdate(TypedDict): class ChannelPositionUpdate(TypedDict):
id: Snowflake id: Snowflake
position: Optional[int] position: Optional[int]
lock_permissions: Optional[bool] lock_permissions: NotRequired[Optional[bool]]
parent_id: Optional[Snowflake] parent_id: NotRequired[Optional[Snowflake]]
class _RolePositionRequired(TypedDict): class _RolePositionRequired(TypedDict):

34
discord/types/interactions.py

@ -42,6 +42,16 @@ if TYPE_CHECKING:
InteractionType = Literal[1, 2, 3, 4, 5] InteractionType = Literal[1, 2, 3, 4, 5]
InteractionResponseType = Literal[
1,
4,
5,
6,
7,
8,
9,
10,
]
InteractionContextType = Literal[0, 1, 2] InteractionContextType = Literal[0, 1, 2]
InteractionInstallationType = Literal[0, 1] InteractionInstallationType = Literal[0, 1]
@ -301,3 +311,27 @@ MessageInteractionMetadata = Union[
MessageComponentMessageInteractionMetadata, MessageComponentMessageInteractionMetadata,
ModalSubmitMessageInteractionMetadata, ModalSubmitMessageInteractionMetadata,
] ]
class InteractionCallbackResponse(TypedDict):
id: Snowflake
type: InteractionType
activity_instance_id: NotRequired[str]
response_message_id: NotRequired[Snowflake]
response_message_loading: NotRequired[bool]
response_message_ephemeral: NotRequired[bool]
class InteractionCallbackActivity(TypedDict):
id: str
class InteractionCallbackResource(TypedDict):
type: InteractionResponseType
activity_instance: NotRequired[InteractionCallbackActivity]
message: NotRequired[Message]
class InteractionCallback(TypedDict):
interaction: InteractionCallbackResponse
resource: NotRequired[InteractionCallbackResource]

5
discord/ui/select.py

@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
""" """
from __future__ import annotations from __future__ import annotations
from typing import ( from typing import (
Any, Any,
@ -330,7 +331,9 @@ class BaseSelect(Item[V]):
values = selected_values.get({}) values = selected_values.get({})
payload: List[PossibleValue] payload: List[PossibleValue]
try: try:
resolved = Namespace._get_resolved_items(interaction, data['resolved']) resolved = Namespace._get_resolved_items(
interaction, data['resolved'] # pyright: ignore[reportTypedDictNotRequiredAccess]
)
payload = list(resolved.values()) payload = list(resolved.values())
except KeyError: except KeyError:
payload = data.get("values", []) # type: ignore payload = data.get("values", []) # type: ignore

7
discord/ui/view.py

@ -177,7 +177,7 @@ class View:
children = [] children = []
for func in self.__view_children_items__: for func in self.__view_children_items__:
item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__) item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__)
item.callback = _ViewCallback(func, self, item) item.callback = _ViewCallback(func, self, item) # type: ignore
item._view = self item._view = self
setattr(self, func.__name__, item) setattr(self, func.__name__, item)
children.append(item) children.append(item)
@ -214,6 +214,11 @@ class View:
# Wait N seconds to see if timeout data has been refreshed # Wait N seconds to see if timeout data has been refreshed
await asyncio.sleep(self.__timeout_expiry - now) await asyncio.sleep(self.__timeout_expiry - now)
def is_dispatchable(self) -> bool:
# this is used by webhooks to check whether a view requires a state attached
# or not, this simply is, whether a view has a component other than a url button
return any(item.is_dispatchable() for item in self.children)
def to_components(self) -> List[Dict[str, Any]]: def to_components(self) -> List[Dict[str, Any]]:
def key(item: Item) -> int: def key(item: Item) -> int:
return item._rendered_row or 0 return item._rendered_row or 0

6
discord/utils.py

@ -714,13 +714,13 @@ async def maybe_coroutine(f: MaybeAwaitableFunc[P, T], *args: P.args, **kwargs:
if _isawaitable(value): if _isawaitable(value):
return await value return await value
else: else:
return value # type: ignore return value
async def async_all( async def async_all(
gen: Iterable[Union[T, Awaitable[T]]], gen: Iterable[Union[T, Awaitable[T]]],
*, *,
check: Callable[[Union[T, Awaitable[T]]], TypeGuard[Awaitable[T]]] = _isawaitable, check: Callable[[Union[T, Awaitable[T]]], TypeGuard[Awaitable[T]]] = _isawaitable, # type: ignore
) -> bool: ) -> bool:
for elem in gen: for elem in gen:
if check(elem): if check(elem):
@ -1121,7 +1121,7 @@ def flatten_literal_params(parameters: Iterable[Any]) -> Tuple[Any, ...]:
literal_cls = type(Literal[0]) literal_cls = type(Literal[0])
for p in parameters: for p in parameters:
if isinstance(p, literal_cls): if isinstance(p, literal_cls):
params.extend(p.__args__) params.extend(p.__args__) # type: ignore
else: else:
params.append(p) params.append(p)
return tuple(params) return tuple(params)

2
discord/voice_state.py

@ -344,7 +344,7 @@ class VoiceConnectionState:
elif self.state is not ConnectionFlowState.disconnected: elif self.state is not ConnectionFlowState.disconnected:
# eventual consistency # eventual consistency
if previous_token == self.token and previous_server_id == self.server_id and previous_token == self.token: if previous_token == self.token and previous_server_id == self.server_id and previous_endpoint == self.endpoint:
return return
_log.debug('Unexpected server update event, attempting to handle') _log.debug('Unexpected server update event, attempting to handle')

45
discord/webhook/async_.py

@ -90,6 +90,9 @@ if TYPE_CHECKING:
) )
from ..types.emoji import PartialEmoji as PartialEmojiPayload from ..types.emoji import PartialEmoji as PartialEmojiPayload
from ..types.snowflake import SnowflakeList from ..types.snowflake import SnowflakeList
from ..types.interactions import (
InteractionCallback as InteractionCallbackResponsePayload,
)
BE = TypeVar('BE', bound=BaseException) BE = TypeVar('BE', bound=BaseException)
_State = Union[ConnectionState, '_WebhookState'] _State = Union[ConnectionState, '_WebhookState']
@ -310,8 +313,9 @@ class AsyncWebhookAdapter:
files: Optional[Sequence[File]] = None, files: Optional[Sequence[File]] = None,
thread_id: Optional[int] = None, thread_id: Optional[int] = None,
wait: bool = False, wait: bool = False,
with_components: bool = False,
) -> Response[Optional[MessagePayload]]: ) -> Response[Optional[MessagePayload]]:
params = {'wait': int(wait)} params = {'wait': int(wait), 'with_components': int(with_components)}
if thread_id: if thread_id:
params['thread_id'] = thread_id params['thread_id'] = thread_id
route = Route('POST', '/webhooks/{webhook_id}/{webhook_token}', webhook_id=webhook_id, webhook_token=token) route = Route('POST', '/webhooks/{webhook_id}/{webhook_token}', webhook_id=webhook_id, webhook_token=token)
@ -434,13 +438,14 @@ class AsyncWebhookAdapter:
proxy: Optional[str] = None, proxy: Optional[str] = None,
proxy_auth: Optional[aiohttp.BasicAuth] = None, proxy_auth: Optional[aiohttp.BasicAuth] = None,
params: MultipartParameters, params: MultipartParameters,
) -> Response[None]: ) -> Response[InteractionCallbackResponsePayload]:
route = Route( route = Route(
'POST', 'POST',
'/interactions/{webhook_id}/{webhook_token}/callback', '/interactions/{webhook_id}/{webhook_token}/callback',
webhook_id=interaction_id, webhook_id=interaction_id,
webhook_token=token, webhook_token=token,
) )
request_params = {'with_response': '1'}
if params.files: if params.files:
return self.request( return self.request(
@ -450,9 +455,17 @@ class AsyncWebhookAdapter:
proxy_auth=proxy_auth, proxy_auth=proxy_auth,
files=params.files, files=params.files,
multipart=params.multipart, multipart=params.multipart,
params=request_params,
) )
else: else:
return self.request(route, session=session, proxy=proxy, proxy_auth=proxy_auth, payload=params.payload) return self.request(
route,
session=session,
proxy=proxy,
proxy_auth=proxy_auth,
payload=params.payload,
params=request_params,
)
def get_original_interaction_response( def get_original_interaction_response(
self, self,
@ -660,6 +673,11 @@ class PartialWebhookChannel(Hashable):
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<PartialWebhookChannel name={self.name!r} id={self.id}>' return f'<PartialWebhookChannel name={self.name!r} id={self.id}>'
@property
def mention(self) -> str:
""":class:`str`: The string that allows you to mention the channel that the webhook is following."""
return f'<#{self.id}>'
class PartialWebhookGuild(Hashable): class PartialWebhookGuild(Hashable):
"""Represents a partial guild for webhooks. """Represents a partial guild for webhooks.
@ -1710,10 +1728,9 @@ class Webhook(BaseWebhook):
.. versionadded:: 1.4 .. versionadded:: 1.4
view: :class:`discord.ui.View` view: :class:`discord.ui.View`
The view to send with the message. You can only send a view The view to send with the message. If the webhook is partial or
if this webhook is not partial and has state attached. A is not managed by the library, then you can only send URL buttons.
webhook has state attached if the webhook is managed by the Otherwise, you can send views with any type of components.
library.
.. versionadded:: 2.0 .. versionadded:: 2.0
thread: :class:`~discord.abc.Snowflake` thread: :class:`~discord.abc.Snowflake`
@ -1765,7 +1782,8 @@ class Webhook(BaseWebhook):
The length of ``embeds`` was invalid, there was no token The length of ``embeds`` was invalid, there was no token
associated with this webhook or ``ephemeral`` was passed associated with this webhook or ``ephemeral`` was passed
with the improper webhook type or there was no state with the improper webhook type or there was no state
attached with this webhook when giving it a view. attached with this webhook when giving it a view that had
components other than URL buttons.
Returns Returns
--------- ---------
@ -1795,13 +1813,15 @@ class Webhook(BaseWebhook):
wait = True wait = True
if view is not MISSING: if view is not MISSING:
if isinstance(self._state, _WebhookState):
raise ValueError('Webhook views require an associated state with the webhook')
if not hasattr(view, '__discord_ui_view__'): if not hasattr(view, '__discord_ui_view__'):
raise TypeError(f'expected view parameter to be of type View not {view.__class__.__name__}') raise TypeError(f'expected view parameter to be of type View not {view.__class__.__name__}')
if ephemeral is True and view.timeout is None: if isinstance(self._state, _WebhookState) and view.is_dispatchable():
raise ValueError(
'Webhook views with any component other than URL buttons require an associated state with the webhook'
)
if ephemeral is True and view.timeout is None and view.is_dispatchable():
view.timeout = 15 * 60.0 view.timeout = 15 * 60.0
if thread_name is not MISSING and thread is not MISSING: if thread_name is not MISSING and thread is not MISSING:
@ -1845,6 +1865,7 @@ class Webhook(BaseWebhook):
files=params.files, files=params.files,
thread_id=thread_id, thread_id=thread_id,
wait=wait, wait=wait,
with_components=view is not MISSING,
) )
msg = None msg = None

26
discord/webhook/sync.py

@ -66,6 +66,7 @@ if TYPE_CHECKING:
from ..message import Attachment from ..message import Attachment
from ..abc import Snowflake from ..abc import Snowflake
from ..state import ConnectionState from ..state import ConnectionState
from ..ui import View
from ..types.webhook import ( from ..types.webhook import (
Webhook as WebhookPayload, Webhook as WebhookPayload,
) )
@ -290,8 +291,9 @@ class WebhookAdapter:
files: Optional[Sequence[File]] = None, files: Optional[Sequence[File]] = None,
thread_id: Optional[int] = None, thread_id: Optional[int] = None,
wait: bool = False, wait: bool = False,
with_components: bool = False,
) -> MessagePayload: ) -> MessagePayload:
params = {'wait': int(wait)} params = {'wait': int(wait), 'with_components': int(with_components)}
if thread_id: if thread_id:
params['thread_id'] = thread_id params['thread_id'] = thread_id
route = Route('POST', '/webhooks/{webhook_id}/{webhook_token}', webhook_id=webhook_id, webhook_token=token) route = Route('POST', '/webhooks/{webhook_id}/{webhook_token}', webhook_id=webhook_id, webhook_token=token)
@ -919,6 +921,7 @@ class SyncWebhook(BaseWebhook):
silent: bool = False, silent: bool = False,
applied_tags: List[ForumTag] = MISSING, applied_tags: List[ForumTag] = MISSING,
poll: Poll = MISSING, poll: Poll = MISSING,
view: View = MISSING,
) -> Optional[SyncWebhookMessage]: ) -> Optional[SyncWebhookMessage]:
"""Sends a message using the webhook. """Sends a message using the webhook.
@ -991,6 +994,13 @@ class SyncWebhook(BaseWebhook):
When sending a Poll via webhook, you cannot manually end it. When sending a Poll via webhook, you cannot manually end it.
.. versionadded:: 2.4 .. versionadded:: 2.4
view: :class:`~discord.ui.View`
The view to send with the message. This can only have URL buttons, which donnot
require a state to be attached to it.
If you want to send a view with any component attached to it, check :meth:`Webhook.send`.
.. versionadded:: 2.5
Raises Raises
-------- --------
@ -1004,8 +1014,9 @@ class SyncWebhook(BaseWebhook):
You specified both ``embed`` and ``embeds`` or ``file`` and ``files`` You specified both ``embed`` and ``embeds`` or ``file`` and ``files``
or ``thread`` and ``thread_name``. or ``thread`` and ``thread_name``.
ValueError ValueError
The length of ``embeds`` was invalid or The length of ``embeds`` was invalid, there was no token
there was no token associated with this webhook. associated with this webhook or you tried to send a view
with components other than URL buttons.
Returns Returns
--------- ---------
@ -1027,6 +1038,13 @@ class SyncWebhook(BaseWebhook):
else: else:
flags = MISSING flags = MISSING
if view is not MISSING:
if not hasattr(view, '__discord_ui_view__'):
raise TypeError(f'expected view parameter to be of type View not {view.__class__.__name__}')
if view.is_dispatchable():
raise ValueError('SyncWebhook views can only contain URL buttons')
if thread_name is not MISSING and thread is not MISSING: if thread_name is not MISSING and thread is not MISSING:
raise TypeError('Cannot mix thread_name and thread keyword arguments.') raise TypeError('Cannot mix thread_name and thread keyword arguments.')
@ -1050,6 +1068,7 @@ class SyncWebhook(BaseWebhook):
flags=flags, flags=flags,
applied_tags=applied_tag_ids, applied_tags=applied_tag_ids,
poll=poll, poll=poll,
view=view,
) as params: ) as params:
adapter: WebhookAdapter = _get_webhook_adapter() adapter: WebhookAdapter = _get_webhook_adapter()
thread_id: Optional[int] = None thread_id: Optional[int] = None
@ -1065,6 +1084,7 @@ class SyncWebhook(BaseWebhook):
files=params.files, files=params.files,
thread_id=thread_id, thread_id=thread_id,
wait=wait, wait=wait,
with_components=view is not MISSING,
) )
msg = None msg = None

2
discord/widget.py

@ -184,7 +184,7 @@ class WidgetMember(BaseUser):
self.suppress: Optional[bool] = data.get('suppress', False) self.suppress: Optional[bool] = data.get('suppress', False)
try: try:
game = data['game'] game = data['game'] # pyright: ignore[reportTypedDictNotRequiredAccess]
except KeyError: except KeyError:
activity = None activity = None
else: else:

16
docs/api.rst

@ -80,6 +80,14 @@ AppInstallParams
.. autoclass:: AppInstallParams() .. autoclass:: AppInstallParams()
:members: :members:
IntegrationTypeConfig
~~~~~~~~~~~~~~~~~~~~~~
.. attributetable:: IntegrationTypeConfig
.. autoclass:: IntegrationTypeConfig()
:members:
Team Team
~~~~~ ~~~~~
@ -5708,6 +5716,14 @@ SKUFlags
.. autoclass:: SKUFlags() .. autoclass:: SKUFlags()
:members: :members:
EmbedFlags
~~~~~~~~~~
.. attributetable:: EmbedFlags
.. autoclass:: EmbedFlags()
:members:
ForumTag ForumTag
~~~~~~~~~ ~~~~~~~~~

5
docs/ext/commands/api.rst

@ -531,6 +531,11 @@ Converters
.. autoclass:: discord.ext.commands.ScheduledEventConverter .. autoclass:: discord.ext.commands.ScheduledEventConverter
:members: :members:
.. attributetable:: discord.ext.commands.SoundboardSoundConverter
.. autoclass:: discord.ext.commands.SoundboardSoundConverter
:members:
.. attributetable:: discord.ext.commands.clean_content .. attributetable:: discord.ext.commands.clean_content
.. autoclass:: discord.ext.commands.clean_content .. autoclass:: discord.ext.commands.clean_content

16
docs/interactions/api.rst

@ -28,6 +28,22 @@ InteractionResponse
.. autoclass:: InteractionResponse() .. autoclass:: InteractionResponse()
:members: :members:
InteractionCallbackResponse
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. attributetable:: InteractionCallbackResponse
.. autoclass:: InteractionCallbackResponse()
:members:
InteractionCallbackActivityInstance
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. attributetable:: InteractionCallbackActivityInstance
.. autoclass:: InteractionCallbackActivityInstance()
:members:
InteractionMessage InteractionMessage
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~

154
docs/whats_new.rst

@ -11,6 +11,160 @@ Changelog
This page keeps a detailed human friendly rendering of what's new and changed This page keeps a detailed human friendly rendering of what's new and changed
in specific versions. in specific versions.
.. _vp2p5p0:
v2.5.0
-------
New Features
~~~~~~~~~~~~~
- Add support for message forwarding (:issue:`9950`)
- Adds :class:`MessageReferenceType`
- Adds :class:`MessageSnapshot`
- Adds ``type`` parameter to :class:`MessageReference`, :meth:`MessageReference.from_message`, and :meth:`PartialMessage.to_reference`
- Add :meth:`PartialMessage.forward`
- Add SKU subscriptions support (:issue:`9930`)
- Adds new events :func:`on_subscription_create`, :func:`on_subscription_update`, and :func:`on_subscription_delete`
- Add :class:`SubscriptionStatus` enum
- Add :class:`Subscription` model
- Add :meth:`SKU.fetch_subscription` and :meth:`SKU.subscriptions`
- Add support for application emojis (:issue:`9891`)
- Add :meth:`Client.create_application_emoji`
- Add :meth:`Client.fetch_application_emoji`
- Add :meth:`Client.fetch_application_emojis`
- Add :meth:`Emoji.is_application_owned`
- Support for Soundboard and VC effects (:issue:`9349`)
- Add :class:`BaseSoundboardSound`, :class:`SoundboardDefaultSound`, and :class:`SoundboardSound`
- Add :class:`VoiceChannelEffect`
- Add :class:`VoiceChannelEffectAnimation`
- Add :class:`VoiceChannelEffectAnimationType`
- Add :class:`VoiceChannelSoundEffect`
- Add :meth:`VoiceChannel.send_sound`
- Add new audit log actions: :attr:`AuditLogAction.soundboard_sound_create`, :attr:`AuditLogAction.soundboard_sound_update`, and :attr:`AuditLogAction.soundboard_sound_delete`.
- Add :attr:`Intents.expressions` and make :attr:`Intents.emojis` and :attr:`Intents.emojis_and_stickers` aliases of that intent.
- Add new events: :func:`on_soundboard_sound_create`, :func:`on_soundboard_sound_update`, :func:`on_soundboard_sound_delete`, and :func:`on_voice_channel_effect`.
- Add methods and properties dealing with soundboards:
- :attr:`Client.soundboard_sounds`
- :attr:`Guild.soundboard_sounds`
- :meth:`Client.get_soundboard_sound`
- :meth:`Guild.get_soundboard_sound`
- :meth:`Client.fetch_soundboard_default_sounds`
- :meth:`Guild.fetch_soundboard_sound`
- :meth:`Guild.fetch_soundboard_sounds`
- :meth:`Guild.create_soundboard_sound`
- Add support for retrieving interaction responses when sending a response (:issue:`9957`)
- Methods from :class:`InteractionResponse` now return :class:`InteractionCallbackResponse`
- Depending on the interaction response type, :attr:`InteractionCallbackResponse.resource` will be different
- Add :attr:`PartialWebhookChannel.mention` attribute (:issue:`10101`)
- Add support for sending stateless views for :class:`SyncWebhook` or webhooks with no state (:issue:`10089`)
- Add
- Add richer :meth:`Role.move` interface (:issue:`10100`)
- Add support for :class:`EmbedFlags` via :attr:`Embed.flags` (:issue:`10085`)
- Add new flags for :class:`AttachmentFlags` (:issue:`10085`)
- Add :func:`on_raw_presence_update` event that does not depend on cache state (:issue:`10048`)
- This requires setting the ``enable_raw_presences`` keyword argument within :class:`Client`.
- Add :attr:`ForumChannel.members` property. (:issue:`10034`)
- Add ``exclude_deleted`` parameter to :meth:`Client.entitlements` (:issue:`10027`)
- Add :meth:`Client.fetch_guild_preview` (:issue:`9986`)
- Add :meth:`AutoShardedClient.fetch_session_start_limits` (:issue:`10007`)
- Add :attr:`PartialMessageable.mention` (:issue:`9988`)
- Add command target to :class:`MessageInteractionMetadata` (:issue:`10004`)
- :attr:`MessageInteractionMetadata.target_user`
- :attr:`MessageInteractionMetadata.target_message_id`
- :attr:`MessageInteractionMetadata.target_message`
- Add :attr:`Message.forward` flag (:issue:`9978`)
- Add support for purchase notification messages (:issue:`9906`)
- Add new type :attr:`MessageType.purchase_notification`
- Add new models :class:`GuildProductPurchase` and :class:`PurchaseNotification`
- Add :attr:`Message.purchase_notification`
- Add ``category`` parameter to :meth:`.abc.GuildChannel.clone` (:issue:`9941`)
- Add support for message call (:issue:`9911`)
- Add new models :class:`CallMessage`
- Add :attr:`Message.call` attribute
- Parse full message for message edit event (:issue:`10035`)
- Adds :attr:`RawMessageUpdateEvent.message` attribute
- Potentially speeds up :func:`on_message_edit` by no longer copying data
- Add support for retrieving and editing integration type configuration (:issue:`9818`)
- This adds :class:`IntegrationTypeConfig`
- Retrievable via :attr:`AppInfo.guild_integration_config` and :attr:`AppInfo.user_integration_config`.
- Editable via :meth:`AppInfo.edit`
- Allow passing ``None`` for ``scopes`` parameter in :func:`utils.oauth_url` (:issue:`10078`)
- Add support for :attr:`MessageType.poll_result` messages (:issue:`9905`)
- Add various new :class:`MessageFlags`
- Add :meth:`Member.fetch_voice` (:issue:`9908`)
- Add :attr:`Guild.dm_spam_detected_at` and :meth:`Guild.is_dm_spam_detected` (:issue:`9808`)
- Add :attr:`Guild.raid_detected_at` and :meth:`Guild.is_raid_detected` (:issue:`9808`)
- Add :meth:`Client.fetch_premium_sticker_pack` (:issue:`9909`)
- Add :attr:`AppInfo.approximate_user_install_count` (:issue:`9915`)
- Add :meth:`Guild.fetch_role` (:issue:`9921`)
- Add :attr:`Attachment.title` (:issue:`9904`)
- Add :attr:`Member.guild_banner` and :attr:`Member.display_banner`
- Re-add ``connector`` parameter that was removed during v2.0 (:issue:`9900`)
- |commands| Add :class:`~discord.ext.commands.SoundboardSoundConverter` (:issue:`9973`)
Bug Fixes
~~~~~~~~~~
- Change the default file size limit for :attr:`Guild.filesize_limit` to match new Discord limit of 10 MiB (:issue:`10084`)
- Handle improper 1000 close code closures by Discord
- This fixes an issue causing excessive IDENTIFY in large bots
- Fix potential performance regression when dealing with cookies in the library owned session (:issue:`9916`)
- Add support for AEAD XChaCha20 Poly1305 encryption mode (:issue:`9953`)
- This allows voice to continue working when the older encryption modes eventually get removed.
- Support for DAVE is still tentative.
- Fix large performance regression due to polls when creating messages
- Fix cases where :attr:`Member.roles` contains a ``None`` role (:issue:`10093`)
- Update all channel clone implementations to work as expected (:issue:`9935`)
- Fix bug in :meth:`Client.entitlements` only returning 100 entries (:issue:`10051`)
- Fix :meth:`TextChannel.clone` always sending slowmode when not applicable to news channels (:issue:`9967`)
- Fix :attr:`Message.system_content` for :attr:`MessageType.role_subscription_purchase` renewals (:issue:`9955`)
- Fix :attr:`Sticker.url` for GIF stickers (:issue:`9913`)
- Fix :attr:`User.default_avatar` for team users and webhooks (:issue:`9907`)
- Fix potential rounding error in :attr:`Poll.duration` (:issue:`9903`)
- Fix introduced potential TypeError when raising :exc:`app_commands.CommandSyncFailure`
- Fix :attr:`AuditLogEntry.target` causing errors for :attr:`AuditLogAction.message_pin` and :attr:`AuditLogAction.message_unpin` actions (:issue:`10061`).
- Fix incorrect :class:`ui.Select` maximum option check (:issue:`9878`, :issue:`9879`)
- Fix path sanitisation for absolute Windows paths when using ``__main__`` (:issue:`10096`, :issue:`10097`)
- |tasks| Fix race condition when setting timer handle when using uvloop (:issue:`10020`)
- |commands| Fix issue with category cooldowns outside of guild channels (:issue:`9959`)
- |commands| Fix :meth:`Context.defer <ext.commands.Context.defer>` unconditionally deferring
- |commands| Fix callable FlagConverter defaults on hybrid commands not being called (:issue:`10037`)
- |commands| Unwrap :class:`~discord.ext.commands.Parameter` if given as default to :func:`~ext.commands.parameter` (:issue:`9977`)
- |commands| Fix fallback behaviour not being respected when calling replace for :class:`~.ext.commands.Parameter` (:issue:`10076`, :issue:`10077`)
- |commands| Respect ``enabled`` keyword argument for hybrid app commands (:issue:`10001`)
Miscellaneous
~~~~~~~~~~~~~~
- Use a fallback package for ``audioop`` to allow the library to work in Python 3.13 or newer.
- Remove ``aiodns`` from being used on Windows (:issue:`9898`)
- Add zstd gateway compression to ``speed`` extras (:issue:`9947`)
- This can be installed using ``discord.py[speed]``
- Add proxy support fetching from the CDN (:issue:`9966`)
- Remove ``/`` from being safe from URI encoding when constructing paths internally
- Sanitize invite argument before calling the invite info endpoint
- Avoid returning in finally in specific places to prevent exception swallowing (:issue:`9981`, :issue:`9984`)
- Enforce and create random nonces when creating messages throughout the library
- Revert IPv6 block in the library (:issue:`9870`)
- Allow passing :class:`Permissions` object to :func:`app_commands.default_permissions` decorator (:issue:`9951`, :issue:`9971`)
.. _vp2p4p0: .. _vp2p4p0:
v2.4.0 v2.4.0

Loading…
Cancel
Save