|
|
@ -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. |
|
|
|
|
|
|
|