Skip to content

Commit e312a2f

Browse files
authored
Merge branch 'master' into optional-option
Signed-off-by: Lumouille <[email protected]>
2 parents 97af386 + 9fd9b3e commit e312a2f

File tree

10 files changed

+362
-32
lines changed

10 files changed

+362
-32
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ These changes are available on the `master` branch, but have not yet been releas
5757
([#2714](https://github.com/Pycord-Development/pycord/pull/2714))
5858
- Added the ability to pass a `datetime.time` object to `format_dt`.
5959
([#2747](https://github.com/Pycord-Development/pycord/pull/2747))
60+
- Added various missing channel parameters and allow `default_reaction_emoji` to be
61+
`None`. ([#2772](https://github.com/Pycord-Development/pycord/pull/2772))
6062
- Added support for type hinting slash command options with `typing.Annotated`.
6163
([#2782](https://github.com/Pycord-Development/pycord/pull/2782))
6264
- Added conversion to `Member` in `MentionableConverter`.
@@ -67,6 +69,8 @@ These changes are available on the `master` branch, but have not yet been releas
6769
([#2817](https://github.com/Pycord-Development/pycord/pull/2817))
6870
- Added role gradients support with `Role.colours` and the `RoleColours` class.
6971
([#2818](https://github.com/Pycord-Development/pycord/pull/2818))
72+
- Added `ThreadArchiveDuration` enum to improve clarity of thread archive durations.
73+
([#2826](https://github.com/Pycord-Development/pycord/pull/2826))
7074
- Added `Interaction.attachment_size_limit`.
7175
([#2854](https://github.com/Pycord-Development/pycord/pull/2854))
7276
- Added support for selects and text displays in modals.
@@ -75,6 +79,8 @@ These changes are available on the `master` branch, but have not yet been releas
7579
([#2883](https://github.com/Pycord-Development/pycord/pull/2883))
7680
- Added `discord.User.primary_guild` and the `PrimaryGuild` class.
7781
([#2876](https://github.com/Pycord-Development/pycord/pull/2876))
82+
- Added `get_component` to `Message`, `Section`, `Container` and `ActionRow`.
83+
([#2849](https://github.com/Pycord-Development/pycord/pull/2849))
7884

7985
### Fixed
8086

@@ -148,6 +154,8 @@ These changes are available on the `master` branch, but have not yet been releas
148154
`channel_types`. ([#2835](https://github.com/Pycord-Development/pycord/pull/2835))
149155
- Fixed `TypeError` when using `Optional[...]` or `... | None` in command option type.
150156
([#2852](https://github.com/Pycord-Development/pycord/pull/2852))
157+
- Fixed type-hinting for `PermissionOverwrite.update`.
158+
([#2878](https://github.com/Pycord-Development/pycord/pull/2878))
151159
- Fixed `AttributeError` when accessing `AuditLogEntry.changes` more than once.
152160
([#2882])(https://github.com/Pycord-Development/pycord/pull/2882))
153161

@@ -182,6 +190,8 @@ These changes are available on the `master` branch, but have not yet been releas
182190
([#2501](https://github.com/Pycord-Development/pycord/pull/2501))
183191
- Deprecated `Interaction.cached_channel` in favor of `Interaction.channel`.
184192
([#2658](https://github.com/Pycord-Development/pycord/pull/2658))
193+
- Deprecated `is_nsfw` for categories since it was never supported by the API.
194+
([#2772](https://github.com/Pycord-Development/pycord/pull/2772))
185195
- Deprecated `Messageable.pins()` returning a list of `Message`; it should be used as an
186196
iterator of `MessagePin` instead.
187197
([#2872](https://github.com/Pycord-Development/pycord/pull/2872))

discord/channel.py

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@
4848
InviteTarget,
4949
SortOrder,
5050
StagePrivacyLevel,
51+
)
52+
from .enums import ThreadArchiveDuration as ThreadArchiveDurationEnum
53+
from .enums import (
5154
VideoQualityMode,
5255
VoiceRegion,
5356
try_enum,
@@ -229,7 +232,7 @@ def __init__(
229232

230233
@property
231234
def _repr_attrs(self) -> tuple[str, ...]:
232-
return "id", "name", "position", "nsfw", "category_id"
235+
return "id", "name", "position", "category_id"
233236

234237
def __repr__(self) -> str:
235238
attrs = [(val, getattr(self, val)) for val in self._repr_attrs]
@@ -825,7 +828,7 @@ async def edit(self, *, reason=None, **options):
825828
position: :class:`int`
826829
The new channel's position.
827830
nsfw: :class:`bool`
828-
To mark the channel as NSFW or not.
831+
Whether the channel is marked as NSFW.
829832
sync_permissions: :class:`bool`
830833
Whether to sync permissions with the channel's new or pre-existing
831834
category. Defaults to ``False``.
@@ -1052,6 +1055,8 @@ def _update(self, guild: Guild, data: ForumChannelPayload) -> None:
10521055
if self.default_sort_order is not None:
10531056
self.default_sort_order = try_enum(SortOrder, self.default_sort_order)
10541057

1058+
self.default_reaction_emoji = None
1059+
10551060
reaction_emoji_ctx: dict = data.get("default_reaction_emoji")
10561061
if reaction_emoji_ctx is not None:
10571062
emoji_name = reaction_emoji_ctx.get("emoji_name")
@@ -1097,7 +1102,9 @@ async def edit(
10971102
sync_permissions: bool = ...,
10981103
category: CategoryChannel | None = ...,
10991104
slowmode_delay: int = ...,
1100-
default_auto_archive_duration: ThreadArchiveDuration = ...,
1105+
default_auto_archive_duration: (
1106+
ThreadArchiveDuration | ThreadArchiveDurationEnum
1107+
) = ...,
11011108
default_thread_slowmode_delay: int = ...,
11021109
default_sort_order: SortOrder = ...,
11031110
default_reaction_emoji: GuildEmoji | int | str | None = ...,
@@ -1126,7 +1133,7 @@ async def edit(self, *, reason=None, **options):
11261133
position: :class:`int`
11271134
The new channel's position.
11281135
nsfw: :class:`bool`
1129-
To mark the channel as NSFW or not.
1136+
Whether the channel is marked as NSFW.
11301137
sync_permissions: :class:`bool`
11311138
Whether to sync permissions with the channel's new or pre-existing
11321139
category. Defaults to ``False``.
@@ -1143,6 +1150,7 @@ async def edit(self, *, reason=None, **options):
11431150
default_auto_archive_duration: :class:`int`
11441151
The new default auto archive duration in minutes for threads created in this channel.
11451152
Must be one of ``60``, ``1440``, ``4320``, or ``10080``.
1153+
:class:`ThreadArchiveDuration` can be used alternatively.
11461154
default_thread_slowmode_delay: :class:`int`
11471155
The new default slowmode delay in seconds for threads created in this channel.
11481156
@@ -1503,7 +1511,7 @@ async def edit(self, *, reason=None, **options):
15031511
position: :class:`int`
15041512
The new channel's position.
15051513
nsfw: :class:`bool`
1506-
To mark the channel as NSFW or not.
1514+
Whether the channel is marked as NSFW.
15071515
sync_permissions: :class:`bool`
15081516
Whether to sync permissions with the channel's new or pre-existing
15091517
category. Defaults to ``False``.
@@ -1751,6 +1759,11 @@ class VoiceChannel(discord.abc.Messageable, VocalGuildChannel):
17511759
Extra features of the channel.
17521760
17531761
.. versionadded:: 2.0
1762+
1763+
nsfw: :class:`bool`
1764+
Whether the channel is marked as NSFW.
1765+
1766+
.. versionadded:: 2.7
17541767
"""
17551768

17561769
def __init__(
@@ -2069,6 +2082,7 @@ async def edit(
20692082
rtc_region: VoiceRegion | None = ...,
20702083
video_quality_mode: VideoQualityMode = ...,
20712084
slowmode_delay: int = ...,
2085+
nsfw: bool = ...,
20722086
reason: str | None = ...,
20732087
) -> VoiceChannel | None: ...
20742088

@@ -2119,6 +2133,15 @@ async def edit(self, *, reason=None, **options):
21192133
21202134
.. versionadded:: 2.0
21212135
2136+
slowmode_delay: :class:`int`
2137+
Specifies the slowmode rate limit for user in this channel, in seconds.
2138+
A value of `0` disables slowmode. The maximum value possible is `21600`.
2139+
2140+
nsfw: :class:`bool`
2141+
Whether the channel is marked as NSFW.
2142+
2143+
.. versionadded:: 2.7
2144+
21222145
Returns
21232146
-------
21242147
Optional[:class:`.VoiceChannel`]
@@ -2277,6 +2300,15 @@ class StageChannel(discord.abc.Messageable, VocalGuildChannel):
22772300
last_message_id: Optional[:class:`int`]
22782301
The ID of the last message sent to this channel. It may not always point to an existing or valid message.
22792302
.. versionadded:: 2.5
2303+
2304+
slowmode_delay: :class:`int`
2305+
Specifies the slowmode rate limit for user in this channel, in seconds.
2306+
The maximum value possible is `21600`.
2307+
2308+
nsfw: :class:`bool`
2309+
Whether the channel is marked as NSFW.
2310+
2311+
.. versionadded:: 2.7
22802312
"""
22812313

22822314
__slots__ = ("topic",)
@@ -2761,6 +2793,16 @@ async def edit(self, *, reason=None, **options):
27612793
27622794
.. versionadded:: 2.0
27632795
2796+
bitrate: :class:`int`
2797+
The channel's preferred audio bitrate in bits per second.
2798+
2799+
user_limit: :class:`int`
2800+
The channel's limit for number of members that can be in a voice channel.
2801+
2802+
slowmode_delay: :class:`int`
2803+
Specifies the slowmode rate limit for user in this channel, in seconds.
2804+
A value of `0` disables slowmode. The maximum value possible is `21600`.
2805+
27642806
Returns
27652807
-------
27662808
Optional[:class:`.StageChannel`]
@@ -2817,12 +2859,9 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable):
28172859
position: Optional[:class:`int`]
28182860
The position in the category list. This is a number that starts at 0. e.g. the
28192861
top category is position 0. Can be ``None`` if the channel was received in an interaction.
2820-
nsfw: :class:`bool`
2821-
If the channel is marked as "not safe for work".
28222862
28232863
.. note::
28242864
2825-
To check if the channel or the guild of that channel are marked as NSFW, consider :meth:`is_nsfw` instead.
28262865
flags: :class:`ChannelFlags`
28272866
Extra features of the channel.
28282867
@@ -2833,7 +2872,6 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable):
28332872
"name",
28342873
"id",
28352874
"guild",
2836-
"nsfw",
28372875
"_state",
28382876
"position",
28392877
"_overwrites",
@@ -2850,8 +2888,7 @@ def __init__(
28502888

28512889
def __repr__(self) -> str:
28522890
return (
2853-
"<CategoryChannel"
2854-
f" id={self.id} name={self.name!r} position={self.position} nsfw={self.nsfw}>"
2891+
f"<CategoryChannel id={self.id} name={self.name!r} position={self.position}"
28552892
)
28562893

28572894
def _update(self, guild: Guild, data: CategoryChannelPayload) -> None:
@@ -2862,7 +2899,6 @@ def _update(self, guild: Guild, data: CategoryChannelPayload) -> None:
28622899

28632900
# This data may be missing depending on how this object is being created/updated
28642901
if not data.pop("_invoke_flag", False):
2865-
self.nsfw: bool = data.get("nsfw", False)
28662902
self.position: int = data.get("position")
28672903
self.flags: ChannelFlags = ChannelFlags._from_value(data.get("flags", 0))
28682904
self._fill_overwrites(data)
@@ -2876,23 +2912,18 @@ def type(self) -> ChannelType:
28762912
"""The channel's Discord type."""
28772913
return ChannelType.category
28782914

2879-
def is_nsfw(self) -> bool:
2880-
"""Checks if the category is NSFW."""
2881-
return self.nsfw
2882-
28832915
@utils.copy_doc(discord.abc.GuildChannel.clone)
28842916
async def clone(
28852917
self, *, name: str | None = None, reason: str | None = None
28862918
) -> CategoryChannel:
2887-
return await self._clone_impl({"nsfw": self.nsfw}, name=name, reason=reason)
2919+
return await self._clone_impl({}, name=name, reason=reason)
28882920

28892921
@overload
28902922
async def edit(
28912923
self,
28922924
*,
28932925
name: str = ...,
28942926
position: int = ...,
2895-
nsfw: bool = ...,
28962927
overwrites: Mapping[Role | Member, PermissionOverwrite] = ...,
28972928
reason: str | None = ...,
28982929
) -> CategoryChannel | None: ...
@@ -2920,8 +2951,6 @@ async def edit(self, *, reason=None, **options):
29202951
The new category's name.
29212952
position: :class:`int`
29222953
The new category's position.
2923-
nsfw: :class:`bool`
2924-
To mark the category as NSFW or not.
29252954
reason: Optional[:class:`str`]
29262955
The reason for editing this category. Shows up on the audit log.
29272956
overwrites: Dict[Union[:class:`Role`, :class:`Member`, :class:`~discord.abc.Snowflake`], :class:`PermissionOverwrite`]
@@ -3069,6 +3098,25 @@ async def create_forum_channel(self, name: str, **options: Any) -> ForumChannel:
30693098
"""
30703099
return await self.guild.create_forum_channel(name, category=self, **options)
30713100

3101+
@utils.deprecated(
3102+
since="2.7",
3103+
removed="3.0",
3104+
reference="NSFW categories are not available in the Discord API.",
3105+
)
3106+
def is_nsfw(self) -> bool:
3107+
return False
3108+
3109+
# TODO: Remove in 3.0
3110+
3111+
@property
3112+
@utils.deprecated(
3113+
since="2.7",
3114+
removed="3.0",
3115+
reference="NSFW categories are not available in the Discord API.",
3116+
)
3117+
def nsfw(self) -> bool:
3118+
return False
3119+
30723120

30733121
DMC = TypeVar("DMC", bound="DMChannel")
30743122

discord/components.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
)
4040
from .flags import AttachmentFlags
4141
from .partial_emoji import PartialEmoji, _EmojiTag
42-
from .utils import MISSING, get_slots
42+
from .utils import MISSING, find, get_slots
4343

4444
if TYPE_CHECKING:
4545
from .emoji import AppEmoji, GuildEmoji
@@ -189,6 +189,25 @@ def to_dict(self) -> ActionRowPayload:
189189
def walk_components(self) -> Iterator[Component]:
190190
yield from self.children
191191

192+
def get_component(self, id: str | int) -> Component | None:
193+
"""Get a component from this action row. Roughly equivalent to `utils.get(row.children, ...)`.
194+
If an ``int`` is provided, the component will be retrieved by ``id``, otherwise by ``custom_id``.
195+
196+
Parameters
197+
----------
198+
id: Union[:class:`str`, :class:`int`]
199+
The custom_id or id of the component to get.
200+
201+
Returns
202+
-------
203+
Optional[:class:`Component`]
204+
The component with the matching ``id`` or ``custom_id`` if it exists.
205+
"""
206+
if not id:
207+
return None
208+
attr = "id" if isinstance(id, int) else "custom_id"
209+
return find(lambda i: getattr(i, attr, None) == id, self.children)
210+
192211
@classmethod
193212
def with_components(cls, *components, id=None):
194213
return cls._raw_construct(
@@ -632,6 +651,28 @@ def walk_components(self) -> Iterator[Component]:
632651
yield from r + [self.accessory]
633652
yield from r
634653

654+
def get_component(self, id: str | int) -> Component | None:
655+
"""Get a component from this section. Roughly equivalent to `utils.get(section.walk_components(), ...)`.
656+
If an ``int`` is provided, the component will be retrieved by ``id``, otherwise by ``custom_id``.
657+
658+
Parameters
659+
----------
660+
id: Union[:class:`str`, :class:`int`]
661+
The custom_id or id of the component to get.
662+
663+
Returns
664+
-------
665+
Optional[:class:`Component`]
666+
The component with the matching ``id`` or ``custom_id`` if it exists.
667+
"""
668+
if not id:
669+
return None
670+
attr = "id" if isinstance(id, int) else "custom_id"
671+
if self.accessory and id == getattr(self.accessory, attr, None):
672+
return self.accessory
673+
component = find(lambda i: getattr(i, attr, None) == id, self.components)
674+
return component
675+
635676

636677
class TextDisplay(Component):
637678
"""Represents a Text Display from Components V2.
@@ -1048,6 +1089,32 @@ def walk_components(self) -> Iterator[Component]:
10481089
else:
10491090
yield c
10501091

1092+
def get_component(self, id: str | int) -> Component | None:
1093+
"""Get a component from this container. Roughly equivalent to `utils.get(container.components, ...)`.
1094+
If an ``int`` is provided, the component will be retrieved by ``id``, otherwise by ``custom_id``.
1095+
This method will also search for nested components.
1096+
1097+
Parameters
1098+
----------
1099+
id: Union[:class:`str`, :class:`int`]
1100+
The custom_id or id of the component to get.
1101+
1102+
Returns
1103+
-------
1104+
Optional[:class:`Component`]
1105+
The component with the matching ``id`` or ``custom_id`` if it exists.
1106+
"""
1107+
if not id:
1108+
return None
1109+
attr = "id" if isinstance(id, int) else "custom_id"
1110+
for i in self.components:
1111+
if getattr(i, attr, None) == id:
1112+
return i
1113+
elif hasattr(i, "get_component"):
1114+
if component := i.get_component(id):
1115+
return component
1116+
return None
1117+
10511118

10521119
class Label(Component):
10531120
"""Represents a Label used in modals as the top-level component.

0 commit comments

Comments
 (0)