mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-07 12:18:59 +00:00
First pass at preliminary thread support
This is missing a lot of functionality right now, such as two gateway events and all the HTTP CRUD endpoints.
This commit is contained in:
parent
6c79714b42
commit
68c7c538f5
@ -58,6 +58,7 @@ from .sticker import *
|
|||||||
from .stage_instance import *
|
from .stage_instance import *
|
||||||
from .interactions import *
|
from .interactions import *
|
||||||
from .components import *
|
from .components import *
|
||||||
|
from .threads import *
|
||||||
|
|
||||||
VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial')
|
VersionInfo = namedtuple('VersionInfo', 'major minor micro releaselevel serial')
|
||||||
|
|
||||||
|
@ -173,6 +173,14 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
|||||||
"""List[:class:`Member`]: Returns all members that can see this channel."""
|
"""List[:class:`Member`]: Returns all members that can see this channel."""
|
||||||
return [m for m in self.guild.members if self.permissions_for(m).read_messages]
|
return [m for m in self.guild.members if self.permissions_for(m).read_messages]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def threads(self):
|
||||||
|
"""List[:class:`Thread`]: Returns all the threads that you can see.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
"""
|
||||||
|
return [thread for thread in self.guild.threads if thread.parent_id == self.id]
|
||||||
|
|
||||||
def is_nsfw(self):
|
def is_nsfw(self):
|
||||||
""":class:`bool`: Checks if the channel is NSFW."""
|
""":class:`bool`: Checks if the channel is NSFW."""
|
||||||
return self.nsfw
|
return self.nsfw
|
||||||
|
@ -155,14 +155,16 @@ else:
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
class ChannelType(Enum):
|
class ChannelType(Enum):
|
||||||
text = 0
|
text = 0
|
||||||
private = 1
|
private = 1
|
||||||
voice = 2
|
voice = 2
|
||||||
group = 3
|
group = 3
|
||||||
category = 4
|
category = 4
|
||||||
news = 5
|
news = 5
|
||||||
store = 6
|
store = 6
|
||||||
stage_voice = 13
|
public_thread = 11
|
||||||
|
private_thread = 12
|
||||||
|
stage_voice = 13
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -186,8 +188,10 @@ class MessageType(Enum):
|
|||||||
guild_discovery_requalified = 15
|
guild_discovery_requalified = 15
|
||||||
guild_discovery_grace_period_initial_warning = 16
|
guild_discovery_grace_period_initial_warning = 16
|
||||||
guild_discovery_grace_period_final_warning = 17
|
guild_discovery_grace_period_final_warning = 17
|
||||||
|
thread_created = 18
|
||||||
reply = 19
|
reply = 19
|
||||||
application_command = 20
|
application_command = 20
|
||||||
|
thread_starter_message = 21
|
||||||
guild_invite_reminder = 22
|
guild_invite_reminder = 22
|
||||||
|
|
||||||
class VoiceRegion(Enum):
|
class VoiceRegion(Enum):
|
||||||
|
@ -279,6 +279,13 @@ class MessageFlags(BaseFlags):
|
|||||||
"""
|
"""
|
||||||
return 16
|
return 16
|
||||||
|
|
||||||
|
@flag_value
|
||||||
|
def has_thread(self):
|
||||||
|
""":class:`bool`: Returns ``True`` if the source message is associated with a thread.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
"""
|
||||||
|
return 32
|
||||||
|
|
||||||
@fill_with_flags()
|
@fill_with_flags()
|
||||||
class PublicUserFlags(BaseFlags):
|
class PublicUserFlags(BaseFlags):
|
||||||
|
@ -47,6 +47,7 @@ from .asset import Asset
|
|||||||
from .flags import SystemChannelFlags
|
from .flags import SystemChannelFlags
|
||||||
from .integrations import Integration, _integration_factory
|
from .integrations import Integration, _integration_factory
|
||||||
from .stage_instance import StageInstance
|
from .stage_instance import StageInstance
|
||||||
|
from .threads import Thread
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Guild',
|
'Guild',
|
||||||
@ -182,7 +183,7 @@ class Guild(Hashable):
|
|||||||
'description', 'max_presences', 'max_members', 'max_video_channel_users',
|
'description', 'max_presences', 'max_members', 'max_video_channel_users',
|
||||||
'premium_tier', 'premium_subscription_count', '_system_channel_flags',
|
'premium_tier', 'premium_subscription_count', '_system_channel_flags',
|
||||||
'preferred_locale', '_discovery_splash', '_rules_channel_id',
|
'preferred_locale', '_discovery_splash', '_rules_channel_id',
|
||||||
'_public_updates_channel_id', '_stage_instances', 'nsfw_level')
|
'_public_updates_channel_id', '_stage_instances', 'nsfw_level', '_threads')
|
||||||
|
|
||||||
_PREMIUM_GUILD_LIMITS = {
|
_PREMIUM_GUILD_LIMITS = {
|
||||||
None: _GuildLimit(emoji=50, bitrate=96e3, filesize=8388608),
|
None: _GuildLimit(emoji=50, bitrate=96e3, filesize=8388608),
|
||||||
@ -196,6 +197,7 @@ class Guild(Hashable):
|
|||||||
self._channels = {}
|
self._channels = {}
|
||||||
self._members = {}
|
self._members = {}
|
||||||
self._voice_states = {}
|
self._voice_states = {}
|
||||||
|
self._threads = {}
|
||||||
self._state = state
|
self._state = state
|
||||||
self._from_data(data)
|
self._from_data(data)
|
||||||
|
|
||||||
@ -214,6 +216,12 @@ class Guild(Hashable):
|
|||||||
def _remove_member(self, member):
|
def _remove_member(self, member):
|
||||||
self._members.pop(member.id, None)
|
self._members.pop(member.id, None)
|
||||||
|
|
||||||
|
def _add_thread(self, thread):
|
||||||
|
self._threads[thread.id] = thread
|
||||||
|
|
||||||
|
def _remove_thread(self, thread):
|
||||||
|
self._threads.pop(thread.id, None)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name or ''
|
return self.name or ''
|
||||||
|
|
||||||
@ -360,11 +368,24 @@ class Guild(Hashable):
|
|||||||
if factory:
|
if factory:
|
||||||
self._add_channel(factory(guild=self, data=c, state=self._state))
|
self._add_channel(factory(guild=self, data=c, state=self._state))
|
||||||
|
|
||||||
|
if 'threads' in data:
|
||||||
|
threads = data['threads']
|
||||||
|
for thread in threads:
|
||||||
|
self._add_thread(Thread(guild=self, data=thread))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def channels(self):
|
def channels(self):
|
||||||
"""List[:class:`abc.GuildChannel`]: A list of channels that belongs to this guild."""
|
"""List[:class:`abc.GuildChannel`]: A list of channels that belongs to this guild."""
|
||||||
return list(self._channels.values())
|
return list(self._channels.values())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def threads(self):
|
||||||
|
"""List[:class:`Thread`]: A list of threads that you have permission to view.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
"""
|
||||||
|
return list(self._threads.values())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def large(self):
|
def large(self):
|
||||||
""":class:`bool`: Indicates if the guild is a 'large' guild.
|
""":class:`bool`: Indicates if the guild is a 'large' guild.
|
||||||
@ -484,6 +505,23 @@ class Guild(Hashable):
|
|||||||
"""
|
"""
|
||||||
return self._channels.get(channel_id)
|
return self._channels.get(channel_id)
|
||||||
|
|
||||||
|
def get_thread(self, thread_id):
|
||||||
|
"""Returns a thread with the given ID.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
thread_id: :class:`int`
|
||||||
|
The ID to search for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
Optional[:class:`Thread`]
|
||||||
|
The returned thread or ``None`` if not found.
|
||||||
|
"""
|
||||||
|
return self._threads.get(thread_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def system_channel(self):
|
def system_channel(self):
|
||||||
"""Optional[:class:`TextChannel`]: Returns the guild's channel used for system messages.
|
"""Optional[:class:`TextChannel`]: Returns the guild's channel used for system messages.
|
||||||
|
@ -695,6 +695,8 @@ class HTTPClient:
|
|||||||
'type',
|
'type',
|
||||||
'rtc_region',
|
'rtc_region',
|
||||||
'video_quality_mode',
|
'video_quality_mode',
|
||||||
|
'archived',
|
||||||
|
'auto_archive_duration',
|
||||||
)
|
)
|
||||||
payload = {k: v for k, v in options.items() if k in valid_keys}
|
payload = {k: v for k, v in options.items() if k in valid_keys}
|
||||||
return self.request(r, reason=reason, json=payload)
|
return self.request(r, reason=reason, json=payload)
|
||||||
@ -720,6 +722,7 @@ class HTTPClient:
|
|||||||
'rate_limit_per_user',
|
'rate_limit_per_user',
|
||||||
'rtc_region',
|
'rtc_region',
|
||||||
'video_quality_mode',
|
'video_quality_mode',
|
||||||
|
'auto_archive_duration',
|
||||||
)
|
)
|
||||||
payload.update({k: v for k, v in options.items() if k in valid_keys and v is not None})
|
payload.update({k: v for k, v in options.items() if k in valid_keys and v is not None})
|
||||||
|
|
||||||
@ -728,6 +731,80 @@ class HTTPClient:
|
|||||||
def delete_channel(self, channel_id, *, reason=None):
|
def delete_channel(self, channel_id, *, reason=None):
|
||||||
return self.request(Route('DELETE', '/channels/{channel_id}', channel_id=channel_id), reason=reason)
|
return self.request(Route('DELETE', '/channels/{channel_id}', channel_id=channel_id), reason=reason)
|
||||||
|
|
||||||
|
# Thread management
|
||||||
|
|
||||||
|
def start_public_thread(
|
||||||
|
self,
|
||||||
|
channel_id: int,
|
||||||
|
message_id: int,
|
||||||
|
*,
|
||||||
|
name: str,
|
||||||
|
auto_archive_duration: int,
|
||||||
|
type: int,
|
||||||
|
):
|
||||||
|
payload = {
|
||||||
|
'name': name,
|
||||||
|
'auto_archive_duration': auto_archive_duration,
|
||||||
|
'type': type,
|
||||||
|
}
|
||||||
|
|
||||||
|
route = Route(
|
||||||
|
'POST', '/channels/{channel_id}/messages/{message_id}/threads', channel_id=channel_id, message_id=message_id
|
||||||
|
)
|
||||||
|
return self.request(route, json=payload)
|
||||||
|
|
||||||
|
def start_private_thread(
|
||||||
|
self,
|
||||||
|
channel_id: int,
|
||||||
|
*,
|
||||||
|
name: str,
|
||||||
|
auto_archive_duration: int,
|
||||||
|
type: int,
|
||||||
|
):
|
||||||
|
payload = {
|
||||||
|
'name': name,
|
||||||
|
'auto_archive_duration': auto_archive_duration,
|
||||||
|
'type': type,
|
||||||
|
}
|
||||||
|
|
||||||
|
route = Route('POST', '/channels/{channel_id}/threads', channel_id=channel_id)
|
||||||
|
return self.request(route, json=payload)
|
||||||
|
|
||||||
|
def join_thread(self, channel_id: int):
|
||||||
|
return self.request(Route('POST', '/channels/{channel_id}/thread-members/@me', channel_id=channel_id))
|
||||||
|
|
||||||
|
def add_user_to_thread(self, channel_id: int, user_id: int):
|
||||||
|
return self.request(
|
||||||
|
Route('POST', '/channels/{channel_id}/thread-members/{user_id}', channel_id=channel_id, user_id=user_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
def leave_thread(self, channel_id: int):
|
||||||
|
return self.request(Route('DELETE', '/channels/{channel_id}/thread-members/@me', channel_id=channel_id))
|
||||||
|
|
||||||
|
def remove_user_from_thread(self, channel_id: int, user_id: int):
|
||||||
|
route = Route('DELETE', '/channels/{channel_id}/thread-members/{user_id}', channel_id=channel_id, user_id=user_id)
|
||||||
|
return self.request(route)
|
||||||
|
|
||||||
|
def get_archived_threads(self, channel_id: int, before=None, limit: int = 50, public: bool = True):
|
||||||
|
if public:
|
||||||
|
route = Route('GET', '/channels/{channel_id}/threads/archived/public', channel_id=channel_id)
|
||||||
|
else:
|
||||||
|
route = Route('GET', '/channels/{channel_id}/threads/archived/private', channel_id=channel_id)
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
if before:
|
||||||
|
params['before'] = before
|
||||||
|
params['limit'] = limit
|
||||||
|
return self.request(route, params=params)
|
||||||
|
|
||||||
|
def get_joined_private_archived_threads(self, channel_id, before=None, limit: int = 50):
|
||||||
|
route = Route('GET', '/channels/{channel_id}/users/@me/threads/archived/private', channel_id=channel_id)
|
||||||
|
params = {}
|
||||||
|
if before:
|
||||||
|
params['before'] = before
|
||||||
|
params['limit'] = limit
|
||||||
|
return self.request(route, params=params)
|
||||||
|
|
||||||
# Webhook management
|
# Webhook management
|
||||||
|
|
||||||
def create_webhook(self, channel_id, *, name, avatar=None, reason=None):
|
def create_webhook(self, channel_id, *, name, avatar=None, reason=None):
|
||||||
|
@ -55,6 +55,7 @@ from .integrations import _integration_factory
|
|||||||
from .interactions import Interaction
|
from .interactions import Interaction
|
||||||
from .ui.view import ViewStore
|
from .ui.view import ViewStore
|
||||||
from .stage_instance import StageInstance
|
from .stage_instance import StageInstance
|
||||||
|
from .threads import Thread, ThreadMember
|
||||||
|
|
||||||
class ChunkRequest:
|
class ChunkRequest:
|
||||||
def __init__(self, guild_id, loop, resolver, *, cache=True):
|
def __init__(self, guild_id, loop, resolver, *, cache=True):
|
||||||
@ -483,7 +484,7 @@ class ConnectionState:
|
|||||||
self.dispatch('message', message)
|
self.dispatch('message', message)
|
||||||
if self._messages is not None:
|
if self._messages is not None:
|
||||||
self._messages.append(message)
|
self._messages.append(message)
|
||||||
if channel and channel.__class__ is TextChannel:
|
if channel and channel.__class__ in (TextChannel, Thread):
|
||||||
channel.last_message_id = message.id
|
channel.last_message_id = message.id
|
||||||
|
|
||||||
def parse_message_delete(self, data):
|
def parse_message_delete(self, data):
|
||||||
@ -704,6 +705,44 @@ class ConnectionState:
|
|||||||
else:
|
else:
|
||||||
self.dispatch('guild_channel_pins_update', channel, last_pin)
|
self.dispatch('guild_channel_pins_update', channel, last_pin)
|
||||||
|
|
||||||
|
def parse_thread_create(self, data):
|
||||||
|
guild_id = int(data['guild_id'])
|
||||||
|
guild = self._get_guild(guild_id)
|
||||||
|
if guild is None:
|
||||||
|
log.debug('THREAD_CREATE referencing an unknown guild ID: %s. Discarding', guild_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
thread = Thread(guild=guild, data=data)
|
||||||
|
guild._add_thread(thread)
|
||||||
|
self.dispatch('thread_create', thread)
|
||||||
|
|
||||||
|
def parse_thread_update(self, data):
|
||||||
|
guild_id = int(data['guild_id'])
|
||||||
|
guild = self._get_guild(guild_id)
|
||||||
|
if guild is None:
|
||||||
|
log.debug('THREAD_UPDATE referencing an unknown guild ID: %s. Discarding', guild_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
thread_id = int(data['id'])
|
||||||
|
thread = guild._get_thread(thread_id)
|
||||||
|
if thread is not None:
|
||||||
|
old = copy.copy(thread)
|
||||||
|
thread._update(data)
|
||||||
|
self.dispatch('thread_update', old, thread)
|
||||||
|
|
||||||
|
def parse_thread_delete(self, data):
|
||||||
|
guild_id = int(data['guild_id'])
|
||||||
|
guild = self._get_guild(guild_id)
|
||||||
|
if guild is None:
|
||||||
|
log.debug('THREAD_UPDATE referencing an unknown guild ID: %s. Discarding', guild_id)
|
||||||
|
return
|
||||||
|
|
||||||
|
thread_id = int(data['id'])
|
||||||
|
thread = guild._get_thread(thread_id)
|
||||||
|
if thread is not None:
|
||||||
|
guild._remove_thread(thread)
|
||||||
|
self.dispatch('thread_delete', thread)
|
||||||
|
|
||||||
def parse_guild_member_add(self, data):
|
def parse_guild_member_add(self, data):
|
||||||
guild = self._get_guild(int(data['guild_id']))
|
guild = self._get_guild(int(data['guild_id']))
|
||||||
if guild is None:
|
if guild is None:
|
||||||
|
244
discord/threads.py
Normal file
244
discord/threads.py
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
"""
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-present Rapptz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import Optional, TYPE_CHECKING
|
||||||
|
|
||||||
|
from .mixins import Hashable
|
||||||
|
from .abc import Messageable
|
||||||
|
from .enums import ChannelType, try_enum
|
||||||
|
from . import utils
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'Thread',
|
||||||
|
'ThreadMember',
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .types.threads import (
|
||||||
|
Thread as ThreadPayload,
|
||||||
|
ThreadMember as ThreadMemberPayload,
|
||||||
|
ThreadMetadata,
|
||||||
|
)
|
||||||
|
from .guild import Guild
|
||||||
|
from .channel import TextChannel
|
||||||
|
from .member import Member
|
||||||
|
|
||||||
|
|
||||||
|
class Thread(Messageable, Hashable):
|
||||||
|
"""Represents a Discord thread.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two threads are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two threads are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the thread's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the thread's name.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
name: :class:`str`
|
||||||
|
The thread name.
|
||||||
|
guild: :class:`Guild`
|
||||||
|
The guild the thread belongs to.
|
||||||
|
id: :class:`int`
|
||||||
|
The thread ID.
|
||||||
|
parent_id: :class:`int`
|
||||||
|
The parent :class:`TextChannel` ID this thread belongs to.
|
||||||
|
owner_id: :class:`int`
|
||||||
|
The user's ID that created this thread.
|
||||||
|
last_message_id: Optional[:class:`int`]
|
||||||
|
The last message ID of the message sent to this thread. It may
|
||||||
|
*not* point to an existing or valid message.
|
||||||
|
message_count: :class:`int`
|
||||||
|
An approximate number of messages in this thread. This caps at 50.
|
||||||
|
member_count: :class:`int`
|
||||||
|
An approximate number of members in this thread. This caps at 50.
|
||||||
|
me: Optional[:class:`ThreadMember`]
|
||||||
|
A thread member representing yourself, if you've joined the thread.
|
||||||
|
This could not be available.
|
||||||
|
archived: :class:`bool`
|
||||||
|
Whether the thread is archived.
|
||||||
|
archiver_id: Optional[:class:`int`]
|
||||||
|
The user's ID that archived this thread.
|
||||||
|
auto_archive_duration: :class:`int`
|
||||||
|
The duration in minutes until the thread is automatically archived due to inactivity.
|
||||||
|
Usually a value of 60, 1440, 4320 and 10080.
|
||||||
|
archive_timestamp: :class:`datetime.datetime`
|
||||||
|
An aware timestamp of when the thread's archived status was last updated in UTC.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'name',
|
||||||
|
'id',
|
||||||
|
'guild',
|
||||||
|
'_type',
|
||||||
|
'_state',
|
||||||
|
'owner_id',
|
||||||
|
'last_message_id',
|
||||||
|
'message_count',
|
||||||
|
'member_count',
|
||||||
|
'me',
|
||||||
|
'archived',
|
||||||
|
'archiver_id',
|
||||||
|
'auto_archive_duration',
|
||||||
|
'archive_timestamp',
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *, guild: Guild, data: ThreadPayload):
|
||||||
|
self._state = guild._state
|
||||||
|
self.guild = guild
|
||||||
|
self._from_data(data)
|
||||||
|
|
||||||
|
async def _get_channel(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def _from_data(self, data: ThreadPayload):
|
||||||
|
self.id = int(data['id'])
|
||||||
|
self.parent_id = int(data['parent_id'])
|
||||||
|
self.owner_id = int(data['owner_id'])
|
||||||
|
self.name = data['name']
|
||||||
|
self.type = try_enum(ChannelType, data['type'])
|
||||||
|
self.last_message_id = utils._get_as_snowflake(data, 'last_message_id')
|
||||||
|
self._unroll_metadata(data['thread_metadata'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
member = data['member']
|
||||||
|
except KeyError:
|
||||||
|
self.me = None
|
||||||
|
else:
|
||||||
|
self.me = ThreadMember(member, self._state)
|
||||||
|
|
||||||
|
def _unroll_metadata(self, data: ThreadMetadata):
|
||||||
|
self.archived = data['archived']
|
||||||
|
self.archiver_id = utils._get_as_snowflake(data, 'archiver_id')
|
||||||
|
self.auto_archive_duration = data['auto_archive_duration']
|
||||||
|
self.archive_timestamp = utils.parse_time(data['archive_timestamp'])
|
||||||
|
|
||||||
|
def _update(self, data):
|
||||||
|
try:
|
||||||
|
self.name = data['name']
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._unroll_metadata(data['thread_metadata'])
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parent(self) -> Optional[TextChannel]:
|
||||||
|
"""Optional[:class:`TextChannel`]: The parent channel this thread belongs to."""
|
||||||
|
return self.guild.get_channel(self.parent_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def owner(self) -> Optional[Member]:
|
||||||
|
"""Optional[:class:`Member`]: The member this thread belongs to."""
|
||||||
|
return self.guild.get_member(self.owner_id)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def last_message(self):
|
||||||
|
"""Fetches the last message from this channel in cache.
|
||||||
|
|
||||||
|
The message might not be valid or point to an existing message.
|
||||||
|
|
||||||
|
.. admonition:: Reliable Fetching
|
||||||
|
:class: helpful
|
||||||
|
|
||||||
|
For a slightly more reliable method of fetching the
|
||||||
|
last message, consider using either :meth:`history`
|
||||||
|
or :meth:`fetch_message` with the :attr:`last_message_id`
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
Optional[:class:`Message`]
|
||||||
|
The last message in this channel or ``None`` if not found.
|
||||||
|
"""
|
||||||
|
return self._state._get_message(self.last_message_id) if self.last_message_id else None
|
||||||
|
|
||||||
|
|
||||||
|
class ThreadMember(Hashable):
|
||||||
|
"""Represents a Discord thread member.
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two thread members are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two thread members are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the thread member's hash.
|
||||||
|
|
||||||
|
.. describe:: str(x)
|
||||||
|
|
||||||
|
Returns the thread member's name.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
id: :class:`int`
|
||||||
|
The thread member's ID.
|
||||||
|
thread_id: :class:`int`
|
||||||
|
The thread's ID.
|
||||||
|
joined_at: :class:`datetime.datetime`
|
||||||
|
The time the member joined the thread in UTC.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
'id',
|
||||||
|
'thread_id',
|
||||||
|
'joined_at',
|
||||||
|
'flags',
|
||||||
|
'_state',
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, data: ThreadMemberPayload, state):
|
||||||
|
self._state = state
|
||||||
|
self._from_data(data)
|
||||||
|
|
||||||
|
def _from_data(self, data: ThreadMemberPayload):
|
||||||
|
self.id = int(data['user_id'])
|
||||||
|
self.thread_id = int(data['id'])
|
||||||
|
self.joined_at = utils.parse_time(data['join_timestamp'])
|
||||||
|
self.flags = data['flags']
|
76
docs/api.rst
76
docs/api.rst
@ -658,6 +658,33 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
|||||||
:param last_pin: The latest message that was pinned as an aware datetime in UTC. Could be ``None``.
|
:param last_pin: The latest message that was pinned as an aware datetime in UTC. Could be ``None``.
|
||||||
:type last_pin: Optional[:class:`datetime.datetime`]
|
:type last_pin: Optional[:class:`datetime.datetime`]
|
||||||
|
|
||||||
|
.. function:: on_thread_delete(thread)
|
||||||
|
on_thread_create(thread)
|
||||||
|
|
||||||
|
Called whenever a thread is deleted or created.
|
||||||
|
|
||||||
|
Note that you can get the guild from :attr:`Thread.guild`.
|
||||||
|
|
||||||
|
This requires :attr:`Intents.guilds` to be enabled.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
:param thread: The thread that got created or deleted.
|
||||||
|
:type thread: :class:`Thread`
|
||||||
|
|
||||||
|
.. function:: on_thread_update(before, after)
|
||||||
|
|
||||||
|
Called whenever a thread is updated.
|
||||||
|
|
||||||
|
This requires :attr:`Intents.guilds` to be enabled.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
:param before: The updated thread's old info.
|
||||||
|
:type before: :class:`Thread`
|
||||||
|
:param after: The updated thread's new info.
|
||||||
|
:type after: :class:`Thread`
|
||||||
|
|
||||||
.. function:: on_guild_integrations_update(guild)
|
.. function:: on_guild_integrations_update(guild)
|
||||||
|
|
||||||
Called whenever an integration is created, modified, or removed from a guild.
|
Called whenever an integration is created, modified, or removed from a guild.
|
||||||
@ -1038,6 +1065,18 @@ of :class:`enum.Enum`.
|
|||||||
|
|
||||||
.. versionadded:: 1.7
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
.. attribute:: public_thread
|
||||||
|
|
||||||
|
A public thread
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
.. attribute:: private_thread
|
||||||
|
|
||||||
|
A private thread
|
||||||
|
|
||||||
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
.. 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
|
||||||
@ -1129,9 +1168,14 @@ of :class:`enum.Enum`.
|
|||||||
Discovery requirements for 3 weeks in a row.
|
Discovery requirements for 3 weeks in a row.
|
||||||
|
|
||||||
.. versionadded:: 1.7
|
.. versionadded:: 1.7
|
||||||
|
.. attribute:: thread_created
|
||||||
|
|
||||||
|
The system message denoting that a thread has been created
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
.. attribute:: reply
|
.. attribute:: reply
|
||||||
|
|
||||||
The message type denoting that the author is replying to a message.
|
The system message denoting that the author is replying to a message.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
.. attribute:: application_command
|
.. attribute:: application_command
|
||||||
@ -1143,6 +1187,12 @@ of :class:`enum.Enum`.
|
|||||||
|
|
||||||
The system message sent as a reminder to invite people to the guild.
|
The system message sent as a reminder to invite people to the guild.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
.. attribute:: thread_starter_message
|
||||||
|
|
||||||
|
The system message denoting that this message is the one that started a thread's
|
||||||
|
conversation topic.
|
||||||
|
|
||||||
.. versionadded:: 2.0
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
.. class:: UserFlags
|
.. class:: UserFlags
|
||||||
@ -3197,6 +3247,30 @@ TextChannel
|
|||||||
.. automethod:: typing
|
.. automethod:: typing
|
||||||
:async-with:
|
:async-with:
|
||||||
|
|
||||||
|
Thread
|
||||||
|
~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: Thread
|
||||||
|
|
||||||
|
.. autoclass:: Thread()
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
:exclude-members: history, typing
|
||||||
|
|
||||||
|
.. automethod:: history
|
||||||
|
:async-for:
|
||||||
|
|
||||||
|
.. automethod:: typing
|
||||||
|
:async-with:
|
||||||
|
|
||||||
|
ThreadMember
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: ThreadMember
|
||||||
|
|
||||||
|
.. autoclass:: ThreadMember()
|
||||||
|
:members:
|
||||||
|
|
||||||
StoreChannel
|
StoreChannel
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user