diff options
| author | Rapptz <[email protected]> | 2016-10-17 01:10:22 -0400 |
|---|---|---|
| committer | Rapptz <[email protected]> | 2017-01-03 09:51:52 -0500 |
| commit | 53ab2631252bf0977446d762f07b3821edb151ee (patch) | |
| tree | abb8a2e7a966aadb22df8a3ca2220b646eae3765 /discord/abc.py | |
| parent | [commands] Bot skip check now works with the new __eq__ changes. (diff) | |
| download | discord.py-53ab2631252bf0977446d762f07b3821edb151ee.tar.xz discord.py-53ab2631252bf0977446d762f07b3821edb151ee.zip | |
Split channel types.
This splits them into the following:
* DMChannel
* GroupChannel
* VoiceChannel
* TextChannel
This also makes the channels "stateful".
Diffstat (limited to 'discord/abc.py')
| -rw-r--r-- | discord/abc.py | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/discord/abc.py b/discord/abc.py index 2bda266e..0b42b0e8 100644 --- a/discord/abc.py +++ b/discord/abc.py @@ -25,6 +25,12 @@ DEALINGS IN THE SOFTWARE. """ import abc +import io +import os +import asyncio + +from .message import Message +from .iterators import LogsFromIterator class Snowflake(metaclass=abc.ABCMeta): __slots__ = () @@ -75,3 +81,274 @@ class User(metaclass=abc.ABCMeta): return NotImplemented return True return NotImplemented + +class GuildChannel(metaclass=abc.ABCMeta): + __slots__ = () + + @property + @abc.abstractmethod + def mention(self): + raise NotImplementedError + + @abc.abstractmethod + def overwrites_for(self, obj): + raise NotImplementedError + + @abc.abstractmethod + def permissions_for(self, user): + raise NotImplementedError + + @classmethod + def __subclasshook__(cls, C): + if cls is GuildChannel: + if Snowflake.__subclasshook__(C) is NotImplemented: + return NotImplemented + + mro = C.__mro__ + for attr in ('name', 'server', 'overwrites_for', 'permissions_for', 'mention'): + for base in mro: + if attr in base.__dict__: + break + else: + return NotImplemented + return True + return NotImplemented + +class PrivateChannel(metaclass=abc.ABCMeta): + __slots__ = () + + @classmethod + def __subclasshook__(cls, C): + if cls is PrivateChannel: + if Snowflake.__subclasshook__(C) is NotImplemented: + return NotImplemented + + mro = C.__mro__ + for base in mro: + if 'me' in base.__dict__: + return True + return NotImplemented + return NotImplemented + +class MessageChannel(metaclass=abc.ABCMeta): + __slots__ = () + + @abc.abstractmethod + def _get_destination(self): + raise NotImplementedError + + @asyncio.coroutine + def send_message(self, content, *, tts=False): + """|coro| + + Sends a message to the channel with the content given. + + The content must be a type that can convert to a string through ``str(content)``. + + Parameters + ------------ + content + The content of the message to send. + tts: bool + Indicates if the message should be sent using text-to-speech. + + Raises + -------- + HTTPException + Sending the message failed. + Forbidden + You do not have the proper permissions to send the message. + + Returns + --------- + :class:`Message` + The message that was sent. + """ + + channel_id, guild_id = self._get_destination() + content = str(content) + data = yield from self._state.http.send_message(channel_id, content, guild_id=guild_id, tts=tts) + return Message(channel=self, state=self._state, data=data) + + @asyncio.coroutine + def send_typing(self): + """|coro| + + Send a *typing* status to the channel. + + *Typing* status will go away after 10 seconds, or after a message is sent. + """ + + channel_id, _ = self._get_destination() + yield from self._state.http.send_typing(channel_id) + + @asyncio.coroutine + def upload(self, fp, *, filename=None, content=None, tts=False): + """|coro| + + Sends a message to the channel with the file given. + + The ``fp`` parameter should be either a string denoting the location for a + file or a *file-like object*. The *file-like object* passed is **not closed** + at the end of execution. You are responsible for closing it yourself. + + .. note:: + + If the file-like object passed is opened via ``open`` then the modes + 'rb' should be used. + + The ``filename`` parameter is the filename of the file. + If this is not given then it defaults to ``fp.name`` or if ``fp`` is a string + then the ``filename`` will default to the string given. You can overwrite + this value by passing this in. + + Parameters + ------------ + fp + The *file-like object* or file path to send. + filename: str + The filename of the file. Defaults to ``fp.name`` if it's available. + content: str + The content of the message to send along with the file. This is + forced into a string by a ``str(content)`` call. + tts: bool + If the content of the message should be sent with TTS enabled. + + Raises + ------- + HTTPException + Sending the file failed. + + Returns + -------- + :class:`Message` + The message sent. + """ + + channel_id, guild_id = self._get_destination() + + try: + with open(fp, 'rb') as f: + buffer = io.BytesIO(f.read()) + if filename is None: + _, filename = os.path.split(fp) + except TypeError: + buffer = fp + + state = self._state + data = yield from state.http.send_file(channel_id, buffer, guild_id=guild_id, + filename=filename, content=content, tts=tts) + + return Message(channel=self, state=state, data=data) + + @asyncio.coroutine + def get_message(self, id): + """|coro| + + Retrieves a single :class:`Message` from a channel. + + This can only be used by bot accounts. + + Parameters + ------------ + id: int + The message ID to look for. + + Returns + -------- + :class:`Message` + The message asked for. + + Raises + -------- + NotFound + The specified message was not found. + Forbidden + You do not have the permissions required to get a message. + HTTPException + Retrieving the message failed. + """ + + data = yield from self._state.http.get_message(self.id, id) + return Message(channel=self, state=self._state, data=data) + + @asyncio.coroutine + def pins(self): + """|coro| + + Returns a list of :class:`Message` that are currently pinned. + + Raises + ------- + HTTPException + Retrieving the pinned messages failed. + """ + + state = self._state + data = yield from state.http.pins_from(self.id) + return [Message(channel=self, state=state, data=m) for m in data] + + def history(self, *, limit=100, before=None, after=None, around=None, reverse=None): + """Return an async iterator that enables receiving the channel's message history. + + You must have Read Message History permissions to use this. + + All parameters are optional. + + Parameters + ----------- + limit: int + The number of messages to retrieve. + before: :class:`Message` or `datetime` + Retrieve messages before this date or message. + If a date is provided it must be a timezone-naive datetime representing UTC time. + after: :class:`Message` or `datetime` + Retrieve messages after this date or message. + If a date is provided it must be a timezone-naive datetime representing UTC time. + around: :class:`Message` or `datetime` + Retrieve messages around this date or message. + If a date is provided it must be a timezone-naive datetime representing UTC time. + When using this argument, the maximum limit is 101. Note that if the limit is an + even number then this will return at most limit + 1 messages. + reverse: bool + If set to true, return messages in oldest->newest order. If unspecified, + this defaults to ``False`` for most cases. However if passing in a + ``after`` parameter then this is set to ``True``. This avoids getting messages + out of order in the ``after`` case. + + Raises + ------ + Forbidden + You do not have permissions to get channel message history. + HTTPException + The request to get message history failed. + + Yields + ------- + :class:`Message` + The message with the message data parsed. + + Examples + --------- + + Usage :: + + counter = 0 + async for message in channel.history(limit=200): + if message.author == client.user: + counter += 1 + + Python 3.4 Usage :: + + count = 0 + iterator = channel.history(limit=200) + while True: + try: + message = yield from iterator.get() + except discord.NoMoreMessages: + break + else: + if message.author == client.user: + counter += 1 + """ + return LogsFromIterator(self, limit=limit, before=before, after=after, around=around, reverse=reverse) |