mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-04-16 06:03:11 +00:00
parent
bc91e1667f
commit
23f6876492
@ -1377,6 +1377,10 @@ class Messageable:
|
||||
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.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
file: :class:`~discord.File`
|
||||
The file to upload.
|
||||
files: List[:class:`~discord.File`]
|
||||
@ -1412,10 +1416,6 @@ class Messageable:
|
||||
.. versionadded:: 1.6
|
||||
view: :class:`discord.ui.View`
|
||||
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`]]
|
||||
A list of stickers to upload. Must be a maximum of 3.
|
||||
|
||||
@ -1432,7 +1432,7 @@ class Messageable:
|
||||
~discord.Forbidden
|
||||
You do not have the proper permissions to send the message.
|
||||
ValueError
|
||||
The ``files`` list is not of the appropriate size.
|
||||
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``,
|
||||
|
@ -34,6 +34,7 @@ from typing import (
|
||||
Mapping,
|
||||
Optional,
|
||||
TYPE_CHECKING,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Union,
|
||||
overload,
|
||||
@ -51,6 +52,7 @@ from .asset import Asset
|
||||
from .errors import ClientException
|
||||
from .stage_instance import StageInstance
|
||||
from .threads import Thread
|
||||
from .http import handle_message_parameters
|
||||
|
||||
__all__ = (
|
||||
'TextChannel',
|
||||
@ -58,6 +60,7 @@ __all__ = (
|
||||
'StageChannel',
|
||||
'DMChannel',
|
||||
'CategoryChannel',
|
||||
'ForumChannel',
|
||||
'GroupChannel',
|
||||
'PartialMessageable',
|
||||
)
|
||||
@ -69,11 +72,16 @@ if TYPE_CHECKING:
|
||||
from .role import Role
|
||||
from .member import Member, VoiceState
|
||||
from .abc import Snowflake, SnowflakeTime
|
||||
from .embeds import Embed
|
||||
from .message import Message, PartialMessage
|
||||
from .mentions import AllowedMentions
|
||||
from .webhook import Webhook
|
||||
from .state import ConnectionState
|
||||
from .sticker import GuildSticker, StickerItem
|
||||
from .file import File
|
||||
from .user import ClientUser, User, BaseUser
|
||||
from .guild import Guild, GuildChannel as GuildChannelType
|
||||
from .ui.view import View
|
||||
from .types.channel import (
|
||||
TextChannel as TextChannelPayload,
|
||||
VoiceChannel as VoiceChannelPayload,
|
||||
@ -81,6 +89,7 @@ if TYPE_CHECKING:
|
||||
DMChannel as DMChannelPayload,
|
||||
CategoryChannel as CategoryChannelPayload,
|
||||
GroupDMChannel as GroupChannelPayload,
|
||||
ForumChannel as ForumChannelPayload,
|
||||
)
|
||||
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)
|
||||
|
||||
|
||||
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):
|
||||
"""Represents a Discord direct message channel.
|
||||
|
||||
@ -2251,6 +2604,8 @@ def _guild_channel_factory(channel_type: int):
|
||||
return TextChannel, value
|
||||
elif value is ChannelType.stage_voice:
|
||||
return StageChannel, value
|
||||
elif value is ChannelType.forum:
|
||||
return ForumChannel, value
|
||||
else:
|
||||
return None, value
|
||||
|
||||
|
@ -195,6 +195,7 @@ class ChannelType(Enum):
|
||||
public_thread = 11
|
||||
private_thread = 12
|
||||
stage_voice = 13
|
||||
forum = 15
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
@ -39,6 +39,7 @@ __all__ = (
|
||||
'Intents',
|
||||
'MemberCacheFlags',
|
||||
'ApplicationFlags',
|
||||
'ChannelFlags',
|
||||
)
|
||||
|
||||
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
|
||||
read message content in guilds."""
|
||||
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 .permissions import Permissions
|
||||
from .channel import VoiceChannel, StageChannel, TextChannel, CategoryChannel
|
||||
from .channel import VoiceChannel, StageChannel, TextChannel, ForumChannel, CategoryChannel
|
||||
from .template import Template
|
||||
from .webhook import Webhook
|
||||
from .state import ConnectionState
|
||||
@ -127,7 +127,7 @@ if TYPE_CHECKING:
|
||||
from .types.widget import EditWidgetSettings
|
||||
|
||||
VocalGuildChannel = Union[VoiceChannel, StageChannel]
|
||||
GuildChannel = Union[VocalGuildChannel, TextChannel, CategoryChannel]
|
||||
GuildChannel = Union[VocalGuildChannel, ForumChannel, TextChannel, CategoryChannel]
|
||||
ByCategoryItem = Tuple[Optional[CategoryChannel], List[GuildChannel]]
|
||||
|
||||
|
||||
|
@ -147,6 +147,7 @@ def handle_message_parameters(
|
||||
stickers: Optional[SnowflakeList] = MISSING,
|
||||
previous_allowed_mentions: Optional[AllowedMentions] = None,
|
||||
mention_author: Optional[bool] = None,
|
||||
extras: Dict[str, Any] = MISSING,
|
||||
) -> MultipartParameters:
|
||||
if files is not MISSING and file is not MISSING:
|
||||
raise TypeError('Cannot mix file and files keyword arguments.')
|
||||
@ -234,6 +235,9 @@ def handle_message_parameters(
|
||||
|
||||
payload['attachments'] = attachments_payload
|
||||
|
||||
if extras is not MISSING:
|
||||
payload.update(extras)
|
||||
|
||||
multipart = []
|
||||
if files:
|
||||
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)
|
||||
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]:
|
||||
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 .app_commands.models import Choice, ChoiceT
|
||||
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 .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
|
||||
|
||||
|
@ -851,6 +851,9 @@ class ConnectionState:
|
||||
guild._add_thread(thread)
|
||||
if not has_thread:
|
||||
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)
|
||||
else:
|
||||
self.dispatch('thread_join', thread)
|
||||
|
@ -31,6 +31,7 @@ from .mixins import Hashable
|
||||
from .abc import Messageable, _purge_helper
|
||||
from .enums import ChannelType, try_enum
|
||||
from .errors import ClientException
|
||||
from .flags import ChannelFlags
|
||||
from .utils import MISSING, parse_time, _get_as_snowflake
|
||||
|
||||
__all__ = (
|
||||
@ -49,7 +50,7 @@ if TYPE_CHECKING:
|
||||
)
|
||||
from .types.snowflake import SnowflakeList
|
||||
from .guild import Guild
|
||||
from .channel import TextChannel, CategoryChannel
|
||||
from .channel import TextChannel, CategoryChannel, ForumChannel
|
||||
from .member import Member
|
||||
from .message import Message, PartialMessage
|
||||
from .abc import Snowflake, SnowflakeTime
|
||||
@ -145,6 +146,7 @@ class Thread(Messageable, Hashable):
|
||||
'auto_archive_duration',
|
||||
'archive_timestamp',
|
||||
'_created_at',
|
||||
'_flags',
|
||||
)
|
||||
|
||||
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.message_count: int = data['message_count']
|
||||
self.member_count: int = data['member_count']
|
||||
self._flags: int = data.get('flags', 0)
|
||||
self._unroll_metadata(data['thread_metadata'])
|
||||
|
||||
self.me: Optional[ThreadMember]
|
||||
@ -213,10 +216,15 @@ class Thread(Messageable, Hashable):
|
||||
return self._type
|
||||
|
||||
@property
|
||||
def parent(self) -> Optional[TextChannel]:
|
||||
"""Optional[:class:`TextChannel`]: The parent channel this thread belongs to."""
|
||||
def parent(self) -> Optional[Union[ForumChannel, TextChannel]]:
|
||||
"""Optional[Union[:class:`ForumChannel`, :class:`TextChannel`]]: The parent channel this thread belongs to."""
|
||||
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
|
||||
def owner(self) -> Optional[Member]:
|
||||
"""Optional[:class:`Member`]: The member this thread belongs to."""
|
||||
|
@ -40,7 +40,7 @@ class PermissionOverwrite(TypedDict):
|
||||
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]
|
||||
|
||||
|
||||
@ -116,9 +116,14 @@ class ThreadChannel(_BaseChannel):
|
||||
rate_limit_per_user: NotRequired[int]
|
||||
last_message_id: NotRequired[Optional[Snowflake]]
|
||||
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):
|
||||
|
@ -65,6 +65,7 @@ class Thread(TypedDict):
|
||||
last_message_id: NotRequired[Optional[Snowflake]]
|
||||
last_pin_timestamp: NotRequired[Optional[Snowflake]]
|
||||
newly_created: NotRequired[bool]
|
||||
flags: NotRequired[int]
|
||||
|
||||
|
||||
class ThreadPaginationPayload(TypedDict):
|
||||
|
24
docs/api.rst
24
docs/api.rst
@ -1310,6 +1310,12 @@ of :class:`enum.Enum`.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. attribute:: forum
|
||||
|
||||
A forum channel.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. class:: MessageType
|
||||
|
||||
Specifies the type of :class:`Message`. This is used to denote if a message
|
||||
@ -3736,6 +3742,15 @@ TextChannel
|
||||
.. automethod:: typing
|
||||
:async-with:
|
||||
|
||||
ForumChannel
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: ForumChannel
|
||||
|
||||
.. autoclass:: ForumChannel()
|
||||
:members:
|
||||
:inherited-members:
|
||||
|
||||
Thread
|
||||
~~~~~~~~
|
||||
|
||||
@ -4069,6 +4084,15 @@ ApplicationFlags
|
||||
.. autoclass:: ApplicationFlags
|
||||
:members:
|
||||
|
||||
ChannelFlags
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: ChannelFlags
|
||||
|
||||
.. autoclass:: ChannelFlags
|
||||
:members:
|
||||
|
||||
|
||||
File
|
||||
~~~~~
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user