aboutsummaryrefslogtreecommitdiff
path: root/docs/migrating.rst
diff options
context:
space:
mode:
authorRapptz <[email protected]>2017-05-12 20:14:34 -0400
committerRapptz <[email protected]>2017-05-12 20:14:34 -0400
commitb44bba6ee6e29b38d1e579c602821582e155ec3b (patch)
tree355df44874b3e5f8ee4e825339cb57783e3677ca /docs/migrating.rst
parentRename abc.Callable to abc.Connectable. (diff)
downloaddiscord.py-b44bba6ee6e29b38d1e579c602821582e155ec3b.tar.xz
discord.py-b44bba6ee6e29b38d1e579c602821582e155ec3b.zip
First pass at documentation reform.
Diffstat (limited to 'docs/migrating.rst')
-rw-r--r--docs/migrating.rst1113
1 files changed, 880 insertions, 233 deletions
diff --git a/docs/migrating.rst b/docs/migrating.rst
index 7ad9e6a1..d7365d08 100644
--- a/docs/migrating.rst
+++ b/docs/migrating.rst
@@ -1,336 +1,983 @@
.. currentmodule:: discord
-.. _migrating-to-async:
+.. _migrating_1_0:
-Migrating to v0.10.0
+Migrating to v1.0
======================
-v0.10.0 is one of the biggest breaking changes in the library due to massive
-fundamental changes in how the library operates.
+v1.0 is one of the biggest breaking changes in the library due to a complete
+redesign.
+
+The amount of changes are so massive and long that for all intents and purposes, it is a completely
+new library.
+
+Part of the redesign involves making things more easy to use and natural. Things are done on the
+:ref:`models <discord_api_models>` instead of requiring a :class:`Client` instance to do any work.
+
+Major Model Changes
+---------------------
+
+Below are major model changes that have happened in v1.0
+
+Snowflakes are int
+~~~~~~~~~~~~~~~~~~~~
+
+Before v1.0, all snowflakes (the ``id`` attribute) were strings. This has been changed to ``int``.
+
+Quick example: ::
+
+ # before
+ ch = client.get_channel('84319995256905728')
+ if message.author.id == '80528701850124288':
+ ...
+
+ # after
+ ch = client.get_channel(84319995256905728)
+ if message.author.id == 80528701850124288:
+ ...
+
+This change allows for fewer errors when using the Copy ID feature in the official client since you no longer have
+to wrap it in quotes and allows for optimisation opportunities by allowing ETF to be used instead of JSON internally.
+
+Server is now Guild
+~~~~~~~~~~~~~~~~~~~~~
+
+The official API documentation calls the "Server" concept a "Guild" instead. In order to be more consistent with the
+API documentation when necessary, the model has been renamed to :class:`Guild` and all instances referring to it has
+been changed as well.
+
+A list of changes is as follows:
+
++-------------------------------+----------------------------------+
+| Before | After |
++-------------------------------+----------------------------------+
+| ``Message.server`` | :attr:`Message.guild` |
++-------------------------------+----------------------------------+
+| ``Channel.server`` | :attr:`abc.GuildChannel.guild` |
++-------------------------------+----------------------------------+
+| ``Client.servers`` | :attr:`Client.guilds` |
++-------------------------------+----------------------------------+
+| ``Client.get_server`` | :meth:`Client.get_guild` |
++-------------------------------+----------------------------------+
+| ``Emoji.server`` | :attr:`Emoji.guild` |
++-------------------------------+----------------------------------+
+| ``Role.server`` | :attr:`Role.guild` |
++-------------------------------+----------------------------------+
+| ``Invite.server`` | :attr:`Invite.guild` |
++-------------------------------+----------------------------------+
+| ``Member.server`` | :attr:`Member.guild` |
++-------------------------------+----------------------------------+
+| ``Permissions.manage_server`` | :attr:`Permissions.manage_guild` |
++-------------------------------+----------------------------------+
+| ``VoiceClient.server`` | :attr:`VoiceClient.guild` |
++-------------------------------+----------------------------------+
+| ``Client.create_server`` | :meth:`Client.create_guild` |
++-------------------------------+----------------------------------+
+
+.. _migrating_1_0_model_state:
+
+Models are Stateful
+~~~~~~~~~~~~~~~~~~~~~
+
+As mentioned earlier, a lot of functionality was moved out of :class:`Client` and
+put into their respective :ref:`model <discord_api_models>`.
+
+A list of these changes is enumerated below.
+
++---------------------------------------+------------------------------------------------------------------------------+
+| Before | After |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.add_reaction`` | :meth:`Message.add_reaction` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.add_roles`` | :meth:`Member.add_roles` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.ban`` | :meth:`Member.ban` or :meth:`Guild.ban` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.change_nickname`` | :meth:`Member.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.clear_reactions`` | :meth:`Message.clear_reactions` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.create_channel`` | :meth:`Guild.create_text_channel` and :meth:`Guild.create_voice_channel` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.create_custom_emoji`` | :meth:`Guild.create_custom_emoji` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.create_invite`` | :meth:`Guild.create_invite` or :meth:`abc.GuildChannel.create_invite` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.create_role`` | :meth:`Guild.create_role` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_channel`` | :meth:`abc.GuildChannel.delete` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_channel_permissions`` | :meth:`abc.GuildChannel.set_permissions` with ``overwrites`` set to ``None`` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_custom_emoji`` | :meth:`Emoji.delete` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_invite`` | :meth:`Invite.delete` or :meth:`Client.delete_invite` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_message`` | :meth:`Message.delete` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_messages`` | :meth:`TextChannel.delete_messages` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_role`` | :meth:`Role.delete` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.delete_server`` | :meth:`Guild.delete` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.edit_channel`` | :meth:`TextChannel.edit` or :meth:`VoiceChannel.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.edit_channel_permissions`` | :meth:`abc.GuildChannel.set_permissions` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.edit_custom_emoji`` | :meth:`Emoji.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.edit_message`` | :meth:`Message.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.edit_profile`` | :meth:`ClientUser.edit` (you get this from :attr:`Client.user`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.edit_role`` | :meth:`Role.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.edit_server`` | :meth:`Guild.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.estimate_pruned_members`` | :meth:`Guild.estimate_pruned_members` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.get_all_emojis`` | :meth:`Client.emojis` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.get_bans`` | :meth:`Guild.bans` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.get_message`` | :meth:`abc.Messageable.get_message` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.get_reaction_users`` | :meth:`Reaction.users` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.invites_from`` | :meth:`abc.GuildChannel.invites` or :meth:`Guild.invites` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.join_voice_channel`` | :meth:`VoiceChannel.connect` (see :ref:`migrating_1_0_voice`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.kick`` | :meth:`Guild.kick` or :meth:`Member.kick` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.leave_server`` | :meth:`Guild.leave` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.logs_from`` | :meth:`abc.Messageable.history` (see :ref:`migrating_1_0_async_iter`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.move_channel`` | :meth:`TextChannel.edit` or :meth:`VoiceChannel.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.move_member`` | :meth:`Member.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.move_role`` | :meth:`Role.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.pin_message`` | :meth:`Message.pin` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.pins_from`` | :meth:`abc.Messageable.pins` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.prune_members`` | :meth:`Guild.prune_members` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.purge_from`` | :meth:`abc.Messageable.purge` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.remove_reaction`` | :meth:`Message.remove_reaction` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.remove_roles`` | :meth:`Member.remove_roles` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.replace_roles`` | :meth:`Member.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.send_file`` | :meth:`abc.Messageable.send` (see :ref:`migrating_1_0_sending_messages`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.send_message`` | :meth:`abc.Messageable.send` (see :ref:`migrating_1_0_sending_messages`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.send_typing`` | :meth:`abc.Messageable.trigger_typing` (use :meth:`abc.Messageable.typing`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.server_voice_state`` | :meth:`Member.edit` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.start_private_message`` | :meth:`User.create_dm` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.unban`` | :meth:`Guild.unban` or :meth:`Member.unban` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.unpin_message`` | :meth:`Message.unpin` |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.wait_for_message`` | :meth:`Client.wait_for` (see :ref:`migrating_1_0_wait_for`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.wait_for_reaction`` | :meth:`Client.wait_for` (see :ref:`migrating_1_0_wait_for`) |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.wait_until_login`` | Removed |
++---------------------------------------+------------------------------------------------------------------------------+
+| ``Client.wait_until_ready`` | No change |
++---------------------------------------+------------------------------------------------------------------------------+
+
+Property Changes
+~~~~~~~~~~~~~~~~~~
+
+In order to be a bit more consistent, certain things that were properties were changed to methods instead.
+
+The following are now methods instead of properties (requires parentheses):
+
+- :meth:`TextChannel.is_default`
+- :meth:`Role.is_default`
+- :meth:`Client.is_ready`
+- :meth:`Client.is_closed`
+
+Dict Value Change
+~~~~~~~~~~~~~~~~~~~~~
+
+Prior to v1.0 some aggregating properties that retrieved models would return "dict view" objects.
+
+As a consequence, when the dict would change size while you would iterate over it, a RuntimeError would
+be raised and crash the task. To alleviate this, the "dict view" objects were changed into lists.
+
+The following views were changed to a list:
+
+- :attr:`Client.guilds`
+- :attr:`Client.users` (new in v1.0)
+- :attr:`Client.emojis` (new in v1.0)
+- :attr:`Guild.channels`
+- :attr:`Guild.text_channels` (new in v1.0)
+- :attr:`Guild.voice_channels` (new in v1.0)
+- :attr:`Guild.emojis`
+- :attr:`Guild.members`
-The biggest major change is that the library has dropped support to all versions prior to
-Python 3.4.2. This was made to support ``asyncio``, in which more detail can be seen
-:issue:`in the corresponding issue <50>`. To reiterate this, the implication is that
-**python version 2.7 and 3.3 are no longer supported**.
+Voice State Changes
+~~~~~~~~~~~~~~~~~~~~~
-Below are all the other major changes from v0.9.0 to v0.10.0.
+Earlier, in v0.11.0 a :class:`VoiceState` class was added to refer to voice states along with a
+:attr:`Member.voice` attribute to refer to it.
-.. _migrating-event-registration:
+However, it was transparent to the user. In an effort to make the library save more memory, the
+voice state change is now more visible.
-Event Registration
---------------------
+The only way to access voice attributes if via the :attr:`Member.voice` attribute. Note that if
+the member does not have a voice state this attribute can be ``None``.
-All events before were registered using :meth:`Client.event`. While this is still
-possible, the events must be decorated with ``@asyncio.coroutine``.
+Quick example: ::
-Before:
+ # before
+ member.deaf
+ member.voice.voice_channel
-.. code-block:: python
+ # after
+ if member.voice: # can be None
+ member.voice.deaf
+ member.voice.channel
- @client.event
- def on_message(message):
- pass
-After:
+User and Member Type Split
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. code-block:: python
+In v1.0 to save memory, :class:`User` and :class:`Member` are no longer inherited. Instead, they are "flattened"
+by having equivalent properties that map out to the functional underlying :class:`User`. Thus, there is no functional
+change in how they are used. However this breaks ``isinstance`` checks and thus is something to keep in mind.
- @client.event
- @asyncio.coroutine
- def on_message(message):
- pass
+These memory savings were accomplished by having a global :class:`User` cache, and as a positive consequence you
+can now easily fetch a :class:`User` by their ID by using the new :meth:`Client.get_user`. You can also get a list
+of all :class:`User` your client can see with :attr:`Client.users`.
-Or in Python 3.5+:
+.. _migrating_1_0_channel_split:
-.. code-block:: python
+Channel Type Split
+~~~~~~~~~~~~~~~~~~~~~
- @client.event
- async def on_message(message):
- pass
+Prior to v1.0, channels were two different types, ``Channel`` and ``PrivateChannel`` with a ``is_private``
+property to help differentiate between them.
-Because there is a lot of typing, a utility decorator (:meth:`Client.async_event`) is provided
-for easier registration. For example:
+In order to save memory the channels have been split into 4 different types:
-.. code-block:: python
+- :class:`TextChannel` for guild text channels
+- :class:`VoiceChannel` for guild voice channels
+- :class:`DMChannel` for DM channels with members
+- :class:`GroupChannel` for Group DM channels with members
- @client.async_event
- def on_message(message):
- pass
+With this split came the removal of the ``is_private`` attribute. You should now use ``isinstance``.
+
+The types are split into two different :ref:`discord_api_abcs`:
+
+- :class:`abc.GuildChannel` for guild channels
+- :class:`abc.PrivateChannel` for private channels (DMs and group DMs).
+
+So to check if something is a guild channel you would do: ::
+
+ isinstance(channel, discord.abc.GuildChannel)
+
+And to check if it's a private channel you would do: ::
+
+ isinstance(channel, discord.abc.PrivateChannel)
+
+Of course, if you're looking for only a specific type you can pass that too, e.g. ::
+
+ isintance(channel, discord.TextChannel)
+
+With this type split also came event changes, which are enumerated in :ref:`migrating_1_0_event_changes`.
+
+
+Miscellaneous Model Changes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There were lots of other things added or removed in the models in general.
+
+They will be enumerated here.
+
+**Removed**
+
+- :meth:`Client.login` no longer accepts email and password logins.
+
+ - Use a token and ``bot=False``
+
+- ``Client.get_all_emojis``
+
+ - Use :attr:`Client.emojis` instead.
+
+- ``Client.wait_for_message`` and ``Client.wait_for_reaction`` are gone
+
+ - Use :meth:`Client.wait_for` instead.
+
+- ``Channel.voice_members``
+
+ - Use :attr:`VoiceChannel.members` instead.
+
+- ``Channel.is_private``
+
+ - Use ``isinstance`` instead with one of the :ref:`discord_api_abcs` instead.
+ - e.g. ``isinstance(channel, discord.abc.GuildChannel)`` will check if it isn't a private channel.
+
+- ``Client.accept_invite``
+
+ - There is no replacement for this one. This functionality is deprecated API wise.
+
+- ``Message.edited_timestamp``
+
+ - Use :attr:`Message.edited_at` instead.
+
+- ``Message.timestamp``
+
+ - Use :attr:`Message.created_at` instead.
+
+**Added**
+
+- :attr:`VoiceChannel.members` for fetching members connected to a voice channel.
+- :attr:`TextChannel.members` for fetching members that can see the channel.
+- :attr:`Role.members` for fetching members that have the role.
+- :attr:`Guild.text_channels` for fetching text channels only.
+- :attr:`Guild.voice_channels` for fetching voice channels only.
+- :attr:`Guild.chunked` to check member chunking status.
+- :attr:`Guild.explicit_content_filter` to fetch the content filter
+- :attr:`Guild.shard_id` to get a guild's Shard ID if you're sharding.
+- :attr:`Client.users` to get all visible :class:`User` instances.
+- :meth:`Client.get_user` to get a :class:`User` by ID.
+- :meth:`User.avatar_url_as` to get an avatar in a specific size or format.
+- :meth:`Guild.vanity_invite` to fetch the guild's vanity invite.
+- :meth:`Guild.audit_logs` to fetch the guild's audit logs.
+- :attr:`Message.webhook_id` to fetch the message's webhook ID.
+- :meth:`TextChannel.is_nsfw` to check if a text channel is NSFW.
+
+.. _migrating_1_0_sending_messages:
+
+Sending Messages
+------------------
+
+One of the changes that were done was the merger of the previous ``Client.send_message`` and ``Client.send_file``
+functionality into a single method, :meth:`~abc.Messageable.send`.
+
+Basically: ::
+
+ # before
+ await client.send_message(channel, 'Hello')
+
+ # after
+ await channel.send('Hello')
+
+This supports everything that the old ``send_message`` supported such as embeds: ::
+
+ e = discord.Embed(title='foo')
+ await channel.send('Hello', embed=e)
+
+There is a caveat with sending files however, as this functionality was expanded to support multiple
+file attachments, you must now use a :class:`File` pseudo-namedtuple to upload a single file. ::
+
+ # before
+ await client.send_file(channel, 'cool.png', filename='testing.png', content='Hello')
+ # after
+ await channel.send('Hello', file=discord.File('cool.png', 'testing.png'))
-Be aware however, that this is still a coroutine and your other functions that are coroutines must
-be decorated with ``@asyncio.coroutine`` or be ``async def``.
+This change was to facilitate multiple file uploads: ::
-.. _migrating_event_changes:
+ my_files = [
+ discord.File('cool.png', 'testing.png'),
+ discord.File(some_fp, 'cool_filename.png'),
+ ]
+
+ await channel.send('Your images:', files=my_files)
+
+.. _migrating_1_0_async_iter:
+
+Asynchronous Iterators
+------------------------
+
+Prior to v1.0, certain functions like ``Client.logs_from`` would return a different type if done in Python 3.4 or 3.5+.
+
+In v1.0, this change has been reverted and will now return a singular type meeting an abstract concept called
+:class:`AsyncIterator`.
+
+This allows you to iterate over it like normal in Python 3.5+: ::
+
+ async for msg in channel.history():
+ print(msg)
+
+Or turn it into a list for either Python 3.4 or 3.5+: ::
+
+ messages = yield from channel.history().flatten()
+ for message in messages:
+ print(messages)
+
+A handy aspect of returning :class:`AsyncIterator` is that it allows you to chain functions together such as
+:meth:`AsyncIterator.map` or :meth:`AsyncIterator.filter`: ::
+
+ async for m_id in channel.history().filter(lambda m: m.author == client.user).map(lambda m: m.id):
+ print(m_id)
+
+The functions passed to :meth:`AsyncIterator.map` or :meth:`AsyncIterator.filter` can be either coroutines or regular
+functions.
+
+You can also get single elements a la :func:`discord.utils.find` or :func:`discord.utils.get` via
+:meth:`AsyncIterator.get` or :meth:`AsyncIterator.find`: ::
+
+ my_last_message = await channel.history().get(author=client.user)
+
+The following return :class:`AsyncIterator`:
+
+- :meth:`abc.Messageable.history`
+- :meth:`Guild.audit_logs`
+- :meth:`Reaction.users`
+
+.. _migrating_1_0_event_changes:
Event Changes
--------------
-Some events in v0.9.0 were considered pretty useless due to having no separate states. The main
-events that were changed were the ``_update`` events since previously they had no context on what
-was changed.
+A lot of events have gone through some changes.
+
+Many events with ``server`` in the name where changed to use ``guild`` instead.
Before:
-.. code-block:: python
+- ``on_server_join``
+- ``on_server_remove``
+- ``on_server_update``
+- ``on_server_role_create``
+- ``on_server_role_delete``
+- ``on_server_role_update``
+- ``on_server_emojis_update``
+- ``on_server_available``
+- ``on_server_unavailable``
- def on_channel_update(channel): pass
- def on_member_update(member): pass
- def on_status(member): pass
- def on_server_role_update(role): pass
- def on_voice_state_update(member): pass
- def on_socket_raw_send(payload, is_binary): pass
+After:
+- :func:`on_guild_join`
+- :func:`on_guild_remove`
+- :func:`on_guild_update`
+- :func:`on_guild_role_create`
+- :func:`on_guild_role_delete`
+- :func:`on_guild_role_update`
+- :func:`on_guild_emojis_update`
+- :func:`on_guild_available`
+- :func:`on_guild_unavailable`
-After:
-.. code-block:: python
+The :func:`on_voice_state_update` event has received an argument change.
- def on_channel_update(before, after): pass
- def on_member_update(before, after): pass
- def on_server_role_update(before, after): pass
- def on_voice_state_update(before, after): pass
- def on_socket_raw_send(payload): pass
+Before: ::
-Note that ``on_status`` was removed. If you want its functionality, use :func:`on_member_update`.
-See :ref:`discord-api-events` for more information. Other removed events include ``on_socket_closed``, ``on_socket_receive``, and ``on_socket_opened``.
+ async def on_voice_state_update(before, after)
+After: ::
-.. _migrating-coroutines:
+ async def on_voice_state_update(member, before, after)
-Coroutines
------------
+Instead of two :class:`Member` objects, the new event takes one :class:`Member` object and two :class:`VoiceState` objects.
-The biggest change that the library went through is that almost every function in :class:`Client`
-was changed to be a `coroutine <https://docs.python.org/3/library/asyncio-task.html>`_. Functions
-that are marked as a coroutine in the documentation must be awaited from or yielded from in order
-for the computation to be done. For example...
+The :func:`on_guild_emojis_update` event has received an argument change.
-Before:
+Before: ::
-.. code-block:: python
+ async def on_guild_emojis_update(before, after)
+
+After: ::
+
+ async def on_guild_emojis_update(guild, before, after)
- client.send_message(message.channel, 'Hello')
+The first argument is now the :class:`Guild` that the emojis were updated from.
+
+The ``on_channel_`` events have received a type level split (see :ref:`migrating_1_0_channel_split`).
+
+Before:
+
+- ``on_channel_delete``
+- ``on_channel_create``
+- ``on_channel_update``
After:
-.. code-block:: python
+- :func:`on_guild_channel_delete`
+- :func:`on_guild_channel_create`
+- :func:`on_guild_channel_update`
+- :func:`on_private_channel_delete`
+- :func:`on_private_channel_create`
+- :func:`on_private_channel_update`
- yield from client.send_message(message.channel, 'Hello')
+The ``on_guild_channel_`` events correspond to :class:`abc.GuildChannel` being updated (i.e. :class:`TextChannel`
+and :class:`VoiceChannel`) and the ``on_private_channel_`` events correspond to :class:`abc.PrivateChannel` being
+updated (i.e. :class:`DMChannel` and :class:`GroupChannel`).
- # or in python 3.5+
- await client.send_message(message.channel, 'Hello')
+.. _migrating_1_0_voice:
-In order for you to ``yield from`` or ``await`` a coroutine then your function must be decorated
-with ``@asyncio.coroutine`` or ``async def``.
+Voice Changes
+---------------
-.. _migrating-iterable:
+Voice sending has gone through a complete redesign.
-Iterables
-----------
+In particular:
-For performance reasons, many of the internal data structures were changed into a dictionary to support faster
-lookup. As a consequence, this meant that some lists that were exposed via the API have changed into iterables
-and not sequences. In short, this means that certain attributes now only support iteration and not any of the
-sequence functions.
+- Connection is done through :meth:`VoiceChannel.connect` instead of ``Client.join_voice_channel``.
+- You no longer create players and operate on them (you no longer store them).
+- You instead request :class:`VoiceClient` to play an :class:`AudioSource` via :meth:`VoiceClient.play`.
+- There are different built-in :class:`AudioSource`\s
-The affected attributes are as follows:
+ - :class:`FFmpegPCMAudio` is the equivalent of ``create_ffmpeg_player``
-- :attr:`Client.servers`
-- :attr:`Client.private_channels`
-- :attr:`Server.channels`
-- :attr:`Server.members`
+- create_ffmpeg_player/create_stream_player/create_ytdl_player have all been removed
-Some examples of previously valid behaviour that is now invalid
+ - The goal is to create :class:`AudioSource` instead.
-.. code-block:: python
+- Using :meth:`VoiceClient.play` will not return an ``AudioPlayer``.
- if client.servers[0].name == "test":
- # do something
+ - Instead, it's "flattened" like :class:`User` -> :class:`Member` is.
-Since they are no longer ``list``\s, they no longer support indexing or any operation other than iterating.
-In order to get the old behaviour you should explicitly cast it to a list.
+- The ``after`` parameter now takes a single parameter (the error).
-.. code-block:: python
+Basically:
- servers = list(client.servers)
- # work with servers
+Before: ::
-.. warning::
+ vc = await client.join_voice_channel(channel)
+ player = vc.create_ffmpeg_player('testing.mp3', after=lambda: print('done'))
+ player.start()
- Due to internal changes of the structure, the order you receive the data in
- is not in a guaranteed order.
+ player.is_playing()
+ player.pause()
+ player.resume()
+ player.stop()
+ # ...
-.. _migrating-enums:
+After: ::
-Enumerations
-------------
+ vc = await channel.connect()
+ vc.play(discord.FFmpegPCMAudio('testing.mp3'), after=lambda e: print('done', e))
+ vc.is_playing()
+ vc.pause()
+ vc.resume()
+ vc.stop()
+ # ...
-Due to dropping support for versions lower than Python 3.4.2, the library can now use
-`enumerations <https://docs.python.org/3/library/enum.html>`_ in places where it makes sense.
+With the changed :class:`AudioSource` design, you can now change the source that the :class:`VoiceClient` is
+playing at runtime via :attr:`VoiceClient.source`.
-The common places where this was changed was in the server region, member status, and channel type.
+For example, you can add a :class:`PCMVolumeTransformer` to allow changing the volume: ::
-Before:
+ vc.source = discord.PCMVolumeTransformer(vc.source)
+ vc.source.volume = 0.6
-.. code-block:: python
+An added benefit of the redesign is that it will be much more resilient towards reconnections:
- server.region == 'us-west'
- member.status == 'online'
- channel.type == 'text'
+- The voice websocket will now automatically re-connect and re-do the handshake when disconnected.
+- The initial connect handshake will now retry up to 5 times so you no longer get as many ``asyncio.TimeoutError``
+- Audio will now stop and resume when a disconnect is found.
-After:
+ - This includes changing voice regions etc.
+
+
+.. _migrating_1_0_wait_for:
+
+Waiting For Events
+--------------------
+
+Prior to v1.0, the machinery for waiting for an event outside of the event itself was done through two different
+functions, ``Client.wait_for_message`` and ``Client.wait_for_reaction``. One problem with one such approach is that it did
+not allow you to wait for events outside of the ones provided by the library.
+
+In v1.0 the concept of waiting for another event has been generalised to work with any event as :meth:`Client.wait_for`.
+
+For example, to wait for a message: ::
+
+ # before
+ msg = await client.wait_for_message(author=message.author, channel=message.channel)
+
+ # after
+ def pred(m):
+ return m.author == message.author and m.channel == message.channel
+
+ msg = await client.wait_for('message', check=m)
+
+To facilitate multiple returns, :meth:`Client.wait_for` returns either a single argument, no arguments, or a tuple of
+arguments.
+
+For example, to wait for a reaction: ::
+
+ user, reaction = await client.wait_for('reaction_add', check=lambda u, r: u.id == 176995180300206080)
+
+ # use user and reaction
+
+Since this function now can return multiple arguments, the ``timeout`` parameter will now raise a ``asyncio.TimeoutError``
+when reached instead of setting the return to ``None``. For example:
.. code-block:: python
- server.region == discord.ServerRegion.us_west
- member.status = discord.Status.online
- channel.type == discord.ChannelType.text
-
-The main reason for this change was to reduce the use of finicky strings in the API as this
-could give users a false sense of power. More information can be found in the :ref:`discord-api-enums` page.
-
-.. _migrating-properties:
-
-Properties
------------
-
-A lot of function calls that returned constant values were changed into Python properties for ease of use
-in format strings.
-
-The following functions were changed into properties:
-
-+----------------------------------------+--------------------------------------+
-| Before | After |
-+----------------------------------------+--------------------------------------+
-| ``User.avatar_url()`` | :attr:`User.avatar_url` |
-+----------------------------------------+--------------------------------------+
-| ``User.mention()`` | :attr:`User.mention` |
-+----------------------------------------+--------------------------------------+
-| ``Channel.mention()`` | :attr:`Channel.mention` |
-+----------------------------------------+--------------------------------------+
-| ``Channel.is_default_channel()`` | :attr:`Channel.is_default` |
-+----------------------------------------+--------------------------------------+
-| ``Role.is_everyone()`` | :attr:`Role.is_everyone` |
-+----------------------------------------+--------------------------------------+
-| ``Server.get_default_role()`` | :attr:`Server.default_role` |
-+----------------------------------------+--------------------------------------+
-| ``Server.icon_url()`` | :attr:`Server.icon_url` |
-+----------------------------------------+--------------------------------------+
-| ``Server.get_default_channel()`` | :attr:`Server.default_channel` |
-+----------------------------------------+--------------------------------------+
-| ``Message.get_raw_mentions()`` | :attr:`Message.raw_mentions` |
-+----------------------------------------+--------------------------------------+
-| ``Message.get_raw_channel_mentions()`` | :attr:`Message.raw_channel_mentions` |
-+----------------------------------------+--------------------------------------+
-
-.. _migrating-member:
-
-Member Management
--------------------
-
-Functions that involved banning and kicking were changed.
-
-+--------------------------------+--------------------------+
-| Before | After |
-+--------------------------------+--------------------------+
-| ``Client.ban(server, user)`` | ``Client.ban(member)`` |
-+--------------------------------+--------------------------+
-| ``Client.kick(server, user)`` | ``Client.kick(member)`` |
-+--------------------------------+--------------------------+
-
-.. migrating-renames:
-
-Renamed Functions
--------------------
-
-Functions have been renamed.
-
-+------------------------------------+-------------------------------------------+
-| Before | After |
-+------------------------------------+-------------------------------------------+
-| ``Client.set_channel_permissions`` | :meth:`Client.edit_channel_permissions` |
-+------------------------------------+-------------------------------------------+
-
-All the :class:`Permissions` related attributes have been renamed and the `can_` prefix has been
-dropped. So for example, ``can_manage_messages`` has become ``manage_messages``.
-
-.. _migrating-kwargs:
-
-Forced Keyword Arguments
+ def pred(m):
+ return m.author == message.author and m.channel == message.channel
+
+ try:
+
+ msg = await client.wait_for('message', check=pred, timeout=60.0)
+ except asyncio.TimeoutError:
+ await channel.send('You took too long...')
+ else:
+ await channel.send('You said {0.content}, {0.author}.'.format(msg))
+
+Sharding
+----------
+
+The library has received significant changes on how it handles sharding and now has sharding as a first-class citizen.
+
+If using a Bot account and you want to shard your bot in a single process then you can use the :class:`AutoShardedClient`.
+
+This class allows you to use sharding without having to launch multiple processes or deal with complicated IPC.
+
+It should be noted that **the sharded client does not support user accounts**. This is due to the changes in connection
+logic and state handling.
+
+Usage is as simple as doing: ::
+
+ client = discord.AutoShardedClient()
+
+instead of using :class:`Client`.
+
+This will launch as many shards as your bot needs using the ``/gateway/bot`` endpoint, which allocates about 1000 guilds
+per shard.
+
+If you want more control over the sharding you can specify ``shard_count`` and ``shard_ids``. ::
+
+ # launch 10 shards regardless
+ client = discord.AutoShardedClient(shard_count=10)
+
+ # launch specific shard IDs in this process
+ client = discord.AutoShardedClient(shard_count=10, shard_ids=(1, 2, 5, 6))
+
+For users of the command extension, there is also :class:`~ext.commands.AutoShardedBot` which behaves similarly.
+
+Connection Improvements
-------------------------
-Since 3.0+ of Python, we can now force questions to take in forced keyword arguments. A keyword argument is when you
-explicitly specify the name of the variable and assign to it, for example: ``foo(name='test')``. Due to this support,
-some functions in the library were changed to force things to take said keyword arguments. This is to reduce errors of
-knowing the argument order and the issues that could arise from them.
+In v1.0, the auto reconnection logic has been powered up significantly.
-The following parameters are now exclusively keyword arguments:
+:meth:`Client.connect` has gained a new keyword argument, ``reconnect`` that defaults to ``True`` which controls
+the reconnect logic. When enabled, the client will automatically reconnect in all instances of your internet going
+offline or Discord going offline with exponential back-off.
-- :meth:`Client.send_message`
- - ``tts``
-- :meth:`Client.logs_from`
- - ``before``
- - ``after``
-- :meth:`Client.edit_channel_permissions`
- - ``allow``
- - ``deny``
+:meth:`Client.run` and :meth:`Client.start` gains this keyword argument as well, but for most cases you will not
+need to specify it unless turning it off.
-In the documentation you can tell if a function parameter is a forced keyword argument if it is after ``\*,``
-in the function signature.
+.. _migrating_1_0_commands:
-.. _migrating-running:
+Command Extension Changes
+--------------------------
-Running the Client
---------------------
+Due to the :ref:`migrating_1_0_model_state` changes, some of the design of the extension module had to
+undergo some design changes as well.
+
+Context Changes
+~~~~~~~~~~~~~~~~~
-In earlier versions of discord.py, ``client.run()`` was a blocking call to the main thread
-that called it. In v0.10.0 it is still a blocking call but it handles the event loop for you.
-However, in order to do that you must pass in your credentials to :meth:`Client.run`.
+In v1.0, the :class:`~ext.commands.Context` has received a lot of changes with how it's retrieved and used.
-Basically, before:
+The biggest change is that ``pass_context=True`` is now the default behaviour. Ergo:
.. code-block:: python
- client.login('token')
- client.run()
+ # before
+ @bot.command()
+ async def foo():
+ await bot.say('Hello')
-After:
+ # after
+ @bot.command()
+ async def foo(ctx):
+ await ctx.send('Hello')
+
+The reason for this is because :class:`~ext.commands.Context` now meets the requirements of :class:`abc.Messageable`. This
+makes it have similar functionality to :class:`TextChannel` or :class:`DMChannel`. Using :meth:`~ext.commands.Context.send`
+will either DM the user in a DM context or send a message in the channel it was in, similar to the old ``bot.say``
+functionality. The old helpers have been removed in favour of the new :class:`abc.Messageable` interface. See
+:ref:`migrating_1_0_removed_helpers` for more information.
+
+Since the :class:`~ext.commands.Context` is now by default passed, several shortcuts have been added:
+
+**New Shortcuts**
+
+- :attr:`~ext.commands.Context.author` is a shortcut for ``ctx.message.author``
+- :attr:`~ext.commands.Context.guild` is a shortcut for ``ctx.message.guild``
+- :attr:`~ext.commands.Context.channel` is a shortcut for ``ctx.message.channel``
+- :attr:`~ext.commands.Context.me` is a shortcut for ``ctx.message.guild.me`` or ``ctx.bot.user``.
+- :attr:`~ext.commands.Context.voice_client` is a shortcut for ``ctx.message.guild.voice_client``.
+
+Subclassing Context
+++++++++++++++++++++
+
+In v1.0, there is now the ability to subclass :class:`~ext.commands.Context` and use it instead of the default
+provided one.
+
+For example, if you want to add some functionality to the context:
.. code-block:: python
- client.run('token')
+ class MyContext(commands.Context):
+ @property
+ def secret(self):
+ return 'my secret here'
-.. warning::
+Then you can use :meth:`~ext.commands.Bot.get_context` inside :func:`on_message` with combination with
+:meth:`~ext.commands.Bot.invoke` to use your custom context:
+
+.. code-block:: python
- Like in the older ``Client.run`` function, the newer one must be the one of
- the last functions to call. This is because the function is **blocking**. Registering
- events or doing anything after :meth:`Client.run` will not execute until the function
- returns.
+ class MyBot(commands.Bot):
+ async def on_message(self, message):
+ ctx = await self.get_context(message, cls=MyContext)
+ await self.invoke(ctx)
-This is a utility function that abstracts the event loop for you. There's no need for
-the run call to be blocking and out of your control. Indeed, if you want control of the
-event loop then doing so is quite straightforward:
+Now inside your commands you will have access to your custom context:
.. code-block:: python
- import discord
- import asyncio
+ @bot.command()
+ async def secret(ctx):
+ await ctx.send(ctx.secret)
- client = discord.Client()
+.. _migrating_1_0_removed_helpers:
- @asyncio.coroutine
- def main_task():
- yield from client.login('token')
- yield from client.connect()
+Removed Helpers
++++++++++++++++++
- loop = asyncio.get_event_loop()
- try:
- loop.run_until_complete(main_task())
- except:
- loop.run_until_complete(client.logout())
- finally:
- loop.close()
+.. currentmodule:: discord.ext.commands
+
+With the new :class:`Context` changes, a lot of message sending helpers have been removed.
+
+For a full list of changes, see below:
+
++-----------------+----------------------------------------------------------+
+| Before | After |
++-----------------+----------------------------------------------------------+
+| ``Bot.say`` | :meth:`Context.send` |
++-----------------+----------------------------------------------------------+
+| ``Bot.upload`` | :meth:`Context.send` |
++-----------------+----------------------------------------------------------+
+| ``Bot.whisper`` | ``ctx.author.send`` |
++-----------------+----------------------------------------------------------+
+| ``Bot.type`` | :meth:`Context.typing` or :meth:`Context.trigger_typing` |
++-----------------+----------------------------------------------------------+
+| ``Bot.reply`` | No replacement. |
++-----------------+----------------------------------------------------------+
+
+.. currentmodule:: discord
+
+Command Changes
+~~~~~~~~~~~~~~~~~
+
+As mentioned earlier, the first command change is that ``pass_context=True`` is now the
+default, so there is no need to pass this as a parameter.
+
+Another change is the removal of ``no_pm=True``. Instead, use the new :func:`~ext.commands.guild_only` built-in
+check.
+
+The ``commands`` attribute of :class:`~ext.commands.Bot` and :class:`~ext.commands.Group` have been changed from a
+dictionary to a set that does not have aliases. To retrieve the previous dictionary behaviour, use ``all_commands`` instead.
+
+Command instances have gained a new property, :attr:`~ext.commands.Command.signature` to get the signature of command along
+with a :attr:`~ext.commands.Command.usage` attribute to override the default signature.
+
+Check Changes
+~~~~~~~~~~~~~~~
+
+Prior to v1.0, :func:`~ext.command.check`\s could only be synchronous. As of v1.0 checks can now be coroutines.
+
+Along with this change, a couple new checks were added.
+
+- :func:`~ext.commands.guild_only` replaces the old ``no_pm=True`` functionality
+- :func:`~ext.commands.is_owner` uses the :meth:`Client.application_info` endpoint by default to fetch owner ID.
+
+ - This is actually powered by a different function, :meth:`~ext.commands.Bot.is_owner`.
+ - You can set the owner ID yourself by setting :attr:`Bot.owner_id <ext.commands.Bot.owner_id>`.
+
+- :func:`~ext.commands.is_nsfw` checks if the channel the command is in is a NSFW channel.
+
+ - This is powered by the new :meth:`TextChannel.is_nsfw` check.
+
+Event Changes
+~~~~~~~~~~~~~~~
+
+All command extension events have changed.
+
+Before: ::
+
+ on_command(ctx, command)
+ on_command_completion(ctx, command)
+ on_command_error(error, ctx)
+
+After: ::
+
+ on_command(ctx)
+ on_command_completion(ctx)
+ on_command_error(ctx, error)
+
+The extraneous ``command`` parameter in :func:`~ext.commands.on_command` and :func:`~ext.commands.on_command_completion`
+have been removed. The :class:`~ext.commands.Command` instance was not kept up-to date so it was incorrect. In order to get
+the up to date :class:`~ext.commands.Command` instance, use the :attr:`Context.command <ext.commands.Context.command>`
+attribute.
+
+The error handlers, either :attr:`Command.error <ext.commands.Command.error>` or :func:`~ext.commands.on_command_error`,
+have been re-ordered to use the :class:`~ext.commands.Context` as its first parameter to be consistent with other events
+and commands.
+
+Cog Changes
+~~~~~~~~~~~~~
+
+Cog special methods have changed slightly.
+
+The previous ``__check`` special method has been renamed to ``__global_check`` to make it more clear that it's a global
+check.
+
+To complement the new ``__global_check`` there is now a new ``__local_check`` to facilitate a check that will run on
+every command in the cog.
+
+Cogs have also gained a ``__before_invoke`` and ``__after_invoke`` cog local before and after invocation hook, which
+can be seen in :ref:`migrating_1_0_before_after_hook`.
+
+The final addition is cog-local error handler, ``__error``, that is run on every command in the cog.
+
+An example cog with every special method registered is as follows: ::
+
+ class Cog:
+ def __global_check(self, ctx):
+ print('cog global check')
+ return True
+
+ async def __local_check(self, ctx):
+ print('cog local check')
+ return await ctx.bot.is_owner(ctx.author)
+
+ async def __error(self, ctx, error):
+ print('Error in {0.command.qualified_name}: {1}'.format(ctx, error))
+
+ async def __before_invoke(self, ctx):
+ print('cog local before: {0.command.qualified_name}'.format(ctx))
+
+ async def __after_invoke(self, ctx):
+ print('cog local after: {0.command.qualified_name}'.format(ctx))
+
+
+.. _migrating_1_0_before_after_hook:
+
+Before and After Invocation Hooks
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Commands have gained new before and after invocation hooks that allow you to do an action before and after a command is
+run.
+
+They take a single parameter, :class:`~ext.commands.Context` and they must be a coroutine.
+
+They are on a global, per-cog, or per-command basis.
+
+Basically: ::
+
+
+ # global hooks:
+
+ @bot.before_invoke
+ async def before_any_command(ctx):
+ # do something before a command is called
+ pass
+
+ @bot.after_invoke
+ async def after_any_command(ctx):
+ # do something after a command is called
+ pass
+
+The after invocation is hook always called, **regardless of an error in the command**. This makes it ideal for some error
+handling or clean up of certain resources such a database connection.
+
+The per-command registration is as follows: ::
+
+ @bot.command()
+ async def foo(ctx):
+ await ctx.send('foo')
+
+ @foo.before_invoke
+ async def before_foo_command(ctx):
+ # do something before the foo command is called
+ pass
+
+ @foo.after_invoke
+ async def after_foo_command(ctx):
+ # do something after the foo command is called
+ pass
+
+The special cog method for these is ``__before_invoke`` and ``__after_invoke``, e.g.: ::
+
+ class Cog:
+ async def __before_invoke(self, ctx):
+ ctx.secret_cog_data = 'foo'
+
+ async def __after_invoke(self, ctx):
+ print('{0.command} is done...'.format(ctx))
+
+ @commands.command()
+ async def foo(self, ctx):
+ await ctx.send(ctx.secret_cog_data)
+
+To check if a command failed in the after invocation hook, you can use
+:attr:`Context.command_failed <ext.commands.Context.command_failed>`.
+
+The invocation order is as follows:
+
+1. Command local before invocation hook
+2. Cog local before invocation hook
+3. Global before invocation hook
+4. The actual command
+5. Command local after invocation hooko
+6. Cog local after invocation hooko
+7. Global after invocation hooko
+
+Converter Changes
+~~~~~~~~~~~~~~~~~~~
+
+Prior to v1.0, a converter was a type hint that could be a callable that could be invoked
+with a singular argument denoting the argument passed by the user as a string.
+
+This system was eventually expanded to support a :class:`~ext.commands.Converter` system to
+allow plugging in the :class:`~ext.commands.Context` and do more complicated conversions such
+as the built-in "discord" converters.
+
+In v1.0 this converter system was revamped to allow instances of :class:`~ext.commands.Converter` derived
+classes to be passed. For consistency, the :meth:`~ext.commands.Converter.convert` method was changed to
+always be a coroutine and will now take the two arguments as parameters.
+
+Essentially, before: ::
+
+ class MyConverter(commands.Converter):
+ def convert(self):
+ return self.ctx.message.server.me
+
+After: ::
+
+ class MyConverter(commands.Converter):
+ async def convert(self, ctx, argument):
+ return ctx.me
+The command framework also got a couple new converters:
+- :class:`~ext.commands.clean_content` this is akin to :attr:`Message.clean_content` which scrubs mentions.
+- :class:`~ext.commands.UserConverter` will now appropriately convert :class:`User` only.
+- ``ChannelConverter`` is now split into two different converters
+ - :class:`~ext.commands.TextChannelConverter` for :class:`TextChannel`
+ - :class:`~ext.commands.VoiceChannelConverter` for :class:`VoiceChannel`