mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-07 12:18:59 +00:00
Implement remaining HTTP endpoints on threads
I'm not sure if I missed any -- but this is the entire documented set so far.
This commit is contained in:
parent
68c7c538f5
commit
c1ce3b949f
@ -27,6 +27,7 @@ from __future__ import annotations
|
|||||||
import time
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Callable, Dict, List, Optional, TYPE_CHECKING, Union, overload
|
from typing import Callable, Dict, List, Optional, TYPE_CHECKING, Union, overload
|
||||||
|
import datetime
|
||||||
|
|
||||||
import discord.abc
|
import discord.abc
|
||||||
from .permissions import PermissionOverwrite, Permissions
|
from .permissions import PermissionOverwrite, Permissions
|
||||||
@ -36,6 +37,8 @@ from . import utils
|
|||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
from .errors import ClientException, NoMoreItems, InvalidArgument
|
from .errors import ClientException, NoMoreItems, InvalidArgument
|
||||||
from .stage_instance import StageInstance
|
from .stage_instance import StageInstance
|
||||||
|
from .threads import Thread
|
||||||
|
from .iterators import ArchivedThreadIterator
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'TextChannel',
|
'TextChannel',
|
||||||
@ -49,12 +52,12 @@ __all__ = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from .types.threads import ThreadArchiveDuration
|
||||||
from .role import Role
|
from .role import Role
|
||||||
from .member import Member, VoiceState
|
from .member import Member, VoiceState
|
||||||
from .abc import Snowflake
|
from .abc import Snowflake, SnowflakeTime
|
||||||
from .message import Message
|
from .message import Message
|
||||||
from .webhook import Webhook
|
from .webhook import Webhook
|
||||||
from .abc import SnowflakeTime
|
|
||||||
|
|
||||||
async def _single_delete_strategy(messages):
|
async def _single_delete_strategy(messages):
|
||||||
for m in messages:
|
for m in messages:
|
||||||
@ -586,6 +589,80 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
|||||||
from .message import PartialMessage
|
from .message import PartialMessage
|
||||||
return PartialMessage(channel=self, id=message_id)
|
return PartialMessage(channel=self, id=message_id)
|
||||||
|
|
||||||
|
async def start_private_thread(self, *, name: str, auto_archive_duration: ThreadArchiveDuration = 1440) -> Thread:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Starts a private thread in this text channel.
|
||||||
|
|
||||||
|
You must have :attr:`~discord.Permissions.send_messages` and
|
||||||
|
:attr:`~discord.Permissions.use_private_threads` in order to start a thread.
|
||||||
|
|
||||||
|
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.
|
||||||
|
Defaults to ``1440`` or 24 hours.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to start a thread.
|
||||||
|
HTTPException
|
||||||
|
Starting the thread failed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = await self._state.http.start_public_thread(
|
||||||
|
self.id,
|
||||||
|
name=name,
|
||||||
|
auto_archive_duration=auto_archive_duration,
|
||||||
|
type=ChannelType.private_thread.value,
|
||||||
|
)
|
||||||
|
return Thread(guild=self.guild, data=data)
|
||||||
|
|
||||||
|
async def archive_threads(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
private: bool = True,
|
||||||
|
joined: bool = False,
|
||||||
|
limit: Optional[int] = 50,
|
||||||
|
before: Optional[Union[Snowflake, datetime.datetime]] = None,
|
||||||
|
) -> ArchivedThreadIterator:
|
||||||
|
"""Returns an :class:`~discord.AsyncIterator` that iterates over all archived threads in the guild.
|
||||||
|
|
||||||
|
You must have :attr:`~Permissions.read_message_history` to use this. If iterating over private threads
|
||||||
|
then :attr:`~Permissions.manage_messages` is also required.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
limit: Optional[:class:`bool`]
|
||||||
|
The number of threads to retrieve.
|
||||||
|
If ``None``, retrieves every archived thread in the channel. Note, however,
|
||||||
|
that this would make it a slow operation.
|
||||||
|
before: Optional[Union[:class:`abc.Snowflake`, :class:`datetime.datetime`]]
|
||||||
|
Retrieve archived channels before the given date or ID.
|
||||||
|
private: :class:`bool`
|
||||||
|
Whether to retrieve private archived threads.
|
||||||
|
joined: :class:`bool`
|
||||||
|
Whether to retrieve private archived threads that you've joined.
|
||||||
|
You cannot set ``joined`` to ``True`` and ``private`` to ``False``.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to get archived threads.
|
||||||
|
HTTPException
|
||||||
|
The request to get the archived threads failed.
|
||||||
|
|
||||||
|
Yields
|
||||||
|
-------
|
||||||
|
:class:`Thread`
|
||||||
|
The archived threads.
|
||||||
|
"""
|
||||||
|
return ArchivedThreadIterator(self.id, self.guild, limit=limit, joined=joined, private=private, before=before)
|
||||||
|
|
||||||
|
|
||||||
class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
|
class VocalGuildChannel(discord.abc.Connectable, discord.abc.GuildChannel, Hashable):
|
||||||
__slots__ = ('name', 'id', 'guild', 'bitrate', 'user_limit',
|
__slots__ = ('name', 'id', 'guild', 'bitrate', 'user_limit',
|
||||||
'_state', 'position', '_overwrites', 'category_id',
|
'_state', 'position', '_overwrites', 'category_id',
|
||||||
|
@ -785,11 +785,17 @@ class HTTPClient:
|
|||||||
route = Route('DELETE', '/channels/{channel_id}/thread-members/{user_id}', channel_id=channel_id, user_id=user_id)
|
route = Route('DELETE', '/channels/{channel_id}/thread-members/{user_id}', channel_id=channel_id, user_id=user_id)
|
||||||
return self.request(route)
|
return self.request(route)
|
||||||
|
|
||||||
def get_archived_threads(self, channel_id: int, before=None, limit: int = 50, public: bool = True):
|
def get_public_archived_threads(self, channel_id: int, before=None, limit: int = 50):
|
||||||
if public:
|
route = Route('GET', '/channels/{channel_id}/threads/archived/public', channel_id=channel_id)
|
||||||
route = Route('GET', '/channels/{channel_id}/threads/archived/public', channel_id=channel_id)
|
|
||||||
else:
|
params = {}
|
||||||
route = Route('GET', '/channels/{channel_id}/threads/archived/private', channel_id=channel_id)
|
if before:
|
||||||
|
params['before'] = before
|
||||||
|
params['limit'] = limit
|
||||||
|
return self.request(route, params=params)
|
||||||
|
|
||||||
|
def get_private_archived_threads(self, channel_id: int, before=None, limit: int = 50):
|
||||||
|
route = Route('GET', '/channels/{channel_id}/threads/archived/private', channel_id=channel_id)
|
||||||
|
|
||||||
params = {}
|
params = {}
|
||||||
if before:
|
if before:
|
||||||
|
@ -29,7 +29,7 @@ import datetime
|
|||||||
from typing import Awaitable, TYPE_CHECKING, TypeVar, Optional, Any, Callable, Union, List, AsyncIterator
|
from typing import Awaitable, TYPE_CHECKING, TypeVar, Optional, Any, Callable, Union, List, AsyncIterator
|
||||||
|
|
||||||
from .errors import NoMoreItems
|
from .errors import NoMoreItems
|
||||||
from .utils import time_snowflake, maybe_coroutine
|
from .utils import snowflake_time, time_snowflake, maybe_coroutine
|
||||||
from .object import Object
|
from .object import Object
|
||||||
from .audit_logs import AuditLogEntry
|
from .audit_logs import AuditLogEntry
|
||||||
|
|
||||||
@ -55,11 +55,17 @@ if TYPE_CHECKING:
|
|||||||
PartialUser as PartialUserPayload,
|
PartialUser as PartialUserPayload,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .types.threads import (
|
||||||
|
Thread as ThreadPayload,
|
||||||
|
)
|
||||||
|
|
||||||
from .member import Member
|
from .member import Member
|
||||||
from .user import User
|
from .user import User
|
||||||
from .message import Message
|
from .message import Message
|
||||||
from .audit_logs import AuditLogEntry
|
from .audit_logs import AuditLogEntry
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
|
from .threads import Thread
|
||||||
|
from .abc import Snowflake
|
||||||
|
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
OT = TypeVar('OT')
|
OT = TypeVar('OT')
|
||||||
@ -655,3 +661,92 @@ class MemberIterator(_AsyncIterator['Member']):
|
|||||||
from .member import Member
|
from .member import Member
|
||||||
|
|
||||||
return Member(data=data, guild=self.guild, state=self.state)
|
return Member(data=data, guild=self.guild, state=self.state)
|
||||||
|
|
||||||
|
|
||||||
|
class ArchivedThreadIterator(_AsyncIterator['Thread']):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
channel_id: int,
|
||||||
|
guild: Guild,
|
||||||
|
limit: Optional[int],
|
||||||
|
joined: bool,
|
||||||
|
private: bool,
|
||||||
|
before: Optional[Union[Snowflake, datetime.datetime]] = None,
|
||||||
|
):
|
||||||
|
self.channel_id = channel_id
|
||||||
|
self.guild = guild
|
||||||
|
self.limit = limit
|
||||||
|
self.joined = joined
|
||||||
|
self.private = private
|
||||||
|
self.http = guild._state.http
|
||||||
|
|
||||||
|
if joined and not private:
|
||||||
|
raise ValueError('Cannot iterate over joined public archived threads')
|
||||||
|
|
||||||
|
self.before: Optional[str]
|
||||||
|
if before is None:
|
||||||
|
self.before = None
|
||||||
|
elif isinstance(before, datetime.datetime):
|
||||||
|
if joined:
|
||||||
|
self.before = str(time_snowflake(before, high=False))
|
||||||
|
else:
|
||||||
|
self.before = before.isoformat()
|
||||||
|
else:
|
||||||
|
if joined:
|
||||||
|
self.before = str(before.id)
|
||||||
|
else:
|
||||||
|
self.before = snowflake_time(before.id).isoformat()
|
||||||
|
|
||||||
|
self.update_before: Callable[[ThreadPayload], str] = self.get_archive_timestamp
|
||||||
|
|
||||||
|
if joined:
|
||||||
|
self.endpoint = self.http.get_joined_private_archived_threads
|
||||||
|
self.update_before = self.get_thread_id
|
||||||
|
elif private:
|
||||||
|
self.endpoint = self.http.get_private_archived_threads
|
||||||
|
else:
|
||||||
|
self.endpoint = self.http.get_archived_threads
|
||||||
|
|
||||||
|
self.queue: asyncio.Queue[Thread] = asyncio.Queue()
|
||||||
|
self.has_more: bool = True
|
||||||
|
|
||||||
|
async def next(self) -> Thread:
|
||||||
|
if self.queue.empty():
|
||||||
|
await self.fill_queue()
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.queue.get_nowait()
|
||||||
|
except asyncio.QueueEmpty:
|
||||||
|
raise NoMoreItems()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_archive_timestamp(data: ThreadPayload) -> str:
|
||||||
|
return data['thread_metadata']['archive_timestamp']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_thread_id(data: ThreadPayload) -> str:
|
||||||
|
return data['id'] # type: ignore
|
||||||
|
|
||||||
|
async def fill_queue(self) -> None:
|
||||||
|
if not self.has_more:
|
||||||
|
raise NoMoreItems()
|
||||||
|
|
||||||
|
limit = 50 if self.limit is None else max(self.limit, 50)
|
||||||
|
data = await self.endpoint(self.channel_id, before=self.before, limit=limit)
|
||||||
|
|
||||||
|
# This stuff is obviously WIP because 'members' is always empty
|
||||||
|
threads: List[ThreadPayload] = data.get('threads', [])
|
||||||
|
for d in reversed(threads):
|
||||||
|
self.queue.put_nowait(self.create_thread(d))
|
||||||
|
|
||||||
|
self.has_more = data.get('has_more', False)
|
||||||
|
if self.limit is not None:
|
||||||
|
self.limit -= len(threads)
|
||||||
|
if self.limit <= 0:
|
||||||
|
self.has_more = False
|
||||||
|
|
||||||
|
if self.has_more:
|
||||||
|
self.before = self.update_before(threads[-1])
|
||||||
|
|
||||||
|
def create_thread(self, data: ThreadPayload) -> Thread:
|
||||||
|
return Thread(guild=self.guild, data=data)
|
||||||
|
@ -46,6 +46,7 @@ from .utils import escape_mentions
|
|||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .mixins import Hashable
|
from .mixins import Hashable
|
||||||
from .sticker import Sticker
|
from .sticker import Sticker
|
||||||
|
from .threads import Thread
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .types.message import (
|
from .types.message import (
|
||||||
@ -58,7 +59,7 @@ if TYPE_CHECKING:
|
|||||||
)
|
)
|
||||||
|
|
||||||
from .types.components import Component as ComponentPayload
|
from .types.components import Component as ComponentPayload
|
||||||
|
from .types.threads import ThreadArchiveDuration
|
||||||
from .types.member import Member as MemberPayload
|
from .types.member import Member as MemberPayload
|
||||||
from .types.user import User as UserPayload
|
from .types.user import User as UserPayload
|
||||||
from .types.embed import Embed as EmbedPayload
|
from .types.embed import Embed as EmbedPayload
|
||||||
@ -79,7 +80,6 @@ __all__ = (
|
|||||||
'DeletedReferencedMessage',
|
'DeletedReferencedMessage',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def convert_emoji_reaction(emoji):
|
def convert_emoji_reaction(emoji):
|
||||||
if isinstance(emoji, Reaction):
|
if isinstance(emoji, Reaction):
|
||||||
emoji = emoji.emoji
|
emoji = emoji.emoji
|
||||||
@ -1429,6 +1429,45 @@ class Message(Hashable):
|
|||||||
"""
|
"""
|
||||||
await self._state.http.clear_reactions(self.channel.id, self.id)
|
await self._state.http.clear_reactions(self.channel.id, self.id)
|
||||||
|
|
||||||
|
async def start_public_thread(self, *, name: str, auto_archive_duration: ThreadArchiveDuration = 1440) -> Thread:
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Starts a public thread from this message.
|
||||||
|
|
||||||
|
You must have :attr:`~discord.Permissions.send_messages` and
|
||||||
|
:attr:`~discord.Permissions.use_threads` in order to start a thread.
|
||||||
|
|
||||||
|
The channel this message belongs in must be a :class:`TextChannel`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
Defaults to ``1440`` or 24 hours.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to start a thread.
|
||||||
|
HTTPException
|
||||||
|
Starting the thread failed.
|
||||||
|
InvalidArgument
|
||||||
|
This message does not have guild info attached.
|
||||||
|
"""
|
||||||
|
if self.guild is None:
|
||||||
|
raise InvalidArgument('This message does not have guild info attached.')
|
||||||
|
|
||||||
|
data = await self._state.http.start_public_thread(
|
||||||
|
self.channel.id,
|
||||||
|
self.id,
|
||||||
|
name=name,
|
||||||
|
auto_archive_duration=auto_archive_duration,
|
||||||
|
type=ChannelType.public_thread.value,
|
||||||
|
)
|
||||||
|
return Thread(guild=self.guild, data=data) # type: ignore
|
||||||
|
|
||||||
async def reply(self, content: Optional[str] = None, **kwargs) -> Message:
|
async def reply(self, content: Optional[str] = None, **kwargs) -> Message:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
|
@ -40,10 +40,13 @@ if TYPE_CHECKING:
|
|||||||
Thread as ThreadPayload,
|
Thread as ThreadPayload,
|
||||||
ThreadMember as ThreadMemberPayload,
|
ThreadMember as ThreadMemberPayload,
|
||||||
ThreadMetadata,
|
ThreadMetadata,
|
||||||
|
ThreadArchiveDuration,
|
||||||
)
|
)
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .channel import TextChannel
|
from .channel import TextChannel
|
||||||
from .member import Member
|
from .member import Member
|
||||||
|
from .message import Message
|
||||||
|
from .abc import Snowflake
|
||||||
|
|
||||||
|
|
||||||
class Thread(Messageable, Hashable):
|
class Thread(Messageable, Hashable):
|
||||||
@ -171,7 +174,7 @@ class Thread(Messageable, Hashable):
|
|||||||
return self.guild.get_member(self.owner_id)
|
return self.guild.get_member(self.owner_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_message(self):
|
def last_message(self) -> Optional[Message]:
|
||||||
"""Fetches the last message from this channel in cache.
|
"""Fetches the last message from this channel in cache.
|
||||||
|
|
||||||
The message might not be valid or point to an existing message.
|
The message might not be valid or point to an existing message.
|
||||||
@ -191,6 +194,140 @@ class Thread(Messageable, Hashable):
|
|||||||
"""
|
"""
|
||||||
return self._state._get_message(self.last_message_id) if self.last_message_id else None
|
return self._state._get_message(self.last_message_id) if self.last_message_id else None
|
||||||
|
|
||||||
|
def is_private(self) -> bool:
|
||||||
|
""":class:`bool`: Whether the thread is a private thread."""
|
||||||
|
return self.type is ChannelType.private_thread
|
||||||
|
|
||||||
|
async def edit(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
name: str = ...,
|
||||||
|
archived: bool = ...,
|
||||||
|
auto_archive_duration: ThreadArchiveDuration = ...,
|
||||||
|
):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Edits the thread.
|
||||||
|
|
||||||
|
To unarchive a thread :attr:`~.Permissions.send_messages` is required. Otherwise,
|
||||||
|
:attr:`~.Permissions.manage_messages` is required to edit the thread.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
name: :class:`str`
|
||||||
|
The new name of the thread.
|
||||||
|
archived: :class:`bool`
|
||||||
|
Whether to archive the thread or not.
|
||||||
|
auto_archive_duration: :class:`int`
|
||||||
|
The new duration to auto archive threads for inactivity.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to edit the thread.
|
||||||
|
HTTPException
|
||||||
|
Editing the thread failed.
|
||||||
|
"""
|
||||||
|
payload = {}
|
||||||
|
if name is not ...:
|
||||||
|
payload['name'] = str(name)
|
||||||
|
if archived is not ...:
|
||||||
|
payload['archived'] = archived
|
||||||
|
if auto_archive_duration is not ...:
|
||||||
|
payload['auto_archive_duration'] = auto_archive_duration
|
||||||
|
await self._state.http.edit_channel(self.id, **payload)
|
||||||
|
|
||||||
|
async def join(self):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Joins this thread.
|
||||||
|
|
||||||
|
You must have :attr:`~Permissions.send_messages` and :attr:`~Permissions.use_threads`
|
||||||
|
to join a public thread. If the thread is private then :attr:`~Permissions.send_messages`
|
||||||
|
and either :attr:`~Permissions.use_private_threads` or :attr:`~Permissions.manage_messages`
|
||||||
|
is required to join the thread.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to join the thread.
|
||||||
|
HTTPException
|
||||||
|
Joining the thread failed.
|
||||||
|
"""
|
||||||
|
await self._state.http.join_thread(self.id)
|
||||||
|
|
||||||
|
async def leave(self):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Leaves this thread.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
HTTPException
|
||||||
|
Leaving the thread failed.
|
||||||
|
"""
|
||||||
|
await self._state.http.leave_thread(self.id)
|
||||||
|
|
||||||
|
async def add_user(self, user: Snowflake):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Adds a user to this thread.
|
||||||
|
|
||||||
|
You must have :attr:`~Permissions.send_messages` and :attr:`~Permissions.use_threads`
|
||||||
|
to add a user to a public thread. If the thread is private then :attr:`~Permissions.send_messages`
|
||||||
|
and either :attr:`~Permissions.use_private_threads` or :attr:`~Permissions.manage_messages`
|
||||||
|
is required to add a user to the thread.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
user: :class:`abc.Snowflake`
|
||||||
|
The user to add to the thread.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to add the user to the thread.
|
||||||
|
HTTPException
|
||||||
|
Adding the user to the thread failed.
|
||||||
|
"""
|
||||||
|
await self._state.http.add_user_to_thread(self.id, user.id)
|
||||||
|
|
||||||
|
async def remove_user(self, user: Snowflake):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Removes a user from this thread.
|
||||||
|
|
||||||
|
You must have :attr:`~Permissions.manage_messages` or be the creator of the thread to remove a user.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
user: :class:`abc.Snowflake`
|
||||||
|
The user to add to the thread.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to remove the user from the thread.
|
||||||
|
HTTPException
|
||||||
|
Removing the user from the thread failed.
|
||||||
|
"""
|
||||||
|
await self._state.http.remove_user_from_thread(self.id, user.id)
|
||||||
|
|
||||||
|
async def delete(self):
|
||||||
|
"""|coro|
|
||||||
|
|
||||||
|
Deletes this thread.
|
||||||
|
|
||||||
|
You must have :attr:`~Permissions.manage_channels` to delete threads.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
-------
|
||||||
|
Forbidden
|
||||||
|
You do not have permissions to delete this thread.
|
||||||
|
HTTPException
|
||||||
|
Deleting the thread failed.
|
||||||
|
"""
|
||||||
|
await self._state.http.delete_channel(self.id)
|
||||||
|
|
||||||
class ThreadMember(Hashable):
|
class ThreadMember(Hashable):
|
||||||
"""Represents a Discord thread member.
|
"""Represents a Discord thread member.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user