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:
		| @@ -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,10 +785,16 @@ 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 = {} | ||||||
|  |         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) |         route = Route('GET', '/channels/{channel_id}/threads/archived/private', channel_id=channel_id) | ||||||
|  |  | ||||||
|         params = {} |         params = {} | ||||||
|   | |||||||
| @@ -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. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user