mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-18 23:15:48 +00:00
parent
bc91e1667f
commit
23f6876492
@ -1377,6 +1377,10 @@ class Messageable:
|
|||||||
Indicates if the message should be sent using text-to-speech.
|
Indicates if the message should be sent using text-to-speech.
|
||||||
embed: :class:`~discord.Embed`
|
embed: :class:`~discord.Embed`
|
||||||
The rich embed for the content.
|
The rich embed for the content.
|
||||||
|
embeds: List[:class:`~discord.Embed`]
|
||||||
|
A list of embeds to upload. Must be a maximum of 10.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
file: :class:`~discord.File`
|
file: :class:`~discord.File`
|
||||||
The file to upload.
|
The file to upload.
|
||||||
files: List[:class:`~discord.File`]
|
files: List[:class:`~discord.File`]
|
||||||
@ -1412,10 +1416,6 @@ class Messageable:
|
|||||||
.. versionadded:: 1.6
|
.. versionadded:: 1.6
|
||||||
view: :class:`discord.ui.View`
|
view: :class:`discord.ui.View`
|
||||||
A Discord UI View to add to the message.
|
A Discord UI View to add to the message.
|
||||||
embeds: List[:class:`~discord.Embed`]
|
|
||||||
A list of embeds to upload. Must be a maximum of 10.
|
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
|
||||||
stickers: Sequence[Union[:class:`~discord.GuildSticker`, :class:`~discord.StickerItem`]]
|
stickers: Sequence[Union[:class:`~discord.GuildSticker`, :class:`~discord.StickerItem`]]
|
||||||
A list of stickers to upload. Must be a maximum of 3.
|
A list of stickers to upload. Must be a maximum of 3.
|
||||||
|
|
||||||
@ -1432,7 +1432,7 @@ class Messageable:
|
|||||||
~discord.Forbidden
|
~discord.Forbidden
|
||||||
You do not have the proper permissions to send the message.
|
You do not have the proper permissions to send the message.
|
||||||
ValueError
|
ValueError
|
||||||
The ``files`` list is not of the appropriate size.
|
The ``files`` or ``embeds`` list is not of the appropriate size.
|
||||||
TypeError
|
TypeError
|
||||||
You specified both ``file`` and ``files``,
|
You specified both ``file`` and ``files``,
|
||||||
or you specified both ``embed`` and ``embeds``,
|
or you specified both ``embed`` and ``embeds``,
|
||||||
|
@ -34,6 +34,7 @@ from typing import (
|
|||||||
Mapping,
|
Mapping,
|
||||||
Optional,
|
Optional,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
|
Sequence,
|
||||||
Tuple,
|
Tuple,
|
||||||
Union,
|
Union,
|
||||||
overload,
|
overload,
|
||||||
@ -51,6 +52,7 @@ from .asset import Asset
|
|||||||
from .errors import ClientException
|
from .errors import ClientException
|
||||||
from .stage_instance import StageInstance
|
from .stage_instance import StageInstance
|
||||||
from .threads import Thread
|
from .threads import Thread
|
||||||
|
from .http import handle_message_parameters
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'TextChannel',
|
'TextChannel',
|
||||||
@ -58,6 +60,7 @@ __all__ = (
|
|||||||
'StageChannel',
|
'StageChannel',
|
||||||
'DMChannel',
|
'DMChannel',
|
||||||
'CategoryChannel',
|
'CategoryChannel',
|
||||||
|
'ForumChannel',
|
||||||
'GroupChannel',
|
'GroupChannel',
|
||||||
'PartialMessageable',
|
'PartialMessageable',
|
||||||
)
|
)
|
||||||
@ -69,11 +72,16 @@ if TYPE_CHECKING:
|
|||||||
from .role import Role
|
from .role import Role
|
||||||
from .member import Member, VoiceState
|
from .member import Member, VoiceState
|
||||||
from .abc import Snowflake, SnowflakeTime
|
from .abc import Snowflake, SnowflakeTime
|
||||||
|
from .embeds import Embed
|
||||||
from .message import Message, PartialMessage
|
from .message import Message, PartialMessage
|
||||||
|
from .mentions import AllowedMentions
|
||||||
from .webhook import Webhook
|
from .webhook import Webhook
|
||||||
from .state import ConnectionState
|
from .state import ConnectionState
|
||||||
|
from .sticker import GuildSticker, StickerItem
|
||||||
|
from .file import File
|
||||||
from .user import ClientUser, User, BaseUser
|
from .user import ClientUser, User, BaseUser
|
||||||
from .guild import Guild, GuildChannel as GuildChannelType
|
from .guild import Guild, GuildChannel as GuildChannelType
|
||||||
|
from .ui.view import View
|
||||||
from .types.channel import (
|
from .types.channel import (
|
||||||
TextChannel as TextChannelPayload,
|
TextChannel as TextChannelPayload,
|
||||||
VoiceChannel as VoiceChannelPayload,
|
VoiceChannel as VoiceChannelPayload,
|
||||||
@ -81,6 +89,7 @@ if TYPE_CHECKING:
|
|||||||
DMChannel as DMChannelPayload,
|
DMChannel as DMChannelPayload,
|
||||||
CategoryChannel as CategoryChannelPayload,
|
CategoryChannel as CategoryChannelPayload,
|
||||||
GroupDMChannel as GroupChannelPayload,
|
GroupDMChannel as GroupChannelPayload,
|
||||||
|
ForumChannel as ForumChannelPayload,
|
||||||
)
|
)
|
||||||
from .types.snowflake import SnowflakeList
|
from .types.snowflake import SnowflakeList
|
||||||
|
|
||||||
@ -1893,6 +1902,350 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable):
|
|||||||
return await self.guild.create_stage_channel(name, category=self, **options)
|
return await self.guild.create_stage_channel(name, category=self, **options)
|
||||||
|
|
||||||
|
|
||||||
|
class ForumChannel(discord.abc.GuildChannel, Hashable):
|
||||||
|
"""Represents a Discord guild forum channel.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two forums are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two forums are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the forum's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the forum's name.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The forum name.
|
||||||
|
guild: :class:`Guild`
|
||||||
|
The guild the forum belongs to.
|
||||||
|
id: :class:`int`
|
||||||
|
The forum ID.
|
||||||
|
category_id: Optional[:class:`int`]
|
||||||
|
The category channel ID this forum belongs to, if applicable.
|
||||||
|
topic: Optional[:class:`str`]
|
||||||
|
The forum's topic. ``None`` if it doesn't exist.
|
||||||
|
position: :class:`int`
|
||||||
|
The position in the channel list. This is a number that starts at 0. e.g. the
|
||||||
|
top channel is position 0.
|
||||||
|
last_message_id: Optional[:class:`int`]
|
||||||
|
The last thread ID that was created on this forum. This technically also
|
||||||
|
coincides with the message ID that started the thread that was created.
|
||||||
|
It may *not* point to an existing or valid thread or message.
|
||||||
|
slowmode_delay: :class:`int`
|
||||||
|
The number of seconds a member must wait between creating threads
|
||||||
|
in this forum. A value of `0` denotes that it is disabled.
|
||||||
|
Bots and users with :attr:`~Permissions.manage_channels` or
|
||||||
|
:attr:`~Permissions.manage_messages` bypass slowmode.
|
||||||
|
nsfw: :class:`bool`
|
||||||
|
If the forum is marked as "not safe for work" or "age restricted".
|
||||||
|
default_auto_archive_duration: :class:`int`
|
||||||
|
The default auto archive duration in minutes for threads created in this forum.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'name',
|
||||||
|
'id',
|
||||||
|
'guild',
|
||||||
|
'topic',
|
||||||
|
'_state',
|
||||||
|
'_flags',
|
||||||
|
'nsfw',
|
||||||
|
'category_id',
|
||||||
|
'position',
|
||||||
|
'slowmode_delay',
|
||||||
|
'_overwrites',
|
||||||
|
'last_message_id',
|
||||||
|
'default_auto_archive_duration',
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *, state: ConnectionState, guild: Guild, data: ForumChannelPayload):
|
||||||
|
self._state: ConnectionState = state
|
||||||
|
self.id: int = int(data['id'])
|
||||||
|
self._update(guild, data)
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
attrs = [
|
||||||
|
('id', self.id),
|
||||||
|
('name', self.name),
|
||||||
|
('position', self.position),
|
||||||
|
('nsfw', self.nsfw),
|
||||||
|
('category_id', self.category_id),
|
||||||
|
]
|
||||||
|
joined = ' '.join('%s=%r' % t for t in attrs)
|
||||||
|
return f'<{self.__class__.__name__} {joined}>'
|
||||||
|
|
||||||
|
def _update(self, guild: Guild, data: ForumChannelPayload) -> None:
|
||||||
|
self.guild: Guild = guild
|
||||||
|
self.name: str = data['name']
|
||||||
|
self.category_id: Optional[int] = utils._get_as_snowflake(data, 'parent_id')
|
||||||
|
self.topic: Optional[str] = data.get('topic')
|
||||||
|
self.position: int = data['position']
|
||||||
|
self.nsfw: bool = data.get('nsfw', False)
|
||||||
|
self.slowmode_delay: int = data.get('rate_limit_per_user', 0)
|
||||||
|
self.default_auto_archive_duration: ThreadArchiveDuration = data.get('default_auto_archive_duration', 1440)
|
||||||
|
self.last_message_id: Optional[int] = utils._get_as_snowflake(data, 'last_message_id')
|
||||||
|
self._fill_overwrites(data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> ChannelType:
|
||||||
|
""":class:`ChannelType`: The channel's Discord type."""
|
||||||
|
return ChannelType.forum
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _sorting_bucket(self) -> int:
|
||||||
|
return ChannelType.text.value
|
||||||
|
|
||||||
|
@utils.copy_doc(discord.abc.GuildChannel.permissions_for)
|
||||||
|
def permissions_for(self, obj: Union[Member, Role], /) -> Permissions:
|
||||||
|
base = super().permissions_for(obj)
|
||||||
|
|
||||||
|
# text channels do not have voice related permissions
|
||||||
|
denied = Permissions.voice()
|
||||||
|
base.value &= ~denied.value
|
||||||
|
return base
|
||||||
|
|
||||||
|
@property
|
||||||
|
def threads(self) -> List[Thread]:
|
||||||
|
"""List[:class:`Thread`]: Returns all the threads that you can see."""
|
||||||
|
return [thread for thread in self.guild._threads.values() if thread.parent_id == self.id]
|
||||||
|
|
||||||
|
def is_nsfw(self) -> bool:
|
||||||
|
""":class:`bool`: Checks if the forum is NSFW."""
|
||||||
|
return self.nsfw
|
||||||
|
|
||||||
|
@utils.copy_doc(discord.abc.GuildChannel.clone)
|
||||||
|
async def clone(self, *, name: Optional[str] = None, reason: Optional[str] = None) -> ForumChannel:
|
||||||
|
return await self._clone_impl(
|
||||||
|
{'topic': self.topic, 'nsfw': self.nsfw, 'rate_limit_per_user': self.slowmode_delay}, name=name, reason=reason
|
||||||
|
)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def edit(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
reason: Optional[str] = ...,
|
||||||
|
name: str = ...,
|
||||||
|
topic: str = ...,
|
||||||
|
position: int = ...,
|
||||||
|
nsfw: bool = ...,
|
||||||
|
sync_permissions: bool = ...,
|
||||||
|
category: Optional[CategoryChannel] = ...,
|
||||||
|
slowmode_delay: int = ...,
|
||||||
|
default_auto_archive_duration: ThreadArchiveDuration = ...,
|
||||||
|
type: ChannelType = ...,
|
||||||
|
overwrites: Mapping[Union[Role, Member, Snowflake], PermissionOverwrite] = ...,
|
||||||
|
) -> Optional[ForumChannel]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def edit(self) -> Optional[ForumChannel]:
|
||||||
|
...
|
||||||
|
|
||||||
|
async def edit(self, *, reason: Optional[str] = None, **options: Any) -> Optional[ForumChannel]:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Edits the forum.
|
||||||
|
|
||||||
|
You must have the :attr:`~Permissions.manage_channels` permission to
|
||||||
|
use this.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
name: :class:`str`
|
||||||
|
The new forum name.
|
||||||
|
topic: :class:`str`
|
||||||
|
The new forum's topic.
|
||||||
|
position: :class:`int`
|
||||||
|
The new forum's position.
|
||||||
|
nsfw: :class:`bool`
|
||||||
|
To mark the forum as NSFW or not.
|
||||||
|
sync_permissions: :class:`bool`
|
||||||
|
Whether to sync permissions with the forum's new or pre-existing
|
||||||
|
category. Defaults to ``False``.
|
||||||
|
category: Optional[:class:`CategoryChannel`]
|
||||||
|
The new category for this forum. Can be ``None`` to remove the
|
||||||
|
category.
|
||||||
|
slowmode_delay: :class:`int`
|
||||||
|
Specifies the slowmode rate limit for user in this forum, in seconds.
|
||||||
|
A value of `0` disables slowmode. The maximum value possible is `21600`.
|
||||||
|
type: :class:`ChannelType`
|
||||||
|
Change the type of this text forum. Currently, only conversion between
|
||||||
|
:attr:`ChannelType.text` and :attr:`ChannelType.news` is supported. This
|
||||||
|
is only available to guilds that contain ``NEWS`` in :attr:`Guild.features`.
|
||||||
|
reason: Optional[:class:`str`]
|
||||||
|
The reason for editing this forum. Shows up on the audit log.
|
||||||
|
overwrites: :class:`Mapping`
|
||||||
|
A :class:`Mapping` of target (either a role or a member) to
|
||||||
|
:class:`PermissionOverwrite` to apply to the forum.
|
||||||
|
default_auto_archive_duration: :class:`int`
|
||||||
|
The new default auto archive duration in minutes for threads created in this channel.
|
||||||
|
Must be one of ``60``, ``1440``, ``4320``, or ``10080``.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
ValueError
|
||||||
|
The new ``position`` is less than 0 or greater than the number of channels.
|
||||||
|
TypeError
|
||||||
|
The permission overwrite information is not in proper form.
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to edit the forum.
|
||||||
|
HTTPException
|
||||||
|
Editing the forum failed.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
Optional[:class:`.ForumChannel`]
|
||||||
|
The newly edited forum channel. If the edit was only positional
|
||||||
|
then ``None`` is returned instead.
|
||||||
|
"""
|
||||||
|
|
||||||
|
payload = await self._edit(options, reason=reason)
|
||||||
|
if payload is not None:
|
||||||
|
# the payload will always be the proper channel payload
|
||||||
|
return self.__class__(state=self._state, guild=self.guild, data=payload) # type: ignore
|
||||||
|
|
||||||
|
async def create_thread(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
name: str,
|
||||||
|
auto_archive_duration: ThreadArchiveDuration = MISSING,
|
||||||
|
slowmode_delay: Optional[int] = None,
|
||||||
|
content: Optional[str] = None,
|
||||||
|
tts: bool = False,
|
||||||
|
embed: Embed = MISSING,
|
||||||
|
embeds: Sequence[Embed] = MISSING,
|
||||||
|
file: File = MISSING,
|
||||||
|
files: Sequence[File] = MISSING,
|
||||||
|
stickers: Sequence[Union[GuildSticker, StickerItem]] = MISSING,
|
||||||
|
allowed_mentions: AllowedMentions = MISSING,
|
||||||
|
mention_author: bool = MISSING,
|
||||||
|
view: View = MISSING,
|
||||||
|
suppress_embeds: bool = False,
|
||||||
|
reason: Optional[str] = None,
|
||||||
|
) -> Thread:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Creates a thread in this forum.
|
||||||
|
|
||||||
|
This thread is a public thread with the initial message given. Currently in order
|
||||||
|
to start a thread in this forum, the user needs :attr:`~discord.Permissions.send_messages`.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The name of the thread.
|
||||||
|
auto_archive_duration: :class:`int`
|
||||||
|
The duration in minutes before a thread is automatically archived for inactivity.
|
||||||
|
If not provided, the channel's default auto archive duration is used.
|
||||||
|
slowmode_delay: Optional[:class:`int`]
|
||||||
|
Specifies the slowmode rate limit for user in this channel, in seconds.
|
||||||
|
The maximum value possible is `21600`. By default no slowmode rate limit
|
||||||
|
if this is ``None``.
|
||||||
|
content: Optional[:class:`str`]
|
||||||
|
The content of the message to send with the thread.
|
||||||
|
tts: :class:`bool`
|
||||||
|
Indicates if the message should be sent using text-to-speech.
|
||||||
|
embed: :class:`~discord.Embed`
|
||||||
|
The rich embed for the content.
|
||||||
|
embeds: List[:class:`~discord.Embed`]
|
||||||
|
A list of embeds to upload. Must be a maximum of 10.
|
||||||
|
file: :class:`~discord.File`
|
||||||
|
The file to upload.
|
||||||
|
files: List[:class:`~discord.File`]
|
||||||
|
A list of files to upload. Must be a maximum of 10.
|
||||||
|
allowed_mentions: :class:`~discord.AllowedMentions`
|
||||||
|
Controls the mentions being processed in this message. If this is
|
||||||
|
passed, then the object is merged with :attr:`~discord.Client.allowed_mentions`.
|
||||||
|
The merging behaviour only overrides attributes that have been explicitly passed
|
||||||
|
to the object, otherwise it uses the attributes set in :attr:`~discord.Client.allowed_mentions`.
|
||||||
|
If no object is passed at all then the defaults given by :attr:`~discord.Client.allowed_mentions`
|
||||||
|
are used instead.
|
||||||
|
mention_author: :class:`bool`
|
||||||
|
If set, overrides the :attr:`~discord.AllowedMentions.replied_user` attribute of ``allowed_mentions``.
|
||||||
|
view: :class:`discord.ui.View`
|
||||||
|
A Discord UI View to add to the message.
|
||||||
|
stickers: Sequence[Union[:class:`~discord.GuildSticker`, :class:`~discord.StickerItem`]]
|
||||||
|
A list of stickers to upload. Must be a maximum of 3.
|
||||||
|
suppress_embeds: :class:`bool`
|
||||||
|
Whether to suppress embeds for the message. This sends the message without any embeds if set to ``True``.
|
||||||
|
reason: :class:`str`
|
||||||
|
The reason for creating a new thread. Shows up on the audit log.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to create a thread.
|
||||||
|
HTTPException
|
||||||
|
Starting the thread failed.
|
||||||
|
ValueError
|
||||||
|
The ``files`` or ``embeds`` list is not of the appropriate size.
|
||||||
|
TypeError
|
||||||
|
You specified both ``file`` and ``files``,
|
||||||
|
or you specified both ``embed`` and ``embeds``.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
:class:`Thread`
|
||||||
|
The created thread
|
||||||
|
"""
|
||||||
|
|
||||||
|
state = self._state
|
||||||
|
previous_allowed_mention = state.allowed_mentions
|
||||||
|
if stickers is MISSING:
|
||||||
|
sticker_ids = MISSING
|
||||||
|
else:
|
||||||
|
sticker_ids: SnowflakeList = [s.id for s in stickers]
|
||||||
|
|
||||||
|
if view and not hasattr(view, '__discord_ui_view__'):
|
||||||
|
raise TypeError(f'view parameter must be View not {view.__class__!r}')
|
||||||
|
|
||||||
|
if suppress_embeds:
|
||||||
|
from .message import MessageFlags # circular import
|
||||||
|
|
||||||
|
flags = MessageFlags._from_value(4)
|
||||||
|
else:
|
||||||
|
flags = MISSING
|
||||||
|
|
||||||
|
content = str(content) if content else MISSING
|
||||||
|
|
||||||
|
extras = {
|
||||||
|
'name': name,
|
||||||
|
'auto_archive_duration': auto_archive_duration or self.default_auto_archive_duration,
|
||||||
|
'rate_limit_per_user': slowmode_delay,
|
||||||
|
}
|
||||||
|
|
||||||
|
with handle_message_parameters(
|
||||||
|
content=content,
|
||||||
|
tts=tts,
|
||||||
|
file=file,
|
||||||
|
files=files,
|
||||||
|
embed=embed,
|
||||||
|
embeds=embeds,
|
||||||
|
allowed_mentions=allowed_mentions,
|
||||||
|
previous_allowed_mentions=previous_allowed_mention,
|
||||||
|
mention_author=None if mention_author is MISSING else mention_author,
|
||||||
|
stickers=sticker_ids,
|
||||||
|
view=view,
|
||||||
|
flags=flags,
|
||||||
|
extras=extras,
|
||||||
|
) as params:
|
||||||
|
data = await state.http.start_thread_in_forum(self.id, params=params, reason=reason)
|
||||||
|
return Thread(guild=self.guild, state=self._state, data=data)
|
||||||
|
|
||||||
|
|
||||||
class DMChannel(discord.abc.Messageable, Hashable):
|
class DMChannel(discord.abc.Messageable, Hashable):
|
||||||
"""Represents a Discord direct message channel.
|
"""Represents a Discord direct message channel.
|
||||||
|
|
||||||
@ -2251,6 +2604,8 @@ def _guild_channel_factory(channel_type: int):
|
|||||||
return TextChannel, value
|
return TextChannel, value
|
||||||
elif value is ChannelType.stage_voice:
|
elif value is ChannelType.stage_voice:
|
||||||
return StageChannel, value
|
return StageChannel, value
|
||||||
|
elif value is ChannelType.forum:
|
||||||
|
return ForumChannel, value
|
||||||
else:
|
else:
|
||||||
return None, value
|
return None, value
|
||||||
|
|
||||||
|
@ -195,6 +195,7 @@ class ChannelType(Enum):
|
|||||||
public_thread = 11
|
public_thread = 11
|
||||||
private_thread = 12
|
private_thread = 12
|
||||||
stage_voice = 13
|
stage_voice = 13
|
||||||
|
forum = 15
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -39,6 +39,7 @@ __all__ = (
|
|||||||
'Intents',
|
'Intents',
|
||||||
'MemberCacheFlags',
|
'MemberCacheFlags',
|
||||||
'ApplicationFlags',
|
'ApplicationFlags',
|
||||||
|
'ChannelFlags',
|
||||||
)
|
)
|
||||||
|
|
||||||
BF = TypeVar('BF', bound='BaseFlags')
|
BF = TypeVar('BF', bound='BaseFlags')
|
||||||
@ -1175,3 +1176,39 @@ class ApplicationFlags(BaseFlags):
|
|||||||
""":class:`bool`: Returns ``True`` if the application is unverified and is allowed to
|
""":class:`bool`: Returns ``True`` if the application is unverified and is allowed to
|
||||||
read message content in guilds."""
|
read message content in guilds."""
|
||||||
return 1 << 19
|
return 1 << 19
|
||||||
|
|
||||||
|
|
||||||
|
@fill_with_flags()
|
||||||
|
class ChannelFlags(BaseFlags):
|
||||||
|
r"""Wraps up the Discord :class:`~discord.abc.GuildChannel` or :class:`Thread` flags.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two channel flags are equal.
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two channel flags are not equal.
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Return the flag's hash.
|
||||||
|
.. describe:: iter(x)
|
||||||
|
|
||||||
|
Returns an iterator of ``(name, value)`` pairs. This allows it
|
||||||
|
to be, for example, constructed as a dict or a list of pairs.
|
||||||
|
Note that aliases are not shown.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
value: :class:`int`
|
||||||
|
The raw value. You should query flags via the properties
|
||||||
|
rather than using this raw value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@flag_value
|
||||||
|
def pinned(self):
|
||||||
|
""":class:`bool`: Returns ``True`` if the"""
|
||||||
|
return 1 << 1
|
||||||
|
@ -109,7 +109,7 @@ if TYPE_CHECKING:
|
|||||||
)
|
)
|
||||||
from .types.voice import GuildVoiceState
|
from .types.voice import GuildVoiceState
|
||||||
from .permissions import Permissions
|
from .permissions import Permissions
|
||||||
from .channel import VoiceChannel, StageChannel, TextChannel, CategoryChannel
|
from .channel import VoiceChannel, StageChannel, TextChannel, ForumChannel, CategoryChannel
|
||||||
from .template import Template
|
from .template import Template
|
||||||
from .webhook import Webhook
|
from .webhook import Webhook
|
||||||
from .state import ConnectionState
|
from .state import ConnectionState
|
||||||
@ -127,7 +127,7 @@ if TYPE_CHECKING:
|
|||||||
from .types.widget import EditWidgetSettings
|
from .types.widget import EditWidgetSettings
|
||||||
|
|
||||||
VocalGuildChannel = Union[VoiceChannel, StageChannel]
|
VocalGuildChannel = Union[VoiceChannel, StageChannel]
|
||||||
GuildChannel = Union[VocalGuildChannel, TextChannel, CategoryChannel]
|
GuildChannel = Union[VocalGuildChannel, ForumChannel, TextChannel, CategoryChannel]
|
||||||
ByCategoryItem = Tuple[Optional[CategoryChannel], List[GuildChannel]]
|
ByCategoryItem = Tuple[Optional[CategoryChannel], List[GuildChannel]]
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,6 +147,7 @@ def handle_message_parameters(
|
|||||||
stickers: Optional[SnowflakeList] = MISSING,
|
stickers: Optional[SnowflakeList] = MISSING,
|
||||||
previous_allowed_mentions: Optional[AllowedMentions] = None,
|
previous_allowed_mentions: Optional[AllowedMentions] = None,
|
||||||
mention_author: Optional[bool] = None,
|
mention_author: Optional[bool] = None,
|
||||||
|
extras: Dict[str, Any] = MISSING,
|
||||||
) -> MultipartParameters:
|
) -> MultipartParameters:
|
||||||
if files is not MISSING and file is not MISSING:
|
if files is not MISSING and file is not MISSING:
|
||||||
raise TypeError('Cannot mix file and files keyword arguments.')
|
raise TypeError('Cannot mix file and files keyword arguments.')
|
||||||
@ -234,6 +235,9 @@ def handle_message_parameters(
|
|||||||
|
|
||||||
payload['attachments'] = attachments_payload
|
payload['attachments'] = attachments_payload
|
||||||
|
|
||||||
|
if extras is not MISSING:
|
||||||
|
payload.update(extras)
|
||||||
|
|
||||||
multipart = []
|
multipart = []
|
||||||
if files:
|
if files:
|
||||||
multipart.append({'name': 'payload_json', 'value': utils._to_json(payload)})
|
multipart.append({'name': 'payload_json', 'value': utils._to_json(payload)})
|
||||||
@ -976,6 +980,19 @@ class HTTPClient:
|
|||||||
route = Route('POST', '/channels/{channel_id}/threads', channel_id=channel_id)
|
route = Route('POST', '/channels/{channel_id}/threads', channel_id=channel_id)
|
||||||
return self.request(route, json=payload, reason=reason)
|
return self.request(route, json=payload, reason=reason)
|
||||||
|
|
||||||
|
def start_thread_in_forum(
|
||||||
|
self,
|
||||||
|
channel_id: Snowflake,
|
||||||
|
*,
|
||||||
|
params: MultipartParameters,
|
||||||
|
reason: Optional[str] = None,
|
||||||
|
) -> Response[threads.Thread]:
|
||||||
|
r = Route('POST', '/channels/{channel_id}/threads', channel_id=channel_id)
|
||||||
|
if params.files:
|
||||||
|
return self.request(r, files=params.files, form=params.multipart, reason=reason)
|
||||||
|
else:
|
||||||
|
return self.request(r, json=params.payload, reason=reason)
|
||||||
|
|
||||||
def join_thread(self, channel_id: Snowflake) -> Response[None]:
|
def join_thread(self, channel_id: Snowflake) -> Response[None]:
|
||||||
return self.request(Route('POST', '/channels/{channel_id}/thread-members/@me', channel_id=channel_id))
|
return self.request(Route('POST', '/channels/{channel_id}/thread-members/@me', channel_id=channel_id))
|
||||||
|
|
||||||
|
@ -69,11 +69,13 @@ if TYPE_CHECKING:
|
|||||||
from .ui.view import View
|
from .ui.view import View
|
||||||
from .app_commands.models import Choice, ChoiceT
|
from .app_commands.models import Choice, ChoiceT
|
||||||
from .ui.modal import Modal
|
from .ui.modal import Modal
|
||||||
from .channel import VoiceChannel, StageChannel, TextChannel, CategoryChannel
|
from .channel import VoiceChannel, StageChannel, TextChannel, ForumChannel, CategoryChannel
|
||||||
from .threads import Thread
|
from .threads import Thread
|
||||||
from .app_commands.commands import Command, ContextMenu
|
from .app_commands.commands import Command, ContextMenu
|
||||||
|
|
||||||
InteractionChannel = Union[VoiceChannel, StageChannel, TextChannel, CategoryChannel, Thread, PartialMessageable]
|
InteractionChannel = Union[
|
||||||
|
VoiceChannel, StageChannel, TextChannel, ForumChannel, CategoryChannel, Thread, PartialMessageable
|
||||||
|
]
|
||||||
|
|
||||||
MISSING: Any = utils.MISSING
|
MISSING: Any = utils.MISSING
|
||||||
|
|
||||||
|
@ -851,6 +851,9 @@ class ConnectionState:
|
|||||||
guild._add_thread(thread)
|
guild._add_thread(thread)
|
||||||
if not has_thread:
|
if not has_thread:
|
||||||
if data.get('newly_created'):
|
if data.get('newly_created'):
|
||||||
|
if thread.parent.__class__ is ForumChannel:
|
||||||
|
thread.parent.last_message_id = thread.id # type: ignore
|
||||||
|
|
||||||
self.dispatch('thread_create', thread)
|
self.dispatch('thread_create', thread)
|
||||||
else:
|
else:
|
||||||
self.dispatch('thread_join', thread)
|
self.dispatch('thread_join', thread)
|
||||||
|
@ -31,6 +31,7 @@ from .mixins import Hashable
|
|||||||
from .abc import Messageable, _purge_helper
|
from .abc import Messageable, _purge_helper
|
||||||
from .enums import ChannelType, try_enum
|
from .enums import ChannelType, try_enum
|
||||||
from .errors import ClientException
|
from .errors import ClientException
|
||||||
|
from .flags import ChannelFlags
|
||||||
from .utils import MISSING, parse_time, _get_as_snowflake
|
from .utils import MISSING, parse_time, _get_as_snowflake
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
@ -49,7 +50,7 @@ if TYPE_CHECKING:
|
|||||||
)
|
)
|
||||||
from .types.snowflake import SnowflakeList
|
from .types.snowflake import SnowflakeList
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .channel import TextChannel, CategoryChannel
|
from .channel import TextChannel, CategoryChannel, ForumChannel
|
||||||
from .member import Member
|
from .member import Member
|
||||||
from .message import Message, PartialMessage
|
from .message import Message, PartialMessage
|
||||||
from .abc import Snowflake, SnowflakeTime
|
from .abc import Snowflake, SnowflakeTime
|
||||||
@ -145,6 +146,7 @@ class Thread(Messageable, Hashable):
|
|||||||
'auto_archive_duration',
|
'auto_archive_duration',
|
||||||
'archive_timestamp',
|
'archive_timestamp',
|
||||||
'_created_at',
|
'_created_at',
|
||||||
|
'_flags',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *, guild: Guild, state: ConnectionState, data: ThreadPayload) -> None:
|
def __init__(self, *, guild: Guild, state: ConnectionState, data: ThreadPayload) -> None:
|
||||||
@ -175,6 +177,7 @@ class Thread(Messageable, Hashable):
|
|||||||
self.slowmode_delay: int = data.get('rate_limit_per_user', 0)
|
self.slowmode_delay: int = data.get('rate_limit_per_user', 0)
|
||||||
self.message_count: int = data['message_count']
|
self.message_count: int = data['message_count']
|
||||||
self.member_count: int = data['member_count']
|
self.member_count: int = data['member_count']
|
||||||
|
self._flags: int = data.get('flags', 0)
|
||||||
self._unroll_metadata(data['thread_metadata'])
|
self._unroll_metadata(data['thread_metadata'])
|
||||||
|
|
||||||
self.me: Optional[ThreadMember]
|
self.me: Optional[ThreadMember]
|
||||||
@ -213,10 +216,15 @@ class Thread(Messageable, Hashable):
|
|||||||
return self._type
|
return self._type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self) -> Optional[TextChannel]:
|
def parent(self) -> Optional[Union[ForumChannel, TextChannel]]:
|
||||||
"""Optional[:class:`TextChannel`]: The parent channel this thread belongs to."""
|
"""Optional[Union[:class:`ForumChannel`, :class:`TextChannel`]]: The parent channel this thread belongs to."""
|
||||||
return self.guild.get_channel(self.parent_id) # type: ignore
|
return self.guild.get_channel(self.parent_id) # type: ignore
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flags(self) -> ChannelFlags:
|
||||||
|
""":class:`ChannelFlags`: The flags associated with this thread."""
|
||||||
|
return ChannelFlags._from_value(self._flags)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def owner(self) -> Optional[Member]:
|
def owner(self) -> Optional[Member]:
|
||||||
"""Optional[:class:`Member`]: The member this thread belongs to."""
|
"""Optional[:class:`Member`]: The member this thread belongs to."""
|
||||||
|
@ -40,7 +40,7 @@ class PermissionOverwrite(TypedDict):
|
|||||||
deny: str
|
deny: str
|
||||||
|
|
||||||
|
|
||||||
ChannelTypeWithoutThread = Literal[0, 1, 2, 3, 4, 5, 6, 13]
|
ChannelTypeWithoutThread = Literal[0, 1, 2, 3, 4, 5, 6, 13, 15]
|
||||||
ChannelType = Union[ChannelTypeWithoutThread, ThreadType]
|
ChannelType = Union[ChannelTypeWithoutThread, ThreadType]
|
||||||
|
|
||||||
|
|
||||||
@ -116,9 +116,14 @@ class ThreadChannel(_BaseChannel):
|
|||||||
rate_limit_per_user: NotRequired[int]
|
rate_limit_per_user: NotRequired[int]
|
||||||
last_message_id: NotRequired[Optional[Snowflake]]
|
last_message_id: NotRequired[Optional[Snowflake]]
|
||||||
last_pin_timestamp: NotRequired[str]
|
last_pin_timestamp: NotRequired[str]
|
||||||
|
flags: NotRequired[int]
|
||||||
|
|
||||||
|
|
||||||
GuildChannel = Union[TextChannel, NewsChannel, VoiceChannel, CategoryChannel, StageChannel, ThreadChannel]
|
class ForumChannel(_BaseTextChannel):
|
||||||
|
type: Literal[15]
|
||||||
|
|
||||||
|
|
||||||
|
GuildChannel = Union[TextChannel, NewsChannel, VoiceChannel, CategoryChannel, StageChannel, ThreadChannel, ForumChannel]
|
||||||
|
|
||||||
|
|
||||||
class DMChannel(_BaseChannel):
|
class DMChannel(_BaseChannel):
|
||||||
|
@ -65,6 +65,7 @@ class Thread(TypedDict):
|
|||||||
last_message_id: NotRequired[Optional[Snowflake]]
|
last_message_id: NotRequired[Optional[Snowflake]]
|
||||||
last_pin_timestamp: NotRequired[Optional[Snowflake]]
|
last_pin_timestamp: NotRequired[Optional[Snowflake]]
|
||||||
newly_created: NotRequired[bool]
|
newly_created: NotRequired[bool]
|
||||||
|
flags: NotRequired[int]
|
||||||
|
|
||||||
|
|
||||||
class ThreadPaginationPayload(TypedDict):
|
class ThreadPaginationPayload(TypedDict):
|
||||||
|
24
docs/api.rst
24
docs/api.rst
@ -1310,6 +1310,12 @@ of :class:`enum.Enum`.
|
|||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
.. attribute:: forum
|
||||||
|
|
||||||
|
A forum channel.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
.. class:: MessageType
|
.. class:: MessageType
|
||||||
|
|
||||||
Specifies the type of :class:`Message`. This is used to denote if a message
|
Specifies the type of :class:`Message`. This is used to denote if a message
|
||||||
@ -3736,6 +3742,15 @@ TextChannel
|
|||||||
.. automethod:: typing
|
.. automethod:: typing
|
||||||
:async-with:
|
:async-with:
|
||||||
|
|
||||||
|
ForumChannel
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: ForumChannel
|
||||||
|
|
||||||
|
.. autoclass:: ForumChannel()
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
Thread
|
Thread
|
||||||
~~~~~~~~
|
~~~~~~~~
|
||||||
|
|
||||||
@ -4069,6 +4084,15 @@ ApplicationFlags
|
|||||||
.. autoclass:: ApplicationFlags
|
.. autoclass:: ApplicationFlags
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
ChannelFlags
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: ChannelFlags
|
||||||
|
|
||||||
|
.. autoclass:: ChannelFlags
|
||||||
|
:members:
|
||||||
|
|
||||||
|
|
||||||
File
|
File
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user