aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--discord/abc.py123
-rw-r--r--discord/invite.py27
-rw-r--r--discord/message.py87
3 files changed, 221 insertions, 16 deletions
diff --git a/discord/abc.py b/discord/abc.py
index 6963e28a..01cef255 100644
--- a/discord/abc.py
+++ b/discord/abc.py
@@ -32,6 +32,7 @@ import asyncio
from .message import Message
from .iterators import LogsFromIterator
from .context_managers import Typing
+from .errors import ClientException, NoMoreMessages
class Snowflake(metaclass=abc.ABCMeta):
__slots__ = ()
@@ -288,6 +289,40 @@ class MessageChannel(metaclass=abc.ABCMeta):
return Message(channel=self, state=self._state, 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_id, guild_id = self._get_destination()
+
+ yield from self._state.http.delete_messages(channel_id, message_ids, guild_id)
+
+ @asyncio.coroutine
def pins(self):
"""|coro|
@@ -367,3 +402,91 @@ class MessageChannel(metaclass=abc.ABCMeta):
counter += 1
"""
return LogsFromIterator(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. The
+ :attr:`Permissions.read_message_history` permission is also needed to
+ retrieve message history.
+
+ Usable only by bot accounts.
+
+ 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 or
+ you're not using a bot account.
+ 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
+
+ while True:
+ try:
+ msg = yield from iterator.get()
+ except NoMoreMessages:
+ # no more messages to poll
+ if count >= 2:
+ # more than 2 messages -> bulk delete
+ to_delete = ret[-count:]
+ yield from self.delete_messages(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 self.delete_messages(to_delete)
+ count = 0
+ yield from asyncio.sleep(1)
+
+ if check(msg):
+ count += 1
+ ret.append(msg)
diff --git a/discord/invite.py b/discord/invite.py
index 4e19b2d6..91136ac2 100644
--- a/discord/invite.py
+++ b/discord/invite.py
@@ -50,34 +50,32 @@ class Invite(Hashable):
Attributes
-----------
- max_age : int
+ max_age: int
How long the before the invite expires in seconds. A value of 0 indicates that it doesn't expire.
- code : str
+ code: str
The URL fragment used for the invite. :attr:`xkcd` is also a possible fragment.
- server : :class:`Server`
+ server: :class:`Server`
The server the invite is for.
- revoked : bool
+ revoked: bool
Indicates if the invite has been revoked.
- created_at : `datetime.datetime`
+ created_at: `datetime.datetime`
A datetime object denoting the time the invite was created.
- temporary : bool
+ temporary: bool
Indicates that the invite grants temporary membership.
If True, members who joined via this invite will be kicked upon disconnect.
- uses : int
+ uses: int
How many times the invite has been used.
- max_uses : int
+ max_uses: int
How many times the invite can be used.
- xkcd : str
- The URL fragment used for the invite if it is human readable.
- inviter : :class:`User`
+ inviter: :class:`User`
The user who created the invite.
- channel : :class:`Channel`
+ channel: :class:`Channel`
The channel the invite is for.
"""
__slots__ = ( 'max_age', 'code', 'server', 'revoked', 'created_at', 'uses',
- 'temporary', 'max_uses', 'xkcd', 'inviter', 'channel', '_state' )
+ 'temporary', 'max_uses', 'inviter', 'channel', '_state' )
def __init__(self, *, state, data):
self._state = state
@@ -89,7 +87,6 @@ class Invite(Hashable):
self.temporary = data.get('temporary')
self.uses = data.get('uses')
self.max_uses = data.get('max_uses')
- self.xkcd = data.get('xkcdpass')
inviter_data = data.get('inviter')
self.inviter = None if inviter_data is None else User(state=state, data=data)
@@ -101,7 +98,7 @@ class Invite(Hashable):
@property
def id(self):
"""Returns the proper code portion of the invite."""
- return self.xkcd if self.xkcd else self.code
+ return self.code
@property
def url(self):
diff --git a/discord/message.py b/discord/message.py
index c2caaf9d..0d31d6f6 100644
--- a/discord/message.py
+++ b/discord/message.py
@@ -24,12 +24,14 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
+import asyncio
+import re
+
from .user import User
from .reaction import Reaction
from . import utils, abc
from .object import Object
from .calls import CallMessage
-import re
from .enums import MessageType, try_enum
class Message:
@@ -343,3 +345,86 @@ class Message:
return 'You missed a call from {0.author.name}'.format(self)
else:
return '{0.author.name} started a call \N{EM DASH} Join the call.'.format(self)
+
+ @asyncio.coroutine
+ def delete(self):
+ """|coro|
+
+ Deletes the message.
+
+ Your own messages could be deleted without any proper permissions. However to
+ delete other people's messages, you need the :attr:`Permissions.manage_messages`
+ permission.
+
+ Raises
+ ------
+ Forbidden
+ You do not have proper permissions to delete the message.
+ HTTPException
+ Deleting the message failed.
+ """
+ yield from self._state.http.delete_message(self.channel.id, self.id, getattr(self.server, 'id', None))
+
+ @asyncio.coroutine
+ def edit(self, *, content: str):
+ """|coro|
+
+ Edits the message.
+
+ The content must be able to be transformed into a string via ``str(content)``.
+
+ Parameters
+ -----------
+ content: str
+ The new content to replace the message with.
+
+ Raises
+ -------
+ HTTPException
+ Editing the message failed.
+ """
+
+ guild_id = getattr(self.server, 'id', None)
+ data = yield from self._state.http.edit_message(self.id, self.channel.id, str(content), guild_id=guild_id)
+ self._update(channel=self.channel, data=data)
+
+ @asyncio.coroutine
+ def pin(self):
+ """|coro|
+
+ Pins the message. You must have :attr:`Permissions.manage_messages`
+ permissions to do this in a non-private channel context.
+
+ Raises
+ -------
+ Forbidden
+ You do not have permissions to pin the message.
+ NotFound
+ The message or channel was not found or deleted.
+ HTTPException
+ Pinning the message failed, probably due to the channel
+ having more than 50 pinned messages.
+ """
+
+ yield from self._state.http.pin_message(self.channel.id, self.id)
+ self.pinned = True
+
+ @asyncio.coroutine
+ def unpin(self):
+ """|coro|
+
+ Unpins the message. You must have :attr:`Permissions.manage_messages`
+ permissions to do this in a non-private channel context.
+
+ Raises
+ -------
+ Forbidden
+ You do not have permissions to unpin the message.
+ NotFound
+ The message or channel was not found or deleted.
+ HTTPException
+ Unpinning the message failed.
+ """
+
+ yield from self._state.http.unpin_message(self.channel.id, self.id)
+ self.pinned = False