Guest User

client

a guest
Jul 16th, 2017
721
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 109.95 KB | None | 0 0
  1. from . import __version__ as library_version
  2. from .user import User
  3. from .member import Member
  4. from .channel import Channel, PrivateChannel
  5. from .server import Server
  6. from .message import Message
  7. from .invite import Invite
  8. from .object import Object
  9. from .reaction import Reaction
  10. from .role import Role
  11. from .errors import *
  12. from .state import ConnectionState
  13. from .permissions import Permissions, PermissionOverwrite
  14. from . import utils, compat
  15. from .enums import ChannelType, ServerRegion, VerificationLevel, Status
  16. from .voice_client import VoiceClient
  17. from .iterators import LogsFromIterator
  18. from .gateway import *
  19. from .emoji import Emoji
  20. from .http import HTTPClient
  21.  
  22. import asyncio
  23. import aiohttp
  24. import websockets
  25.  
  26. import logging, traceback
  27. import sys, re, io, enum
  28. import tempfile, os, hashlib
  29. import itertools
  30. import datetime
  31. from collections import namedtuple
  32. from os.path import split as path_split
  33.  
  34. PY35 = sys.version_info >= (3, 5)
  35. log = logging.getLogger(__name__)
  36.  
  37. AppInfo = namedtuple('AppInfo', 'id name description icon owner')
  38. WaitedReaction = namedtuple('WaitedReaction', 'reaction user')
  39.  
  40. def app_info_icon_url(self):
  41.     """Retrieves the application's icon_url if it exists. Empty string otherwise."""
  42.     if not self.icon:
  43.         return ''
  44.  
  45.     return 'https://cdn.discordapp.com/app-icons/{0.id}/{0.icon}.jpg'.format(self)
  46.  
  47. AppInfo.icon_url = property(app_info_icon_url)
  48.  
  49. class WaitForType(enum.Enum):
  50.     message  = 0
  51.     reaction = 1
  52.  
  53. ChannelPermissions = namedtuple('ChannelPermissions', 'target overwrite')
  54. ChannelPermissions.__new__.__defaults__ = (PermissionOverwrite(),)
  55.  
  56. class Client:
  57.     """Represents a client connection that connects to Discord.
  58.    This class is used to interact with the Discord WebSocket and API.
  59.  
  60.    A number of options can be passed to the :class:`Client`.
  61.  
  62.    .. _deque: https://docs.python.org/3.4/library/collections.html#collections.deque
  63.    .. _event loop: https://docs.python.org/3/library/asyncio-eventloops.html
  64.    .. _connector: http://aiohttp.readthedocs.org/en/stable/client_reference.html#connectors
  65.    .. _ProxyConnector: http://aiohttp.readthedocs.org/en/stable/client_reference.html#proxyconnector
  66.  
  67.    Parameters
  68.    ----------
  69.    max_messages : Optional[int]
  70.        The maximum number of messages to store in :attr:`messages`.
  71.        This defaults to 5000. Passing in `None` or a value less than 100
  72.        will use the default instead of the passed in value.
  73.    loop : Optional[event loop].
  74.        The `event loop`_ to use for asynchronous operations. Defaults to ``None``,
  75.        in which case the default event loop is used via ``asyncio.get_event_loop()``.
  76.    cache_auth : Optional[bool]
  77.        Indicates if :meth:`login` should cache the authentication tokens. Defaults
  78.        to ``True``. The method in which the cache is written is done by writing to
  79.        disk to a temporary directory.
  80.    connector : aiohttp.BaseConnector
  81.        The `connector`_ to use for connection pooling. Useful for proxies, e.g.
  82.        with a `ProxyConnector`_.
  83.    shard_id : Optional[int]
  84.        Integer starting at 0 and less than shard_count.
  85.    shard_count : Optional[int]
  86.        The total number of shards.
  87.  
  88.    Attributes
  89.    -----------
  90.    user : Optional[:class:`User`]
  91.        Represents the connected client. None if not logged in.
  92.    voice_clients : iterable of :class:`VoiceClient`
  93.        Represents a list of voice connections. To connect to voice use
  94.        :meth:`join_voice_channel`. To query the voice connection state use
  95.        :meth:`is_voice_connected`.
  96.    servers : iterable of :class:`Server`
  97.        The servers that the connected client is a member of.
  98.    private_channels : iterable of :class:`PrivateChannel`
  99.        The private channels that the connected client is participating on.
  100.    messages
  101.        A deque_ of :class:`Message` that the client has received from all
  102.        servers and private messages. The number of messages stored in this
  103.        deque is controlled by the ``max_messages`` parameter.
  104.    email
  105.        The email used to login. This is only set if login is successful,
  106.        otherwise it's None.
  107.    ws
  108.        The websocket gateway the client is currently connected to. Could be None.
  109.    loop
  110.        The `event loop`_ that the client uses for HTTP requests and websocket operations.
  111.  
  112.    """
  113.     def __init__(self, *, loop=None, **options):
  114.         self.ws = None
  115.         self.email = None
  116.         self.loop = asyncio.get_event_loop() if loop is None else loop
  117.         self._listeners = []
  118.         self.cache_auth = options.get('cache_auth', True)
  119.         self.shard_id = options.get('shard_id')
  120.         self.shard_count = options.get('shard_count')
  121.  
  122.         max_messages = options.get('max_messages')
  123.         if max_messages is None or max_messages < 100:
  124.             max_messages = 5000
  125.  
  126.         self.connection = ConnectionState(self.dispatch, self.request_offline_members,
  127.                                           self._syncer, max_messages, loop=self.loop)
  128.  
  129.         connector = options.pop('connector', None)
  130.         self.http = HTTPClient(connector, loop=self.loop)
  131.  
  132.         self._closed = asyncio.Event(loop=self.loop)
  133.         self._is_logged_in = asyncio.Event(loop=self.loop)
  134.         self._is_ready = asyncio.Event(loop=self.loop)
  135.  
  136.         if VoiceClient.warn_nacl:
  137.             VoiceClient.warn_nacl = False
  138.             log.warning("PyNaCl is not installed, voice will NOT be supported")
  139.  
  140.     # internals
  141.  
  142.     @asyncio.coroutine
  143.     def _syncer(self, guilds):
  144.         yield from self.ws.request_sync(guilds)
  145.  
  146.     def _get_cache_filename(self, email):
  147.         filename = hashlib.md5(email.encode('utf-8')).hexdigest()
  148.         return os.path.join(tempfile.gettempdir(), 'discord_py', filename)
  149.  
  150.     def _get_cache_token(self, email, password):
  151.         try:
  152.             log.info('attempting to login via cache')
  153.             cache_file = self._get_cache_filename(email)
  154.             self.email = email
  155.             with open(cache_file, 'r') as f:
  156.                 log.info('login cache file found')
  157.                 return f.read()
  158.  
  159.             # at this point our check failed
  160.             # so we have to login and get the proper token and then
  161.             # redo the cache
  162.         except OSError:
  163.             log.info('a problem occurred while opening login cache')
  164.             return None # file not found et al
  165.  
  166.     def _update_cache(self, email, password):
  167.         try:
  168.             cache_file = self._get_cache_filename(email)
  169.             os.makedirs(os.path.dirname(cache_file), exist_ok=True)
  170.             with os.fdopen(os.open(cache_file, os.O_WRONLY | os.O_CREAT, 0o0600), 'w') as f:
  171.                 log.info('updating login cache')
  172.                 f.write(self.http.token)
  173.         except OSError:
  174.             log.info('a problem occurred while updating the login cache')
  175.             pass
  176.  
  177.     def handle_reaction_add(self, reaction, user):
  178.         removed = []
  179.         for i, (condition, future, event_type) in enumerate(self._listeners):
  180.             if event_type is not WaitForType.reaction:
  181.                 continue
  182.  
  183.             if future.cancelled():
  184.                 removed.append(i)
  185.                 continue
  186.  
  187.             try:
  188.                 result = condition(reaction, user)
  189.             except Exception as e:
  190.                 future.set_exception(e)
  191.                 removed.append(i)
  192.             else:
  193.                 if result:
  194.                     future.set_result(WaitedReaction(reaction, user))
  195.                     removed.append(i)
  196.  
  197.  
  198.         for idx in reversed(removed):
  199.             del self._listeners[idx]
  200.  
  201.     def handle_message(self, message):
  202.         removed = []
  203.         for i, (condition, future, event_type) in enumerate(self._listeners):
  204.             if event_type is not WaitForType.message:
  205.                 continue
  206.  
  207.             if future.cancelled():
  208.                 removed.append(i)
  209.                 continue
  210.  
  211.             try:
  212.                 result = condition(message)
  213.             except Exception as e:
  214.                 future.set_exception(e)
  215.                 removed.append(i)
  216.             else:
  217.                 if result:
  218.                     future.set_result(message)
  219.                     removed.append(i)
  220.  
  221.  
  222.         for idx in reversed(removed):
  223.             del self._listeners[idx]
  224.  
  225.     def handle_ready(self):
  226.         self._is_ready.set()
  227.  
  228.     def _resolve_invite(self, invite):
  229.         if isinstance(invite, Invite) or isinstance(invite, Object):
  230.             return invite.id
  231.         else:
  232.             rx = r'(?:https?\:\/\/)?discord\.gg\/(.+)'
  233.             m = re.match(rx, invite)
  234.             if m:
  235.                 return m.group(1)
  236.         return invite
  237.  
  238.     @asyncio.coroutine
  239.     def _resolve_destination(self, destination):
  240.         if isinstance(destination, Channel):
  241.             return destination.id, destination.server.id
  242.         elif isinstance(destination, PrivateChannel):
  243.             return destination.id, None
  244.         elif isinstance(destination, Server):
  245.             return destination.id, destination.id
  246.         elif isinstance(destination, User):
  247.             found = self.connection._get_private_channel_by_user(destination.id)
  248.             if found is None:
  249.                 # Couldn't find the user, so start a PM with them first.
  250.                 channel = yield from self.start_private_message(destination)
  251.                 return channel.id, None
  252.             else:
  253.                 return found.id, None
  254.         elif isinstance(destination, Object):
  255.             found = self.get_channel(destination.id)
  256.             if found is not None:
  257.                 return (yield from self._resolve_destination(found))
  258.  
  259.             # couldn't find it in cache so YOLO
  260.             return destination.id, destination.id
  261.         else:
  262.             fmt = 'Destination must be Channel, PrivateChannel, User, or Object. Received {0.__class__.__name__}'
  263.             raise InvalidArgument(fmt.format(destination))
  264.  
  265.     def __getattr__(self, name):
  266.         if name in ('user', 'servers', 'private_channels', 'messages', 'voice_clients'):
  267.             return getattr(self.connection, name)
  268.         else:
  269.             msg = "'{}' object has no attribute '{}'"
  270.             raise AttributeError(msg.format(self.__class__, name))
  271.  
  272.     def __setattr__(self, name, value):
  273.         if name in ('user', 'servers', 'private_channels', 'messages', 'voice_clients'):
  274.             return setattr(self.connection, name, value)
  275.         else:
  276.             object.__setattr__(self, name, value)
  277.  
  278.     @asyncio.coroutine
  279.     def _run_event(self, event, *args, **kwargs):
  280.         try:
  281.             yield from getattr(self, event)(*args, **kwargs)
  282.         except asyncio.CancelledError:
  283.             pass
  284.         except Exception:
  285.             try:
  286.                 yield from self.on_error(event, *args, **kwargs)
  287.             except asyncio.CancelledError:
  288.                 pass
  289.  
  290.     def dispatch(self, event, *args, **kwargs):
  291.         log.debug('Dispatching event {}'.format(event))
  292.         method = 'on_' + event
  293.         handler = 'handle_' + event
  294.  
  295.         if hasattr(self, handler):
  296.             getattr(self, handler)(*args, **kwargs)
  297.  
  298.         if hasattr(self, method):
  299.             compat.create_task(self._run_event(method, *args, **kwargs), loop=self.loop)
  300.  
  301.     @asyncio.coroutine
  302.     def on_error(self, event_method, *args, **kwargs):
  303.         """|coro|
  304.  
  305.        The default error handler provided by the client.
  306.  
  307.        By default this prints to ``sys.stderr`` however it could be
  308.        overridden to have a different implementation.
  309.        Check :func:`discord.on_error` for more details.
  310.        """
  311.         print('Ignoring exception in {}'.format(event_method), file=sys.stderr)
  312.         traceback.print_exc()
  313.  
  314.     # login state management
  315.  
  316.     @asyncio.coroutine
  317.     def _login_1(self, token, **kwargs):
  318.         log.info('logging in using static token')
  319.         is_bot = kwargs.pop('bot', True)
  320.         data = yield from self.http.static_login(token, bot=is_bot)
  321.         self.email = data.get('email', None)
  322.         self.connection.is_bot = is_bot
  323.         self._is_logged_in.set()
  324.  
  325.     @asyncio.coroutine
  326.     def _login_2(self, email, password, **kwargs):
  327.         # attempt to read the token from cache
  328.         self.connection.is_bot = False
  329.  
  330.         if self.cache_auth:
  331.             token = self._get_cache_token(email, password)
  332.             try:
  333.                 yield from self.http.static_login(token, bot=False)
  334.             except:
  335.                 log.info('cache auth token is out of date')
  336.             else:
  337.                 self._is_logged_in.set()
  338.                 return
  339.  
  340.  
  341.         yield from self.http.email_login(email, password)
  342.         self.email = email
  343.         self._is_logged_in.set()
  344.  
  345.         # since we went through all this trouble
  346.         # let's make sure we don't have to do it again
  347.         if self.cache_auth:
  348.             self._update_cache(email, password)
  349.  
  350.     @asyncio.coroutine
  351.     def login(self, *args, **kwargs):
  352.         """|coro|
  353.  
  354.        Logs in the client with the specified credentials.
  355.  
  356.        This function can be used in two different ways.
  357.  
  358.        .. code-block:: python
  359.  
  360.            await client.login('token')
  361.  
  362.            # or
  363.  
  364.            await client.login('email', 'password')
  365.  
  366.        More than 2 parameters or less than 1 parameter raises a
  367.        :exc:`TypeError`.
  368.  
  369.        Parameters
  370.        -----------
  371.        bot : bool
  372.            Keyword argument that specifies if the account logging on is a bot
  373.            token or not. Only useful for logging in with a static token.
  374.            Ignored for the email and password combo. Defaults to ``True``.
  375.  
  376.        Raises
  377.        ------
  378.        LoginFailure
  379.            The wrong credentials are passed.
  380.        HTTPException
  381.            An unknown HTTP related error occurred,
  382.            usually when it isn't 200 or the known incorrect credentials
  383.            passing status code.
  384.        TypeError
  385.            The incorrect number of parameters is passed.
  386.        """
  387.  
  388.         n = len(args)
  389.         if n in (2, 1):
  390.             yield from getattr(self, '_login_' + str(n))(*args, **kwargs)
  391.         else:
  392.             raise TypeError('login() takes 1 or 2 positional arguments but {} were given'.format(n))
  393.  
  394.     @asyncio.coroutine
  395.     def logout(self):
  396.         """|coro|
  397.  
  398.        Logs out of Discord and closes all connections.
  399.        """
  400.         yield from self.close()
  401.         self._is_logged_in.clear()
  402.  
  403.     @asyncio.coroutine
  404.     def connect(self):
  405.         """|coro|
  406.  
  407.        Creates a websocket connection and lets the websocket listen
  408.        to messages from discord.
  409.  
  410.        Raises
  411.        -------
  412.        GatewayNotFound
  413.            If the gateway to connect to discord is not found. Usually if this
  414.            is thrown then there is a discord API outage.
  415.        ConnectionClosed
  416.            The websocket connection has been terminated.
  417.        """
  418.         self.ws = yield from DiscordWebSocket.from_client(self)
  419.  
  420.         while not self.is_closed:
  421.             try:
  422.                 yield from self.ws.poll_event()
  423.             except (ReconnectWebSocket, ResumeWebSocket) as e:
  424.                 resume = type(e) is ResumeWebSocket
  425.                 log.info('Got ' + type(e).__name__)
  426.                 self.ws = yield from DiscordWebSocket.from_client(self, resume=resume)
  427.             except ConnectionClosed as e:
  428.                 yield from self.close()
  429.                 if e.code != 1000:
  430.                     raise
  431.  
  432.     @asyncio.coroutine
  433.     def close(self):
  434.         """|coro|
  435.  
  436.        Closes the connection to discord.
  437.        """
  438.         if self.is_closed:
  439.             return
  440.  
  441.         for voice in list(self.voice_clients):
  442.             try:
  443.                 yield from voice.disconnect()
  444.             except:
  445.                 # if an error happens during disconnects, disregard it.
  446.                 pass
  447.  
  448.             self.connection._remove_voice_client(voice.server.id)
  449.  
  450.         if self.ws is not None and self.ws.open:
  451.             yield from self.ws.close()
  452.  
  453.  
  454.         yield from self.http.close()
  455.         self._closed.set()
  456.         self._is_ready.clear()
  457.  
  458.     @asyncio.coroutine
  459.     def start(self, *args, **kwargs):
  460.         """|coro|
  461.  
  462.        A shorthand coroutine for :meth:`login` + :meth:`connect`.
  463.        """
  464.         yield from self.login(*args, **kwargs)
  465.         yield from self.connect()
  466.  
  467.     def run(self, *args, **kwargs):
  468.         """A blocking call that abstracts away the `event loop`_
  469.        initialisation from you.
  470.  
  471.        If you want more control over the event loop then this
  472.        function should not be used. Use :meth:`start` coroutine
  473.        or :meth:`connect` + :meth:`login`.
  474.  
  475.        Roughly Equivalent to: ::
  476.  
  477.            try:
  478.                loop.run_until_complete(start(*args, **kwargs))
  479.            except KeyboardInterrupt:
  480.                loop.run_until_complete(logout())
  481.                # cancel all tasks lingering
  482.            finally:
  483.                loop.close()
  484.  
  485.        Warning
  486.        --------
  487.        This function must be the last function to call due to the fact that it
  488.        is blocking. That means that registration of events or anything being
  489.        called after this function call will not execute until it returns.
  490.        """
  491.  
  492.         try:
  493.             self.loop.run_until_complete(self.start(*args, **kwargs))
  494.         except KeyboardInterrupt:
  495.             self.loop.run_until_complete(self.logout())
  496.             pending = asyncio.Task.all_tasks(loop=self.loop)
  497.             gathered = asyncio.gather(*pending, loop=self.loop)
  498.             try:
  499.                 gathered.cancel()
  500.                 self.loop.run_until_complete(gathered)
  501.  
  502.                 # we want to retrieve any exceptions to make sure that
  503.                 # they don't nag us about it being un-retrieved.
  504.                 gathered.exception()
  505.             except:
  506.                 pass
  507.         finally:
  508.             self.loop.close()
  509.  
  510.         # properties
  511.  
  512.     @property
  513.     def is_logged_in(self):
  514.         """bool: Indicates if the client has logged in successfully."""
  515.         return self._is_logged_in.is_set()
  516.  
  517.     @property
  518.     def is_closed(self):
  519.         """bool: Indicates if the websocket connection is closed."""
  520.         return self._closed.is_set()
  521.  
  522.     # helpers/getters
  523.  
  524.     def get_channel(self, id):
  525.         """Returns a :class:`Channel` or :class:`PrivateChannel` with the following ID. If not found, returns None."""
  526.         return self.connection.get_channel(id)
  527.  
  528.     def get_server(self, id):
  529.         """Returns a :class:`Server` with the given ID. If not found, returns None."""
  530.         return self.connection._get_server(id)
  531.  
  532.     def get_all_emojis(self):
  533.         """Returns a generator with every :class:`Emoji` the client can see."""
  534.         for server in self.servers:
  535.             for emoji in server.emojis:
  536.                 yield emoji
  537.  
  538.     def get_all_channels(self):
  539.         """A generator that retrieves every :class:`Channel` the client can 'access'.
  540.  
  541.        This is equivalent to: ::
  542.  
  543.            for server in client.servers:
  544.                for channel in server.channels:
  545.                    yield channel
  546.  
  547.        Note
  548.        -----
  549.        Just because you receive a :class:`Channel` does not mean that
  550.        you can communicate in said channel. :meth:`Channel.permissions_for` should
  551.        be used for that.
  552.        """
  553.  
  554.         for server in self.servers:
  555.             for channel in server.channels:
  556.                 yield channel
  557.  
  558.     def get_all_members(self):
  559.         """Returns a generator with every :class:`Member` the client can see.
  560.  
  561.        This is equivalent to: ::
  562.  
  563.            for server in client.servers:
  564.                for member in server.members:
  565.                    yield member
  566.  
  567.        """
  568.         for server in self.servers:
  569.             for member in server.members:
  570.                 yield member
  571.  
  572.     # listeners/waiters
  573.  
  574.     @asyncio.coroutine
  575.     def wait_until_ready(self):
  576.         """|coro|
  577.  
  578.        This coroutine waits until the client is all ready. This could be considered
  579.        another way of asking for :func:`discord.on_ready` except meant for your own
  580.        background tasks.
  581.        """
  582.         yield from self._is_ready.wait()
  583.  
  584.     @asyncio.coroutine
  585.     def wait_until_login(self):
  586.         """|coro|
  587.  
  588.        This coroutine waits until the client is logged on successfully. This
  589.        is different from waiting until the client's state is all ready. For
  590.        that check :func:`discord.on_ready` and :meth:`wait_until_ready`.
  591.        """
  592.         yield from self._is_logged_in.wait()
  593.  
  594.     @asyncio.coroutine
  595.     def wait_for_message(self, timeout=None, *, author=None, channel=None, content=None, check=None):
  596.         """|coro|
  597.  
  598.        Waits for a message reply from Discord. This could be seen as another
  599.        :func:`discord.on_message` event outside of the actual event. This could
  600.        also be used for follow-ups and easier user interactions.
  601.  
  602.        The keyword arguments passed into this function are combined using the logical and
  603.        operator. The ``check`` keyword argument can be used to pass in more complicated
  604.        checks and must be a regular function (not a coroutine).
  605.  
  606.        The ``timeout`` parameter is passed into `asyncio.wait_for`_. By default, it
  607.        does not timeout. Instead of throwing ``asyncio.TimeoutError`` the coroutine
  608.        catches the exception and returns ``None`` instead of a :class:`Message`.
  609.  
  610.        If the ``check`` predicate throws an exception, then the exception is propagated.
  611.  
  612.        This function returns the **first message that meets the requirements**.
  613.  
  614.        .. _asyncio.wait_for: https://docs.python.org/3/library/asyncio-task.html#asyncio.wait_for
  615.  
  616.        Examples
  617.        ----------
  618.  
  619.        Basic example:
  620.  
  621.        .. code-block:: python
  622.            :emphasize-lines: 5
  623.  
  624.            @client.event
  625.            async def on_message(message):
  626.                if message.content.startswith('$greet'):
  627.                    await client.send_message(message.channel, 'Say hello')
  628.                    msg = await client.wait_for_message(author=message.author, content='hello')
  629.                    await client.send_message(message.channel, 'Hello.')
  630.  
  631.        Asking for a follow-up question:
  632.  
  633.        .. code-block:: python
  634.            :emphasize-lines: 6
  635.  
  636.            @client.event
  637.            async def on_message(message):
  638.                if message.content.startswith('$start'):
  639.                    await client.send_message(message.channel, 'Type $stop 4 times.')
  640.                    for i in range(4):
  641.                        msg = await client.wait_for_message(author=message.author, content='$stop')
  642.                        fmt = '{} left to go...'
  643.                        await client.send_message(message.channel, fmt.format(3 - i))
  644.  
  645.                    await client.send_message(message.channel, 'Good job!')
  646.  
  647.        Advanced filters using ``check``:
  648.  
  649.        .. code-block:: python
  650.            :emphasize-lines: 9
  651.  
  652.            @client.event
  653.            async def on_message(message):
  654.                if message.content.startswith('$cool'):
  655.                    await client.send_message(message.channel, 'Who is cool? Type $name namehere')
  656.  
  657.                    def check(msg):
  658.                        return msg.content.startswith('$name')
  659.  
  660.                    message = await client.wait_for_message(author=message.author, check=check)
  661.                    name = message.content[len('$name'):].strip()
  662.                    await client.send_message(message.channel, '{} is cool indeed'.format(name))
  663.  
  664.  
  665.        Parameters
  666.        -----------
  667.        timeout : float
  668.            The number of seconds to wait before returning ``None``.
  669.        author : :class:`Member` or :class:`User`
  670.            The author the message must be from.
  671.        channel : :class:`Channel` or :class:`PrivateChannel` or :class:`Object`
  672.            The channel the message must be from.
  673.        content : str
  674.            The exact content the message must have.
  675.        check : function
  676.            A predicate for other complicated checks. The predicate must take
  677.            a :class:`Message` as its only parameter.
  678.  
  679.        Returns
  680.        --------
  681.        :class:`Message`
  682.            The message that you requested for.
  683.        """
  684.  
  685.         def predicate(message):
  686.             result = True
  687.             if author is not None:
  688.                 result = result and message.author == author
  689.  
  690.             if content is not None:
  691.                 result = result and message.content == content
  692.  
  693.             if channel is not None:
  694.                 result = result and message.channel.id == channel.id
  695.  
  696.             if callable(check):
  697.                 # the exception thrown by check is propagated through the future.
  698.                 result = result and check(message)
  699.  
  700.             return result
  701.  
  702.         future = asyncio.Future(loop=self.loop)
  703.         self._listeners.append((predicate, future, WaitForType.message))
  704.         try:
  705.             message = yield from asyncio.wait_for(future, timeout, loop=self.loop)
  706.         except asyncio.TimeoutError:
  707.             message = None
  708.         return message
  709.  
  710.  
  711.     @asyncio.coroutine
  712.     def wait_for_reaction(self, emoji=None, *, user=None, timeout=None, message=None, check=None):
  713.         """|coro|
  714.  
  715.        Waits for a message reaction from Discord. This is similar to :meth:`wait_for_message`
  716.        and could be seen as another :func:`on_reaction_add` event outside of the actual event.
  717.        This could be used for follow up situations.
  718.  
  719.        Similar to :meth:`wait_for_message`, the keyword arguments are combined using logical
  720.        AND operator. The ``check`` keyword argument can be used to pass in more complicated
  721.        checks and must a regular function taking in two arguments, ``(reaction, user)``. It
  722.        must not be a coroutine.
  723.  
  724.        The ``timeout`` parameter is passed into asyncio.wait_for. By default, it
  725.        does not timeout. Instead of throwing ``asyncio.TimeoutError`` the coroutine
  726.        catches the exception and returns ``None`` instead of a the ``(reaction, user)``
  727.        tuple.
  728.  
  729.        If the ``check`` predicate throws an exception, then the exception is propagated.
  730.  
  731.        The ``emoji`` parameter can be either a :class:`Emoji`, a ``str`` representing
  732.        an emoji, or a sequence of either type. If the ``emoji`` parameter is a sequence
  733.        then the first reaction emoji that is in the list is returned. If ``None`` is
  734.        passed then the first reaction emoji used is returned.
  735.  
  736.        This function returns the **first reaction that meets the requirements**.
  737.  
  738.        Examples
  739.        ---------
  740.  
  741.        Basic Example:
  742.  
  743.        .. code-block:: python
  744.  
  745.            @client.event
  746.            async def on_message(message):
  747.                if message.content.startswith('$react'):
  748.                    msg = await client.send_message(message.channel, 'React with thumbs up or thumbs down.')
  749.                    res = await client.wait_for_reaction(['\N{THUMBS UP SIGN}', '\N{THUMBS DOWN SIGN}'], message=msg)
  750.                    await client.send_message(message.channel, '{0.user} reacted with {0.reaction.emoji}!'.format(res))
  751.  
  752.        Checking for reaction emoji regardless of skin tone:
  753.  
  754.        .. code-block:: python
  755.  
  756.            @client.event
  757.            async def on_message(message):
  758.                if message.content.startswith('$react'):
  759.                    msg = await client.send_message(message.channel, 'React with thumbs up or thumbs down.')
  760.  
  761.                    def check(reaction, user):
  762.                        e = str(reaction.emoji)
  763.                        return e.startswith(('\N{THUMBS UP SIGN}', '\N{THUMBS DOWN SIGN}'))
  764.  
  765.                    res = await client.wait_for_reaction(message=msg, check=check)
  766.                    await client.send_message(message.channel, '{0.user} reacted with {0.reaction.emoji}!'.format(res))
  767.  
  768.        Parameters
  769.        -----------
  770.        timeout: float
  771.            The number of seconds to wait before returning ``None``.
  772.        user: :class:`Member` or :class:`User`
  773.            The user the reaction must be from.
  774.        emoji: str or :class:`Emoji` or sequence
  775.            The emoji that we are waiting to react with.
  776.        message: :class:`Message`
  777.            The message that we want the reaction to be from.
  778.        check: function
  779.            A predicate for other complicated checks. The predicate must take
  780.            ``(reaction, user)`` as its two parameters, which ``reaction`` being a
  781.            :class:`Reaction` and ``user`` being either a :class:`User` or a
  782.            :class:`Member`.
  783.  
  784.        Returns
  785.        --------
  786.        namedtuple
  787.            A namedtuple with attributes ``reaction`` and ``user`` similar to :func:`on_reaction_add`.
  788.        """
  789.  
  790.         if emoji is None:
  791.             emoji_check = lambda r: True
  792.         elif isinstance(emoji, (str, Emoji)):
  793.             emoji_check = lambda r: r.emoji == emoji
  794.         else:
  795.             emoji_check = lambda r: r.emoji in emoji
  796.  
  797.         def predicate(reaction, reaction_user):
  798.             result = emoji_check(reaction)
  799.  
  800.             if message is not None:
  801.                 result = result and message.id == reaction.message.id
  802.  
  803.             if user is not None:
  804.                 result = result and user.id == reaction_user.id
  805.  
  806.             if callable(check):
  807.                 # the exception thrown by check is propagated through the future.
  808.                 result = result and check(reaction, reaction_user)
  809.  
  810.             return result
  811.  
  812.         future = asyncio.Future(loop=self.loop)
  813.         self._listeners.append((predicate, future, WaitForType.reaction))
  814.         try:
  815.             return (yield from asyncio.wait_for(future, timeout, loop=self.loop))
  816.         except asyncio.TimeoutError:
  817.             return None
  818.  
  819.     # event registration
  820.  
  821.     def event(self, coro):
  822.         """A decorator that registers an event to listen to.
  823.  
  824.        You can find more info about the events on the :ref:`documentation below <discord-api-events>`.
  825.  
  826.        The events must be a |corourl|_, if not, :exc:`ClientException` is raised.
  827.  
  828.        Examples
  829.        ---------
  830.  
  831.        Using the basic :meth:`event` decorator: ::
  832.  
  833.            @client.event
  834.            @asyncio.coroutine
  835.            def on_ready():
  836.                print('Ready!')
  837.  
  838.        Saving characters by using the :meth:`async_event` decorator: ::
  839.  
  840.            @client.async_event
  841.            def on_ready():
  842.                print('Ready!')
  843.  
  844.        """
  845.  
  846.         if not asyncio.iscoroutinefunction(coro):
  847.             raise ClientException('event registered must be a coroutine function')
  848.  
  849.         setattr(self, coro.__name__, coro)
  850.         log.info('{0.__name__} has successfully been registered as an event'.format(coro))
  851.         return coro
  852.  
  853.     def async_event(self, coro):
  854.         """A shorthand decorator for ``asyncio.coroutine`` + :meth:`event`."""
  855.         if not asyncio.iscoroutinefunction(coro):
  856.             coro = asyncio.coroutine(coro)
  857.  
  858.         return self.event(coro)
  859.  
  860.     # Message sending/management
  861.  
  862.     @asyncio.coroutine
  863.     def start_private_message(self, user):
  864.         """|coro|
  865.  
  866.        Starts a private message with the user. This allows you to
  867.        :meth:`send_message` to the user.
  868.  
  869.        Note
  870.        -----
  871.        This method should rarely be called as :meth:`send_message`
  872.        does it automatically for you.
  873.  
  874.        Parameters
  875.        -----------
  876.        user : :class:`User`
  877.            The user to start the private message with.
  878.  
  879.        Raises
  880.        ------
  881.        HTTPException
  882.            The request failed.
  883.        InvalidArgument
  884.            The user argument was not of :class:`User`.
  885.        """
  886.  
  887.         if not isinstance(user, User):
  888.             raise InvalidArgument('user argument must be a User')
  889.  
  890.         data = yield from self.http.start_private_message(user.id)
  891.         channel = PrivateChannel(me=self.user, **data)
  892.         self.connection._add_private_channel(channel)
  893.         return channel
  894.  
  895.     @asyncio.coroutine
  896.     def add_reaction(self, message, emoji):
  897.         """|coro|
  898.  
  899.        Add a reaction to the given message.
  900.  
  901.        The message must be a :class:`Message` that exists. emoji may be a unicode emoji,
  902.        or a custom server :class:`Emoji`.
  903.  
  904.        Parameters
  905.        ------------
  906.        message : :class:`Message`
  907.            The message to react to.
  908.        emoji : :class:`Emoji` or str
  909.            The emoji to react with.
  910.  
  911.        Raises
  912.        --------
  913.        HTTPException
  914.            Adding the reaction failed.
  915.        Forbidden
  916.            You do not have the proper permissions to react to the message.
  917.        NotFound
  918.            The message or emoji you specified was not found.
  919.        InvalidArgument
  920.            The message or emoji parameter is invalid.
  921.        """
  922.         if not isinstance(message, Message):
  923.             raise InvalidArgument('message argument must be a Message')
  924.         if not isinstance(emoji, (str, Emoji)):
  925.             raise InvalidArgument('emoji argument must be a string or Emoji')
  926.  
  927.         if isinstance(emoji, Emoji):
  928.             emoji = '{}:{}'.format(emoji.name, emoji.id)
  929.  
  930.         yield from self.http.add_reaction(message.id, message.channel.id, emoji)
  931.  
  932.     @asyncio.coroutine
  933.     def remove_reaction(self, message, emoji, member):
  934.         """|coro|
  935.  
  936.        Remove a reaction by the member from the given message.
  937.  
  938.        If member != server.me, you need Manage Messages to remove the reaction.
  939.  
  940.        The message must be a :class:`Message` that exists. emoji may be a unicode emoji,
  941.        or a custom server :class:`Emoji`.
  942.  
  943.        Parameters
  944.        ------------
  945.        message : :class:`Message`
  946.            The message.
  947.        emoji : :class:`Emoji` or str
  948.            The emoji to remove.
  949.        member : :class:`Member`
  950.            The member for which to delete the reaction.
  951.  
  952.        Raises
  953.        --------
  954.        HTTPException
  955.            Removing the reaction failed.
  956.        Forbidden
  957.            You do not have the proper permissions to remove the reaction.
  958.        NotFound
  959.            The message or emoji you specified was not found.
  960.        InvalidArgument
  961.            The message or emoji parameter is invalid.
  962.        """
  963.         if not isinstance(message, Message):
  964.             raise InvalidArgument('message argument must be a Message')
  965.         if not isinstance(emoji, (str, Emoji)):
  966.             raise InvalidArgument('emoji must be a string or Emoji')
  967.  
  968.         if isinstance(emoji, Emoji):
  969.             emoji = '{}:{}'.format(emoji.name, emoji.id)
  970.  
  971.         if member == self.user:
  972.             member_id = '@me'
  973.         else:
  974.             member_id = member.id
  975.  
  976.         yield from self.http.remove_reaction(message.id, message.channel.id, emoji, member_id)
  977.  
  978.     @asyncio.coroutine
  979.     def get_reaction_users(self, reaction, limit=100, after=None):
  980.         """|coro|
  981.  
  982.        Get the users that added a reaction to a message.
  983.  
  984.        Parameters
  985.        ------------
  986.        reaction : :class:`Reaction`
  987.            The reaction to retrieve users for.
  988.        limit : int
  989.            The maximum number of results to return.
  990.        after : :class:`Member` or :class:`Object`
  991.            For pagination, reactions are sorted by member.
  992.  
  993.        Raises
  994.        --------
  995.        HTTPException
  996.            Getting the users for the reaction failed.
  997.        NotFound
  998.            The message or emoji you specified was not found.
  999.        InvalidArgument
  1000.            The reaction parameter is invalid.
  1001.        """
  1002.         if not isinstance(reaction, Reaction):
  1003.             raise InvalidArgument('reaction must be a Reaction')
  1004.  
  1005.         emoji = reaction.emoji
  1006.  
  1007.         if isinstance(emoji, Emoji):
  1008.             emoji = '{}:{}'.format(emoji.name, emoji.id)
  1009.  
  1010.         if after:
  1011.             after = after.id
  1012.  
  1013.         data = yield from self.http.get_reaction_users(
  1014.             reaction.message.id, reaction.message.channel.id,
  1015.             emoji, limit, after=after)
  1016.  
  1017.         return [User(**user) for user in data]
  1018.  
  1019.     @asyncio.coroutine
  1020.     def clear_reactions(self, message):
  1021.         """|coro|
  1022.  
  1023.        Removes all the reactions from a given message.
  1024.  
  1025.        You need Manage Messages permission to use this.
  1026.  
  1027.        Parameters
  1028.        -----------
  1029.        message: :class:`Message`
  1030.            The message to remove all reactions from.
  1031.  
  1032.        Raises
  1033.        --------
  1034.        HTTPException
  1035.            Removing the reactions failed.
  1036.        Forbidden
  1037.            You do not have the proper permissions to remove all the reactions.
  1038.        """
  1039.         yield from self.http.clear_reactions(message.id, message.channel.id)
  1040.  
  1041.     @asyncio.coroutine
  1042.     def send_message(self, destination, content=None, *, tts=False, embed=None):
  1043.         """|coro|
  1044.  
  1045.        Sends a message to the destination given with the content given.
  1046.  
  1047.        The destination could be a :class:`Channel`, :class:`PrivateChannel` or :class:`Server`.
  1048.        For convenience it could also be a :class:`User`. If it's a :class:`User` or :class:`PrivateChannel`
  1049.        then it sends the message via private message, otherwise it sends the message to the channel.
  1050.        If the destination is a :class:`Server` then it's equivalent to calling
  1051.        :attr:`Server.default_channel` and sending it there.
  1052.  
  1053.        If it is a :class:`Object` instance then it is assumed to be the
  1054.        destination ID. The destination ID is a *channel* so passing in a user
  1055.        ID will not be a valid destination.
  1056.  
  1057.        .. versionchanged:: 0.9.0
  1058.            ``str`` being allowed was removed and replaced with :class:`Object`.
  1059.  
  1060.        The content must be a type that can convert to a string through ``str(content)``.
  1061.        If the content is set to ``None`` (the default), then the ``embed`` parameter must
  1062.        be provided.
  1063.  
  1064.        If the ``embed`` parameter is provided, it must be of type :class:`Embed` and
  1065.        it must be a rich embed type.
  1066.  
  1067.        Parameters
  1068.        ------------
  1069.        destination
  1070.            The location to send the message.
  1071.        content
  1072.            The content of the message to send. If this is missing,
  1073.            then the ``embed`` parameter must be present.
  1074.        tts : bool
  1075.            Indicates if the message should be sent using text-to-speech.
  1076.        embed: :class:`Embed`
  1077.            The rich embed for the content.
  1078.  
  1079.        Raises
  1080.        --------
  1081.        HTTPException
  1082.            Sending the message failed.
  1083.        Forbidden
  1084.            You do not have the proper permissions to send the message.
  1085.        NotFound
  1086.            The destination was not found and hence is invalid.
  1087.        InvalidArgument
  1088.            The destination parameter is invalid.
  1089.  
  1090.        Examples
  1091.        ----------
  1092.  
  1093.        Sending a regular message:
  1094.  
  1095.        .. code-block:: python
  1096.  
  1097.            await client.send_message(message.channel, 'Hello')
  1098.  
  1099.        Sending a TTS message:
  1100.  
  1101.        .. code-block:: python
  1102.  
  1103.            await client.send_message(message.channel, 'Goodbye.', tts=True)
  1104.  
  1105.        Sending an embed message:
  1106.  
  1107.        .. code-block:: python
  1108.  
  1109.            em = discord.Embed(title='My Embed Title', description='My Embed Content.', colour=0xDEADBF)
  1110.            em.set_author(name='Someone', icon_url=client.user.default_avatar_url)
  1111.            await client.send_message(message.channel, embed=em)
  1112.  
  1113.        Returns
  1114.        ---------
  1115.        :class:`Message`
  1116.            The message that was sent.
  1117.        """
  1118.  
  1119.         channel_id, guild_id = yield from self._resolve_destination(destination)
  1120.  
  1121.         content = str(content) if content is not None else None
  1122.  
  1123.         if embed is not None:
  1124.             embed = embed.to_dict()
  1125.  
  1126.         data = yield from self.http.send_message(channel_id, content, guild_id=guild_id, tts=tts, embed=embed)
  1127.         channel = self.get_channel(data.get('channel_id'))
  1128.         message = self.connection._create_message(channel=channel, **data)
  1129.         return message
  1130.  
  1131.     @asyncio.coroutine
  1132.     def send_typing(self, destination):
  1133.         """|coro|
  1134.  
  1135.        Send a *typing* status to the destination.
  1136.  
  1137.        *Typing* status will go away after 10 seconds, or after a message is sent.
  1138.  
  1139.        The destination parameter follows the same rules as :meth:`send_message`.
  1140.  
  1141.        Parameters
  1142.        ----------
  1143.        destination
  1144.            The location to send the typing update.
  1145.        """
  1146.  
  1147.         channel_id, guild_id = yield from self._resolve_destination(destination)
  1148.         yield from self.http.send_typing(channel_id)
  1149.  
  1150.     @asyncio.coroutine
  1151.     def send_file(self, destination, fp, *, filename=None, content=None, tts=False):
  1152.         """|coro|
  1153.  
  1154.        Sends a message to the destination given with the file given.
  1155.  
  1156.        The destination parameter follows the same rules as :meth:`send_message`.
  1157.  
  1158.        The ``fp`` parameter should be either a string denoting the location for a
  1159.        file or a *file-like object*. The *file-like object* passed is **not closed**
  1160.        at the end of execution. You are responsible for closing it yourself.
  1161.  
  1162.        .. note::
  1163.  
  1164.            If the file-like object passed is opened via ``open`` then the modes
  1165.            'rb' should be used.
  1166.  
  1167.        The ``filename`` parameter is the filename of the file.
  1168.        If this is not given then it defaults to ``fp.name`` or if ``fp`` is a string
  1169.        then the ``filename`` will default to the string given. You can overwrite
  1170.        this value by passing this in.
  1171.  
  1172.        Parameters
  1173.        ------------
  1174.        destination
  1175.            The location to send the message.
  1176.        fp
  1177.            The *file-like object* or file path to send.
  1178.        filename : str
  1179.            The filename of the file. Defaults to ``fp.name`` if it's available.
  1180.        content
  1181.            The content of the message to send along with the file. This is
  1182.            forced into a string by a ``str(content)`` call.
  1183.        tts : bool
  1184.            If the content of the message should be sent with TTS enabled.
  1185.  
  1186.        Raises
  1187.        -------
  1188.        HTTPException
  1189.            Sending the file failed.
  1190.  
  1191.        Returns
  1192.        --------
  1193.        :class:`Message`
  1194.            The message sent.
  1195.        """
  1196.  
  1197.         channel_id, guild_id = yield from self._resolve_destination(destination)
  1198.  
  1199.         try:
  1200.             with open(fp, 'rb') as f:
  1201.                 buffer = io.BytesIO(f.read())
  1202.                 if filename is None:
  1203.                     _, filename = path_split(fp)
  1204.         except TypeError:
  1205.             buffer = fp
  1206.  
  1207.         content = str(content) if content is not None else None
  1208.         data = yield from self.http.send_file(channel_id, buffer, guild_id=guild_id,
  1209.                                               filename=filename, content=content, tts=tts)
  1210.         channel = self.get_channel(data.get('channel_id'))
  1211.         message = self.connection._create_message(channel=channel, **data)
  1212.         return message
  1213.  
  1214.     @asyncio.coroutine
  1215.     def delete_message(self, message):
  1216.         """|coro|
  1217.  
  1218.        Deletes a :class:`Message`.
  1219.  
  1220.        Your own messages could be deleted without any proper permissions. However to
  1221.        delete other people's messages, you need the proper permissions to do so.
  1222.  
  1223.        Parameters
  1224.        -----------
  1225.        message : :class:`Message`
  1226.            The message to delete.
  1227.  
  1228.        Raises
  1229.        ------
  1230.        Forbidden
  1231.            You do not have proper permissions to delete the message.
  1232.        HTTPException
  1233.            Deleting the message failed.
  1234.        """
  1235.         channel = message.channel
  1236.         guild_id = channel.server.id if not getattr(channel, 'is_private', True) else None
  1237.         yield from self.http.delete_message(channel.id, message.id, guild_id)
  1238.  
  1239.     @asyncio.coroutine
  1240.     def delete_messages(self, messages):
  1241.         """|coro|
  1242.  
  1243.        Deletes a list of messages. This is similar to :func:`delete_message`
  1244.        except it bulk deletes multiple messages.
  1245.  
  1246.        The channel to check where the message is deleted from is handled via
  1247.        the first element of the iterable's ``.channel.id`` attributes. If the
  1248.        channel is not consistent throughout the entire sequence, then an
  1249.        :exc:`HTTPException` will be raised.
  1250.  
  1251.        Usable only by bot accounts.
  1252.  
  1253.        Parameters
  1254.        -----------
  1255.        messages : iterable of :class:`Message`
  1256.            An iterable of messages denoting which ones to bulk delete.
  1257.  
  1258.        Raises
  1259.        ------
  1260.        ClientException
  1261.            The number of messages to delete is less than 2 or more than 100.
  1262.        Forbidden
  1263.            You do not have proper permissions to delete the messages or
  1264.            you're not using a bot account.
  1265.        HTTPException
  1266.            Deleting the messages failed.
  1267.        """
  1268.  
  1269.         messages = list(messages)
  1270.         if len(messages) > 100 or len(messages) < 2:
  1271.             raise ClientException('Can only delete messages in the range of [2, 100]')
  1272.  
  1273.         channel = messages[0].channel
  1274.         message_ids = [m.id for m in messages]
  1275.         guild_id = channel.server.id if not getattr(channel, 'is_private', True) else None
  1276.         yield from self.http.delete_messages(channel.id, message_ids, guild_id)
  1277.  
  1278.     @asyncio.coroutine
  1279.     def purge_from(self, channel, *, limit=100, check=None, before=None, after=None, around=None):
  1280.         """|coro|
  1281.  
  1282.        Purges a list of messages that meet the criteria given by the predicate
  1283.        ``check``. If a ``check`` is not provided then all messages are deleted
  1284.        without discrimination.
  1285.  
  1286.        You must have Manage Messages permission to delete messages even if they
  1287.        are your own. The Read Message History permission is also needed to
  1288.        retrieve message history.
  1289.  
  1290.        Usable only by bot accounts.
  1291.  
  1292.        Parameters
  1293.        -----------
  1294.        channel : :class:`Channel`
  1295.            The channel to purge from.
  1296.        limit : int
  1297.            The number of messages to search through. This is not the number
  1298.            of messages that will be deleted, though it can be.
  1299.        check : predicate
  1300.            The function used to check if a message should be deleted.
  1301.            It must take a :class:`Message` as its sole parameter.
  1302.        before : :class:`Message` or `datetime`
  1303.            The message or date before which all deleted messages must be.
  1304.            If a date is provided it must be a timezone-naive datetime representing UTC time.
  1305.        after : :class:`Message` or `datetime`
  1306.            The message or date after which all deleted messages must be.
  1307.            If a date is provided it must be a timezone-naive datetime representing UTC time.
  1308.        around : :class:`Message` or `datetime`
  1309.            The message or date around which all deleted messages must be.
  1310.            If a date is provided it must be a timezone-naive datetime representing UTC time.
  1311.  
  1312.        Raises
  1313.        -------
  1314.        Forbidden
  1315.            You do not have proper permissions to do the actions required or
  1316.            you're not using a bot account.
  1317.        HTTPException
  1318.            Purging the messages failed.
  1319.  
  1320.        Examples
  1321.        ---------
  1322.  
  1323.        Deleting bot's messages ::
  1324.  
  1325.            def is_me(m):
  1326.                return m.author == client.user
  1327.  
  1328.            deleted = await client.purge_from(channel, limit=100, check=is_me)
  1329.            await client.send_message(channel, 'Deleted {} message(s)'.format(len(deleted)))
  1330.  
  1331.        Returns
  1332.        --------
  1333.        list
  1334.            The list of messages that were deleted.
  1335.        """
  1336.  
  1337.         if check is None:
  1338.             check = lambda m: True
  1339.  
  1340.         if isinstance(before, datetime.datetime):
  1341.             before = Object(utils.time_snowflake(before, high=False))
  1342.         if isinstance(after, datetime.datetime):
  1343.             after = Object(utils.time_snowflake(after, high=True))
  1344.         if isinstance(around, datetime.datetime):
  1345.             around = Object(utils.time_snowflake(around, high=True))
  1346.  
  1347.         iterator = LogsFromIterator(self, channel, limit, before=before, after=after, around=around)
  1348.         ret = []
  1349.         count = 0
  1350.  
  1351.         while True:
  1352.             try:
  1353.                 msg = yield from iterator.iterate()
  1354.             except asyncio.QueueEmpty:
  1355.                 # no more messages to poll
  1356.                 if count >= 2:
  1357.                     # more than 2 messages -> bulk delete
  1358.                     to_delete = ret[-count:]
  1359.                     yield from self.delete_messages(to_delete)
  1360.                 elif count == 1:
  1361.                     # delete a single message
  1362.                     yield from self.delete_message(ret[-1])
  1363.  
  1364.                 return ret
  1365.             else:
  1366.                 if count == 100:
  1367.                     # we've reached a full 'queue'
  1368.                     to_delete = ret[-100:]
  1369.                     yield from self.delete_messages(to_delete)
  1370.                     count = 0
  1371.                     yield from asyncio.sleep(1, loop=self.loop)
  1372.  
  1373.                 if check(msg):
  1374.                     count += 1
  1375.                     ret.append(msg)
  1376.  
  1377.     @asyncio.coroutine
  1378.     def edit_message(self, message, new_content=None, *, embed=None):
  1379.         """|coro|
  1380.  
  1381.        Edits a :class:`Message` with the new message content.
  1382.  
  1383.        The new_content must be able to be transformed into a string via ``str(new_content)``.
  1384.  
  1385.        If the ``new_content`` is not provided, then ``embed`` must be provided, which must
  1386.        be of type :class:`Embed`.
  1387.  
  1388.        The :class:`Message` object is not directly modified afterwards until the
  1389.        corresponding WebSocket event is received.
  1390.  
  1391.        Parameters
  1392.        -----------
  1393.        message : :class:`Message`
  1394.            The message to edit.
  1395.        new_content
  1396.            The new content to replace the message with.
  1397.        embed: :class:`Embed`
  1398.            The new embed to replace the original embed with.
  1399.  
  1400.        Raises
  1401.        -------
  1402.        HTTPException
  1403.            Editing the message failed.
  1404.  
  1405.        Returns
  1406.        --------
  1407.        :class:`Message`
  1408.            The new edited message.
  1409.        """
  1410.  
  1411.         channel = message.channel
  1412.         content = str(new_content) if new_content else None
  1413.         embed = embed.to_dict() if embed else None
  1414.         guild_id = channel.server.id if not getattr(channel, 'is_private', True) else None
  1415.         data = yield from self.http.edit_message(message.id, channel.id, content, guild_id=guild_id, embed=embed)
  1416.         return self.connection._create_message(channel=channel, **data)
  1417.  
  1418.     @asyncio.coroutine
  1419.     def get_message(self, channel, id):
  1420.         """|coro|
  1421.  
  1422.        Retrieves a single :class:`Message` from a :class:`Channel`.
  1423.  
  1424.        This can only be used by bot accounts.
  1425.  
  1426.        Parameters
  1427.        ------------
  1428.        channel: :class:`Channel` or :class:`PrivateChannel`
  1429.            The text channel to retrieve the message from.
  1430.        id: str
  1431.            The message ID to look for.
  1432.  
  1433.        Returns
  1434.        --------
  1435.        :class:`Message`
  1436.            The message asked for.
  1437.  
  1438.        Raises
  1439.        --------
  1440.        NotFound
  1441.            The specified channel or message was not found.
  1442.        Forbidden
  1443.            You do not have the permissions required to get a message.
  1444.        HTTPException
  1445.            Retrieving the message failed.
  1446.        """
  1447.  
  1448.         data = yield from self.http.get_message(channel.id, id)
  1449.         return self.connection._create_message(channel=channel, **data)
  1450.  
  1451.     @asyncio.coroutine
  1452.     def pin_message(self, message):
  1453.         """|coro|
  1454.  
  1455.        Pins a message. You must have Manage Messages permissions
  1456.        to do this in a non-private channel context.
  1457.  
  1458.        Parameters
  1459.        -----------
  1460.        message: :class:`Message`
  1461.            The message to pin.
  1462.  
  1463.        Raises
  1464.        -------
  1465.        Forbidden
  1466.            You do not have permissions to pin the message.
  1467.        NotFound
  1468.            The message or channel was not found.
  1469.        HTTPException
  1470.            Pinning the message failed, probably due to the channel
  1471.            having more than 50 pinned messages.
  1472.        """
  1473.         yield from self.http.pin_message(message.channel.id, message.id)
  1474.  
  1475.     @asyncio.coroutine
  1476.     def unpin_message(self, message):
  1477.         """|coro|
  1478.  
  1479.        Unpins a message. You must have Manage Messages permissions
  1480.        to do this in a non-private channel context.
  1481.  
  1482.        Parameters
  1483.        -----------
  1484.        message: :class:`Message`
  1485.            The message to unpin.
  1486.  
  1487.        Raises
  1488.        -------
  1489.        Forbidden
  1490.            You do not have permissions to unpin the message.
  1491.        NotFound
  1492.            The message or channel was not found.
  1493.        HTTPException
  1494.            Unpinning the message failed.
  1495.        """
  1496.         yield from self.http.unpin_message(message.channel.id, message.id)
  1497.  
  1498.     @asyncio.coroutine
  1499.     def pins_from(self, channel):
  1500.         """|coro|
  1501.  
  1502.        Returns a list of :class:`Message` that are currently pinned for
  1503.        the specified :class:`Channel` or :class:`PrivateChannel`.
  1504.  
  1505.        Parameters
  1506.        -----------
  1507.        channel: :class:`Channel` or :class:`PrivateChannel`
  1508.            The channel to look through pins for.
  1509.  
  1510.        Raises
  1511.        -------
  1512.        NotFound
  1513.            The channel was not found.
  1514.        HTTPException
  1515.            Retrieving the pinned messages failed.
  1516.        """
  1517.  
  1518.         data = yield from self.http.pins_from(channel.id)
  1519.         return [self.connection._create_message(channel=channel, **m) for m in data]
  1520.  
  1521.     def _logs_from(self, channel, limit=100, before=None, after=None, around=None):
  1522.         """|coro|
  1523.  
  1524.        This coroutine returns a generator that obtains logs from a specified channel.
  1525.  
  1526.        Parameters
  1527.        -----------
  1528.        channel : :class:`Channel` or :class:`PrivateChannel`
  1529.            The channel to obtain the logs from.
  1530.        limit : int
  1531.            The number of messages to retrieve.
  1532.        before : :class:`Message` or `datetime`
  1533.            The message or date before which all returned messages must be.
  1534.            If a date is provided it must be a timezone-naive datetime representing UTC time.
  1535.        after : :class:`Message` or `datetime`
  1536.            The message or date after which all returned messages must be.
  1537.            If a date is provided it must be a timezone-naive datetime representing UTC time.
  1538.        around : :class:`Message` or `datetime`
  1539.            The message or date around which all returned messages must be.
  1540.            If a date is provided it must be a timezone-naive datetime representing UTC time.
  1541.  
  1542.        Raises
  1543.        ------
  1544.        Forbidden
  1545.            You do not have permissions to get channel logs.
  1546.        NotFound
  1547.            The channel you are requesting for doesn't exist.
  1548.        HTTPException
  1549.            The request to get logs failed.
  1550.  
  1551.        Yields
  1552.        -------
  1553.        :class:`Message`
  1554.            The message with the message data parsed.
  1555.  
  1556.        Examples
  1557.        ---------
  1558.  
  1559.        Basic logging: ::
  1560.  
  1561.            logs = yield from client.logs_from(channel)
  1562.            for message in logs:
  1563.                if message.content.startswith('!hello'):
  1564.                    if message.author == client.user:
  1565.                        yield from client.edit_message(message, 'goodbye')
  1566.  
  1567.        Python 3.5 Usage ::
  1568.  
  1569.            counter = 0
  1570.            async for message in client.logs_from(channel, limit=500):
  1571.                if message.author == client.user:
  1572.                    counter += 1
  1573.        """
  1574.         before = getattr(before, 'id', None)
  1575.         after  = getattr(after, 'id', None)
  1576.         around  = getattr(around, 'id', None)
  1577.  
  1578.         return self.http.logs_from(channel.id, limit, before=before, after=after, around=around)
  1579.  
  1580.     if PY35:
  1581.         def logs_from(self, channel, limit=100, *, before=None, after=None, around=None, reverse=False):
  1582.             if isinstance(before, datetime.datetime):
  1583.                 before = Object(utils.time_snowflake(before, high=False))
  1584.             if isinstance(after, datetime.datetime):
  1585.                 after = Object(utils.time_snowflake(after, high=True))
  1586.             if isinstance(around, datetime.datetime):
  1587.                 around = Object(utils.time_snowflake(around))
  1588.  
  1589.             return LogsFromIterator(self, channel, limit, before=before, after=after, around=around, reverse=reverse)
  1590.     else:
  1591.         @asyncio.coroutine
  1592.         def logs_from(self, channel, limit=100, *, before=None, after=None):
  1593.             if isinstance(before, datetime.datetime):
  1594.                 before = Object(utils.time_snowflake(before, high=False))
  1595.             if isinstance(after, datetime.datetime):
  1596.                 after = Object(utils.time_snowflake(after, high=True))
  1597.  
  1598.             def generator(data):
  1599.                 for message in data:
  1600.                     yield self.connection._create_message(channel=channel, **message)
  1601.  
  1602.             result = []
  1603.             while limit > 0:
  1604.                 retrieve = limit if limit <= 100 else 100
  1605.                 data = yield from self._logs_from(channel, retrieve, before, after)
  1606.                 if len(data):
  1607.                     limit -= retrieve
  1608.                     result.extend(data)
  1609.                     before = Object(id=data[-1]['id'])
  1610.                 else:
  1611.                     break
  1612.  
  1613.             return generator(result)
  1614.  
  1615.     logs_from.__doc__ = _logs_from.__doc__
  1616.  
  1617.     # Member management
  1618.  
  1619.     @asyncio.coroutine
  1620.     def request_offline_members(self, server):
  1621.         """|coro|
  1622.  
  1623.        Requests previously offline members from the server to be filled up
  1624.        into the :attr:`Server.members` cache. This function is usually not
  1625.        called.
  1626.  
  1627.        When the client logs on and connects to the websocket, Discord does
  1628.        not provide the library with offline members if the number of members
  1629.        in the server is larger than 250. You can check if a server is large
  1630.        if :attr:`Server.large` is ``True``.
  1631.  
  1632.        Parameters
  1633.        -----------
  1634.        server : :class:`Server` or iterable
  1635.            The server to request offline members for. If this parameter is a
  1636.            iterable then it is interpreted as an iterator of servers to
  1637.            request offline members for.
  1638.        """
  1639.  
  1640.         if hasattr(server, 'id'):
  1641.             guild_id = server.id
  1642.         else:
  1643.             guild_id = [s.id for s in server]
  1644.  
  1645.         payload = {
  1646.             'op': 8,
  1647.             'd': {
  1648.                 'guild_id': guild_id,
  1649.                 'query': '',
  1650.                 'limit': 0
  1651.             }
  1652.         }
  1653.  
  1654.         yield from self.ws.send_as_json(payload)
  1655.  
  1656.     @asyncio.coroutine
  1657.     def kick(self, member):
  1658.         """|coro|
  1659.  
  1660.        Kicks a :class:`Member` from the server they belong to.
  1661.  
  1662.        Warning
  1663.        --------
  1664.        This function kicks the :class:`Member` based on the server it
  1665.        belongs to, which is accessed via :attr:`Member.server`. So you
  1666.        must have the proper permissions in that server.
  1667.  
  1668.        Parameters
  1669.        -----------
  1670.        member : :class:`Member`
  1671.            The member to kick from their server.
  1672.  
  1673.        Raises
  1674.        -------
  1675.        Forbidden
  1676.            You do not have the proper permissions to kick.
  1677.        HTTPException
  1678.            Kicking failed.
  1679.        """
  1680.         yield from self.http.kick(member.id, member.server.id)
  1681.  
  1682.     @asyncio.coroutine
  1683.     def ban(self, member, delete_message_days=1):
  1684.         """|coro|
  1685.  
  1686.        Bans a :class:`Member` from the server they belong to.
  1687.  
  1688.        Warning
  1689.        --------
  1690.        This function bans the :class:`Member` based on the server it
  1691.        belongs to, which is accessed via :attr:`Member.server`. So you
  1692.        must have the proper permissions in that server.
  1693.  
  1694.        Parameters
  1695.        -----------
  1696.        member : :class:`Member`
  1697.            The member to ban from their server.
  1698.        delete_message_days : int
  1699.            The number of days worth of messages to delete from the user
  1700.            in the server. The minimum is 0 and the maximum is 7.
  1701.  
  1702.        Raises
  1703.        -------
  1704.        Forbidden
  1705.            You do not have the proper permissions to ban.
  1706.        HTTPException
  1707.            Banning failed.
  1708.        """
  1709.         yield from self.http.ban(member.id, member.server.id, delete_message_days)
  1710.  
  1711.     @asyncio.coroutine
  1712.     def unban(self, server, user):
  1713.         """|coro|
  1714.  
  1715.        Unbans a :class:`User` from the server they are banned from.
  1716.  
  1717.        Parameters
  1718.        -----------
  1719.        server : :class:`Server`
  1720.            The server to unban the user from.
  1721.        user : :class:`User`
  1722.            The user to unban.
  1723.  
  1724.        Raises
  1725.        -------
  1726.        Forbidden
  1727.            You do not have the proper permissions to unban.
  1728.        HTTPException
  1729.            Unbanning failed.
  1730.        """
  1731.         yield from self.http.unban(user.id, server.id)
  1732.  
  1733.     @asyncio.coroutine
  1734.     def server_voice_state(self, member, *, mute=None, deafen=None):
  1735.         """|coro|
  1736.  
  1737.        Server mutes or deafens a specific :class:`Member`.
  1738.  
  1739.        Warning
  1740.        --------
  1741.        This function mutes or un-deafens the :class:`Member` based on the
  1742.        server it belongs to, which is accessed via :attr:`Member.server`.
  1743.        So you must have the proper permissions in that server.
  1744.  
  1745.        Parameters
  1746.        -----------
  1747.        member : :class:`Member`
  1748.            The member to unban from their server.
  1749.        mute: Optional[bool]
  1750.            Indicates if the member should be server muted or un-muted.
  1751.        deafen: Optional[bool]
  1752.            Indicates if the member should be server deafened or un-deafened.
  1753.  
  1754.        Raises
  1755.        -------
  1756.        Forbidden
  1757.            You do not have the proper permissions to deafen or mute.
  1758.        HTTPException
  1759.            The operation failed.
  1760.        """
  1761.         yield from self.http.server_voice_state(member.id, member.server.id, mute=mute, deafen=deafen)
  1762.  
  1763.     @asyncio.coroutine
  1764.     def edit_profile(self, password=None, **fields):
  1765.         """|coro|
  1766.  
  1767.        Edits the current profile of the client.
  1768.  
  1769.        If a bot account is used then the password field is optional,
  1770.        otherwise it is required.
  1771.  
  1772.        The :attr:`Client.user` object is not modified directly afterwards until the
  1773.        corresponding WebSocket event is received.
  1774.  
  1775.        Note
  1776.        -----
  1777.        To upload an avatar, a *bytes-like object* must be passed in that
  1778.        represents the image being uploaded. If this is done through a file
  1779.        then the file must be opened via ``open('some_filename', 'rb')`` and
  1780.        the *bytes-like object* is given through the use of ``fp.read()``.
  1781.  
  1782.        The only image formats supported for uploading is JPEG and PNG.
  1783.  
  1784.        Parameters
  1785.        -----------
  1786.        password : str
  1787.            The current password for the client's account. Not used
  1788.            for bot accounts.
  1789.        new_password : str
  1790.            The new password you wish to change to.
  1791.        email : str
  1792.            The new email you wish to change to.
  1793.        username :str
  1794.            The new username you wish to change to.
  1795.        avatar : bytes
  1796.            A *bytes-like object* representing the image to upload.
  1797.            Could be ``None`` to denote no avatar.
  1798.  
  1799.        Raises
  1800.        ------
  1801.        HTTPException
  1802.            Editing your profile failed.
  1803.        InvalidArgument
  1804.            Wrong image format passed for ``avatar``.
  1805.        ClientException
  1806.            Password is required for non-bot accounts.
  1807.        """
  1808.  
  1809.         try:
  1810.             avatar_bytes = fields['avatar']
  1811.         except KeyError:
  1812.             avatar = self.user.avatar
  1813.         else:
  1814.             if avatar_bytes is not None:
  1815.                 avatar = utils._bytes_to_base64_data(avatar_bytes)
  1816.             else:
  1817.                 avatar = None
  1818.  
  1819.         not_bot_account = not self.user.bot
  1820.         if not_bot_account and password is None:
  1821.             raise ClientException('Password is required for non-bot accounts.')
  1822.  
  1823.         args = {
  1824.             'password': password,
  1825.             'username': fields.get('username', self.user.name),
  1826.             'avatar': avatar
  1827.         }
  1828.  
  1829.         if not_bot_account:
  1830.             args['email'] = fields.get('email', self.email)
  1831.  
  1832.             if 'new_password' in fields:
  1833.                 args['new_password'] = fields['new_password']
  1834.  
  1835.         data = yield from self.http.edit_profile(**args)
  1836.         if not_bot_account:
  1837.             self.email = data['email']
  1838.             if 'token' in data:
  1839.                 self.http._token(data['token'], bot=False)
  1840.  
  1841.             if self.cache_auth:
  1842.                 self._update_cache(self.email, password)
  1843.  
  1844.     @asyncio.coroutine
  1845.     @utils.deprecated('change_presence')
  1846.     def change_status(self, game=None, idle=False):
  1847.         """|coro|
  1848.  
  1849.        Changes the client's status.
  1850.  
  1851.        The game parameter is a Game object (not a string) that represents
  1852.        a game being played currently.
  1853.  
  1854.        The idle parameter is a boolean parameter that indicates whether the
  1855.        client should go idle or not.
  1856.  
  1857.        .. deprecated:: v0.13.0
  1858.            Use :meth:`change_presence` instead.
  1859.  
  1860.        Parameters
  1861.        ----------
  1862.        game : Optional[:class:`Game`]
  1863.            The game being played. None if no game is being played.
  1864.        idle : bool
  1865.            Indicates if the client should go idle.
  1866.  
  1867.        Raises
  1868.        ------
  1869.        InvalidArgument
  1870.            If the ``game`` parameter is not :class:`Game` or None.
  1871.        """
  1872.         yield from self.ws.change_presence(game=game, idle=idle)
  1873.  
  1874.     @asyncio.coroutine
  1875.     def change_presence(self, *, game=None, status=None, afk=False):
  1876.         """|coro|
  1877.  
  1878.        Changes the client's presence.
  1879.  
  1880.        The game parameter is a Game object (not a string) that represents
  1881.        a game being played currently.
  1882.  
  1883.        Parameters
  1884.        ----------
  1885.        game: Optional[:class:`Game`]
  1886.            The game being played. None if no game is being played.
  1887.        status: Optional[:class:`Status`]
  1888.            Indicates what status to change to. If None, then
  1889.            :attr:`Status.online` is used.
  1890.        afk: bool
  1891.            Indicates if you are going AFK. This allows the discord
  1892.            client to know how to handle push notifications better
  1893.            for you in case you are actually idle and not lying.
  1894.  
  1895.        Raises
  1896.        ------
  1897.        InvalidArgument
  1898.            If the ``game`` parameter is not :class:`Game` or None.
  1899.        """
  1900.  
  1901.         if status is None:
  1902.             status = 'online'
  1903.         elif status is Status.offline:
  1904.             status = 'invisible'
  1905.         else:
  1906.             status = str(status)
  1907.  
  1908.         yield from self.ws.change_presence(game=game, status=status, afk=afk)
  1909.  
  1910.     @asyncio.coroutine
  1911.     def change_nickname(self, member, nickname):
  1912.         """|coro|
  1913.  
  1914.        Changes a member's nickname.
  1915.  
  1916.        You must have the proper permissions to change someone's
  1917.        (or your own) nickname.
  1918.  
  1919.        Parameters
  1920.        ----------
  1921.        member : :class:`Member`
  1922.            The member to change the nickname for.
  1923.        nickname : Optional[str]
  1924.            The nickname to change it to. ``None`` to remove
  1925.            the nickname.
  1926.  
  1927.        Raises
  1928.        ------
  1929.        Forbidden
  1930.            You do not have permissions to change the nickname.
  1931.        HTTPException
  1932.            Changing the nickname failed.
  1933.        """
  1934.  
  1935.         nickname = nickname if nickname else ''
  1936.  
  1937.         if member == self.user:
  1938.             yield from self.http.change_my_nickname(member.server.id, nickname)
  1939.         else:
  1940.             yield from self.http.change_nickname(member.server.id, member.id, nickname)
  1941.  
  1942.     # Channel management
  1943.  
  1944.     @asyncio.coroutine
  1945.     def edit_channel(self, channel, **options):
  1946.         """|coro|
  1947.  
  1948.        Edits a :class:`Channel`.
  1949.  
  1950.        You must have the proper permissions to edit the channel.
  1951.  
  1952.        To move the channel's position use :meth:`move_channel` instead.
  1953.  
  1954.        The :class:`Channel` object is not directly modified afterwards until the
  1955.        corresponding WebSocket event is received.
  1956.  
  1957.        Parameters
  1958.        ----------
  1959.        channel : :class:`Channel`
  1960.            The channel to update.
  1961.        name : str
  1962.            The new channel name.
  1963.        topic : str
  1964.            The new channel's topic.
  1965.        bitrate : int
  1966.            The new channel's bitrate. Voice only.
  1967.        user_limit : int
  1968.            The new channel's user limit. Voice only.
  1969.  
  1970.        Raises
  1971.        ------
  1972.        Forbidden
  1973.            You do not have permissions to edit the channel.
  1974.        HTTPException
  1975.            Editing the channel failed.
  1976.        """
  1977.  
  1978.         keys = ('name', 'topic', 'position')
  1979.         for key in keys:
  1980.             if key not in options:
  1981.                 options[key] = getattr(channel, key)
  1982.  
  1983.         yield from self.http.edit_channel(channel.id, **options)
  1984.  
  1985.     @asyncio.coroutine
  1986.     def move_channel(self, channel, position):
  1987.         """|coro|
  1988.  
  1989.        Moves the specified :class:`Channel` to the given position in the GUI.
  1990.        Note that voice channels and text channels have different position values.
  1991.  
  1992.        The :class:`Channel` object is not directly modified afterwards until the
  1993.        corresponding WebSocket event is received.
  1994.  
  1995.        .. warning::
  1996.  
  1997.            :class:`Object` instances do not work with this function.
  1998.  
  1999.        Parameters
  2000.        -----------
  2001.        channel : :class:`Channel`
  2002.            The channel to change positions of.
  2003.        position : int
  2004.            The position to insert the channel to.
  2005.  
  2006.        Raises
  2007.        -------
  2008.        InvalidArgument
  2009.            If position is less than 0 or greater than the number of channels.
  2010.        Forbidden
  2011.            You do not have permissions to change channel order.
  2012.        HTTPException
  2013.            If moving the channel failed, or you are of too low rank to move the channel.
  2014.        """
  2015.  
  2016.         if position < 0:
  2017.             raise InvalidArgument('Channel position cannot be less than 0.')
  2018.  
  2019.         channels = [c for c in channel.server.channels if c.type is channel.type]
  2020.  
  2021.         if position >= len(channels):
  2022.             raise InvalidArgument('Channel position cannot be greater than {}'.format(len(channels) - 1))
  2023.  
  2024.         channels.sort(key=lambda c: c.position)
  2025.  
  2026.         try:
  2027.             # remove ourselves from the channel list
  2028.             channels.remove(channel)
  2029.         except ValueError:
  2030.             # not there somehow lol
  2031.             return
  2032.         else:
  2033.             # add ourselves at our designated position
  2034.             channels.insert(position, channel)
  2035.  
  2036.         payload = [{'id': c.id, 'position': index } for index, c in enumerate(channels)]
  2037.         yield from self.http.move_channel_position(channel.server.id, payload)
  2038.  
  2039.     @asyncio.coroutine
  2040.     def create_channel(self, server, name, *overwrites, type=None):
  2041.         """|coro|
  2042.  
  2043.        Creates a :class:`Channel` in the specified :class:`Server`.
  2044.  
  2045.        Note that you need the proper permissions to create the channel.
  2046.  
  2047.        The ``overwrites`` argument list can be used to create a 'secret'
  2048.        channel upon creation. A namedtuple of :class:`ChannelPermissions`
  2049.        is exposed to create a channel-specific permission overwrite in a more
  2050.        self-documenting matter. You can also use a regular tuple of ``(target, overwrite)``
  2051.        where the ``overwrite`` expected has to be of type :class:`PermissionOverwrite`.
  2052.  
  2053.        Examples
  2054.        ----------
  2055.  
  2056.        Creating a voice channel:
  2057.  
  2058.        .. code-block:: python
  2059.  
  2060.            await client.create_channel(server, 'Voice', type=discord.ChannelType.voice)
  2061.  
  2062.        Creating a 'secret' text channel:
  2063.  
  2064.        .. code-block:: python
  2065.  
  2066.            everyone_perms = discord.PermissionOverwrite(read_messages=False)
  2067.            my_perms = discord.PermissionOverwrite(read_messages=True)
  2068.  
  2069.            everyone = discord.ChannelPermissions(target=server.default_role, overwrite=everyone_perms)
  2070.            mine = discord.ChannelPermissions(target=server.me, overwrite=my_perms)
  2071.            await client.create_channel(server, 'secret', everyone, mine)
  2072.  
  2073.        Or in a more 'compact' way:
  2074.  
  2075.        .. code-block:: python
  2076.  
  2077.            everyone = discord.PermissionOverwrite(read_messages=False)
  2078.            mine = discord.PermissionOverwrite(read_messages=True)
  2079.            await client.create_channel(server, 'secret', (server.default_role, everyone), (server.me, mine))
  2080.  
  2081.        Parameters
  2082.        -----------
  2083.        server : :class:`Server`
  2084.            The server to create the channel in.
  2085.        name : str
  2086.            The channel's name.
  2087.        type : :class:`ChannelType`
  2088.            The type of channel to create. Defaults to :attr:`ChannelType.text`.
  2089.        overwrites:
  2090.            An argument list of channel specific overwrites to apply on the channel on
  2091.            creation. Useful for creating 'secret' channels.
  2092.  
  2093.        Raises
  2094.        -------
  2095.        Forbidden
  2096.            You do not have the proper permissions to create the channel.
  2097.        NotFound
  2098.            The server specified was not found.
  2099.        HTTPException
  2100.            Creating the channel failed.
  2101.        InvalidArgument
  2102.            The permission overwrite array is not in proper form.
  2103.  
  2104.        Returns
  2105.        -------
  2106.        :class:`Channel`
  2107.            The channel that was just created. This channel is
  2108.            different than the one that will be added in cache.
  2109.        """
  2110.  
  2111.         if type is None:
  2112.             type = ChannelType.text
  2113.  
  2114.         perms = []
  2115.         for overwrite in overwrites:
  2116.             target = overwrite[0]
  2117.             perm = overwrite[1]
  2118.             if not isinstance(perm, PermissionOverwrite):
  2119.                 raise InvalidArgument('Expected PermissionOverwrite received {0.__name__}'.format(type(perm)))
  2120.  
  2121.             allow, deny = perm.pair()
  2122.             payload = {
  2123.                 'allow': allow.value,
  2124.                 'deny': deny.value,
  2125.                 'id': target.id
  2126.             }
  2127.  
  2128.             if isinstance(target, User):
  2129.                 payload['type'] = 'member'
  2130.             elif isinstance(target, Role):
  2131.                 payload['type'] = 'role'
  2132.             else:
  2133.                 raise InvalidArgument('Expected Role, User, or Member target, received {0.__name__}'.format(type(target)))
  2134.  
  2135.             perms.append(payload)
  2136.  
  2137.         data = yield from self.http.create_channel(server.id, name, str(type), permission_overwrites=perms)
  2138.         channel = Channel(server=server, **data)
  2139.         return channel
  2140.  
  2141.     @asyncio.coroutine
  2142.     def delete_channel(self, channel):
  2143.         """|coro|
  2144.  
  2145.        Deletes a :class:`Channel`.
  2146.  
  2147.        In order to delete the channel, the client must have the proper permissions
  2148.        in the server the channel belongs to.
  2149.  
  2150.        Parameters
  2151.        ------------
  2152.        channel : :class:`Channel`
  2153.            The channel to delete.
  2154.  
  2155.        Raises
  2156.        -------
  2157.        Forbidden
  2158.            You do not have proper permissions to delete the channel.
  2159.        NotFound
  2160.            The specified channel was not found.
  2161.        HTTPException
  2162.            Deleting the channel failed.
  2163.        """
  2164.         yield from self.http.delete_channel(channel.id)
  2165.  
  2166.     # Server management
  2167.  
  2168.     @asyncio.coroutine
  2169.     def leave_server(self, server):
  2170.         """|coro|
  2171.  
  2172.        Leaves a :class:`Server`.
  2173.  
  2174.        Note
  2175.        --------
  2176.        You cannot leave the server that you own, you must delete it instead
  2177.        via :meth:`delete_server`.
  2178.  
  2179.        Parameters
  2180.        ----------
  2181.        server : :class:`Server`
  2182.            The server to leave.
  2183.  
  2184.        Raises
  2185.        --------
  2186.        HTTPException
  2187.            If leaving the server failed.
  2188.        """
  2189.         yield from self.http.leave_server(server.id)
  2190.  
  2191.     @asyncio.coroutine
  2192.     def delete_server(self, server):
  2193.         """|coro|
  2194.  
  2195.        Deletes a :class:`Server`. You must be the server owner to delete the
  2196.        server.
  2197.  
  2198.        Parameters
  2199.        ----------
  2200.        server : :class:`Server`
  2201.            The server to delete.
  2202.  
  2203.        Raises
  2204.        --------
  2205.        HTTPException
  2206.            If deleting the server failed.
  2207.        Forbidden
  2208.            You do not have permissions to delete the server.
  2209.        """
  2210.  
  2211.         yield from self.http.delete_server(server.id)
  2212.  
  2213.     @asyncio.coroutine
  2214.     def create_server(self, name, region=None, icon=None):
  2215.         """|coro|
  2216.  
  2217.        Creates a :class:`Server`.
  2218.  
  2219.        Bot accounts generally are not allowed to create servers.
  2220.        See Discord's official documentation for more info.
  2221.  
  2222.        Parameters
  2223.        ----------
  2224.        name : str
  2225.            The name of the server.
  2226.        region : :class:`ServerRegion`
  2227.            The region for the voice communication server.
  2228.            Defaults to :attr:`ServerRegion.us_west`.
  2229.        icon : bytes
  2230.            The *bytes-like* object representing the icon. See :meth:`edit_profile`
  2231.            for more details on what is expected.
  2232.  
  2233.        Raises
  2234.        ------
  2235.        HTTPException
  2236.            Server creation failed.
  2237.        InvalidArgument
  2238.            Invalid icon image format given. Must be PNG or JPG.
  2239.  
  2240.        Returns
  2241.        -------
  2242.        :class:`Server`
  2243.            The server created. This is not the same server that is
  2244.            added to cache.
  2245.        """
  2246.         if icon is not None:
  2247.             icon = utils._bytes_to_base64_data(icon)
  2248.  
  2249.         if region is None:
  2250.             region = ServerRegion.us_west.value
  2251.         else:
  2252.             region = region.value
  2253.  
  2254.         data = yield from self.http.create_server(name, region, icon)
  2255.         return Server(**data)
  2256.  
  2257.     @asyncio.coroutine
  2258.     def edit_server(self, server, **fields):
  2259.         """|coro|
  2260.  
  2261.        Edits a :class:`Server`.
  2262.  
  2263.        You must have the proper permissions to edit the server.
  2264.  
  2265.        The :class:`Server` object is not directly modified afterwards until the
  2266.        corresponding WebSocket event is received.
  2267.  
  2268.        Parameters
  2269.        ----------
  2270.        server: :class:`Server`
  2271.            The server to edit.
  2272.        name: str
  2273.            The new name of the server.
  2274.        icon: bytes
  2275.            A *bytes-like* object representing the icon. See :meth:`edit_profile`
  2276.            for more details. Could be ``None`` to denote no icon.
  2277.        splash: bytes
  2278.            A *bytes-like* object representing the invite splash. See
  2279.            :meth:`edit_profile` for more details. Could be ``None`` to denote
  2280.            no invite splash. Only available for partnered servers with
  2281.            ``INVITE_SPLASH`` feature.
  2282.        region: :class:`ServerRegion`
  2283.            The new region for the server's voice communication.
  2284.        afk_channel: Optional[:class:`Channel`]
  2285.            The new channel that is the AFK channel. Could be ``None`` for no AFK channel.
  2286.        afk_timeout: int
  2287.            The number of seconds until someone is moved to the AFK channel.
  2288.        owner: :class:`Member`
  2289.            The new owner of the server to transfer ownership to. Note that you must
  2290.            be owner of the server to do this.
  2291.        verification_level: :class:`VerificationLevel`
  2292.            The new verification level for the server.
  2293.  
  2294.        Raises
  2295.        -------
  2296.        Forbidden
  2297.            You do not have permissions to edit the server.
  2298.        NotFound
  2299.            The server you are trying to edit does not exist.
  2300.        HTTPException
  2301.            Editing the server failed.
  2302.        InvalidArgument
  2303.            The image format passed in to ``icon`` is invalid. It must be
  2304.            PNG or JPG. This is also raised if you are not the owner of the
  2305.            server and request an ownership transfer.
  2306.        """
  2307.  
  2308.         try:
  2309.             icon_bytes = fields['icon']
  2310.         except KeyError:
  2311.             icon = server.icon
  2312.         else:
  2313.             if icon_bytes is not None:
  2314.                 icon = utils._bytes_to_base64_data(icon_bytes)
  2315.             else:
  2316.                 icon = None
  2317.  
  2318.         try:
  2319.             splash_bytes = fields['splash']
  2320.         except KeyError:
  2321.             splash = server.splash
  2322.         else:
  2323.             if splash_bytes is not None:
  2324.                 splash = utils._bytes_to_base64_data(splash_bytes)
  2325.             else:
  2326.                 splash = None
  2327.  
  2328.         fields['icon'] = icon
  2329.         fields['splash'] = splash
  2330.  
  2331.         try:
  2332.             afk_channel = fields.pop('afk_channel')
  2333.         except KeyError:
  2334.             pass
  2335.         else:
  2336.             if afk_channel is None:
  2337.                 fields['afk_channel_id'] = afk_channel
  2338.             else:
  2339.                 fields['afk_channel_id'] = afk_channel.id
  2340.  
  2341.         if 'owner' in fields:
  2342.             if server.owner != server.me:
  2343.                 raise InvalidArgument('To transfer ownership you must be the owner of the server.')
  2344.  
  2345.             fields['owner_id'] = fields['owner'].id
  2346.  
  2347.         if 'region' in fields:
  2348.             fields['region'] = str(fields['region'])
  2349.  
  2350.         level = fields.get('verification_level', server.verification_level)
  2351.         if not isinstance(level, VerificationLevel):
  2352.             raise InvalidArgument('verification_level field must of type VerificationLevel')
  2353.  
  2354.         fields['verification_level'] = level.value
  2355.         yield from self.http.edit_server(server.id, **fields)
  2356.  
  2357.     @asyncio.coroutine
  2358.     def get_bans(self, server):
  2359.         """|coro|
  2360.  
  2361.        Retrieves all the :class:`User` s that are banned from the specified
  2362.        server.
  2363.  
  2364.        You must have proper permissions to get this information.
  2365.  
  2366.        Parameters
  2367.        ----------
  2368.        server : :class:`Server`
  2369.            The server to get ban information from.
  2370.  
  2371.        Raises
  2372.        -------
  2373.        Forbidden
  2374.            You do not have proper permissions to get the information.
  2375.        HTTPException
  2376.            An error occurred while fetching the information.
  2377.  
  2378.        Returns
  2379.        --------
  2380.        list
  2381.            A list of :class:`User` that have been banned.
  2382.        """
  2383.  
  2384.         data = yield from self.http.get_bans(server.id)
  2385.         return [User(**user['user']) for user in data]
  2386.  
  2387.     @asyncio.coroutine
  2388.     def prune_members(self, server, *, days):
  2389.         """|coro|
  2390.  
  2391.        Prunes a :class:`Server` from its inactive members.
  2392.  
  2393.        The inactive members are denoted if they have not logged on in
  2394.        ``days`` number of days and they have no roles.
  2395.  
  2396.        You must have the "Kick Members" permission to use this.
  2397.  
  2398.        To check how many members you would prune without actually pruning,
  2399.        see the :meth:`estimate_pruned_members` function.
  2400.  
  2401.        Parameters
  2402.        -----------
  2403.        server: :class:`Server`
  2404.            The server to prune from.
  2405.        days: int
  2406.            The number of days before counting as inactive.
  2407.  
  2408.        Raises
  2409.        -------
  2410.        Forbidden
  2411.            You do not have permissions to prune members.
  2412.        HTTPException
  2413.            An error occurred while pruning members.
  2414.        InvalidArgument
  2415.            An integer was not passed for ``days``.
  2416.  
  2417.        Returns
  2418.        ---------
  2419.        int
  2420.            The number of members pruned.
  2421.        """
  2422.  
  2423.         if not isinstance(days, int):
  2424.             raise InvalidArgument('Expected int for ``days``, received {0.__class__.__name__} instead.'.format(days))
  2425.  
  2426.         data = yield from self.http.prune_members(server.id, days)
  2427.         return data['pruned']
  2428.  
  2429.     @asyncio.coroutine
  2430.     def estimate_pruned_members(self, server, *, days):
  2431.         """|coro|
  2432.  
  2433.        Similar to :meth:`prune_members` except instead of actually
  2434.        pruning members, it returns how many members it would prune
  2435.        from the server had it been called.
  2436.  
  2437.        Parameters
  2438.        -----------
  2439.        server: :class:`Server`
  2440.            The server to estimate a prune from.
  2441.        days: int
  2442.            The number of days before counting as inactive.
  2443.  
  2444.        Raises
  2445.        -------
  2446.        Forbidden
  2447.            You do not have permissions to prune members.
  2448.        HTTPException
  2449.            An error occurred while fetching the prune members estimate.
  2450.        InvalidArgument
  2451.            An integer was not passed for ``days``.
  2452.  
  2453.        Returns
  2454.        ---------
  2455.        int
  2456.            The number of members estimated to be pruned.
  2457.        """
  2458.  
  2459.         if not isinstance(days, int):
  2460.             raise InvalidArgument('Expected int for ``days``, received {0.__class__.__name__} instead.'.format(days))
  2461.  
  2462.         data = yield from self.http.estimate_pruned_members(server.id, days)
  2463.         return data['pruned']
  2464.  
  2465.     @asyncio.coroutine
  2466.     def create_custom_emoji(self, server, *, name, image):
  2467.         """|coro|
  2468.  
  2469.        Creates a custom :class:`Emoji` for a :class:`Server`.
  2470.  
  2471.        This endpoint is only allowed for user bots or white listed
  2472.        bots. If this is done by a user bot then this is a local
  2473.        emoji that can only be used inside that server.
  2474.  
  2475.        There is currently a limit of 50 local emotes per server.
  2476.  
  2477.        Parameters
  2478.        -----------
  2479.        server: :class:`Server`
  2480.            The server to add the emoji to.
  2481.        name: str
  2482.            The emoji name. Must be at least 2 characters.
  2483.        image: bytes
  2484.            The *bytes-like* object representing the image data to use.
  2485.            Only JPG and PNG images are supported.
  2486.  
  2487.        Returns
  2488.        --------
  2489.        :class:`Emoji`
  2490.            The created emoji.
  2491.  
  2492.        Raises
  2493.        -------
  2494.        Forbidden
  2495.            You are not allowed to create emojis.
  2496.        HTTPException
  2497.            An error occurred creating an emoji.
  2498.        """
  2499.  
  2500.         img = utils._bytes_to_base64_data(image)
  2501.         data = yield from self.http.create_custom_emoji(server.id, name, img)
  2502.         return Emoji(server=server, **data)
  2503.  
  2504.     @asyncio.coroutine
  2505.     def delete_custom_emoji(self, emoji):
  2506.         """|coro|
  2507.  
  2508.        Deletes a custom :class:`Emoji` from a :class:`Server`.
  2509.  
  2510.        This follows the same rules as :meth:`create_custom_emoji`.
  2511.  
  2512.        Parameters
  2513.        -----------
  2514.        emoji: :class:`Emoji`
  2515.            The emoji to delete.
  2516.  
  2517.        Raises
  2518.        -------
  2519.        Forbidden
  2520.            You are not allowed to delete emojis.
  2521.        HTTPException
  2522.            An error occurred deleting the emoji.
  2523.        """
  2524.  
  2525.         yield from self.http.delete_custom_emoji(emoji.server.id, emoji.id)
  2526.  
  2527.     @asyncio.coroutine
  2528.     def edit_custom_emoji(self, emoji, *, name):
  2529.         """|coro|
  2530.  
  2531.        Edits a :class:`Emoji`.
  2532.  
  2533.        Parameters
  2534.        -----------
  2535.        emoji: :class:`Emoji`
  2536.            The emoji to edit.
  2537.        name: str
  2538.            The new emoji name.
  2539.  
  2540.        Raises
  2541.        -------
  2542.        Forbidden
  2543.            You are not allowed to edit emojis.
  2544.        HTTPException
  2545.            An error occurred editing the emoji.
  2546.        """
  2547.  
  2548.         yield from self.http.edit_custom_emoji(emoji.server.id, emoji.id, name=name)
  2549.  
  2550.  
  2551.     # Invite management
  2552.  
  2553.     def _fill_invite_data(self, data):
  2554.         server = self.connection._get_server(data['guild']['id'])
  2555.         if server is not None:
  2556.             ch_id = data['channel']['id']
  2557.             channel = server.get_channel(ch_id)
  2558.         else:
  2559.             server = Object(id=data['guild']['id'])
  2560.             server.name = data['guild']['name']
  2561.             channel = Object(id=data['channel']['id'])
  2562.             channel.name = data['channel']['name']
  2563.         data['server'] = server
  2564.         data['channel'] = channel
  2565.  
  2566.     @asyncio.coroutine
  2567.     def create_invite(self, destination, **options):
  2568.         """|coro|
  2569.  
  2570.        Creates an invite for the destination which could be either a
  2571.        :class:`Server` or :class:`Channel`.
  2572.  
  2573.        Parameters
  2574.        ------------
  2575.        destination
  2576.            The :class:`Server` or :class:`Channel` to create the invite to.
  2577.        max_age : int
  2578.            How long the invite should last. If it's 0 then the invite
  2579.            doesn't expire. Defaults to 0.
  2580.        max_uses : int
  2581.            How many uses the invite could be used for. If it's 0 then there
  2582.            are unlimited uses. Defaults to 0.
  2583.        temporary : bool
  2584.            Denotes that the invite grants temporary membership
  2585.            (i.e. they get kicked after they disconnect). Defaults to False.
  2586.        unique: bool
  2587.            Indicates if a unique invite URL should be created. Defaults to True.
  2588.            If this is set to False then it will return a previously created
  2589.            invite.
  2590.  
  2591.        Raises
  2592.        -------
  2593.        HTTPException
  2594.            Invite creation failed.
  2595.  
  2596.        Returns
  2597.        --------
  2598.        :class:`Invite`
  2599.            The invite that was created.
  2600.        """
  2601.  
  2602.         data = yield from self.http.create_invite(destination.id, **options)
  2603.         self._fill_invite_data(data)
  2604.         return Invite(**data)
  2605.  
  2606.     @asyncio.coroutine
  2607.     def get_invite(self, url):
  2608.         """|coro|
  2609.  
  2610.        Gets a :class:`Invite` from a discord.gg URL or ID.
  2611.  
  2612.        Note
  2613.        ------
  2614.        If the invite is for a server you have not joined, the server and channel
  2615.        attributes of the returned invite will be :class:`Object` with the names
  2616.        patched in.
  2617.  
  2618.        Parameters
  2619.        -----------
  2620.        url : str
  2621.            The discord invite ID or URL (must be a discord.gg URL).
  2622.  
  2623.        Raises
  2624.        -------
  2625.        NotFound
  2626.            The invite has expired or is invalid.
  2627.        HTTPException
  2628.            Getting the invite failed.
  2629.  
  2630.        Returns
  2631.        --------
  2632.        :class:`Invite`
  2633.            The invite from the URL/ID.
  2634.        """
  2635.  
  2636.         invite_id = self._resolve_invite(url)
  2637.         data = yield from self.http.get_invite(invite_id)
  2638.         self._fill_invite_data(data)
  2639.         return Invite(**data)
  2640.  
  2641.     @asyncio.coroutine
  2642.     def invites_from(self, server):
  2643.         """|coro|
  2644.  
  2645.        Returns a list of all active instant invites from a :class:`Server`.
  2646.  
  2647.        You must have proper permissions to get this information.
  2648.  
  2649.        Parameters
  2650.        ----------
  2651.        server : :class:`Server`
  2652.            The server to get invites from.
  2653.  
  2654.        Raises
  2655.        -------
  2656.        Forbidden
  2657.            You do not have proper permissions to get the information.
  2658.        HTTPException
  2659.            An error occurred while fetching the information.
  2660.  
  2661.        Returns
  2662.        -------
  2663.        list of :class:`Invite`
  2664.            The list of invites that are currently active.
  2665.        """
  2666.  
  2667.         data = yield from self.http.invites_from(server.id)
  2668.         result = []
  2669.         for invite in data:
  2670.             channel = server.get_channel(invite['channel']['id'])
  2671.             invite['channel'] = channel
  2672.             invite['server'] = server
  2673.             result.append(Invite(**invite))
  2674.  
  2675.         return result
  2676.  
  2677.     @asyncio.coroutine
  2678.     def accept_invite(self, invite):
  2679.         """|coro|
  2680.  
  2681.        Accepts an :class:`Invite`, URL or ID to an invite.
  2682.  
  2683.        The URL must be a discord.gg URL. e.g. "http://discord.gg/codehere".
  2684.        An ID for the invite is just the "codehere" portion of the invite URL.
  2685.  
  2686.        Parameters
  2687.        -----------
  2688.        invite
  2689.            The :class:`Invite` or URL to an invite to accept.
  2690.  
  2691.        Raises
  2692.        -------
  2693.        HTTPException
  2694.            Accepting the invite failed.
  2695.        NotFound
  2696.            The invite is invalid or expired.
  2697.        Forbidden
  2698.            You are a bot user and cannot use this endpoint.
  2699.        """
  2700.  
  2701.         invite_id = self._resolve_invite(invite)
  2702.         yield from self.http.accept_invite(invite_id)
  2703.  
  2704.     @asyncio.coroutine
  2705.     def delete_invite(self, invite):
  2706.         """|coro|
  2707.  
  2708.        Revokes an :class:`Invite`, URL, or ID to an invite.
  2709.  
  2710.        The ``invite`` parameter follows the same rules as
  2711.        :meth:`accept_invite`.
  2712.  
  2713.        Parameters
  2714.        ----------
  2715.        invite
  2716.            The invite to revoke.
  2717.  
  2718.        Raises
  2719.        -------
  2720.        Forbidden
  2721.            You do not have permissions to revoke invites.
  2722.        NotFound
  2723.            The invite is invalid or expired.
  2724.        HTTPException
  2725.            Revoking the invite failed.
  2726.        """
  2727.  
  2728.         invite_id = self._resolve_invite(invite)
  2729.         yield from self.http.delete_invite(invite_id)
  2730.  
  2731.     # Role management
  2732.  
  2733.     @asyncio.coroutine
  2734.     def move_role(self, server, role, position):
  2735.         """|coro|
  2736.  
  2737.        Moves the specified :class:`Role` to the given position in the :class:`Server`.
  2738.  
  2739.        The :class:`Role` object is not directly modified afterwards until the
  2740.        corresponding WebSocket event is received.
  2741.  
  2742.        Parameters
  2743.        -----------
  2744.        server : :class:`Server`
  2745.            The server the role belongs to.
  2746.        role : :class:`Role`
  2747.            The role to edit.
  2748.        position : int
  2749.            The position to insert the role to.
  2750.  
  2751.        Raises
  2752.        -------
  2753.        InvalidArgument
  2754.            If position is 0, or role is server.default_role
  2755.        Forbidden
  2756.            You do not have permissions to change role order.
  2757.        HTTPException
  2758.            If moving the role failed, or you are of too low rank to move the role.
  2759.        """
  2760.  
  2761.         if position == 0:
  2762.             raise InvalidArgument("Cannot move role to position 0")
  2763.  
  2764.         if role == server.default_role:
  2765.             raise InvalidArgument("Cannot move default role")
  2766.  
  2767.         if role.position == position:
  2768.             return  # Save discord the extra request.
  2769.  
  2770.         change_range = range(min(role.position, position), max(role.position, position) + 1)
  2771.  
  2772.         roles = [r.id for r in sorted(filter(lambda x: (x.position in change_range) and x != role, server.roles), key=lambda x: x.position)]
  2773.  
  2774.         if role.position > position:
  2775.             roles.insert(0, role.id)
  2776.         else:
  2777.             roles.append(role.id)
  2778.  
  2779.         payload = [{"id": z[0], "position": z[1]} for z in zip(roles, change_range)]
  2780.         yield from self.http.move_role_position(server.id, payload)
  2781.  
  2782.     @asyncio.coroutine
  2783.     def edit_role(self, server, role, **fields):
  2784.         """|coro|
  2785.  
  2786.        Edits the specified :class:`Role` for the entire :class:`Server`.
  2787.  
  2788.        The :class:`Role` object is not directly modified afterwards until the
  2789.        corresponding WebSocket event is received.
  2790.  
  2791.        All fields except ``server`` and ``role`` are optional. To change
  2792.        the position of a role, use :func:`move_role` instead.
  2793.  
  2794.        .. versionchanged:: 0.8.0
  2795.            Editing now uses keyword arguments instead of editing the :class:`Role` object directly.
  2796.  
  2797.        Parameters
  2798.        -----------
  2799.        server : :class:`Server`
  2800.            The server the role belongs to.
  2801.        role : :class:`Role`
  2802.            The role to edit.
  2803.        name : str
  2804.            The new role name to change to.
  2805.        permissions : :class:`Permissions`
  2806.            The new permissions to change to.
  2807.        colour : :class:`Colour`
  2808.            The new colour to change to. (aliased to color as well)
  2809.        hoist : bool
  2810.            Indicates if the role should be shown separately in the online list.
  2811.        mentionable : bool
  2812.            Indicates if the role should be mentionable by others.
  2813.  
  2814.        Raises
  2815.        -------
  2816.        Forbidden
  2817.            You do not have permissions to change the role.
  2818.        HTTPException
  2819.            Editing the role failed.
  2820.        """
  2821.  
  2822.         colour = fields.get('colour')
  2823.         if colour is None:
  2824.             colour = fields.get('color', role.colour)
  2825.  
  2826.         payload = {
  2827.             'name': fields.get('name', role.name),
  2828.             'permissions': fields.get('permissions', role.permissions).value,
  2829.             'color': colour.value,
  2830.             'hoist': fields.get('hoist', role.hoist),
  2831.             'mentionable': fields.get('mentionable', role.mentionable)
  2832.         }
  2833.  
  2834.         yield from self.http.edit_role(server.id, role.id, **payload)
  2835.  
  2836.     @asyncio.coroutine
  2837.     def delete_role(self, server, role):
  2838.         """|coro|
  2839.  
  2840.        Deletes the specified :class:`Role` for the entire :class:`Server`.
  2841.  
  2842.        Parameters
  2843.        -----------
  2844.        server : :class:`Server`
  2845.            The server the role belongs to.
  2846.        role : :class:`Role`
  2847.            The role to delete.
  2848.  
  2849.        Raises
  2850.        --------
  2851.        Forbidden
  2852.            You do not have permissions to delete the role.
  2853.        HTTPException
  2854.            Deleting the role failed.
  2855.        """
  2856.  
  2857.         yield from self.http.delete_role(server.id, role.id)
  2858.  
  2859.     @asyncio.coroutine
  2860.     def _replace_roles(self, member, roles):
  2861.         yield from self.http.replace_roles(member.id, member.server.id, roles)
  2862.  
  2863.     @asyncio.coroutine
  2864.     def add_roles(self, member, *roles):
  2865.         """|coro|
  2866.  
  2867.        Gives the specified :class:`Member` a number of :class:`Role` s.
  2868.  
  2869.        You must have the proper permissions to use this function.
  2870.  
  2871.        The :class:`Member` object is not directly modified afterwards until the
  2872.        corresponding WebSocket event is received.
  2873.  
  2874.        Parameters
  2875.        -----------
  2876.        member : :class:`Member`
  2877.            The member to give roles to.
  2878.        \*roles
  2879.            An argument list of :class:`Role` s to give the member.
  2880.  
  2881.        Raises
  2882.        -------
  2883.        Forbidden
  2884.            You do not have permissions to add roles.
  2885.        HTTPException
  2886.            Adding roles failed.
  2887.        """
  2888.  
  2889.         new_roles = utils._unique(role.id for role in itertools.chain(member.roles, roles))
  2890.         yield from self._replace_roles(member, new_roles)
  2891.  
  2892.     @asyncio.coroutine
  2893.     def remove_roles(self, member, *roles):
  2894.         """|coro|
  2895.  
  2896.        Removes the :class:`Role` s from the :class:`Member`.
  2897.  
  2898.        You must have the proper permissions to use this function.
  2899.  
  2900.        The :class:`Member` object is not directly modified afterwards until the
  2901.        corresponding WebSocket event is received.
  2902.  
  2903.        Parameters
  2904.        -----------
  2905.        member : :class:`Member`
  2906.            The member to revoke roles from.
  2907.        \*roles
  2908.            An argument list of :class:`Role` s to revoke the member.
  2909.  
  2910.        Raises
  2911.        -------
  2912.        Forbidden
  2913.            You do not have permissions to revoke roles.
  2914.        HTTPException
  2915.            Removing roles failed.
  2916.        """
  2917.         new_roles = [x.id for x in member.roles]
  2918.         for role in roles:
  2919.             try:
  2920.                 new_roles.remove(role.id)
  2921.             except ValueError:
  2922.                 pass
  2923.  
  2924.         yield from self._replace_roles(member, new_roles)
  2925.  
  2926.     @asyncio.coroutine
  2927.     def replace_roles(self, member, *roles):
  2928.         """|coro|
  2929.  
  2930.        Replaces the :class:`Member`'s roles.
  2931.  
  2932.        You must have the proper permissions to use this function.
  2933.  
  2934.        This function **replaces** all roles that the member has.
  2935.        For example if the member has roles ``[a, b, c]`` and the
  2936.        call is ``client.replace_roles(member, d, e, c)`` then
  2937.        the member has the roles ``[d, e, c]``.
  2938.  
  2939.        The :class:`Member` object is not directly modified afterwards until the
  2940.        corresponding WebSocket event is received.
  2941.  
  2942.        Parameters
  2943.        -----------
  2944.        member : :class:`Member`
  2945.            The member to replace roles from.
  2946.        \*roles
  2947.            An argument list of :class:`Role` s to replace the roles with.
  2948.  
  2949.        Raises
  2950.        -------
  2951.        Forbidden
  2952.            You do not have permissions to revoke roles.
  2953.        HTTPException
  2954.            Removing roles failed.
  2955.        """
  2956.  
  2957.         new_roles = utils._unique(role.id for role in roles)
  2958.         yield from self._replace_roles(member, new_roles)
  2959.  
  2960.     @asyncio.coroutine
  2961.     def create_role(self, server, **fields):
  2962.         """|coro|
  2963.  
  2964.        Creates a :class:`Role`.
  2965.  
  2966.        This function is similar to :class:`edit_role` in both
  2967.        the fields taken and exceptions thrown.
  2968.  
  2969.        Returns
  2970.        --------
  2971.        :class:`Role`
  2972.            The newly created role. This not the same role that
  2973.            is stored in cache.
  2974.        """
  2975.  
  2976.         data = yield from self.http.create_role(server.id)
  2977.         role = Role(server=server, **data)
  2978.  
  2979.         # we have to call edit because you can't pass a payload to the
  2980.         # http request currently.
  2981.         yield from self.edit_role(server, role, **fields)
  2982.         return role
  2983.  
  2984.     @asyncio.coroutine
  2985.     def edit_channel_permissions(self, channel, target, overwrite=None):
  2986.         """|coro|
  2987.  
  2988.        Sets the channel specific permission overwrites for a target in the
  2989.        specified :class:`Channel`.
  2990.  
  2991.        The ``target`` parameter should either be a :class:`Member` or a
  2992.        :class:`Role` that belongs to the channel's server.
  2993.  
  2994.        You must have the proper permissions to do this.
  2995.  
  2996.        Examples
  2997.        ----------
  2998.  
  2999.        Setting allow and deny: ::
  3000.  
  3001.            overwrite = discord.PermissionOverwrite()
  3002.            overwrite.read_messages = True
  3003.            overwrite.ban_members = False
  3004.            await client.edit_channel_permissions(message.channel, message.author, overwrite)
  3005.  
  3006.        Parameters
  3007.        -----------
  3008.        channel : :class:`Channel`
  3009.            The channel to give the specific permissions for.
  3010.        target
  3011.            The :class:`Member` or :class:`Role` to overwrite permissions for.
  3012.        overwrite: :class:`PermissionOverwrite`
  3013.            The permissions to allow and deny to the target.
  3014.  
  3015.        Raises
  3016.        -------
  3017.        Forbidden
  3018.            You do not have permissions to edit channel specific permissions.
  3019.        NotFound
  3020.            The channel specified was not found.
  3021.        HTTPException
  3022.            Editing channel specific permissions failed.
  3023.        InvalidArgument
  3024.            The overwrite parameter was not of type :class:`PermissionOverwrite`
  3025.            or the target type was not :class:`Role` or :class:`Member`.
  3026.        """
  3027.  
  3028.         overwrite = PermissionOverwrite() if overwrite is None else overwrite
  3029.  
  3030.  
  3031.         if not isinstance(overwrite, PermissionOverwrite):
  3032.             raise InvalidArgument('allow and deny parameters must be PermissionOverwrite')
  3033.  
  3034.         allow, deny = overwrite.pair()
  3035.  
  3036.         if isinstance(target, Member):
  3037.             perm_type = 'member'
  3038.         elif isinstance(target, Role):
  3039.             perm_type = 'role'
  3040.         else:
  3041.             raise InvalidArgument('target parameter must be either Member or Role')
  3042.  
  3043.         yield from self.http.edit_channel_permissions(channel.id, target.id, allow.value, deny.value, perm_type)
  3044.  
  3045.     @asyncio.coroutine
  3046.     def delete_channel_permissions(self, channel, target):
  3047.         """|coro|
  3048.  
  3049.        Removes a channel specific permission overwrites for a target
  3050.        in the specified :class:`Channel`.
  3051.  
  3052.        The target parameter follows the same rules as :meth:`edit_channel_permissions`.
  3053.  
  3054.        You must have the proper permissions to do this.
  3055.  
  3056.        Parameters
  3057.        ----------
  3058.        channel : :class:`Channel`
  3059.            The channel to give the specific permissions for.
  3060.        target
  3061.            The :class:`Member` or :class:`Role` to overwrite permissions for.
  3062.  
  3063.        Raises
  3064.        ------
  3065.        Forbidden
  3066.            You do not have permissions to delete channel specific permissions.
  3067.        NotFound
  3068.            The channel specified was not found.
  3069.        HTTPException
  3070.            Deleting channel specific permissions failed.
  3071.        """
  3072.         yield from self.http.delete_channel_permissions(channel.id, target.id)
  3073.  
  3074.     # Voice management
  3075.  
  3076.     @asyncio.coroutine
  3077.     def move_member(self, member, channel):
  3078.         """|coro|
  3079.  
  3080.        Moves a :class:`Member` to a different voice channel.
  3081.  
  3082.        You must have proper permissions to do this.
  3083.  
  3084.        Note
  3085.        -----
  3086.        You cannot pass in a :class:`Object` instead of a :class:`Channel`
  3087.        object in this function.
  3088.  
  3089.        Parameters
  3090.        -----------
  3091.        member : :class:`Member`
  3092.            The member to move to another voice channel.
  3093.        channel : :class:`Channel`
  3094.            The voice channel to move the member to.
  3095.  
  3096.        Raises
  3097.        -------
  3098.        InvalidArgument
  3099.            The channel provided is not a voice channel.
  3100.        HTTPException
  3101.            Moving the member failed.
  3102.        Forbidden
  3103.            You do not have permissions to move the member.
  3104.        """
  3105.  
  3106.         if getattr(channel, 'type', ChannelType.text) != ChannelType.voice:
  3107.             raise InvalidArgument('The channel provided must be a voice channel.')
  3108.  
  3109.         yield from self.http.move_member(member.id, member.server.id, channel.id)
  3110.  
  3111.     @asyncio.coroutine
  3112.     def join_voice_channel(self, channel):
  3113.         """|coro|
  3114.  
  3115.        Joins a voice channel and creates a :class:`VoiceClient` to
  3116.        establish your connection to the voice server.
  3117.  
  3118.        After this function is successfully called, :attr:`voice` is
  3119.        set to the returned :class:`VoiceClient`.
  3120.  
  3121.        Parameters
  3122.        ----------
  3123.        channel : :class:`Channel`
  3124.            The voice channel to join to.
  3125.  
  3126.        Raises
  3127.        -------
  3128.        InvalidArgument
  3129.            The channel was not a voice channel.
  3130.        asyncio.TimeoutError
  3131.            Could not connect to the voice channel in time.
  3132.        ClientException
  3133.            You are already connected to a voice channel.
  3134.        OpusNotLoaded
  3135.            The opus library has not been loaded.
  3136.  
  3137.        Returns
  3138.        -------
  3139.        :class:`VoiceClient`
  3140.            A voice client that is fully connected to the voice server.
  3141.        """
  3142.         if isinstance(channel, Object):
  3143.             channel = self.get_channel(channel.id)
  3144.  
  3145.         if getattr(channel, 'type', ChannelType.text) != ChannelType.voice:
  3146.             raise InvalidArgument('Channel passed must be a voice channel')
  3147.  
  3148.         server = channel.server
  3149.  
  3150.         if self.is_voice_connected(server):
  3151.             raise ClientException('Already connected to a voice channel in this server')
  3152.  
  3153.         log.info('attempting to join voice channel {0.name}'.format(channel))
  3154.  
  3155.         def session_id_found(data):
  3156.             user_id = data.get('user_id')
  3157.             guild_id = data.get('guild_id')
  3158.             return user_id == self.user.id and guild_id == server.id
  3159.  
  3160.         # register the futures for waiting
  3161.         session_id_future = self.ws.wait_for('VOICE_STATE_UPDATE', session_id_found)
  3162.         voice_data_future = self.ws.wait_for('VOICE_SERVER_UPDATE', lambda d: d.get('guild_id') == server.id)
  3163.  
  3164.         # request joining
  3165.         yield from self.ws.voice_state(server.id, channel.id)
  3166.  
  3167.         try:
  3168.             session_id_data = yield from asyncio.wait_for(session_id_future, timeout=10.0, loop=self.loop)
  3169.             data = yield from asyncio.wait_for(voice_data_future, timeout=10.0, loop=self.loop)
  3170.         except asyncio.TimeoutError as e:
  3171.             yield from self.ws.voice_state(server.id, None, self_mute=True)
  3172.             raise e
  3173.  
  3174.         kwargs = {
  3175.             'user': self.user,
  3176.             'channel': channel,
  3177.             'data': data,
  3178.             'loop': self.loop,
  3179.             'session_id': session_id_data.get('session_id'),
  3180.             'main_ws': self.ws
  3181.         }
  3182.  
  3183.         voice = VoiceClient(**kwargs)
  3184.         try:
  3185.             yield from voice.connect()
  3186.         except asyncio.TimeoutError as e:
  3187.             try:
  3188.                 yield from voice.disconnect()
  3189.             except:
  3190.                 # we don't care if disconnect failed because connection failed
  3191.                 pass
  3192.             raise e # re-raise
  3193.  
  3194.         self.connection._add_voice_client(server.id, voice)
  3195.         return voice
  3196.  
  3197.     def is_voice_connected(self, server):
  3198.         """Indicates if we are currently connected to a voice channel in the
  3199.        specified server.
  3200.  
  3201.        Parameters
  3202.        -----------
  3203.        server : :class:`Server`
  3204.            The server to query if we're connected to it.
  3205.        """
  3206.         voice = self.voice_client_in(server)
  3207.         return voice is not None
  3208.  
  3209.     def voice_client_in(self, server):
  3210.         """Returns the voice client associated with a server.
  3211.  
  3212.        If no voice client is found then ``None`` is returned.
  3213.  
  3214.        Parameters
  3215.        -----------
  3216.        server : :class:`Server`
  3217.            The server to query if we have a voice client for.
  3218.  
  3219.        Returns
  3220.        --------
  3221.        :class:`VoiceClient`
  3222.            The voice client associated with the server.
  3223.        """
  3224.         return self.connection._get_voice_client(server.id)
  3225.  
  3226.     def group_call_in(self, channel):
  3227.         """Returns the :class:`GroupCall` associated with a private channel.
  3228.  
  3229.        If no group call is found then ``None`` is returned.
  3230.  
  3231.        Parameters
  3232.        -----------
  3233.        channel: :class:`PrivateChannel`
  3234.            The group private channel to query the group call for.
  3235.  
  3236.        Returns
  3237.        --------
  3238.        Optional[:class:`GroupCall`]
  3239.            The group call.
  3240.        """
  3241.         return self.connection._calls.get(channel.id)
  3242.  
  3243.     # Miscellaneous stuff
  3244.  
  3245.     @asyncio.coroutine
  3246.     def application_info(self):
  3247.         """|coro|
  3248.  
  3249.        Retrieve's the bot's application information.
  3250.  
  3251.        Returns
  3252.        --------
  3253.        :class:`AppInfo`
  3254.            A namedtuple representing the application info.
  3255.  
  3256.        Raises
  3257.        -------
  3258.        HTTPException
  3259.            Retrieving the information failed somehow.
  3260.        """
  3261.         data = yield from self.http.application_info()
  3262.         return AppInfo(id=data['id'], name=data['name'],
  3263.                        description=data['description'], icon=data['icon'],
  3264.                        owner=User(**data['owner']))
  3265.  
  3266.     @asyncio.coroutine
  3267.     def get_user_info(self, user_id):
  3268.         """|coro|
  3269.  
  3270.        Retrieves a :class:`User` based on their ID. This can only
  3271.        be used by bot accounts. You do not have to share any servers
  3272.        with the user to get this information, however many operations
  3273.        do require that you do.
  3274.  
  3275.        Parameters
  3276.        -----------
  3277.        user_id: str
  3278.            The user's ID to fetch from.
  3279.  
  3280.        Returns
  3281.        --------
  3282.        :class:`User`
  3283.            The user you requested.
  3284.  
  3285.        Raises
  3286.        -------
  3287.        NotFound
  3288.            A user with this ID does not exist.
  3289.        HTTPException
  3290.            Fetching the user failed.
  3291.        """
  3292.         data = yield from self.http.get_user_info(user_id)
  3293.         return User(**data)
Add Comment
Please, Sign In to add comment