Skip to content

Commit ad777e4

Browse files
committed
Merge branch 'master' into refactor-utils
2 parents cd4e436 + 2078cef commit ad777e4

File tree

15 files changed

+185
-334
lines changed

15 files changed

+185
-334
lines changed

.github/workflows/docs-localization-download.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
working-directory: ./docs
4242
- name: "Crowdin"
4343
id: crowdin
44-
uses: crowdin/[email protected].0
44+
uses: crowdin/[email protected].1
4545
with:
4646
upload_sources: false
4747
upload_translations: false

.github/workflows/docs-localization-upload.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
sphinx-intl update -p ./build/locales ${{ vars.SPHINX_LANGUAGES }}
4646
working-directory: ./docs
4747
- name: "Crowdin"
48-
uses: crowdin/[email protected].0
48+
uses: crowdin/[email protected].1
4949
with:
5050
upload_sources: true
5151
upload_translations: false

CHANGELOG.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,10 @@ These changes are available on the `master` branch, but have not yet been releas
5353
([#2598](https://github.com/Pycord-Development/pycord/pull/2598))
5454
- Added the ability to change the API's base URL with `Route.API_BASE_URL`.
5555
([#2714](https://github.com/Pycord-Development/pycord/pull/2714))
56-
- Added the ability to pass a `datetime.time` object to `format_dt`
56+
- Added the ability to pass a `datetime.time` object to `format_dt`.
5757
([#2747](https://github.com/Pycord-Development/pycord/pull/2747))
58+
- Added `discord.Interaction.created_at`.
59+
([#2801](https://github.com/Pycord-Development/pycord/pull/2801))
5860

5961
### Fixed
6062

@@ -105,12 +107,18 @@ These changes are available on the `master` branch, but have not yet been releas
105107
([#2739](https://github.com/Pycord-Development/pycord/pull/2739))
106108
- Fixed missing `None` type hints in `Select.__init__`.
107109
([#2746](https://github.com/Pycord-Development/pycord/pull/2746))
110+
- Fixed `TypeError` when using `Flag` with Python 3.11+.
111+
([#2759](https://github.com/Pycord-Development/pycord/pull/2759))
112+
- Fixed `TypeError` when specifying `thread_name` in `Webhook.send`.
113+
([#2761](https://github.com/Pycord-Development/pycord/pull/2761))
108114
- Updated `valid_locales` to support `in` and `es-419`.
109115
([#2767](https://github.com/Pycord-Development/pycord/pull/2767))
110116
- Fixed `Webhook.edit` not working with `attachments=[]`.
111117
([#2779](https://github.com/Pycord-Development/pycord/pull/2779))
112118
- Fixed GIF-based `Sticker` returning the wrong `url`.
113119
([#2781](https://github.com/Pycord-Development/pycord/pull/2781))
120+
- Fixed `VoiceClient` crashing randomly while receiving audio
121+
([#2800](https://github.com/Pycord-Development/pycord/pull/2800))
114122

115123
### Changed
116124

@@ -140,6 +148,11 @@ These changes are available on the `master` branch, but have not yet been releas
140148
- Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`.
141149
([#2658](https://github.com/Pycord-Development/pycord/pull/2658))
142150

151+
### Removed
152+
153+
- Removed deprecated support for `Option` in `BridgeCommand`. Use `BridgeOption`
154+
instead. ([#2731])(https://github.com/Pycord-Development/pycord/pull/2731))
155+
143156
## [2.6.1] - 2024-09-15
144157

145158
### Fixed

discord/bot.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ def _check_command(cmd: ApplicationCommand, match: Mapping[str, Any]) -> bool:
294294
"integration_types": None,
295295
}
296296
for check, value in to_check.items():
297-
if type(to_check[check]) == list:
297+
if type(value) == list:
298298
# We need to do some falsy conversion here
299299
# The API considers False (autocomplete) and [] (choices) to be falsy values
300300
falsy_vals = (False, [])
@@ -358,12 +358,12 @@ def _check_command(cmd: ApplicationCommand, match: Mapping[str, Any]) -> bool:
358358

359359
# Now let's see if there are any commands on discord that we need to delete
360360
for cmd, value_ in registered_commands_dict.items():
361-
match = find(lambda c: c.name == registered_commands_dict[cmd]["name"], pending)
361+
match = find(lambda c: c.name == value_["name"], pending)
362362
if match is None:
363363
# We have this command registered but not in our list
364364
return_value.append(
365365
{
366-
"command": registered_commands_dict[cmd]["name"],
366+
"command": value_["name"],
367367
"id": int(value_["id"]),
368368
"action": "delete",
369369
}

discord/ext/bridge/core.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -97,23 +97,11 @@ class BridgeExtCommand(Command):
9797
def __init__(self, func, **kwargs):
9898
super().__init__(func, **kwargs)
9999

100-
# TODO: v2.7: Remove backwards support for Option in bridge commands.
101-
for name, option in self.params.items():
100+
for option in self.params.values():
102101
if isinstance(option.annotation, Option) and not isinstance(option.annotation, BridgeOption):
103-
# Warn not to do this
104-
warn_deprecated(
105-
"Using Option for bridge commands",
106-
"BridgeOption",
107-
"2.5",
108-
"2.7",
109-
reference="https://github.com/Pycord-Development/pycord/pull/2417",
110-
stacklevel=6,
102+
raise TypeError(
103+
f"{option.annotation.__class__.__name__} is not supported in bridge commands. Use BridgeOption instead."
111104
)
112-
# Override the convert method of the parameter's annotated Option.
113-
# We can use the convert method from BridgeOption, and bind "self"
114-
# using a manual invocation of the descriptor protocol.
115-
# Definitely not a good approach, but gets the job done until removal.
116-
self.params[name].annotation.convert = BridgeOption.convert.__get__(self.params[name].annotation)
117105

118106
async def dispatch_error(self, ctx: BridgeExtContext, error: Exception) -> None:
119107
await super().dispatch_error(ctx, error)

discord/ext/commands/flags.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
from .context import Context
5757

5858

59+
def _missing_field_factory() -> field:
60+
return field(default_factory=lambda: MISSING)
61+
62+
5963
@dataclass
6064
class Flag:
6165
"""Represents a flag parameter for :class:`FlagConverter`.
@@ -83,13 +87,13 @@ class Flag:
8387
Whether multiple given values overrides the previous value.
8488
"""
8589

86-
name: str | Undefined = MISSING
90+
name: str | Undefined = _missing_field_factory() # noqa: RUF009
8791
aliases: list[str] = field(default_factory=list)
88-
attribute: str | Undefined = MISSING
89-
annotation: Any | Undefined = MISSING
90-
default: Any | Undefined = MISSING
91-
max_args: int | Undefined = MISSING
92-
override: bool | Undefined = MISSING
92+
attribute: str | Undefined = _missing_field_factory() # noqa: RUF009
93+
annotation: Any | Undefined = _missing_field_factory() # noqa: RUF009
94+
default: Any | Undefined = _missing_field_factory() # noqa: RUF009
95+
max_args: int | Undefined = _missing_field_factory() # noqa: RUF009
96+
override: bool | Undefined = _missing_field_factory() # noqa: RUF009
9397
cast_to_dict: bool = False
9498

9599
@property

discord/guild.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
from .welcome_screen import WelcomeScreen, WelcomeScreenChannel
9090
from .widget import Widget
9191

92-
__all__ = ("Guild",)
92+
__all__ = ("BanEntry", "Guild")
9393

9494
MISSING = utils.MISSING
9595

discord/interactions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from __future__ import annotations
2727

2828
import asyncio
29+
import datetime
2930
from typing import TYPE_CHECKING, Any, Coroutine, Union
3031

3132
from .utils.private import get_as_snowflake, deprecated, delay_task
@@ -283,6 +284,11 @@ def guild(self) -> Guild | None:
283284
return self._guild
284285
return self._state and self._state._get_guild(self.guild_id)
285286

287+
@property
288+
def created_at(self) -> datetime.datetime:
289+
"""Returns the interaction's creation time in UTC."""
290+
return utils.snowflake_time(self.id)
291+
286292
def is_command(self) -> bool:
287293
"""Indicates whether the interaction is an application command."""
288294
return self.type == InteractionType.application_command

discord/ui/view.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import traceback
3333
from functools import partial
3434
from itertools import groupby
35-
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Iterator, Sequence
35+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Iterator, Sequence, TypeVar
3636

3737
from ..components import ActionRow as ActionRowComponent
3838
from ..components import Button as ButtonComponent
@@ -51,6 +51,8 @@
5151
from ..state import ConnectionState
5252
from ..types.components import Component as ComponentPayload
5353

54+
V = TypeVar("V", bound="View", covariant=True)
55+
5456

5557
def _walk_all_components(components: list[Component]) -> Iterator[Component]:
5658
for item in components:
@@ -60,7 +62,7 @@ def _walk_all_components(components: list[Component]) -> Iterator[Component]:
6062
yield item
6163

6264

63-
def _component_to_item(component: Component) -> Item:
65+
def _component_to_item(component: Component) -> Item[V]:
6466
if isinstance(component, ButtonComponent):
6567
from .button import Button
6668

@@ -75,7 +77,7 @@ def _component_to_item(component: Component) -> Item:
7577
class _ViewWeights:
7678
__slots__ = ("weights",)
7779

78-
def __init__(self, children: list[Item]):
80+
def __init__(self, children: list[Item[V]]):
7981
self.weights: list[int] = [0, 0, 0, 0, 0]
8082

8183
key = lambda i: sys.maxsize if i.row is None else i.row
@@ -84,14 +86,14 @@ def __init__(self, children: list[Item]):
8486
for item in group:
8587
self.add_item(item)
8688

87-
def find_open_space(self, item: Item) -> int:
89+
def find_open_space(self, item: Item[V]) -> int:
8890
for index, weight in enumerate(self.weights):
8991
if weight + item.width <= 5:
9092
return index
9193

9294
raise ValueError("could not find open space for item")
9395

94-
def add_item(self, item: Item) -> None:
96+
def add_item(self, item: Item[V]) -> None:
9597
if item.row is not None:
9698
total = self.weights[item.row] + item.width
9799
if total > 5:
@@ -103,7 +105,7 @@ def add_item(self, item: Item) -> None:
103105
self.weights[index] += item.width
104106
item._rendered_row = index
105107

106-
def remove_item(self, item: Item) -> None:
108+
def remove_item(self, item: Item[V]) -> None:
107109
if item._rendered_row is not None:
108110
self.weights[item._rendered_row] -= item.width
109111
item._rendered_row = None
@@ -161,15 +163,15 @@ def __init_subclass__(cls) -> None:
161163

162164
def __init__(
163165
self,
164-
*items: Item,
166+
*items: Item[V],
165167
timeout: float | None = 180.0,
166168
disable_on_timeout: bool = False,
167169
):
168170
self.timeout = timeout
169171
self.disable_on_timeout = disable_on_timeout
170-
self.children: list[Item] = []
172+
self.children: list[Item[V]] = []
171173
for func in self.__view_children_items__:
172-
item: Item = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__)
174+
item: Item[V] = func.__discord_ui_model_type__(**func.__discord_ui_model_kwargs__)
173175
item.callback = partial(func, self, item)
174176
item._view = self
175177
setattr(self, func.__name__, item)
@@ -209,7 +211,7 @@ async def __timeout_task_impl(self) -> None:
209211
await asyncio.sleep(self.__timeout_expiry - now)
210212

211213
def to_components(self) -> list[dict[str, Any]]:
212-
def key(item: Item) -> int:
214+
def key(item: Item[V]) -> int:
213215
return item._rendered_row or 0
214216

215217
children = sorted(self.children, key=key)
@@ -261,7 +263,7 @@ def _expires_at(self) -> float | None:
261263
return time.monotonic() + self.timeout
262264
return None
263265

264-
def add_item(self, item: Item) -> None:
266+
def add_item(self, item: Item[V]) -> None:
265267
"""Adds an item to the view.
266268
267269
Parameters
@@ -289,7 +291,7 @@ def add_item(self, item: Item) -> None:
289291
item._view = self
290292
self.children.append(item)
291293

292-
def remove_item(self, item: Item) -> None:
294+
def remove_item(self, item: Item[V]) -> None:
293295
"""Removes an item from the view.
294296
295297
Parameters
@@ -310,7 +312,7 @@ def clear_items(self) -> None:
310312
self.children.clear()
311313
self.__weights.clear()
312314

313-
def get_item(self, custom_id: str) -> Item | None:
315+
def get_item(self, custom_id: str) -> Item[V] | None:
314316
"""Get an item from the view with the given custom ID. Alias for `utils.find(lambda i: i.custom_id == custom_id, self.children)`.
315317
316318
Parameters
@@ -384,7 +386,7 @@ async def on_check_failure(self, interaction: Interaction) -> None:
384386
The interaction that occurred.
385387
"""
386388

387-
async def on_error(self, error: Exception, item: Item, interaction: Interaction) -> None:
389+
async def on_error(self, error: Exception, item: Item[V], interaction: Interaction) -> None:
388390
"""|coro|
389391
390392
A callback that is called when an item's callback or :meth:`interaction_check`
@@ -404,7 +406,7 @@ async def on_error(self, error: Exception, item: Item, interaction: Interaction)
404406
print(f"Ignoring exception in view {self} for item {item}:", file=sys.stderr)
405407
traceback.print_exception(error.__class__, error, error.__traceback__, file=sys.stderr)
406408

407-
async def _scheduled_task(self, item: Item, interaction: Interaction):
409+
async def _scheduled_task(self, item: Item[V], interaction: Interaction):
408410
try:
409411
if self.timeout:
410412
self.__timeout_expiry = time.monotonic() + self.timeout
@@ -434,7 +436,7 @@ def _dispatch_timeout(self):
434436
self.__stopped.set_result(True)
435437
asyncio.create_task(self.on_timeout(), name=f"discord-ui-view-timeout-{self.id}")
436438

437-
def _dispatch_item(self, item: Item, interaction: Interaction):
439+
def _dispatch_item(self, item: Item[V], interaction: Interaction):
438440
if self.__stopped.done():
439441
return
440442

@@ -448,12 +450,12 @@ def _dispatch_item(self, item: Item, interaction: Interaction):
448450

449451
def refresh(self, components: list[Component]):
450452
# This is pretty hacky at the moment
451-
old_state: dict[tuple[int, str], Item] = {
453+
old_state: dict[tuple[int, str], Item[V]] = {
452454
(item.type.value, item.custom_id): item
453455
for item in self.children
454456
if item.is_dispatchable() # type: ignore
455457
}
456-
children: list[Item] = [item for item in self.children if not item.is_dispatchable()]
458+
children: list[Item[V]] = [item for item in self.children if not item.is_dispatchable()]
457459
for component in _walk_all_components(components):
458460
try:
459461
older = old_state[(component.type.value, component.custom_id)] # type: ignore
@@ -515,7 +517,7 @@ async def wait(self) -> bool:
515517
"""
516518
return await self.__stopped
517519

518-
def disable_all_items(self, *, exclusions: list[Item] | None = None) -> None:
520+
def disable_all_items(self, *, exclusions: list[Item[V]] | None = None) -> None:
519521
"""
520522
Disables all items in the view.
521523
@@ -528,7 +530,7 @@ def disable_all_items(self, *, exclusions: list[Item] | None = None) -> None:
528530
if exclusions is None or child not in exclusions:
529531
child.disabled = True
530532

531-
def enable_all_items(self, *, exclusions: list[Item] | None = None) -> None:
533+
def enable_all_items(self, *, exclusions: list[Item[V]] | None = None) -> None:
532534
"""
533535
Enables all items in the view.
534536
@@ -553,7 +555,7 @@ def message(self, value):
553555
class ViewStore:
554556
def __init__(self, state: ConnectionState):
555557
# (component_type, message_id, custom_id): (View, Item)
556-
self._views: dict[tuple[int, int | None, str], tuple[View, Item]] = {}
558+
self._views: dict[tuple[int, int | None, str], tuple[View, Item[V]]] = {}
557559
# message_id: View
558560
self._synced_message_views: dict[int, View] = {}
559561
self._state: ConnectionState = state

discord/voice_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ def _decrypt_xsalsa20_poly1305_lite(self, header, data):
602602

603603
@staticmethod
604604
def strip_header_ext(data):
605-
if data[0] == 0xBE and data[1] == 0xDE and len(data) > 4:
605+
if len(data) > 4 and data[0] == 0xBE and data[1] == 0xDE:
606606
_, length = struct.unpack_from(">HH", data)
607607
offset = 4 + length * 4
608608
data = data[offset:]

0 commit comments

Comments
 (0)