aboutsummaryrefslogtreecommitdiff
path: root/discord/threads.py
diff options
context:
space:
mode:
Diffstat (limited to 'discord/threads.py')
-rw-r--r--discord/threads.py178
1 files changed, 175 insertions, 3 deletions
diff --git a/discord/threads.py b/discord/threads.py
index cf6d92aa..4e8e1cc2 100644
--- a/discord/threads.py
+++ b/discord/threads.py
@@ -23,12 +23,14 @@ DEALINGS IN THE SOFTWARE.
"""
from __future__ import annotations
-from discord.types.threads import ThreadMember
-from typing import Dict, Optional, TYPE_CHECKING
+from typing import Callable, Dict, Iterable, List, Optional, Sequence, TYPE_CHECKING
+import time
+import asyncio
from .mixins import Hashable
from .abc import Messageable
from .enums import ChannelType, try_enum
+from .errors import ClientException, NoMoreItems
from .utils import MISSING, parse_time, _get_as_snowflake
__all__ = (
@@ -47,7 +49,7 @@ if TYPE_CHECKING:
from .channel import TextChannel
from .member import Member
from .message import Message
- from .abc import Snowflake
+ from .abc import Snowflake, SnowflakeTime
from .state import ConnectionState
@@ -232,6 +234,175 @@ class Thread(Messageable, Hashable):
"""
return self._type is ChannelType.news_thread
+ async def delete_messages(self, messages: Iterable[Snowflake]) -> None:
+ """|coro|
+
+ Deletes a list of messages. This is similar to :meth:`Message.delete`
+ except it bulk deletes multiple messages.
+
+ As a special case, if the number of messages is 0, then nothing
+ is done. If the number of messages is 1 then single message
+ delete is done. If it's more than two, then bulk delete is used.
+
+ You cannot bulk delete more than 100 messages or messages that
+ are older than 14 days old.
+
+ You must have the :attr:`~Permissions.manage_messages` permission to
+ use this.
+
+ Usable only by bot accounts.
+
+ Parameters
+ -----------
+ messages: Iterable[:class:`abc.Snowflake`]
+ An iterable of messages denoting which ones to bulk delete.
+
+ Raises
+ ------
+ ClientException
+ The number of messages to delete was more than 100.
+ Forbidden
+ You do not have proper permissions to delete the messages or
+ you're not using a bot account.
+ NotFound
+ If single delete, then the message was already deleted.
+ HTTPException
+ Deleting the messages failed.
+ """
+ if not isinstance(messages, (list, tuple)):
+ messages = list(messages)
+
+ if len(messages) == 0:
+ return # do nothing
+
+ if len(messages) == 1:
+ message_id = messages[0].id
+ await self._state.http.delete_message(self.id, message_id)
+ return
+
+ if len(messages) > 100:
+ raise ClientException('Can only bulk delete messages up to 100 messages')
+
+ message_ids = [m.id for m in messages]
+ await self._state.http.delete_messages(self.id, message_ids)
+
+ async def purge(
+ self,
+ *,
+ limit: int = 100,
+ check: Callable[[Message], bool] = MISSING,
+ before: Optional[SnowflakeTime] = None,
+ after: Optional[SnowflakeTime] = None,
+ around: Optional[SnowflakeTime] = None,
+ oldest_first: Optional[bool] = False,
+ bulk: bool = True,
+ ) -> List[Message]:
+ """|coro|
+
+ Purges a list of messages that meet the criteria given by the predicate
+ ``check``. If a ``check`` is not provided then all messages are deleted
+ without discrimination.
+
+ You must have the :attr:`~Permissions.manage_messages` permission to
+ delete messages even if they are your own (unless you are a user
+ account). The :attr:`~Permissions.read_message_history` permission is
+ also needed to retrieve message history.
+
+ Examples
+ ---------
+
+ Deleting bot's messages ::
+
+ def is_me(m):
+ return m.author == client.user
+
+ deleted = await thread.purge(limit=100, check=is_me)
+ await thread.send(f'Deleted {len(deleted)} message(s)')
+
+ Parameters
+ -----------
+ limit: Optional[:class:`int`]
+ The number of messages to search through. This is not the number
+ of messages that will be deleted, though it can be.
+ check: Callable[[:class:`Message`], :class:`bool`]
+ The function used to check if a message should be deleted.
+ It must take a :class:`Message` as its sole parameter.
+ before: Optional[Union[:class:`abc.Snowflake`, :class:`datetime.datetime`]]
+ Same as ``before`` in :meth:`history`.
+ after: Optional[Union[:class:`abc.Snowflake`, :class:`datetime.datetime`]]
+ Same as ``after`` in :meth:`history`.
+ around: Optional[Union[:class:`abc.Snowflake`, :class:`datetime.datetime`]]
+ Same as ``around`` in :meth:`history`.
+ oldest_first: Optional[:class:`bool`]
+ Same as ``oldest_first`` in :meth:`history`.
+ bulk: :class:`bool`
+ If ``True``, use bulk delete. Setting this to ``False`` is useful for mass-deleting
+ a bot's own messages without :attr:`Permissions.manage_messages`. When ``True``, will
+ fall back to single delete if messages are older than two weeks.
+
+ Raises
+ -------
+ Forbidden
+ You do not have proper permissions to do the actions required.
+ HTTPException
+ Purging the messages failed.
+
+ Returns
+ --------
+ List[:class:`.Message`]
+ The list of messages that were deleted.
+ """
+
+ if check is MISSING:
+ check = lambda m: True
+
+ iterator = self.history(limit=limit, before=before, after=after, oldest_first=oldest_first, around=around)
+ ret: List[Message] = []
+ count = 0
+
+ minimum_time = int((time.time() - 14 * 24 * 60 * 60) * 1000.0 - 1420070400000) << 22
+
+ async def _single_delete_strategy(messages: Iterable[Message]):
+ for m in messages:
+ await m.delete()
+
+ strategy = self.delete_messages if bulk else _single_delete_strategy
+
+ async for message in iterator:
+ if count == 100:
+ to_delete = ret[-100:]
+ await strategy(to_delete)
+ count = 0
+ await asyncio.sleep(1)
+
+ if not check(message):
+ continue
+
+ if message.id < minimum_time:
+ # older than 14 days old
+ if count == 1:
+ await ret[-1].delete()
+ elif count >= 2:
+ to_delete = ret[-count:]
+ await strategy(to_delete)
+
+ count = 0
+ strategy = _single_delete_strategy
+
+ count += 1
+ ret.append(message)
+
+ # SOme messages remaining to poll
+ if count >= 2:
+ # more than 2 messages -> bulk delete
+ to_delete = ret[-count:]
+ await strategy(to_delete)
+ elif count == 1:
+ # delete a single message
+ await ret[-1].delete()
+
+ return ret
+
async def edit(
self,
*,
@@ -386,6 +557,7 @@ class Thread(Messageable, Hashable):
def _pop_member(self, member_id: int) -> Optional[ThreadMember]:
return self._members.pop(member_id, None)
+
class ThreadMember(Hashable):
"""Represents a Discord thread member.