Skip to content

Commit d8cd752

Browse files
committed
Merge remote-tracking branch 'pycord/master' into merge-upstream
2 parents 160f5f4 + 8619b69 commit d8cd752

File tree

14 files changed

+1198
-73
lines changed

14 files changed

+1198
-73
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
@@ -293,7 +293,7 @@ def _check_command(cmd: ApplicationCommand, match: Mapping[str, Any]) -> bool:
293293
"integration_types": None,
294294
}
295295
for check, value in to_check.items():
296-
if type(to_check[check]) == list:
296+
if type(value) == list:
297297
# We need to do some falsy conversion here
298298
# The API considers False (autocomplete) and [] (choices) to be falsy values
299299
falsy_vals = (False, [])
@@ -357,12 +357,12 @@ def _check_command(cmd: ApplicationCommand, match: Mapping[str, Any]) -> bool:
357357

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

discord/ext/bridge/core.py

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

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

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

discord/ext/commands/flags.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@
5555
from .context import Context
5656

5757

58+
def _missing_field_factory() -> field:
59+
return field(default_factory=lambda: MISSING)
60+
61+
5862
@dataclass
5963
class Flag:
6064
"""Represents a flag parameter for :class:`FlagConverter`.
@@ -82,14 +86,14 @@ class Flag:
8286
Whether multiple given values overrides the previous value.
8387
"""
8488

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

9498
@property
9599
def required(self) -> bool:

discord/guild.py

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

91-
__all__ = ("Guild",)
91+
__all__ = ("BanEntry", "Guild")
9292

9393
MISSING = utils.MISSING
9494

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 . import utils
@@ -282,6 +283,11 @@ def guild(self) -> Guild | None:
282283
return self._guild
283284
return self._state and self._state._get_guild(self.guild_id)
284285

286+
@property
287+
def created_at(self) -> datetime.datetime:
288+
"""Returns the interaction's creation time in UTC."""
289+
return utils.snowflake_time(self.id)
290+
285291
def is_command(self) -> bool:
286292
"""Indicates whether the interaction is an application command."""
287293
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.get(view.children, custom_id=custom_id)`.
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

0 commit comments

Comments
 (0)