From b1ded0103f1d0e177a1d6f53561dd2d921ee2d03 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Fri, 5 Jun 2026 15:53:49 +0200 Subject: [PATCH 01/17] Add spoiler channel flag and functions --- CHANGELOG.md | 3 +++ discord/channel.py | 21 +++++++++++++++++++++ discord/flags.py | 8 ++++++++ 3 files changed, 32 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c63b835f8c..ba85ecc4ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ These changes are available on the `master` branch, but have not yet been releas ### Added +- Added `ChannelFlags.is_spoiler_channel` and `.is_spoiler()` function to all applicable + channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) + ### Changed ### Fixed diff --git a/discord/channel.py b/discord/channel.py index 2a6d5182ac..60d49b3b1b 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -312,6 +312,13 @@ def is_nsfw(self) -> bool: """Checks if the channel is NSFW.""" return self.nsfw + def is_spoiler(self) -> bool: + """Checks if the channel is a spoiler channel. + + .. versionadded:: 2.9 + """ + return self.flags.is_spoiler_channel + @property def last_message(self) -> Message | None: """Fetches the last message from this channel in cache. @@ -1810,6 +1817,13 @@ def is_nsfw(self) -> bool: """Checks if the channel is NSFW.""" return self.nsfw + def is_spoiler(self) -> bool: + """Checks if the channel is a spoiler channel. + + .. versionadded:: 2.9 + """ + return self.flags.is_spoiler_channel + @property def last_message(self) -> Message | None: """Fetches the last message from this channel in cache. @@ -2398,6 +2412,13 @@ def is_nsfw(self) -> bool: """Checks if the channel is NSFW.""" return self.nsfw + def is_spoiler(self) -> bool: + """Checks if the channel is a spoiler channel. + + .. versionadded:: 2.9 + """ + return self.flags.is_spoiler_channel + @property def last_message(self) -> Message | None: """Fetches the last message from this channel in cache. diff --git a/discord/flags.py b/discord/flags.py index 53c1ccd98b..b67240f9c7 100644 --- a/discord/flags.py +++ b/discord/flags.py @@ -1586,6 +1586,14 @@ def hide_media_download_options(self): """ return 1 << 15 + @flag_value + def is_spoiler_channel(self): + """:class:`bool`: Returns ``True`` if the channel is a spoiler channel. + + .. versionadded:: 2.9 + """ + return 1 << 21 + @fill_with_flags() class AttachmentFlags(BaseFlags): From 7b79faaa8be6f1f489e3e0c378363c257682defa Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Fri, 5 Jun 2026 20:24:03 +0200 Subject: [PATCH 02/17] Add editing spoiler flag to channel editing functions --- CHANGELOG.md | 2 ++ discord/channel.py | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba85ecc4ca..3ef775f4c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ These changes are available on the `master` branch, but have not yet been releas - Added `ChannelFlags.is_spoiler_channel` and `.is_spoiler()` function to all applicable channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) +- Added `spoiler` parameter to the `edit()` function of all applicable channel types. + ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) ### Changed diff --git a/discord/channel.py b/discord/channel.py index 60d49b3b1b..a39cc1c415 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -811,6 +811,7 @@ async def edit( default_thread_slowmode_delay: int = ..., type: ChannelType = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + spoiler: bool = ..., ) -> TextChannel | None: ... @overload @@ -867,6 +868,10 @@ async def edit(self, *, reason=None, **options): The new default slowmode delay in seconds for threads created in this channel. .. versionadded:: 2.3 + spoiler: :class:`bool` + Whether the channel should be a spoiler channel. Mutually exclusive with :attr:`nsfw`. + + .. versionadded:: 2.9 Returns ------- @@ -884,6 +889,10 @@ async def edit(self, *, reason=None, **options): HTTPException Editing the channel failed. """ + if "spoiler" in options: + options["flags"] = ChannelFlags._from_value(self.flags.value) + options["flags"].is_spoiler_channel = options.pop("spoiler") + payload = await self._edit(options, reason=reason) if payload is not None: # the payload will always be the proper channel payload @@ -1125,6 +1134,7 @@ async def edit( available_tags: list[ForumTag] = ..., require_tag: bool = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + spoiler: bool = ..., ) -> ForumChannel | None: ... @overload @@ -1187,6 +1197,10 @@ async def edit(self, *, reason=None, **options): Whether a tag should be required to be specified when creating a thread in this channel. .. versionadded:: 2.3 + spoiler: :class:`bool` + Whether the channel should be a spoiler channel. Mutually exclusive with :attr:`nsfw`. + + .. versionadded:: 2.9 Returns ------- @@ -1207,6 +1221,10 @@ async def edit(self, *, reason=None, **options): if "require_tag" in options: options["flags"] = ChannelFlags._from_value(self.flags.value) options["flags"].require_tag = options.pop("require_tag") + if "spoiler" in options: + if "flags" not in options: + options["flags"] = ChannelFlags._from_value(self.flags.value) + options["flags"].is_spoiler_channel = options.pop("spoiler") payload = await self._edit(options, reason=reason) if payload is not None: @@ -1506,6 +1524,7 @@ async def edit( require_tag: bool = ..., hide_media_download_options: bool = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + spoiler: bool = ..., ) -> ForumChannel | None: ... async def edit(self, *, reason=None, **options): @@ -1562,6 +1581,11 @@ async def edit(self, *, reason=None, **options): hide_media_download_options: :class:`bool` Whether media download options should be hidden in this media channel. + spoiler: :class:`bool` + Whether the channel should be a spoiler channel. Mutually exclusive with :attr:`nsfw`. + + .. versionadded:: 2.9 + Returns ------- Optional[:class:`.MediaChannel`] @@ -1579,14 +1603,18 @@ async def edit(self, *, reason=None, **options): Editing the channel failed. """ - if "require_tag" in options or "hide_media_download_options" in options: + if ( + "require_tag" in options + or "hide_media_download_options" in options + or "spoiler" in options + ): flags = ChannelFlags._from_value(self.flags.value) flags.require_tag = options.pop("require_tag", flags.require_tag) flags.hide_media_download_options = options.pop( "hide_media_download_options", flags.hide_media_download_options ) + flags.is_spoiler_channel = options.pop("spoiler", flags.is_spoiler_channel) options["flags"] = flags - payload = await self._edit(options, reason=reason) if payload is not None: # the payload will always be the proper channel payload @@ -2105,6 +2133,7 @@ async def edit( slowmode_delay: int = ..., nsfw: bool = ..., reason: str | None = ..., + spoiler: bool = ..., ) -> VoiceChannel | None: ... @overload @@ -2163,6 +2192,11 @@ async def edit(self, *, reason=None, **options): .. versionadded:: 2.7 + spoiler: :class:`bool` + Whether the channel should be a spoiler channel. Mutually exclusive with :attr:`nsfw`. + + .. versionadded:: 2.9 + Returns ------- Optional[:class:`.VoiceChannel`] @@ -2178,6 +2212,9 @@ async def edit(self, *, reason=None, **options): HTTPException Editing the channel failed. """ + if "spoiler" in options: + options["flags"] = ChannelFlags._from_value(self.flags.value) + options["flags"].is_spoiler_channel = options.pop("spoiler") payload = await self._edit(options, reason=reason) if payload is not None: From f426f2500a54ff4b13e166132457f18a9aa79166 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Thu, 30 Jul 2026 15:28:04 +0200 Subject: [PATCH 03/17] fix: changelog Signed-off-by: Lala Sabathil --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1640acf07..9e8476b02b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ These changes are available on the `master` branch, but have not yet been releas ### Added +- Added `ChannelFlags.is_spoiler_channel` and `.is_spoiler()` function to all applicable + channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) +- Added `spoiler` parameter to the `edit()` function of all applicable channel types. + ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) + ### Changed ### Fixed @@ -31,10 +36,6 @@ These changes are available on the `master` branch, but have not yet been releas - Add `RoleColours.HOLOGRAPHIC_PRIMARY`, `RoleColours.HOLOGRAPHIC_SECONDARY`, and `RoleColours.HOLOGRAPHIC_TERTIARY` class constants. ([#3268](https://github.com/Pycord-Development/pycord/pull/3268)) -- Added `ChannelFlags.is_spoiler_channel` and `.is_spoiler()` function to all applicable - channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) -- Added `spoiler` parameter to the `edit()` function of all applicable channel types. - ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) ### Fixed From 769cc0801a1890cab13307586f0644a38412a66c Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Tue, 4 Aug 2026 17:27:10 +0200 Subject: [PATCH 04/17] Make spoiler attribute a property --- discord/channel.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/discord/channel.py b/discord/channel.py index e7b5b597f2..f7c532b4a4 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -317,7 +317,8 @@ def is_nsfw(self) -> bool: """Checks if the channel is NSFW.""" return self.nsfw - def is_spoiler(self) -> bool: + @property + def spoiler(self) -> bool: """Checks if the channel is a spoiler channel. .. versionadded:: 2.9 @@ -1850,7 +1851,8 @@ def is_nsfw(self) -> bool: """Checks if the channel is NSFW.""" return self.nsfw - def is_spoiler(self) -> bool: + @property + def spoiler(self) -> bool: """Checks if the channel is a spoiler channel. .. versionadded:: 2.9 @@ -2454,7 +2456,8 @@ def is_nsfw(self) -> bool: """Checks if the channel is NSFW.""" return self.nsfw - def is_spoiler(self) -> bool: + @property + def spoiler(self) -> bool: """Checks if the channel is a spoiler channel. .. versionadded:: 2.9 From 19531ba5955a7670f7bd83523270a7c508c47c25 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Tue, 4 Aug 2026 18:32:19 +0200 Subject: [PATCH 05/17] Add mutual exclusivity check for nsfw and spoiler options --- discord/channel.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/discord/channel.py b/discord/channel.py index f7c532b4a4..ad0c834ce8 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -849,7 +849,8 @@ async def edit(self, *, reason=None, **options): position: :class:`int` The new channel's position. nsfw: :class:`bool` - Whether the channel is marked as NSFW. + Whether the channel is marked as NSFW. Mutually exclusive with :attr:`spoiler`, although + this will convert a spoiler channel into an nsfw channel. sync_permissions: :class:`bool` Whether to sync permissions with the channel's new or pre-existing category. Defaults to ``False``. @@ -875,7 +876,8 @@ async def edit(self, *, reason=None, **options): .. versionadded:: 2.3 spoiler: :class:`bool` - Whether the channel should be a spoiler channel. Mutually exclusive with :attr:`nsfw`. + Whether the channel should be a spoiler channel. Mutually exclusive with :attr:`nsfw`, although + this will convert an nsfw channel into a spoiler channel. .. versionadded:: 2.9 @@ -897,7 +899,13 @@ async def edit(self, *, reason=None, **options): """ if "spoiler" in options: options["flags"] = ChannelFlags._from_value(self.flags.value) - options["flags"].is_spoiler_channel = options.pop("spoiler") + options["flags"].is_spoiler_channel = options["spoiler"] + + if options.get("nsfw") and options["spoiler"]: + raise ValueError( + "NSFW setting is mutually exclusive with spoiler setting" + ) + options.pop("spoiler") payload = await self._edit(options, reason=reason) if payload is not None: From 68608ab8b281b4a4534c72c5050b61d7998cd8d4 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Mon, 10 Aug 2026 14:06:03 +0200 Subject: [PATCH 06/17] Adjust changelog to new naming --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06daaf1a74..939adb8dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ These changes are available on the `master` branch, but have not yet been releas - Added `Member.vr_status` property. ([#3328](https://github.com/Pycord-Development/pycord/pull/3328)) -- Added `ChannelFlags.is_spoiler_channel` and `.is_spoiler()` function to all applicable +- Added `ChannelFlags.is_spoiler_channel` and `.spoiler` property to all applicable channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) - Added `spoiler` parameter to the `edit()` function of all applicable channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) From f2cb6bb0ccd4cb41d9daa5cf6ebf4673cc4bbb4a Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Mon, 10 Aug 2026 14:53:48 +0200 Subject: [PATCH 07/17] Add channel creation support with spoiler flag --- discord/guild.py | 41 ++++++++++++++++++++++++++++++++++++++++- discord/http.py | 1 + 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/discord/guild.py b/discord/guild.py index b485123b52..fbb853dd7a 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -70,7 +70,7 @@ ) from .errors import ClientException, HTTPException, InvalidArgument, InvalidData from .file import File -from .flags import SystemChannelFlags +from .flags import ChannelFlags, SystemChannelFlags from .incidents import IncidentsData from .integrations import Integration, _integration_factory from .invite import Invite @@ -1429,6 +1429,11 @@ def _create_channel( elif not isinstance(overwrites, dict): raise InvalidArgument("overwrites parameter expects a dict.") + try: + options["flags"] = options.pop("flags").value + except KeyError: + pass + perms = [] for target, perm in overwrites.items(): if not isinstance(perm, PermissionOverwrite): @@ -1473,6 +1478,7 @@ async def create_text_channel( overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, default_thread_slowmode_delay: int | None = MISSING, default_auto_archive_duration: int = MISSING, + spoiler: bool = MISSING, ) -> TextChannel: """|coro| @@ -1524,6 +1530,10 @@ async def create_text_channel( The default auto archive duration in minutes for threads created in this channel. .. versionadded:: 2.7 + spoiler: :class:`bool` + Whether the channel is marked as a spoiler channel. + + .. versionadded:: 2.9 Returns ------- @@ -1579,6 +1589,9 @@ async def create_text_channel( if default_auto_archive_duration is not MISSING: options["default_auto_archive_duration"] = default_auto_archive_duration + if spoiler is not MISSING: + options["flags"] = ChannelFlags(is_spoiler_channel=True) + data = await self._create_channel( name, overwrites=overwrites, @@ -1607,6 +1620,7 @@ async def create_voice_channel( overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, slowmode_delay: int = MISSING, nsfw: bool = MISSING, + spoiler: bool = MISSING, ) -> VoiceChannel: """|coro| @@ -1652,6 +1666,11 @@ async def create_voice_channel( .. versionadded:: 2.7 + spoiler: :class:`bool` + Whether the channel is marked as a spoiler channel. + + .. versionadded:: 2.9 + Returns ------- :class:`VoiceChannel` @@ -1688,6 +1707,9 @@ async def create_voice_channel( if nsfw is not MISSING: options["nsfw"] = nsfw + if spoiler is not MISSING: + options["flags"] = ChannelFlags(is_spoiler_channel=True) + data = await self._create_channel( name, overwrites=overwrites, @@ -1717,6 +1739,7 @@ async def create_stage_channel( video_quality_mode: VideoQualityMode = MISSING, slowmode_delay: int = MISSING, nsfw: bool = MISSING, + spoiler: bool = MISSING, ) -> StageChannel: """|coro| @@ -1774,6 +1797,11 @@ async def create_stage_channel( .. versionadded:: 2.7 + nsfw: :class:`bool` + Whether the channel is marked as a spoiler channel. + + .. versionadded:: 2.9 + Returns ------- :class:`StageChannel` @@ -1813,6 +1841,9 @@ async def create_stage_channel( if nsfw is not MISSING: options["nsfw"] = nsfw + if spoiler is not MISSING: + options["flags"] = ChannelFlags(is_spoiler_channel=True) + data = await self._create_channel( name, overwrites=overwrites, @@ -1843,6 +1874,7 @@ async def create_forum_channel( default_sort_order: SortOrder | None = MISSING, default_thread_slowmode_delay: int | None = MISSING, default_auto_archive_duration: int = MISSING, + spoiler: bool = MISSING, ) -> ForumChannel: """|coro| @@ -1910,6 +1942,10 @@ async def create_forum_channel( The default auto archive duration in minutes for threads created in this channel. .. versionadded:: 2.7 + spoiler: :class:`bool` + Whether the channel is marked as a spoiler channel. + + .. versionadded:: 2.9 Returns ------- @@ -1973,6 +2009,9 @@ async def create_forum_channel( if default_auto_archive_duration is not MISSING: options["default_auto_archive_duration"] = default_auto_archive_duration + if spoiler is not MISSING: + options["flags"] = ChannelFlags(is_spoiler_channel=True) + if default_reaction_emoji is not MISSING: if isinstance( default_reaction_emoji, _EmojiTag diff --git a/discord/http.py b/discord/http.py index 40ebcd7c1f..87be923490 100644 --- a/discord/http.py +++ b/discord/http.py @@ -1153,6 +1153,7 @@ def create_channel( "video_quality_mode", "auto_archive_duration", "default_reaction_emoji", + "flags", ) payload.update( {k: v for k, v in options.items() if k in valid_keys and v is not None} From 9508a827fa1a5ddf737ba6a33c3e4516f763653f Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Mon, 10 Aug 2026 15:02:05 +0200 Subject: [PATCH 08/17] Add channel creation support with spoiler flag --- CHANGELOG.md | 2 ++ discord/guild.py | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f90662f67b..31e1e34723 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ These changes are available on the `master` branch, but have not yet been releas channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) - Added `spoiler` parameter to the `edit()` function of all applicable channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) +- Added `spoiler` kwarg to all channel creation methods, except for category channels. + ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) ### Changed diff --git a/discord/guild.py b/discord/guild.py index fbb853dd7a..4bc8847daa 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1518,6 +1518,10 @@ async def create_text_channel( A value of `0` disables slowmode. The maximum value possible is `21600`. nsfw: :class:`bool` Whether the channel is marked as NSFW. + + .. warning:: + Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + reason: Optional[:class:`str`] The reason for creating this channel. Shows up on the audit log. @@ -1535,6 +1539,9 @@ async def create_text_channel( .. versionadded:: 2.9 + .. warning:: + Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Returns ------- :class:`TextChannel` @@ -1666,11 +1673,17 @@ async def create_voice_channel( .. versionadded:: 2.7 + .. warning:: + Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + spoiler: :class:`bool` Whether the channel is marked as a spoiler channel. .. versionadded:: 2.9 + .. warning:: + Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Returns ------- :class:`VoiceChannel` @@ -1797,11 +1810,17 @@ async def create_stage_channel( .. versionadded:: 2.7 + .. warning:: + Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + nsfw: :class:`bool` Whether the channel is marked as a spoiler channel. .. versionadded:: 2.9 + .. warning:: + Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Returns ------- :class:`StageChannel` @@ -1914,6 +1933,10 @@ async def create_forum_channel( A value of ``0`` disables slowmode. The maximum value possible is ``21600``. nsfw: :class:`bool` Whether the channel is marked as NSFW. + + .. warning:: + Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + reason: Optional[:class:`str`] The reason for creating this channel. Shows up on the audit log. default_reaction_emoji: Optional[:class:`GuildEmoji` | :class:`int` | :class:`str`] @@ -1947,6 +1970,9 @@ async def create_forum_channel( .. versionadded:: 2.9 + .. warning:: + Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Returns ------- :class:`ForumChannel` From 4f410832dafd7646be03f1a8ea3bf7e75dbff2de Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Mon, 10 Aug 2026 15:06:31 +0200 Subject: [PATCH 09/17] Minor documentation formatting --- discord/guild.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index 4bc8847daa..63836123a8 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1520,7 +1520,7 @@ async def create_text_channel( Whether the channel is marked as NSFW. .. warning:: - Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``spoiler`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. reason: Optional[:class:`str`] The reason for creating this channel. Shows up on the audit log. @@ -1540,7 +1540,7 @@ async def create_text_channel( .. versionadded:: 2.9 .. warning:: - Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``nsfw`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. Returns ------- @@ -1674,7 +1674,7 @@ async def create_voice_channel( .. versionadded:: 2.7 .. warning:: - Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``spoiler`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. spoiler: :class:`bool` Whether the channel is marked as a spoiler channel. @@ -1682,7 +1682,7 @@ async def create_voice_channel( .. versionadded:: 2.9 .. warning:: - Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``nsfw`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. Returns ------- @@ -1811,7 +1811,7 @@ async def create_stage_channel( .. versionadded:: 2.7 .. warning:: - Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``spoiler`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. nsfw: :class:`bool` Whether the channel is marked as a spoiler channel. @@ -1819,7 +1819,7 @@ async def create_stage_channel( .. versionadded:: 2.9 .. warning:: - Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``nsfw`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. Returns ------- @@ -1935,7 +1935,7 @@ async def create_forum_channel( Whether the channel is marked as NSFW. .. warning:: - Passing both this and `spoiler` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``spoiler`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. reason: Optional[:class:`str`] The reason for creating this channel. Shows up on the audit log. @@ -1971,7 +1971,7 @@ async def create_forum_channel( .. versionadded:: 2.9 .. warning:: - Passing both this and `nsfw` will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``nsfw`` as ``True```will mark the channel as NSFW and ignore the spoiler flag. Returns ------- From 732733885a7abf0c017d154a70cd87a3da626c6b Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Mon, 10 Aug 2026 16:03:42 +0200 Subject: [PATCH 10/17] Fix value defaulting --- discord/guild.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/discord/guild.py b/discord/guild.py index 63836123a8..20ffecd8d1 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1597,7 +1597,7 @@ async def create_text_channel( options["default_auto_archive_duration"] = default_auto_archive_duration if spoiler is not MISSING: - options["flags"] = ChannelFlags(is_spoiler_channel=True) + options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) data = await self._create_channel( name, @@ -1721,7 +1721,7 @@ async def create_voice_channel( options["nsfw"] = nsfw if spoiler is not MISSING: - options["flags"] = ChannelFlags(is_spoiler_channel=True) + options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) data = await self._create_channel( name, @@ -1861,7 +1861,7 @@ async def create_stage_channel( options["nsfw"] = nsfw if spoiler is not MISSING: - options["flags"] = ChannelFlags(is_spoiler_channel=True) + options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) data = await self._create_channel( name, @@ -2036,7 +2036,7 @@ async def create_forum_channel( options["default_auto_archive_duration"] = default_auto_archive_duration if spoiler is not MISSING: - options["flags"] = ChannelFlags(is_spoiler_channel=True) + options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) if default_reaction_emoji is not MISSING: if isinstance( From 57e3dd7881333aeb0bd72d49f13f01d5501a1ed6 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Mon, 10 Aug 2026 18:07:57 +0200 Subject: [PATCH 11/17] Address code review change requests --- CHANGELOG.md | 6 +--- discord/channel.py | 54 +++++++++++++++++++++++++++++--- discord/guild.py | 77 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 121 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31e1e34723..ca110cbaf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,11 +14,7 @@ These changes are available on the `master` branch, but have not yet been releas - Added `Member.vr_status` property. ([#3328](https://github.com/Pycord-Development/pycord/pull/3328)) -- Added `ChannelFlags.is_spoiler_channel` and `.spoiler` property to all applicable - channel types. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) -- Added `spoiler` parameter to the `edit()` function of all applicable channel types. - ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) -- Added `spoiler` kwarg to all channel creation methods, except for category channels. +- Added spoiler channel functionality ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) ### Changed diff --git a/discord/channel.py b/discord/channel.py index ad0c834ce8..38195545f9 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -321,6 +321,9 @@ def is_nsfw(self) -> bool: def spoiler(self) -> bool: """Checks if the channel is a spoiler channel. + .. note:: + This is an alias for :attr:`flags.is_spoiler_channel`. + .. versionadded:: 2.9 """ return self.flags.is_spoiler_channel @@ -817,6 +820,23 @@ async def edit( default_thread_slowmode_delay: int = ..., type: ChannelType = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + ) -> TextChannel | None: ... + + @overload + async def edit( + self, + *, + reason: str | None = ..., + name: str = ..., + topic: str = ..., + position: int = ..., + sync_permissions: bool = ..., + category: CategoryChannel | None = ..., + slowmode_delay: int = ..., + default_auto_archive_duration: ThreadArchiveDuration = ..., + default_thread_slowmode_delay: int = ..., + type: ChannelType = ..., + overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., spoiler: bool = ..., ) -> TextChannel | None: ... @@ -1148,6 +1168,28 @@ async def edit( available_tags: list[ForumTag] = ..., require_tag: bool = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + ) -> ForumChannel | None: ... + + @overload + async def edit( + self, + *, + reason: str | None = ..., + name: str = ..., + topic: str = ..., + position: int = ..., + sync_permissions: bool = ..., + category: CategoryChannel | None = ..., + slowmode_delay: int = ..., + default_auto_archive_duration: ( + ThreadArchiveDuration | ThreadArchiveDurationEnum + ) = ..., + default_thread_slowmode_delay: int = ..., + default_sort_order: SortOrder = ..., + default_reaction_emoji: GuildEmoji | int | str | None = ..., + available_tags: list[ForumTag] = ..., + require_tag: bool = ..., + overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., spoiler: bool = ..., ) -> ForumChannel | None: ... @@ -1617,11 +1659,7 @@ async def edit(self, *, reason=None, **options): Editing the channel failed. """ - if ( - "require_tag" in options - or "hide_media_download_options" in options - or "spoiler" in options - ): + if {"require_tag", "hide_media_download_options", "spoiler"} & options.keys(): flags = ChannelFlags._from_value(self.flags.value) flags.require_tag = options.pop("require_tag", flags.require_tag) flags.hide_media_download_options = options.pop( @@ -1863,6 +1901,9 @@ def is_nsfw(self) -> bool: def spoiler(self) -> bool: """Checks if the channel is a spoiler channel. + .. note:: + This is an alias for :attr:`flags.is_spoiler_channel`. + .. versionadded:: 2.9 """ return self.flags.is_spoiler_channel @@ -2468,6 +2509,9 @@ def is_nsfw(self) -> bool: def spoiler(self) -> bool: """Checks if the channel is a spoiler channel. + .. note:: + This is an alias for :attr:`flags.is_spoiler_channel`. + .. versionadded:: 2.9 """ return self.flags.is_spoiler_channel diff --git a/discord/guild.py b/discord/guild.py index 20ffecd8d1..4d0797cd26 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1429,10 +1429,8 @@ def _create_channel( elif not isinstance(overwrites, dict): raise InvalidArgument("overwrites parameter expects a dict.") - try: + if "flags" in options: options["flags"] = options.pop("flags").value - except KeyError: - pass perms = [] for target, perm in overwrites.items(): @@ -1465,6 +1463,37 @@ def _create_channel( **options, ) + @overload + async def create_text_channel( + self, + name: str, + *, + reason: str | None = None, + category: CategoryChannel | None = None, + position: int = MISSING, + topic: str = MISSING, + slowmode_delay: int = MISSING, + nsfw: bool = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + default_thread_slowmode_delay: int | None = MISSING, + default_auto_archive_duration: int = MISSING, + ) -> TextChannel: ... + + async def create_text_channel( + self, + name: str, + *, + reason: str | None = None, + category: CategoryChannel | None = None, + position: int = MISSING, + topic: str = MISSING, + slowmode_delay: int = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + default_thread_slowmode_delay: int | None = MISSING, + default_auto_archive_duration: int = MISSING, + spoiler: bool = MISSING, + ) -> TextChannel: ... + async def create_text_channel( self, name: str, @@ -1673,7 +1702,7 @@ async def create_voice_channel( .. versionadded:: 2.7 - .. warning:: + .. note:: Passing both this and ``spoiler`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. spoiler: :class:`bool` @@ -1681,7 +1710,7 @@ async def create_voice_channel( .. versionadded:: 2.9 - .. warning:: + .. note:: Passing both this and ``nsfw`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. Returns @@ -1737,6 +1766,42 @@ async def create_voice_channel( self._channels[channel.id] = channel return channel + @overload + async def create_stage_channel( + self, + name: str, + *, + topic: str, + position: int = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + category: CategoryChannel | None = None, + reason: str | None = None, + bitrate: int = MISSING, + user_limit: int = MISSING, + rtc_region: VoiceRegion | None = MISSING, + video_quality_mode: VideoQualityMode = MISSING, + slowmode_delay: int = MISSING, + nsfw: bool = MISSING, + ) -> StageChannel: ... + + @overload + async def create_stage_channel( + self, + name: str, + *, + topic: str, + position: int = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + category: CategoryChannel | None = None, + reason: str | None = None, + bitrate: int = MISSING, + user_limit: int = MISSING, + rtc_region: VoiceRegion | None = MISSING, + video_quality_mode: VideoQualityMode = MISSING, + slowmode_delay: int = MISSING, + spoiler: bool = MISSING, + ) -> StageChannel: ... + async def create_stage_channel( self, name: str, @@ -1971,7 +2036,7 @@ async def create_forum_channel( .. versionadded:: 2.9 .. warning:: - Passing both this and ``nsfw`` as ``True```will mark the channel as NSFW and ignore the spoiler flag. + Passing both this and ``nsfw`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. Returns ------- From 06167d2f4544558c16170977c252c60a8943d22a Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Mon, 10 Aug 2026 19:31:21 +0200 Subject: [PATCH 12/17] Warn when passing both nsfw and spoiler options --- discord/channel.py | 21 ++++++++++++++++++--- discord/guild.py | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/discord/channel.py b/discord/channel.py index 38195545f9..99787570ed 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -26,6 +26,7 @@ from __future__ import annotations import datetime +from _warnings import warn from collections.abc import Callable, Iterable, Mapping, Sequence from typing import ( TYPE_CHECKING, @@ -922,8 +923,8 @@ async def edit(self, *, reason=None, **options): options["flags"].is_spoiler_channel = options["spoiler"] if options.get("nsfw") and options["spoiler"]: - raise ValueError( - "NSFW setting is mutually exclusive with spoiler setting" + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." ) options.pop("spoiler") @@ -1278,6 +1279,10 @@ async def edit(self, *, reason=None, **options): options["flags"] = ChannelFlags._from_value(self.flags.value) options["flags"].require_tag = options.pop("require_tag") if "spoiler" in options: + if "nsfw" in options: + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + ) if "flags" not in options: options["flags"] = ChannelFlags._from_value(self.flags.value) options["flags"].is_spoiler_channel = options.pop("spoiler") @@ -1665,6 +1670,10 @@ async def edit(self, *, reason=None, **options): flags.hide_media_download_options = options.pop( "hide_media_download_options", flags.hide_media_download_options ) + if options.get("nsfw") and options["spoiler"]: + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + ) flags.is_spoiler_channel = options.pop("spoiler", flags.is_spoiler_channel) options["flags"] = flags payload = await self._edit(options, reason=reason) @@ -2270,7 +2279,13 @@ async def edit(self, *, reason=None, **options): """ if "spoiler" in options: options["flags"] = ChannelFlags._from_value(self.flags.value) - options["flags"].is_spoiler_channel = options.pop("spoiler") + options["flags"].is_spoiler_channel = options["spoiler"] + + if options.get("nsfw") and options["spoiler"]: + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + ) + options.pop("spoiler") payload = await self._edit(options, reason=reason) if payload is not None: diff --git a/discord/guild.py b/discord/guild.py index 4d0797cd26..5a7a293982 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -28,6 +28,7 @@ import copy import datetime import unicodedata +from _warnings import warn from collections.abc import Sequence from typing import ( TYPE_CHECKING, @@ -1628,6 +1629,11 @@ async def create_text_channel( if spoiler is not MISSING: options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) + if nsfw and spoiler: + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + ) + data = await self._create_channel( name, overwrites=overwrites, @@ -1752,6 +1758,11 @@ async def create_voice_channel( if spoiler is not MISSING: options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) + if nsfw and spoiler: + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + ) + data = await self._create_channel( name, overwrites=overwrites, @@ -1928,6 +1939,11 @@ async def create_stage_channel( if spoiler is not MISSING: options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) + if nsfw and spoiler: + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + ) + data = await self._create_channel( name, overwrites=overwrites, @@ -2103,6 +2119,11 @@ async def create_forum_channel( if spoiler is not MISSING: options["flags"] = ChannelFlags(is_spoiler_channel=spoiler) + if nsfw and spoiler: + warn( + "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + ) + if default_reaction_emoji is not MISSING: if isinstance( default_reaction_emoji, _EmojiTag From c8f6f610a457d75fb38f912948936b895e92af61 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Tue, 11 Aug 2026 14:50:15 +0200 Subject: [PATCH 13/17] Add overloads for voice and forum channels --- discord/guild.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/discord/guild.py b/discord/guild.py index 5a7a293982..8c74b145e5 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1648,6 +1648,39 @@ async def create_text_channel( self._channels[channel.id] = channel return channel + @overload + async def create_voice_channel( + self, + name: str, + *, + reason: str | None = None, + category: CategoryChannel | None = None, + position: int = MISSING, + bitrate: int = MISSING, + user_limit: int = MISSING, + rtc_region: VoiceRegion | None = MISSING, + video_quality_mode: VideoQualityMode = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + slowmode_delay: int = MISSING, + nsfw: bool = MISSING, + ) -> VoiceChannel: ... + + async def create_voice_channel( + self, + name: str, + *, + reason: str | None = None, + category: CategoryChannel | None = None, + position: int = MISSING, + bitrate: int = MISSING, + user_limit: int = MISSING, + rtc_region: VoiceRegion | None = MISSING, + video_quality_mode: VideoQualityMode = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + slowmode_delay: int = MISSING, + spoiler: bool = MISSING, + ) -> VoiceChannel: ... + async def create_voice_channel( self, name: str, @@ -1958,6 +1991,44 @@ async def create_stage_channel( self._channels[channel.id] = channel return channel + @overload + async def create_forum_channel( + self, + name: str, + *, + reason: str | None = None, + category: CategoryChannel | None = None, + position: int = MISSING, + topic: str = MISSING, + slowmode_delay: int = MISSING, + nsfw: bool = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + default_reaction_emoji: GuildEmoji | int | str = MISSING, + available_tags: list[ForumTag] = MISSING, + default_sort_order: SortOrder | None = MISSING, + default_thread_slowmode_delay: int | None = MISSING, + default_auto_archive_duration: int = MISSING, + ) -> ForumChannel: ... + + @overload + async def create_forum_channel( + self, + name: str, + *, + reason: str | None = None, + category: CategoryChannel | None = None, + position: int = MISSING, + topic: str = MISSING, + slowmode_delay: int = MISSING, + overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + default_reaction_emoji: GuildEmoji | int | str = MISSING, + available_tags: list[ForumTag] = MISSING, + default_sort_order: SortOrder | None = MISSING, + default_thread_slowmode_delay: int | None = MISSING, + default_auto_archive_duration: int = MISSING, + spoiler: bool = MISSING, + ) -> ForumChannel: ... + async def create_forum_channel( self, name: str, From b56e0226f87715cc0b4c599c6f73908ac6d3d7d1 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Tue, 11 Aug 2026 14:55:08 +0200 Subject: [PATCH 14/17] Apply change requests from code review --- CHANGELOG.md | 2 +- discord/channel.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca110cbaf6..41fb4bdc5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ These changes are available on the `master` branch, but have not yet been releas - Added `Member.vr_status` property. ([#3328](https://github.com/Pycord-Development/pycord/pull/3328)) -- Added spoiler channel functionality +- Added spoiler channels. ([#3252](https://github.com/Pycord-Development/pycord/pull/3252)) ### Changed diff --git a/discord/channel.py b/discord/channel.py index 99787570ed..ef0f04f050 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -870,8 +870,11 @@ async def edit(self, *, reason=None, **options): position: :class:`int` The new channel's position. nsfw: :class:`bool` - Whether the channel is marked as NSFW. Mutually exclusive with :attr:`spoiler`, although - this will convert a spoiler channel into an nsfw channel. + Whether the channel is marked as NSFW. + + .. note:: + This setting is mutually exclusive with :attr:`spoiler`. Applying this to a spoiler channel will convert it to an NSFW channel. + Passing both as ``True`` will make the channel an NSFW channel. sync_permissions: :class:`bool` Whether to sync permissions with the channel's new or pre-existing category. Defaults to ``False``. @@ -897,8 +900,11 @@ async def edit(self, *, reason=None, **options): .. versionadded:: 2.3 spoiler: :class:`bool` - Whether the channel should be a spoiler channel. Mutually exclusive with :attr:`nsfw`, although - this will convert an nsfw channel into a spoiler channel. + Whether the channel should be a spoiler channel. + + .. note:: + This setting is mutually exclusive with :attr:`nsfw`. Applying this to an NSFW channel will convert it to a spoiler channel. + Passing both as ``True`` will make the channel an NSFW channel. .. versionadded:: 2.9 From 25826349441cadea6e90558c9d17a0bbe1e2a0b6 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Tue, 11 Aug 2026 15:21:34 +0200 Subject: [PATCH 15/17] Apply change requests from code review --- discord/channel.py | 2 +- discord/guild.py | 165 ++++++++++++++++++++++++--------------------- 2 files changed, 88 insertions(+), 79 deletions(-) diff --git a/discord/channel.py b/discord/channel.py index ef0f04f050..b74773d5a1 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -1287,7 +1287,7 @@ async def edit(self, *, reason=None, **options): if "spoiler" in options: if "nsfw" in options: warn( - "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." + "The NSFW setting is mutually exclusive with the spoiler setting. The channel will become an NSFW channel." ) if "flags" not in options: options["flags"] = ChannelFlags._from_value(self.flags.value) diff --git a/discord/guild.py b/discord/guild.py index 8c74b145e5..ac25cf44f5 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -34,6 +34,7 @@ TYPE_CHECKING, Any, ClassVar, + Literal, NamedTuple, Optional, TypeVar, @@ -1469,30 +1470,32 @@ async def create_text_channel( self, name: str, *, - reason: str | None = None, - category: CategoryChannel | None = None, - position: int = MISSING, - topic: str = MISSING, - slowmode_delay: int = MISSING, - nsfw: bool = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, - default_thread_slowmode_delay: int | None = MISSING, - default_auto_archive_duration: int = MISSING, + reason: str | None = ..., + category: CategoryChannel | None = ..., + position: int = ..., + topic: str = ..., + slowmode_delay: int = ..., + nsfw: bool = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., + default_thread_slowmode_delay: int | None = ..., + default_auto_archive_duration: int = ..., + spoiler: Literal[False] = ..., ) -> TextChannel: ... async def create_text_channel( self, name: str, *, - reason: str | None = None, - category: CategoryChannel | None = None, - position: int = MISSING, - topic: str = MISSING, - slowmode_delay: int = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, - default_thread_slowmode_delay: int | None = MISSING, - default_auto_archive_duration: int = MISSING, - spoiler: bool = MISSING, + reason: str | None = ..., + category: CategoryChannel | None = ..., + position: int = ..., + topic: str = ..., + slowmode_delay: int = ..., + nsfw: Literal[False] = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., + default_thread_slowmode_delay: int | None = ..., + default_auto_archive_duration: int = ..., + spoiler: bool = ..., ) -> TextChannel: ... async def create_text_channel( @@ -1653,16 +1656,17 @@ async def create_voice_channel( self, name: str, *, - reason: str | None = None, - category: CategoryChannel | None = None, - position: int = MISSING, - bitrate: int = MISSING, - user_limit: int = MISSING, - rtc_region: VoiceRegion | None = MISSING, - video_quality_mode: VideoQualityMode = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, - slowmode_delay: int = MISSING, - nsfw: bool = MISSING, + reason: str | None = ..., + category: CategoryChannel | None = ..., + position: int = ..., + bitrate: int = ..., + user_limit: int = ..., + rtc_region: VoiceRegion | None = ..., + video_quality_mode: VideoQualityMode = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., + slowmode_delay: int = ..., + nsfw: bool = ..., + spoiler: Literal[False] = ..., ) -> VoiceChannel: ... async def create_voice_channel( @@ -1671,14 +1675,15 @@ async def create_voice_channel( *, reason: str | None = None, category: CategoryChannel | None = None, - position: int = MISSING, - bitrate: int = MISSING, - user_limit: int = MISSING, - rtc_region: VoiceRegion | None = MISSING, - video_quality_mode: VideoQualityMode = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, - slowmode_delay: int = MISSING, - spoiler: bool = MISSING, + position: int = ..., + bitrate: int = ..., + user_limit: int = ..., + rtc_region: VoiceRegion | None = ..., + video_quality_mode: VideoQualityMode = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., + slowmode_delay: int = ..., + nsfw: Literal[False] = ..., + spoiler: bool = ..., ) -> VoiceChannel: ... async def create_voice_channel( @@ -1816,16 +1821,17 @@ async def create_stage_channel( name: str, *, topic: str, - position: int = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, - category: CategoryChannel | None = None, - reason: str | None = None, - bitrate: int = MISSING, - user_limit: int = MISSING, - rtc_region: VoiceRegion | None = MISSING, - video_quality_mode: VideoQualityMode = MISSING, - slowmode_delay: int = MISSING, - nsfw: bool = MISSING, + position: int = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., + category: CategoryChannel | None = ..., + reason: str | None = ..., + bitrate: int = ..., + user_limit: int = ..., + rtc_region: VoiceRegion | None = ..., + video_quality_mode: VideoQualityMode = ..., + slowmode_delay: int = ..., + nsfw: bool = ..., + spoiler: Literal[False] = ..., ) -> StageChannel: ... @overload @@ -1834,16 +1840,17 @@ async def create_stage_channel( name: str, *, topic: str, - position: int = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, + position: int = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., category: CategoryChannel | None = None, reason: str | None = None, - bitrate: int = MISSING, - user_limit: int = MISSING, - rtc_region: VoiceRegion | None = MISSING, - video_quality_mode: VideoQualityMode = MISSING, - slowmode_delay: int = MISSING, - spoiler: bool = MISSING, + bitrate: int = ..., + user_limit: int = ..., + rtc_region: VoiceRegion | None = ..., + video_quality_mode: VideoQualityMode = ..., + slowmode_delay: int = ..., + nsfw: Literal[False] = ..., + spoiler: bool = ..., ) -> StageChannel: ... async def create_stage_channel( @@ -1996,18 +2003,19 @@ async def create_forum_channel( self, name: str, *, - reason: str | None = None, - category: CategoryChannel | None = None, - position: int = MISSING, - topic: str = MISSING, - slowmode_delay: int = MISSING, - nsfw: bool = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, - default_reaction_emoji: GuildEmoji | int | str = MISSING, - available_tags: list[ForumTag] = MISSING, - default_sort_order: SortOrder | None = MISSING, - default_thread_slowmode_delay: int | None = MISSING, - default_auto_archive_duration: int = MISSING, + reason: str | None = ..., + category: CategoryChannel | None = ..., + position: int = ..., + topic: str = ..., + slowmode_delay: int = ..., + nsfw: bool = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., + default_reaction_emoji: GuildEmoji | int | str = ..., + available_tags: list[ForumTag] = ..., + default_sort_order: SortOrder | None = ..., + default_thread_slowmode_delay: int | None = ..., + default_auto_archive_duration: int = ..., + spoiler: Literal[False] = ..., ) -> ForumChannel: ... @overload @@ -2015,18 +2023,19 @@ async def create_forum_channel( self, name: str, *, - reason: str | None = None, - category: CategoryChannel | None = None, - position: int = MISSING, - topic: str = MISSING, - slowmode_delay: int = MISSING, - overwrites: dict[Role | Member, PermissionOverwrite] = MISSING, - default_reaction_emoji: GuildEmoji | int | str = MISSING, - available_tags: list[ForumTag] = MISSING, - default_sort_order: SortOrder | None = MISSING, - default_thread_slowmode_delay: int | None = MISSING, - default_auto_archive_duration: int = MISSING, - spoiler: bool = MISSING, + reason: str | None = ..., + category: CategoryChannel | None = ..., + position: int = ..., + topic: str = ..., + slowmode_delay: int = ..., + nsfw: Literal[False] = ..., + overwrites: dict[Role | Member, PermissionOverwrite] = ..., + default_reaction_emoji: GuildEmoji | int | str = ..., + available_tags: list[ForumTag] = ..., + default_sort_order: SortOrder | None = ..., + default_thread_slowmode_delay: int | None = ..., + default_auto_archive_duration: int = ..., + spoiler: bool = ..., ) -> ForumChannel: ... async def create_forum_channel( From ccca218828cb73f43dcea393fa0ceb910a83eb56 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Wed, 12 Aug 2026 17:24:09 +0200 Subject: [PATCH 16/17] Fix edit() overloads --- discord/channel.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/discord/channel.py b/discord/channel.py index b74773d5a1..0fe8fc65ce 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -31,6 +31,7 @@ from typing import ( TYPE_CHECKING, Any, + Literal, NamedTuple, TypeVar, overload, @@ -821,6 +822,7 @@ async def edit( default_thread_slowmode_delay: int = ..., type: ChannelType = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + spoiler: Literal[False] = ..., ) -> TextChannel | None: ... @overload @@ -831,6 +833,7 @@ async def edit( name: str = ..., topic: str = ..., position: int = ..., + nsfw: Literal[False] = ..., sync_permissions: bool = ..., category: CategoryChannel | None = ..., slowmode_delay: int = ..., @@ -1175,6 +1178,7 @@ async def edit( available_tags: list[ForumTag] = ..., require_tag: bool = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + spoiler: Literal[False] = ..., ) -> ForumChannel | None: ... @overload @@ -1185,6 +1189,7 @@ async def edit( name: str = ..., topic: str = ..., position: int = ..., + nsfw: Literal[False] = ..., sync_permissions: bool = ..., category: CategoryChannel | None = ..., slowmode_delay: int = ..., @@ -1591,6 +1596,29 @@ async def edit( require_tag: bool = ..., hide_media_download_options: bool = ..., overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., + spoiler: Literal[False] = ..., + ) -> ForumChannel | None: ... + + @overload + async def edit( + self, + *, + reason: str | None = ..., + name: str = ..., + topic: str = ..., + position: int = ..., + nsfw: Literal[False] = ..., + sync_permissions: bool = ..., + category: CategoryChannel | None = ..., + slowmode_delay: int = ..., + default_auto_archive_duration: ThreadArchiveDuration = ..., + default_thread_slowmode_delay: int = ..., + default_sort_order: SortOrder = ..., + default_reaction_emoji: GuildEmoji | int | str | None = ..., + available_tags: list[ForumTag] = ..., + require_tag: bool = ..., + hide_media_download_options: bool = ..., + overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ..., spoiler: bool = ..., ) -> ForumChannel | None: ... @@ -2204,6 +2232,25 @@ async def edit( slowmode_delay: int = ..., nsfw: bool = ..., reason: str | None = ..., + spoiler: Literal[False] = ..., + ) -> VoiceChannel | None: ... + + @overload + async def edit( + self, + *, + name: str = ..., + bitrate: int = ..., + user_limit: int = ..., + position: int = ..., + sync_permissions: int = ..., + category: CategoryChannel | None = ..., + overwrites: Mapping[Role | Member, PermissionOverwrite] = ..., + rtc_region: VoiceRegion | None = ..., + video_quality_mode: VideoQualityMode = ..., + slowmode_delay: int = ..., + nsfw: Literal[False] = ..., + reason: str | None = ..., spoiler: bool = ..., ) -> VoiceChannel | None: ... @@ -2915,6 +2962,7 @@ async def edit( rtc_region: VoiceRegion | None = ..., video_quality_mode: VideoQualityMode = ..., reason: str | None = ..., + spoiler: bool = ..., ) -> StageChannel | None: ... @overload From 74dda970bc1a2d6eda2cdc00d74da3be371fbaa9 Mon Sep 17 00:00:00 2001 From: ToothyDev Date: Thu, 13 Aug 2026 16:13:50 +0200 Subject: [PATCH 17/17] Address change requests --- discord/channel.py | 17 ++++++++--------- discord/guild.py | 4 +++- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/discord/channel.py b/discord/channel.py index 0fe8fc65ce..a39754a7c2 100644 --- a/discord/channel.py +++ b/discord/channel.py @@ -26,7 +26,6 @@ from __future__ import annotations import datetime -from _warnings import warn from collections.abc import Callable, Iterable, Mapping, Sequence from typing import ( TYPE_CHECKING, @@ -36,6 +35,7 @@ TypeVar, overload, ) +from warnings import warn from typing_extensions import deprecated @@ -931,7 +931,7 @@ async def edit(self, *, reason=None, **options): options["flags"] = ChannelFlags._from_value(self.flags.value) options["flags"].is_spoiler_channel = options["spoiler"] - if options.get("nsfw") and options["spoiler"]: + if options.get("nsfw") and options.get("spoiler"): warn( "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." ) @@ -1289,11 +1289,10 @@ async def edit(self, *, reason=None, **options): if "require_tag" in options: options["flags"] = ChannelFlags._from_value(self.flags.value) options["flags"].require_tag = options.pop("require_tag") - if "spoiler" in options: - if "nsfw" in options: - warn( - "The NSFW setting is mutually exclusive with the spoiler setting. The channel will become an NSFW channel." - ) + if options.get("nsfw") and options.get("spoiler"): + warn( + "The NSFW setting is mutually exclusive with the spoiler setting. The channel will become an NSFW channel." + ) if "flags" not in options: options["flags"] = ChannelFlags._from_value(self.flags.value) options["flags"].is_spoiler_channel = options.pop("spoiler") @@ -1704,7 +1703,7 @@ async def edit(self, *, reason=None, **options): flags.hide_media_download_options = options.pop( "hide_media_download_options", flags.hide_media_download_options ) - if options.get("nsfw") and options["spoiler"]: + if options.get("nsfw") and options.get("spoiler"): warn( "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." ) @@ -2334,7 +2333,7 @@ async def edit(self, *, reason=None, **options): options["flags"] = ChannelFlags._from_value(self.flags.value) options["flags"].is_spoiler_channel = options["spoiler"] - if options.get("nsfw") and options["spoiler"]: + if options.get("nsfw") and options.get("spoiler"): warn( "NSFW setting is mutually exclusive with spoiler setting. Channel will become an NSFW channel." ) diff --git a/discord/guild.py b/discord/guild.py index ac25cf44f5..2c8e35e009 100644 --- a/discord/guild.py +++ b/discord/guild.py @@ -1482,6 +1482,7 @@ async def create_text_channel( spoiler: Literal[False] = ..., ) -> TextChannel: ... + @overload async def create_text_channel( self, name: str, @@ -1669,6 +1670,7 @@ async def create_voice_channel( spoiler: Literal[False] = ..., ) -> VoiceChannel: ... + @overload async def create_voice_channel( self, name: str, @@ -1929,7 +1931,7 @@ async def create_stage_channel( .. warning:: Passing both this and ``spoiler`` as ``True`` will mark the channel as NSFW and ignore the spoiler flag. - nsfw: :class:`bool` + spoiler: :class:`bool` Whether the channel is marked as a spoiler channel. .. versionadded:: 2.9