Skip to content

Commit

Permalink
Merge branch 'Rapptz:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
gingershaped authored Nov 24, 2024
2 parents 8b4f120 + e1b6310 commit 0784ab6
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 22 deletions.
4 changes: 4 additions & 0 deletions discord/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import copy
import time
import secrets
import asyncio
from datetime import datetime
from typing import (
Expand Down Expand Up @@ -1614,6 +1615,9 @@ async def send(
else:
flags = MISSING

if nonce is None:
nonce = secrets.randbits(64)

with handle_message_parameters(
content=content,
tts=tts,
Expand Down
4 changes: 1 addition & 3 deletions discord/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
from .stage_instance import StageInstance
from .threads import Thread
from .partial_emoji import _EmojiTag, PartialEmoji
from .flags import ChannelFlags
from .flags import ChannelFlags, MessageFlags
from .http import handle_message_parameters
from .object import Object
from .soundboard import BaseSoundboardSound, SoundboardDefaultSound
Expand Down Expand Up @@ -2938,8 +2938,6 @@ async def create_thread(
raise TypeError(f'view parameter must be View not {view.__class__.__name__}')

if suppress_embeds:
from .message import MessageFlags # circular import

flags = MessageFlags._from_value(4)
else:
flags = MISSING
Expand Down
5 changes: 4 additions & 1 deletion discord/ext/commands/hybrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
from discord import app_commands
from discord.utils import MISSING, maybe_coroutine, async_all
from .core import Command, Group
from .errors import BadArgument, CommandRegistrationError, CommandError, HybridCommandError, ConversionError
from .errors import BadArgument, CommandRegistrationError, CommandError, HybridCommandError, ConversionError, DisabledCommand
from .converter import Converter, Range, Greedy, run_converters, CONVERTER_MAPPING
from .parameters import Parameter
from .flags import is_flag, FlagConverter
Expand Down Expand Up @@ -526,6 +526,9 @@ def cog(self, value: CogT) -> None:
self.app_command.binding = value

async def can_run(self, ctx: Context[BotT], /) -> bool:
if not self.enabled:
raise DisabledCommand(f'{self.name} command is disabled')

if ctx.interaction is not None and self.app_command:
return await self.app_command._check_can_run(ctx.interaction)
else:
Expand Down
5 changes: 4 additions & 1 deletion discord/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,10 @@ def latency(self) -> float:

def _can_handle_close(self) -> bool:
code = self._close_code or self.socket.close_code
return code not in (1000, 4004, 4010, 4011, 4012, 4013, 4014)
# If the socket is closed remotely with 1000 and it's not our own explicit close
# then it's an improper close that should be handled and reconnected
is_improper_close = self._close_code is None and self.socket.close_code == 1000
return is_improper_close or code not in (1000, 4004, 4010, 4011, 4012, 4013, 4014)

async def poll_event(self) -> None:
"""Polls for a DISPATCH event and handles the general gateway loop.
Expand Down
3 changes: 2 additions & 1 deletion discord/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ def handle_message_parameters(

if nonce is not None:
payload['nonce'] = str(nonce)
payload['enforce_nonce'] = True

if message_reference is not MISSING:
payload['message_reference'] = message_reference
Expand Down Expand Up @@ -308,7 +309,7 @@ def __init__(self, method: str, path: str, *, metadata: Optional[str] = None, **
self.metadata: Optional[str] = metadata
url = self.BASE + self.path
if parameters:
url = url.format_map({k: _uriquote(v) if isinstance(v, str) else v for k, v in parameters.items()})
url = url.format_map({k: _uriquote(v, safe='') if isinstance(v, str) else v for k, v in parameters.items()})
self.url: str = url

# major parameters:
Expand Down
4 changes: 2 additions & 2 deletions discord/member.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ async def add_roles(self, *roles: Snowflake, reason: Optional[str] = None, atomi
You must have :attr:`~Permissions.manage_roles` to
use this, and the added :class:`Role`\s must appear lower in the list
of roles than the highest role of the member.
of roles than the highest role of the client.
Parameters
-----------
Expand Down Expand Up @@ -1115,7 +1115,7 @@ async def remove_roles(self, *roles: Snowflake, reason: Optional[str] = None, at
You must have :attr:`~Permissions.manage_roles` to
use this, and the removed :class:`Role`\s must appear lower in the list
of roles than the highest role of the member.
of roles than the highest role of the client.
Parameters
-----------
Expand Down
40 changes: 36 additions & 4 deletions discord/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,14 @@ class MessageInteractionMetadata(Hashable):
The ID of the message that containes the interactive components, if applicable.
modal_interaction: Optional[:class:`.MessageInteractionMetadata`]
The metadata of the modal submit interaction that triggered this interaction, if applicable.
target_user: Optional[:class:`User`]
The user the command was run on, only applicable to user context menus.
.. versionadded:: 2.5
target_message_id: Optional[:class:`int`]
The ID of the message the command was run on, only applicable to message context menus.
.. versionadded:: 2.5
"""

__slots__: Tuple[str, ...] = (
Expand All @@ -837,6 +845,8 @@ class MessageInteractionMetadata(Hashable):
'original_response_message_id',
'interacted_message_id',
'modal_interaction',
'target_user',
'target_message_id',
'_integration_owners',
'_state',
'_guild',
Expand All @@ -848,31 +858,43 @@ def __init__(self, *, state: ConnectionState, guild: Optional[Guild], data: Mess

self.id: int = int(data['id'])
self.type: InteractionType = try_enum(InteractionType, data['type'])
self.user = state.create_user(data['user'])
self.user: User = state.create_user(data['user'])
self._integration_owners: Dict[int, int] = {
int(key): int(value) for key, value in data.get('authorizing_integration_owners', {}).items()
}

self.original_response_message_id: Optional[int] = None
try:
self.original_response_message_id = int(data['original_response_message_id'])
self.original_response_message_id = int(data['original_response_message_id']) # type: ignore # EAFP
except KeyError:
pass

self.interacted_message_id: Optional[int] = None
try:
self.interacted_message_id = int(data['interacted_message_id'])
self.interacted_message_id = int(data['interacted_message_id']) # type: ignore # EAFP
except KeyError:
pass

self.modal_interaction: Optional[MessageInteractionMetadata] = None
try:
self.modal_interaction = MessageInteractionMetadata(
state=state, guild=guild, data=data['triggering_interaction_metadata']
state=state, guild=guild, data=data['triggering_interaction_metadata'] # type: ignore # EAFP
)
except KeyError:
pass

self.target_user: Optional[User] = None
try:
self.target_user = state.create_user(data['target_user']) # type: ignore # EAFP
except KeyError:
pass

self.target_message_id: Optional[int] = None
try:
self.target_message_id = int(data['target_message_id']) # type: ignore # EAFP
except KeyError:
pass

def __repr__(self) -> str:
return f'<MessageInteraction id={self.id} type={self.type!r} user={self.user!r}>'

Expand All @@ -899,6 +921,16 @@ def interacted_message(self) -> Optional[Message]:
return self._state._get_message(self.interacted_message_id)
return None

@property
def target_message(self) -> Optional[Message]:
"""Optional[:class:`~discord.Message`]: The target message, if applicable and is found in cache.
.. versionadded:: 2.5
"""
if self.target_message_id:
return self._state._get_message(self.target_message_id)
return None

def is_guild_integration(self) -> bool:
""":class:`bool`: Returns ``True`` if the interaction is a guild integration."""
if self._guild:
Expand Down
14 changes: 9 additions & 5 deletions discord/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,22 +588,26 @@ async def probe(
loop = asyncio.get_running_loop()
try:
codec, bitrate = await loop.run_in_executor(None, lambda: probefunc(source, executable))
except Exception:
except (KeyboardInterrupt, SystemExit):
raise
except BaseException:
if not fallback:
_log.exception("Probe '%s' using '%s' failed", method, executable)
return # type: ignore
return None, None

_log.exception("Probe '%s' using '%s' failed, trying fallback", method, executable)
try:
codec, bitrate = await loop.run_in_executor(None, lambda: fallback(source, executable))
except Exception:
except (KeyboardInterrupt, SystemExit):
raise
except BaseException:
_log.exception("Fallback probe using '%s' failed", executable)
else:
_log.debug("Fallback probe found codec=%s, bitrate=%s", codec, bitrate)
else:
_log.debug("Probe found codec=%s, bitrate=%s", codec, bitrate)
finally:
return codec, bitrate

return codec, bitrate

@staticmethod
def _probe_codec_native(source, executable: str = 'ffmpeg') -> Tuple[Optional[str], Optional[int]]:
Expand Down
46 changes: 42 additions & 4 deletions discord/types/interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,49 @@ class MessageInteraction(TypedDict):
member: NotRequired[Member]


class MessageInteractionMetadata(TypedDict):
class _MessageInteractionMetadata(TypedDict):
id: Snowflake
type: InteractionType
user: User
authorizing_integration_owners: Dict[Literal['0', '1'], Snowflake]
original_response_message_id: NotRequired[Snowflake]
interacted_message_id: NotRequired[Snowflake]
triggering_interaction_metadata: NotRequired[MessageInteractionMetadata]


class _ApplicationCommandMessageInteractionMetadata(_MessageInteractionMetadata):
type: Literal[2]
# command_type: Literal[1, 2, 3, 4]


class UserApplicationCommandMessageInteractionMetadata(_ApplicationCommandMessageInteractionMetadata):
# command_type: Literal[2]
target_user: User


class MessageApplicationCommandMessageInteractionMetadata(_ApplicationCommandMessageInteractionMetadata):
# command_type: Literal[3]
target_message_id: Snowflake


ApplicationCommandMessageInteractionMetadata = Union[
_ApplicationCommandMessageInteractionMetadata,
UserApplicationCommandMessageInteractionMetadata,
MessageApplicationCommandMessageInteractionMetadata,
]


class MessageComponentMessageInteractionMetadata(_MessageInteractionMetadata):
type: Literal[3]
interacted_message_id: Snowflake


class ModalSubmitMessageInteractionMetadata(_MessageInteractionMetadata):
type: Literal[5]
triggering_interaction_metadata: Union[
ApplicationCommandMessageInteractionMetadata, MessageComponentMessageInteractionMetadata
]


MessageInteractionMetadata = Union[
ApplicationCommandMessageInteractionMetadata,
MessageComponentMessageInteractionMetadata,
ModalSubmitMessageInteractionMetadata,
]
13 changes: 12 additions & 1 deletion discord/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,12 @@ def resolve_invite(invite: Union[Invite, str]) -> ResolvedInvite:
invite: Union[:class:`~discord.Invite`, :class:`str`]
The invite.
Raises
-------
ValueError
The invite is not a valid Discord invite, e.g. is not a URL
or does not contain alphanumeric characters.
Returns
--------
:class:`.ResolvedInvite`
Expand All @@ -887,7 +893,12 @@ def resolve_invite(invite: Union[Invite, str]) -> ResolvedInvite:
event_id = url.query.get('event')

return ResolvedInvite(code, int(event_id) if event_id else None)
return ResolvedInvite(invite, None)

allowed_characters = r'[a-zA-Z0-9\-_]+'
if not re.fullmatch(allowed_characters, invite):
raise ValueError('Invite contains characters that are not allowed')

return ResolvedInvite(invite, None)


def resolve_template(code: Union[Template, str]) -> str:
Expand Down

0 comments on commit 0784ab6

Please sign in to comment.