Skip to content

Commit cb31388

Browse files
committed
✨ Add MediaChannel and fix ForumChannel flags
1 parent 2b15502 commit cb31388

File tree

4 files changed

+233
-15
lines changed

4 files changed

+233
-15
lines changed

discord/abc.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
from .enums import ChannelType
4747
from .errors import ClientException, InvalidArgument
4848
from .file import File
49-
from .flags import MessageFlags
49+
from .flags import ChannelFlags, MessageFlags
5050
from .invite import Invite
5151
from .iterators import HistoryIterator
5252
from .mentions import AllowedMentions
@@ -85,7 +85,6 @@
8585
from .client import Client
8686
from .embeds import Embed
8787
from .enums import InviteTarget
88-
from .flags import ChannelFlags
8988
from .guild import Guild
9089
from .member import Member
9190
from .message import Message, MessageReference, PartialMessage
@@ -417,13 +416,10 @@ async def _edit(
417416
)
418417
except KeyError:
419418
pass
420-
421-
try:
422-
if options.pop("require_tag"):
423-
options["flags"] = ChannelFlags.require_tag.flag
424-
except KeyError:
425-
pass
426-
419+
if options.get("flags") and not isinstance(
420+
options["flags"], int
421+
): # it shouldn't be an int but just in case
422+
options["flags"] = options["flags"].value
427423
try:
428424
options["available_tags"] = [
429425
tag.to_dict() for tag in options.pop("available_tags")

discord/channel.py

Lines changed: 217 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
from __future__ import annotations
2727

28+
import contextlib
2829
import datetime
2930
from typing import TYPE_CHECKING, Any, Callable, Iterable, Mapping, TypeVar, overload
3031

@@ -65,6 +66,7 @@
6566
"GroupChannel",
6667
"PartialMessageable",
6768
"ForumChannel",
69+
"MediaChannel",
6870
"ForumTag",
6971
)
7072

@@ -853,7 +855,6 @@ async def edit(self, *, reason=None, **options):
853855
HTTPException
854856
Editing the channel failed.
855857
"""
856-
857858
payload = await self._edit(options, reason=reason)
858859
if payload is not None:
859860
# the payload will always be the proper channel payload
@@ -1056,7 +1057,7 @@ def guidelines(self) -> str | None:
10561057

10571058
@property
10581059
def requires_tag(self) -> bool:
1059-
"""Whether a tag is required to be specified when creating a thread in this forum channel.
1060+
"""Whether a tag is required to be specified when creating a thread in this forum or media channel.
10601061
10611062
Tags are specified in :attr:`applied_tags`.
10621063
@@ -1169,6 +1170,10 @@ async def edit(self, *, reason=None, **options):
11691170
HTTPException
11701171
Editing the channel failed.
11711172
"""
1173+
with contextlib.suppress(KeyError):
1174+
require_tag = options.pop("require_tag")
1175+
options["flags"] = ChannelFlags._from_value(self.flags.value)
1176+
options["flags"].require_tag = require_tag
11721177

11731178
payload = await self._edit(options, reason=reason)
11741179
if payload is not None:
@@ -1351,6 +1356,213 @@ async def create_thread(
13511356
return ret
13521357

13531358

1359+
class MediaChannel(ForumChannel):
1360+
"""Represents a Discord media channel.
1361+
1362+
.. container:: operations
1363+
1364+
.. describe:: x == y
1365+
1366+
Checks if two channels are equal.
1367+
1368+
.. describe:: x != y
1369+
1370+
Checks if two channels are not equal.
1371+
1372+
.. describe:: hash(x)
1373+
1374+
Returns the channel's hash.
1375+
1376+
.. describe:: str(x)
1377+
1378+
Returns the channel's name.
1379+
1380+
Attributes
1381+
----------
1382+
name: :class:`str`
1383+
The channel name.
1384+
guild: :class:`Guild`
1385+
The guild the channel belongs to.
1386+
id: :class:`int`
1387+
The channel ID.
1388+
category_id: Optional[:class:`int`]
1389+
The category channel ID this channel belongs to, if applicable.
1390+
topic: Optional[:class:`str`]
1391+
The channel's topic. ``None`` if it doesn't exist.
1392+
1393+
.. note::
1394+
1395+
:attr:`guidelines` exists as an alternative to this attribute.
1396+
position: Optional[:class:`int`]
1397+
The position in the channel list. This is a number that starts at 0. e.g. the
1398+
top channel is position 0. Can be ``None`` if the channel was received in an interaction.
1399+
last_message_id: Optional[:class:`int`]
1400+
The last message ID of the message sent to this channel. It may
1401+
*not* point to an existing or valid message.
1402+
slowmode_delay: :class:`int`
1403+
The number of seconds a member must wait between sending messages
1404+
in this channel. A value of `0` denotes that it is disabled.
1405+
Bots and users with :attr:`~Permissions.manage_channels` or
1406+
:attr:`~Permissions.manage_messages` bypass slowmode.
1407+
nsfw: :class:`bool`
1408+
If the channel is marked as "not safe for work".
1409+
1410+
.. note::
1411+
1412+
To check if the channel or the guild of that channel are marked as NSFW, consider :meth:`is_nsfw` instead.
1413+
default_auto_archive_duration: :class:`int`
1414+
The default auto archive duration in minutes for threads created in this channel.
1415+
1416+
.. versionadded:: 2.0
1417+
flags: :class:`ChannelFlags`
1418+
Extra features of the channel.
1419+
1420+
.. versionadded:: 2.0
1421+
available_tags: List[:class:`ForumTag`]
1422+
The set of tags that can be used in a forum channel.
1423+
1424+
.. versionadded:: 2.3
1425+
default_sort_order: Optional[:class:`SortOrder`]
1426+
The default sort order type used to order posts in this channel.
1427+
1428+
.. versionadded:: 2.3
1429+
default_thread_slowmode_delay: Optional[:class:`int`]
1430+
The initial slowmode delay to set on newly created threads in this channel.
1431+
1432+
.. versionadded:: 2.3
1433+
default_reaction_emoji: Optional[:class:`str` | :class:`discord.GuildEmoji`]
1434+
The default forum reaction emoji.
1435+
1436+
.. versionadded:: 2.5
1437+
"""
1438+
1439+
@property
1440+
def hides_media_download_options(self):
1441+
"""Whether media download options are be hidden in this media channel.
1442+
1443+
.. versionadded:: 2.7
1444+
"""
1445+
return self.flags.hide_media_download_options
1446+
1447+
@overload
1448+
async def edit(
1449+
self,
1450+
*,
1451+
reason: str | None = ...,
1452+
name: str = ...,
1453+
topic: str = ...,
1454+
position: int = ...,
1455+
nsfw: bool = ...,
1456+
sync_permissions: bool = ...,
1457+
category: CategoryChannel | None = ...,
1458+
slowmode_delay: int = ...,
1459+
default_auto_archive_duration: ThreadArchiveDuration = ...,
1460+
default_thread_slowmode_delay: int = ...,
1461+
default_sort_order: SortOrder = ...,
1462+
default_reaction_emoji: GuildEmoji | int | str | None = ...,
1463+
available_tags: list[ForumTag] = ...,
1464+
require_tag: bool = ...,
1465+
hide_media_download_options: bool = ...,
1466+
overwrites: Mapping[Role | Member | Snowflake, PermissionOverwrite] = ...,
1467+
) -> ForumChannel | None: ...
1468+
1469+
async def edit(self, *, reason=None, **options):
1470+
"""|coro|
1471+
1472+
Edits the channel.
1473+
1474+
You must have the :attr:`~Permissions.manage_channels` permission to
1475+
use this.
1476+
1477+
Parameters
1478+
----------
1479+
name: :class:`str`
1480+
The new channel name.
1481+
topic: :class:`str`
1482+
The new channel's topic.
1483+
position: :class:`int`
1484+
The new channel's position.
1485+
nsfw: :class:`bool`
1486+
To mark the channel as NSFW or not.
1487+
sync_permissions: :class:`bool`
1488+
Whether to sync permissions with the channel's new or pre-existing
1489+
category. Defaults to ``False``.
1490+
category: Optional[:class:`CategoryChannel`]
1491+
The new category for this channel. Can be ``None`` to remove the
1492+
category.
1493+
slowmode_delay: :class:`int`
1494+
Specifies the slowmode rate limit for user in this channel, in seconds.
1495+
A value of `0` disables slowmode. The maximum value possible is `21600`.
1496+
reason: Optional[:class:`str`]
1497+
The reason for editing this channel. Shows up on the audit log.
1498+
overwrites: Dict[Union[:class:`Role`, :class:`Member`, :class:`~discord.abc.Snowflake`], :class:`PermissionOverwrite`]
1499+
The overwrites to apply to channel permissions. Useful for creating secret channels.
1500+
default_auto_archive_duration: :class:`int`
1501+
The new default auto archive duration in minutes for threads created in this channel.
1502+
Must be one of ``60``, ``1440``, ``4320``, or ``10080``.
1503+
default_thread_slowmode_delay: :class:`int`
1504+
The new default slowmode delay in seconds for threads created in this channel.
1505+
1506+
.. versionadded:: 2.3
1507+
default_sort_order: Optional[:class:`SortOrder`]
1508+
The default sort order type to use to order posts in this channel.
1509+
1510+
.. versionadded:: 2.3
1511+
default_reaction_emoji: Optional[:class:`discord.GuildEmoji` | :class:`int` | :class:`str`]
1512+
The default reaction emoji.
1513+
Can be a unicode emoji or a custom emoji in the forms:
1514+
:class:`GuildEmoji`, snowflake ID, string representation (eg. '<a:emoji_name:emoji_id>').
1515+
1516+
.. versionadded:: 2.5
1517+
available_tags: List[:class:`ForumTag`]
1518+
The set of tags that can be used in this channel. Must be less than `20`.
1519+
1520+
.. versionadded:: 2.3
1521+
require_tag: :class:`bool`
1522+
Whether a tag should be required to be specified when creating a thread in this channel.
1523+
1524+
.. versionadded:: 2.3
1525+
hide_media_download_options: :class:`bool`
1526+
Whether media download options should be hidden in this media channel.
1527+
1528+
.. versionadded:: 2.7
1529+
1530+
Returns
1531+
-------
1532+
Optional[:class:`.MediaChannel`]
1533+
The newly edited media channel. If the edit was only positional
1534+
then ``None`` is returned instead.
1535+
1536+
Raises
1537+
------
1538+
InvalidArgument
1539+
If position is less than 0 or greater than the number of channels, or if
1540+
the permission overwrite information is not in proper form.
1541+
Forbidden
1542+
You do not have permissions to edit the channel.
1543+
HTTPException
1544+
Editing the channel failed.
1545+
"""
1546+
with contextlib.suppress(KeyError):
1547+
require_tag = options.pop("require_tag")
1548+
options["flags"] = options.get("flags") or ChannelFlags._from_value(
1549+
self.flags.value
1550+
)
1551+
options["flags"].require_tag = require_tag
1552+
1553+
with contextlib.suppress(KeyError):
1554+
hide_media_download_options = options.pop("hide_media_download_options")
1555+
options["flags"] = options.get("flags") or ChannelFlags._from_value(
1556+
self.flags.value
1557+
)
1558+
options["flags"].hide_media_download_options = hide_media_download_options
1559+
1560+
payload = await self._edit(options, reason=reason)
1561+
if payload is not None:
1562+
# the payload will always be the proper channel payload
1563+
return self.__class__(state=self._state, guild=self.guild, data=payload) # type: ignore
1564+
1565+
13541566
class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
13551567
__slots__ = (
13561568
"name",
@@ -3234,8 +3446,10 @@ def _guild_channel_factory(channel_type: int):
32343446
return StageChannel, value
32353447
elif value is ChannelType.directory:
32363448
return None, value # todo: Add DirectoryChannel when applicable
3237-
elif value is ChannelType.forum or value is ChannelType.media:
3449+
elif value is ChannelType.forum:
32383450
return ForumChannel, value
3451+
elif value is ChannelType.media:
3452+
return MediaChannel, value
32393453
else:
32403454
return None, value
32413455

discord/flags.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,14 @@ def require_tag(self):
15611561
"""
15621562
return 1 << 4
15631563

1564+
@flag_value
1565+
def hide_media_download_options(self):
1566+
""":class:`bool`: Returns ``True`` if the embedded media download options are hidden for the media channel posts.
1567+
1568+
.. versionadded:: 2.7
1569+
"""
1570+
return 1 << 15
1571+
15641572

15651573
@fill_with_flags()
15661574
class AttachmentFlags(BaseFlags):

discord/threads.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ def members(self) -> list[ThreadMember]:
300300
def applied_tags(self) -> list[ForumTag]:
301301
"""List[:class:`ForumTag`]: A list of tags applied to this thread.
302302
303-
This is only available for threads in forum channels.
303+
This is only available for threads in forum or media channels.
304304
"""
305305
from .channel import ForumChannel # to prevent circular import
306306

@@ -394,7 +394,7 @@ def starting_message(self) -> Message | None:
394394
return self._state._get_message(self.id)
395395

396396
def is_pinned(self) -> bool:
397-
"""Whether the thread is pinned to the top of its parent forum channel.
397+
"""Whether the thread is pinned to the top of its parent forum or media channel.
398398
399399
.. versionadded:: 2.3
400400
"""
@@ -638,7 +638,7 @@ async def edit(
638638
reason: Optional[:class:`str`]
639639
The reason for editing this thread. Shows up on the audit log.
640640
pinned: :class:`bool`
641-
Whether to pin the thread or not. This only works if the thread is part of a forum.
641+
Whether to pin the thread or not. This only works if the thread is part of a forum or media channel.
642642
applied_tags: List[:class:`ForumTag`]
643643
The set of tags to apply to the thread. Each tag object should have an ID set.
644644

0 commit comments

Comments
 (0)