Move purge and delete_messages from Messageable.
This is a breaking change. Move these two to TextChannel since the other things that implement Messageable cannot reliably do bulk delete actions in their respective channels.
This commit is contained in:
		
							
								
								
									
										145
									
								
								discord/abc.py
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								discord/abc.py
									
									
									
									
									
								
							| @@ -27,14 +27,13 @@ DEALINGS IN THE SOFTWARE. | |||||||
| import abc | import abc | ||||||
| import io | import io | ||||||
| import os | import os | ||||||
| import time |  | ||||||
| import asyncio | import asyncio | ||||||
|  |  | ||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
|  |  | ||||||
| from .iterators import HistoryIterator | from .iterators import HistoryIterator | ||||||
| from .context_managers import Typing | from .context_managers import Typing | ||||||
| from .errors import ClientException, NoMoreItems, InvalidArgument | from .errors import InvalidArgument | ||||||
| from .permissions import PermissionOverwrite, Permissions | from .permissions import PermissionOverwrite, Permissions | ||||||
| from .role import Role | from .role import Role | ||||||
| from .invite import Invite | from .invite import Invite | ||||||
| @@ -46,11 +45,6 @@ class _Undefined: | |||||||
|  |  | ||||||
| _undefined = _Undefined() | _undefined = _Undefined() | ||||||
|  |  | ||||||
| @asyncio.coroutine |  | ||||||
| def _single_delete_strategy(messages): |  | ||||||
|     for m in messages: |  | ||||||
|         yield from m.delete() |  | ||||||
|  |  | ||||||
| class Snowflake(metaclass=abc.ABCMeta): | class Snowflake(metaclass=abc.ABCMeta): | ||||||
|     __slots__ = () |     __slots__ = () | ||||||
|  |  | ||||||
| @@ -694,40 +688,6 @@ class Messageable(metaclass=abc.ABCMeta): | |||||||
|         data = yield from self._state.http.get_message(channel.id, id) |         data = yield from self._state.http.get_message(channel.id, id) | ||||||
|         return self._state.create_message(channel=channel, data=data) |         return self._state.create_message(channel=channel, data=data) | ||||||
|  |  | ||||||
|     @asyncio.coroutine |  | ||||||
|     def delete_messages(self, messages): |  | ||||||
|         """|coro| |  | ||||||
|  |  | ||||||
|         Deletes a list of messages. This is similar to :meth:`Message.delete` |  | ||||||
|         except it bulk deletes multiple messages. |  | ||||||
|  |  | ||||||
|         Usable only by bot accounts. |  | ||||||
|  |  | ||||||
|         Parameters |  | ||||||
|         ----------- |  | ||||||
|         messages : iterable of :class:`Message` |  | ||||||
|             An iterable of messages denoting which ones to bulk delete. |  | ||||||
|  |  | ||||||
|         Raises |  | ||||||
|         ------ |  | ||||||
|         ClientException |  | ||||||
|             The number of messages to delete is less than 2 or more than 100. |  | ||||||
|         Forbidden |  | ||||||
|             You do not have proper permissions to delete the messages or |  | ||||||
|             you're not using a bot account. |  | ||||||
|         HTTPException |  | ||||||
|             Deleting the messages failed. |  | ||||||
|         """ |  | ||||||
|  |  | ||||||
|         messages = list(messages) |  | ||||||
|         if len(messages) > 100 or len(messages) < 2: |  | ||||||
|             raise ClientException('Can only delete messages in the range of [2, 100]') |  | ||||||
|  |  | ||||||
|         message_ids = [m.id for m in messages] |  | ||||||
|         channel = yield from self._get_channel() |  | ||||||
|  |  | ||||||
|         yield from self._state.http.delete_messages(channel.id, message_ids) |  | ||||||
|  |  | ||||||
|     @asyncio.coroutine |     @asyncio.coroutine | ||||||
|     def pins(self): |     def pins(self): | ||||||
|         """|coro| |         """|coro| | ||||||
| @@ -816,106 +776,3 @@ class Messageable(metaclass=abc.ABCMeta): | |||||||
|                         counter += 1 |                         counter += 1 | ||||||
|         """ |         """ | ||||||
|         return HistoryIterator(self, limit=limit, before=before, after=after, around=around, reverse=reverse) |         return HistoryIterator(self, limit=limit, before=before, after=after, around=around, reverse=reverse) | ||||||
|  |  | ||||||
|     @asyncio.coroutine |  | ||||||
|     def purge(self, *, limit=100, check=None, before=None, after=None, around=None): |  | ||||||
|         """|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 :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. |  | ||||||
|  |  | ||||||
|         Internally, this employs a different number of strategies depending |  | ||||||
|         on the conditions met such as if a bulk delete is possible or if |  | ||||||
|         the account is a user bot or not. |  | ||||||
|  |  | ||||||
|         Parameters |  | ||||||
|         ----------- |  | ||||||
|         limit: int |  | ||||||
|             The number of messages to search through. This is not the number |  | ||||||
|             of messages that will be deleted, though it can be. |  | ||||||
|         check: predicate |  | ||||||
|             The function used to check if a message should be deleted. |  | ||||||
|             It must take a :class:`Message` as its sole parameter. |  | ||||||
|         before |  | ||||||
|             Same as ``before`` in :meth:`history`. |  | ||||||
|         after |  | ||||||
|             Same as ``after`` in :meth:`history`. |  | ||||||
|         around |  | ||||||
|             Same as ``around`` in :meth:`history`. |  | ||||||
|  |  | ||||||
|         Raises |  | ||||||
|         ------- |  | ||||||
|         Forbidden |  | ||||||
|             You do not have proper permissions to do the actions required. |  | ||||||
|         HTTPException |  | ||||||
|             Purging the messages failed. |  | ||||||
|  |  | ||||||
|         Examples |  | ||||||
|         --------- |  | ||||||
|  |  | ||||||
|         Deleting bot's messages :: |  | ||||||
|  |  | ||||||
|             def is_me(m): |  | ||||||
|                 return m.author == client.user |  | ||||||
|  |  | ||||||
|             deleted = await channel.purge(limit=100, check=is_me) |  | ||||||
|             await channel.send_message('Deleted {} message(s)'.format(len(deleted))) |  | ||||||
|  |  | ||||||
|         Returns |  | ||||||
|         -------- |  | ||||||
|         list |  | ||||||
|             The list of messages that were deleted. |  | ||||||
|         """ |  | ||||||
|  |  | ||||||
|         if check is None: |  | ||||||
|             check = lambda m: True |  | ||||||
|  |  | ||||||
|         iterator = self.history(limit=limit, before=before, after=after, around=around) |  | ||||||
|         ret = [] |  | ||||||
|         count = 0 |  | ||||||
|  |  | ||||||
|         minimum_time = int((time.time() - 14 * 24 * 60 * 60) * 1000.0 - 1420070400000) << 22 |  | ||||||
|         strategy = self.delete_messages if self._state.is_bot else _single_delete_strategy |  | ||||||
|  |  | ||||||
|         while True: |  | ||||||
|             try: |  | ||||||
|                 msg = yield from iterator.get() |  | ||||||
|             except NoMoreItems: |  | ||||||
|                 # no more messages to poll |  | ||||||
|                 if count >= 2: |  | ||||||
|                     # more than 2 messages -> bulk delete |  | ||||||
|                     to_delete = ret[-count:] |  | ||||||
|                     yield from strategy(to_delete) |  | ||||||
|                 elif count == 1: |  | ||||||
|                     # delete a single message |  | ||||||
|                     yield from ret[-1].delete() |  | ||||||
|  |  | ||||||
|                 return ret |  | ||||||
|             else: |  | ||||||
|                 if count == 100: |  | ||||||
|                     # we've reached a full 'queue' |  | ||||||
|                     to_delete = ret[-100:] |  | ||||||
|                     yield from strategy(to_delete) |  | ||||||
|                     count = 0 |  | ||||||
|                     yield from asyncio.sleep(1) |  | ||||||
|  |  | ||||||
|                 if check(msg): |  | ||||||
|                     if msg.id < minimum_time: |  | ||||||
|                         # older than 14 days old |  | ||||||
|                         if count == 1: |  | ||||||
|                             yield from ret[-1].delete() |  | ||||||
|                         elif count >= 2: |  | ||||||
|                             to_delete = ret[-count:] |  | ||||||
|                             yield from strategy(to_delete) |  | ||||||
|  |  | ||||||
|                         count = 0 |  | ||||||
|                         strategy = _single_delete_strategy |  | ||||||
|  |  | ||||||
|                     count += 1 |  | ||||||
|                     ret.append(msg) |  | ||||||
|   | |||||||
| @@ -30,14 +30,21 @@ from .role import Role | |||||||
| from .user import User | from .user import User | ||||||
| from .member import Member | from .member import Member | ||||||
| from . import utils | from . import utils | ||||||
|  | from .errors import ClientException, NoMoreItems | ||||||
|  |  | ||||||
| import discord.abc | import discord.abc | ||||||
|  |  | ||||||
| import copy | import copy | ||||||
|  | import time | ||||||
| import asyncio | import asyncio | ||||||
|  |  | ||||||
| __all__ = ('TextChannel', 'VoiceChannel', 'DMChannel', 'GroupChannel', '_channel_factory') | __all__ = ('TextChannel', 'VoiceChannel', 'DMChannel', 'GroupChannel', '_channel_factory') | ||||||
|  |  | ||||||
|  | @asyncio.coroutine | ||||||
|  | def _single_delete_strategy(messages): | ||||||
|  |     for m in messages: | ||||||
|  |         yield from m.delete() | ||||||
|  |  | ||||||
| class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable): | class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable): | ||||||
|     """Represents a Discord guild text channel. |     """Represents a Discord guild text channel. | ||||||
|  |  | ||||||
| @@ -141,6 +148,143 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable): | |||||||
|             data = yield from self._state.http.edit_channel(self.id, **options) |             data = yield from self._state.http.edit_channel(self.id, **options) | ||||||
|             self._update(self.guild, data) |             self._update(self.guild, data) | ||||||
|  |  | ||||||
|  |     @asyncio.coroutine | ||||||
|  |     def delete_messages(self, messages): | ||||||
|  |         """|coro| | ||||||
|  |  | ||||||
|  |         Deletes a list of messages. This is similar to :meth:`Message.delete` | ||||||
|  |         except it bulk deletes multiple messages. | ||||||
|  |  | ||||||
|  |         Usable only by bot accounts. | ||||||
|  |  | ||||||
|  |         Parameters | ||||||
|  |         ----------- | ||||||
|  |         messages : iterable of :class:`Message` | ||||||
|  |             An iterable of messages denoting which ones to bulk delete. | ||||||
|  |  | ||||||
|  |         Raises | ||||||
|  |         ------ | ||||||
|  |         ClientException | ||||||
|  |             The number of messages to delete is less than 2 or more than 100. | ||||||
|  |         Forbidden | ||||||
|  |             You do not have proper permissions to delete the messages or | ||||||
|  |             you're not using a bot account. | ||||||
|  |         HTTPException | ||||||
|  |             Deleting the messages failed. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         messages = list(messages) | ||||||
|  |         if len(messages) > 100 or len(messages) < 2: | ||||||
|  |             raise ClientException('Can only delete messages in the range of [2, 100]') | ||||||
|  |  | ||||||
|  |         message_ids = [m.id for m in messages] | ||||||
|  |         channel = yield from self._get_channel() | ||||||
|  |  | ||||||
|  |         yield from self._state.http.delete_messages(channel.id, message_ids) | ||||||
|  |  | ||||||
|  |     @asyncio.coroutine | ||||||
|  |     def purge(self, *, limit=100, check=None, before=None, after=None, around=None): | ||||||
|  |         """|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 :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. | ||||||
|  |  | ||||||
|  |         Internally, this employs a different number of strategies depending | ||||||
|  |         on the conditions met such as if a bulk delete is possible or if | ||||||
|  |         the account is a user bot or not. | ||||||
|  |  | ||||||
|  |         Parameters | ||||||
|  |         ----------- | ||||||
|  |         limit: int | ||||||
|  |             The number of messages to search through. This is not the number | ||||||
|  |             of messages that will be deleted, though it can be. | ||||||
|  |         check: predicate | ||||||
|  |             The function used to check if a message should be deleted. | ||||||
|  |             It must take a :class:`Message` as its sole parameter. | ||||||
|  |         before | ||||||
|  |             Same as ``before`` in :meth:`history`. | ||||||
|  |         after | ||||||
|  |             Same as ``after`` in :meth:`history`. | ||||||
|  |         around | ||||||
|  |             Same as ``around`` in :meth:`history`. | ||||||
|  |  | ||||||
|  |         Raises | ||||||
|  |         ------- | ||||||
|  |         Forbidden | ||||||
|  |             You do not have proper permissions to do the actions required. | ||||||
|  |         HTTPException | ||||||
|  |             Purging the messages failed. | ||||||
|  |  | ||||||
|  |         Examples | ||||||
|  |         --------- | ||||||
|  |  | ||||||
|  |         Deleting bot's messages :: | ||||||
|  |  | ||||||
|  |             def is_me(m): | ||||||
|  |                 return m.author == client.user | ||||||
|  |  | ||||||
|  |             deleted = await channel.purge(limit=100, check=is_me) | ||||||
|  |             await channel.send_message('Deleted {} message(s)'.format(len(deleted))) | ||||||
|  |  | ||||||
|  |         Returns | ||||||
|  |         -------- | ||||||
|  |         list | ||||||
|  |             The list of messages that were deleted. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         if check is None: | ||||||
|  |             check = lambda m: True | ||||||
|  |  | ||||||
|  |         iterator = self.history(limit=limit, before=before, after=after, around=around) | ||||||
|  |         ret = [] | ||||||
|  |         count = 0 | ||||||
|  |  | ||||||
|  |         minimum_time = int((time.time() - 14 * 24 * 60 * 60) * 1000.0 - 1420070400000) << 22 | ||||||
|  |         strategy = self.delete_messages if self._state.is_bot else _single_delete_strategy | ||||||
|  |  | ||||||
|  |         while True: | ||||||
|  |             try: | ||||||
|  |                 msg = yield from iterator.get() | ||||||
|  |             except NoMoreItems: | ||||||
|  |                 # no more messages to poll | ||||||
|  |                 if count >= 2: | ||||||
|  |                     # more than 2 messages -> bulk delete | ||||||
|  |                     to_delete = ret[-count:] | ||||||
|  |                     yield from strategy(to_delete) | ||||||
|  |                 elif count == 1: | ||||||
|  |                     # delete a single message | ||||||
|  |                     yield from ret[-1].delete() | ||||||
|  |  | ||||||
|  |                 return ret | ||||||
|  |             else: | ||||||
|  |                 if count == 100: | ||||||
|  |                     # we've reached a full 'queue' | ||||||
|  |                     to_delete = ret[-100:] | ||||||
|  |                     yield from strategy(to_delete) | ||||||
|  |                     count = 0 | ||||||
|  |                     yield from asyncio.sleep(1) | ||||||
|  |  | ||||||
|  |                 if check(msg): | ||||||
|  |                     if msg.id < minimum_time: | ||||||
|  |                         # older than 14 days old | ||||||
|  |                         if count == 1: | ||||||
|  |                             yield from ret[-1].delete() | ||||||
|  |                         elif count >= 2: | ||||||
|  |                             to_delete = ret[-count:] | ||||||
|  |                             yield from strategy(to_delete) | ||||||
|  |  | ||||||
|  |                         count = 0 | ||||||
|  |                         strategy = _single_delete_strategy | ||||||
|  |  | ||||||
|  |                     count += 1 | ||||||
|  |                     ret.append(msg) | ||||||
|  |  | ||||||
| class VoiceChannel(discord.abc.GuildChannel, Hashable): | class VoiceChannel(discord.abc.GuildChannel, Hashable): | ||||||
|     """Represents a Discord guild voice channel. |     """Represents a Discord guild voice channel. | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user