Back to Discord Py

Migrating to v2.0

docs/migrating.rst

2.7.157.9 KB
Original Source

.. currentmodule:: discord

.. _migrating_2_0:

Migrating to v2.0

Compared to v1.0, v2.0 mostly has breaking changes related to better developer experience and API coverage. While the changes aren't as massive to require an entire rewrite, there are still many changes that need to be accounted for.

Python Version Change

In order to ease development, maintain security updates, and use newer features v2.0 drops support for Python 3.7 and earlier.

.. _migrating_2_0_userbot_removal:

Removal of Support For User Accounts

Logging on with a user token is against the Discord Terms of Service <https://support.discord.com/hc/en-us/articles/115002192352>_ and as such all support for user-only endpoints has been removed.

The following have been removed:

  • bot parameter to :meth:Client.login and :meth:Client.start
  • afk parameter to :meth:Client.change_presence
  • password, new_password, email, and house parameters to :meth:ClientUser.edit
  • CallMessage model
  • GroupCall model
  • Profile model
  • Relationship model
  • RelationshipType enumeration
  • HypeSquadHouse enumeration
  • PremiumType enumeration
  • UserContentFilter enumeration
  • FriendFlags enumeration
  • Theme enumeration
  • on_relationship_add event
  • on_relationship_remove event
  • on_relationship_update event
  • Client.fetch_user_profile method
  • ClientUser.create_group method
  • ClientUser.edit_settings method
  • ClientUser.get_relationship method
  • GroupChannel.add_recipients method
  • GroupChannel.remove_recipients method
  • GroupChannel.edit method
  • Guild.ack method
  • Message.ack method
  • User.block method
  • User.is_blocked method
  • User.is_friend method
  • User.profile method
  • User.remove_friend method
  • User.send_friend_request method
  • User.unblock method
  • ClientUser.blocked attribute
  • ClientUser.email attribute
  • ClientUser.friends attribute
  • ClientUser.premium attribute
  • ClientUser.premium_type attribute
  • ClientUser.relationships attribute
  • Message.call attribute
  • User.mutual_friends attribute
  • User.relationship attribute

.. _migrating_2_0_client_async_setup:

asyncio Event Loop Changes

Python 3.7 introduced a new helper function :func:asyncio.run which automatically creates and destroys the asynchronous event loop.

In order to support this, the way discord.py handles the :mod:asyncio event loop has changed.

This allows you to rather than using :meth:Client.run create your own asynchronous loop to setup other asynchronous code as needed.

Quick example:

.. code-block:: python

client = discord.Client()

async def main():
    # do other async things
    await my_async_function()

    # start the client
    async with client:
        await client.start(TOKEN)

asyncio.run(main())

A new :meth:~Client.setup_hook method has also been added to the :class:Client class. This method is called after login but before connecting to the discord gateway.

It is intended to be used to setup various bot features in an asynchronous context.

:meth:~Client.setup_hook can be defined by subclassing the :class:Client class.

Quick example:

.. code-block:: python

class MyClient(discord.Client):
    async def setup_hook(self):
        print('This is asynchronous!')

client = MyClient()
client.run(TOKEN)

With this change, constructor of :class:Client no longer accepts connector and loop parameters.

In parallel with this change, changes were made to loading and unloading of commands extension extensions and cogs, see :ref:migrating_2_0_commands_extension_cog_async for more information.

Intents Are Now Required

In earlier versions, the intents keyword argument was optional and defaulted to :meth:Intents.default. In order to better educate users on their intents and to also make it more explicit, this parameter is now required to pass in.

For example:

.. code-block:: python3

# before
client = discord.Client()

# after
intents = discord.Intents.default()
client = discord.Client(intents=intents)

This change applies to all subclasses of :class:Client.

  • :class:AutoShardedClient
  • :class:~discord.ext.commands.Bot
  • :class:~discord.ext.commands.AutoShardedBot

Abstract Base Classes Changes

:ref:discord_api_abcs that inherited from :class:abc.ABCMeta now inherit from :class:typing.Protocol.

This results in a change of the base metaclass used by these classes but this should generally be completely transparent to the user.

All of the classes are either :func:runtime-checkable <typing.runtime_checkable> protocols or explicitly inherited from and as such usage with :func:isinstance and :func:issubclass is not affected.

The following have been changed to :func:runtime-checkable <typing.runtime_checkable> :class:~typing.Protocol\s:

  • :class:abc.Snowflake
  • :class:abc.User

The following have been changed to subclass :class:~typing.Protocol:

  • :class:abc.GuildChannel
  • :class:abc.Connectable

The following have been changed to use the default metaclass instead of :class:abc.ABCMeta:

  • :class:abc.Messageable
  • :class:abc.PrivateChannel

datetime Objects Are Now UTC-Aware

All usage of naive :class:datetime.datetime objects in the library has been replaced with aware objects using UTC timezone. Methods that accepted naive :class:~datetime.datetime objects now also accept timezone-aware objects. To keep behavior inline with :class:~datetime.datetime's methods, this library's methods now assume that naive :class:~datetime.datetime objects are local time (note that some of the methods may not accept naive :class:~datetime.datetime, such exceptions are listed below).

Because naive :class:~datetime.datetime objects are treated by many of its methods as local times, the previous behavior was more likely to result in programming errors with their usage.

To ease the migration, :func:utils.utcnow helper function has been added.

.. warning:: Using :meth:datetime.datetime.utcnow can be problematic since it returns a naive UTC datetime object.

Quick example:

.. code:: python

# before
week_ago = datetime.datetime.utcnow() - datetime.timedelta(days=7)
if member.created_at > week_ago:
    print(f'Member account {member} was created less than a week ago!')

# after
# The new helper function can be used here:
week_ago = discord.utils.utcnow() - datetime.timedelta(days=7)
# ...or the equivalent result can be achieved with datetime.datetime.now():
week_ago = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=7)
if member.created_at > week_ago:
    print(f'Member account {member} was created less than a week ago!')

The following have been changed from naive to aware :class:~datetime.datetime objects in UTC:

  • :attr:AuditLogEntry.created_at attribute
  • :attr:BaseActivity.created_at attribute
  • :attr:ClientUser.created_at attribute
  • :attr:DMChannel.created_at attribute
  • :attr:Emoji.created_at attribute
  • :attr:GroupChannel.created_at attribute
  • :attr:Guild.created_at attribute
  • :attr:abc.GuildChannel.created_at attribute
  • :attr:Invite.created_at attribute
  • :attr:Object.created_at attribute
  • :attr:Member.created_at attribute
  • :attr:Message.created_at attribute
  • :attr:PartialEmoji.created_at attribute
  • :attr:PartialInviteChannel.created_at attribute
  • :attr:PartialInviteGuild.created_at attribute
  • :attr:PartialMessage.created_at attribute
  • :attr:Role.created_at attribute
  • :attr:Spotify.created_at attribute
  • :attr:Sticker.created_at attribute
  • :attr:TeamMember.created_at attribute
  • :attr:Template.created_at attribute
  • :attr:User.created_at attribute
  • :attr:Webhook.created_at attribute
  • :attr:Widget.created_at attribute
  • :attr:WidgetChannel.created_at attribute
  • :attr:WidgetMember.created_at attribute
  • :attr:Message.edited_at attribute
  • :attr:Invite.expires_at attribute
  • :attr:Activity.end attribute
  • :attr:Game.end attribute
  • :attr:Spotify.end attribute
  • :attr:Member.joined_at attribute
  • :attr:Member.premium_since attribute
  • :attr:VoiceState.requested_to_speak_at attribute
  • :attr:Activity.start attribute
  • :attr:Game.start attribute
  • :attr:Spotify.start attribute
  • :attr:StreamIntegration.synced_at attribute
  • :attr:Embed.timestamp attribute
  • :attr:Template.updated_at attribute
  • timestamp parameter in :func:on_typing event
  • last_pin parameter in :func:on_private_channel_pins_update event
  • last_pin parameter in :func:on_guild_channel_pins_update event
  • Return type of :func:utils.snowflake_time

The following now accept aware :class:~datetime.datetime and assume that if the passed :class:~datetime.datetime is naive, it is a local time:

  • :meth:abc.Messageable.history method
  • :meth:Client.fetch_guilds method
  • :meth:Guild.audit_logs method
  • :meth:Guild.fetch_members method
  • :meth:TextChannel.purge method
  • :attr:Embed constructor
  • :attr:Embed.timestamp property setter
  • :func:utils.sleep_until function
  • :func:utils.time_snowflake function

Currently, there's only one place in this library that doesn't accept naive :class:datetime.datetime objects:

  • timed_out_until parameter in :meth:Member.edit

    This has been done to prevent users from mistakenly applying incorrect timeouts to guild members.

Major Webhook Changes

Webhook support has been rewritten to work better with typings and rate limits.

As a result, synchronous functionality has been split to separate classes.

Quick example for asynchronous webhooks:

.. code:: python

# before
async with aiohttp.ClientSession() as session:
    webhook = discord.Webhook.from_url('url-here', adapter=discord.AsyncWebhookAdapter(session))
    await webhook.send('Hello World', username='Foo')

# after
async with aiohttp.ClientSession() as session:
    webhook = discord.Webhook.from_url('url-here', session=session)
    await webhook.send('Hello World', username='Foo')

Quick example for synchronous webhooks:

.. code:: python

# before
webhook = discord.Webhook.partial(123456, 'token-here', adapter=discord.RequestsWebhookAdapter())
webhook.send('Hello World', username='Foo')

# after
webhook = discord.SyncWebhook.partial(123456, 'token-here')
webhook.send('Hello World', username='Foo')

The following breaking changes have been made:

  • Synchronous functionality of :class:Webhook and :class:WebhookMessage has been split to :class:SyncWebhook and :class:SyncWebhookMessage.
  • WebhookAdapter class has been removed and the interfaces based on it (AsyncWebhookAdapter and RequestsWebhookAdapter) are now considered implementation detail and should not be depended on.
  • execute alias for :meth:Webhook.send/:meth:SyncWebhook.send has been removed.

Asset Redesign and Changes

The :class:Asset object now encompasses all of the methods and attributes related to a CDN asset.

This means that all models with asset-related attribute and methods have been transformed to use this new design. As an example, here's how these changes look for :attr:Guild.icon (of :class:Asset type):

  • Guild.icon (of :class:str type) has been replaced with :attr:Guild.icon.key <Asset.key>.

  • Guild.is_icon_animated has been replaced with :meth:Guild.icon.is_animated <Asset.is_animated>.

  • Guild.icon_url has been replaced with :attr:Guild.icon.

  • Guild.icon_url_as has been replaced with :meth:Guild.icon.replace <Asset.replace>.

    • Helper methods :meth:Asset.with_size, :meth:Asset.with_format, and :meth:Asset.with_static_format have also been added.

In addition to this, :class:Emoji and :class:PartialEmoji now also share an interface similar to :class:Asset's:

  • :attr:Emoji.url is now of :class:str type.
  • Emoji.url_as has been removed.
  • Emoji.url.read has been replaced with :meth:Emoji.read.
  • Emoji.url.save has been replaced with :meth:Emoji.save.

:class:Asset now always represent an actually existing CDN asset. This means that:

  • str(x) on an :class:Asset can no longer return an empty string.
  • bool(x) on an :class:Asset can no longer return False.
  • Attributes containing an optional :class:Asset can now be None.

The following were affected by this change:

  • :attr:AppInfo.cover_image

    • AppInfo.cover_image (replaced by :attr:AppInfo.cover_image.key <Asset.key>)

    • AppInfo.cover_image_url (replaced by :attr:AppInfo.cover_image)

      • The new attribute may now be None.
    • AppInfo.cover_image_url_as (replaced by :meth:AppInfo.cover_image.replace <Asset.replace>)

  • :attr:AppInfo.icon

    • AppInfo.icon (replaced by :attr:AppInfo.icon.key <Asset.key>)

    • AppInfo.icon_url (replaced by :attr:AppInfo.icon)

      • The new attribute may now be None.
    • AppInfo.icon_url_as (replaced by :meth:AppInfo.icon.replace <Asset.replace>)

  • :class:AuditLogDiff

    • :attr:AuditLogDiff.avatar is now of :class:Asset type.
    • :attr:AuditLogDiff.icon is now of :class:Asset type.
    • :attr:AuditLogDiff.splash is now of :class:Asset type.
  • :attr:Emoji.url

    • :attr:Emoji.url is now of :class:str type.
    • Emoji.url_as has been removed.
    • Emoji.url.read (replaced by :meth:Emoji.read)
    • Emoji.url.save (replaced by :meth:Emoji.save)
  • :attr:GroupChannel.icon

    • GroupChannel.icon (replaced by :attr:GroupChannel.icon.key <Asset.key>)

    • GroupChannel.icon_url (replaced by :attr:GroupChannel.icon)

      • The new attribute may now be None.
    • GroupChannel.icon_url_as (replaced by :meth:GroupChannel.icon.replace <Asset.replace>)

  • :attr:Guild.banner

    • Guild.banner (replaced by :attr:Guild.banner.key <Asset.key>)

    • Guild.banner_url (replaced by :attr:Guild.banner)

      • The new attribute may now be None.
    • Guild.banner_url_as (replaced by :meth:Guild.banner.replace <Asset.replace>)

  • :attr:Guild.discovery_splash

    • Guild.discovery_splash (replaced by :attr:Guild.discovery_splash.key <Asset.key>)

    • Guild.discovery_splash_url (replaced by :attr:Guild.discovery_splash)

      • The new attribute may now be None.
    • Guild.discovery_splash_url_as (replaced by :meth:Guild.discovery_splash.replace <Asset.replace>)

  • :attr:Guild.icon

    • Guild.icon (replaced by :attr:Guild.icon.key <Asset.key>)

    • Guild.is_icon_animated (replaced by :meth:Guild.icon.is_animated <Asset.is_animated>)

    • Guild.icon_url (replaced by :attr:Guild.icon)

      • The new attribute may now be None.
    • Guild.icon_url_as (replaced by :meth:Guild.icon.replace <Asset.replace>)

  • :attr:Guild.splash

    • Guild.splash (replaced by :attr:Guild.splash.key <Asset.key>)

    • Guild.splash_url (replaced by :attr:Guild.splash)

      • The new attribute may now be None.
    • Guild.splash_url_as (replaced by :meth:Guild.splash.replace <Asset.replace>)

  • :attr:Member.avatar

    • Member.avatar (replaced by :attr:Member.avatar.key <Asset.key>)

    • Member.is_avatar_animated (replaced by :meth:Member.avatar.is_animated <Asset.is_animated>)

    • Member.avatar_url (replaced by :attr:Member.avatar)

      • The new attribute may now be None.
    • Member.avatar_url_as (replaced by :meth:Member.avatar.replace <Asset.replace>)

  • :attr:Member.default_avatar

    • Member.default_avatar (replaced by :attr:Member.default_avatar.key <Asset.key>)
    • Member.default_avatar_url (replaced by :attr:Member.default_avatar)
    • Member.default_avatar_url_as (replaced by :meth:Member.default_avatar.replace <Asset.replace>)
  • :attr:PartialEmoji.url

    • :attr:PartialEmoji.url is now of :class:str type.
    • PartialEmoji.url_as has been removed.
    • PartialEmoji.url.read (replaced by :meth:PartialEmoji.read)
    • PartialEmoji.url.save (replaced by :meth:PartialEmoji.save)
  • :attr:PartialInviteGuild.banner

    • PartialInviteGuild.banner (replaced by :attr:PartialInviteGuild.banner.key <Asset.key>)

    • PartialInviteGuild.banner_url (replaced by :attr:PartialInviteGuild.banner)

      • The new attribute may now be None.
    • PartialInviteGuild.banner_url_as (replaced by :meth:PartialInviteGuild.banner.replace <Asset.replace>)

  • :attr:PartialInviteGuild.icon

    • PartialInviteGuild.icon (replaced by :attr:PartialInviteGuild.icon.key <Asset.key>)

    • PartialInviteGuild.is_icon_animated (replaced by :meth:PartialInviteGuild.icon.is_animated <Asset.is_animated>)

    • PartialInviteGuild.icon_url (replaced by :attr:PartialInviteGuild.icon)

      • The new attribute may now be None.
    • PartialInviteGuild.icon_url_as (replaced by :meth:PartialInviteGuild.icon.replace <Asset.replace>)

  • :attr:PartialInviteGuild.splash

    • PartialInviteGuild.splash (replaced by :attr:PartialInviteGuild.splash.key <Asset.key>)

    • PartialInviteGuild.splash_url (replaced by :attr:PartialInviteGuild.splash)

      • The new attribute may now be None.
    • PartialInviteGuild.splash_url_as (replaced by :meth:PartialInviteGuild.splash.replace <Asset.replace>)

  • :attr:Team.icon

    • Team.icon (replaced by :attr:Team.icon.key <Asset.key>)

    • Team.icon_url (replaced by :attr:Team.icon)

      • The new attribute may now be None.
    • Team.icon_url_as (replaced by :meth:Team.icon.replace <Asset.replace>)

  • :attr:User.avatar

    • User.avatar (replaced by :attr:User.avatar.key <Asset.key>)

    • User.is_avatar_animated (replaced by :meth:User.avatar.is_animated <Asset.is_animated>)

    • User.avatar_url (replaced by :attr:User.avatar)

      • The new attribute may now be None.
    • User.avatar_url_as (replaced by :meth:User.avatar.replace <Asset.replace>)

  • :attr:User.default_avatar

    • User.default_avatar (replaced by :attr:User.default_avatar.key <Asset.key>)
    • User.default_avatar_url (replaced by :attr:User.default_avatar)
    • User.default_avatar_url_as (replaced by :meth:User.default_avatar.replace <Asset.replace>)
  • :attr:Webhook.avatar

    • Webhook.avatar (replaced by :attr:Webhook.avatar.key <Asset.key>)

    • Webhook.avatar_url (replaced by :attr:Webhook.avatar)

      • The new attribute may now be None.
    • Webhook.avatar_url_as (replaced by :meth:Webhook.avatar.replace <Asset.replace>)

.. _migrating_2_0_thread_support:

Thread Support

v2.0 has been updated to use a newer API gateway version which supports threads and as a result of this had to make few breaking changes. Most notably messages sent in guilds can, in addition to a :class:TextChannel, be sent in a :class:Thread.

The main differences between text channels and threads are:

  • Threads do not have their own permissions, they inherit the permissions of their parent channel.

    • This means that threads do not have these attributes:

      • changed_roles
      • overwrites
      • permissions_synced

    .. note::

      Text channels have a few dedicated permissions for threads:
    
      - :attr:`Permissions.manage_threads`
      - :attr:`Permissions.create_public_threads`
      - :attr:`Permissions.create_private_threads`
      - :attr:`Permissions.send_messages_in_threads`
    
  • Threads do not have their own NSFW status, they inherit it from their parent channel.

    • This means that :class:Thread does not have an nsfw attribute.
  • Threads do not have their own topic.

    • This means that :class:Thread does not have a topic attribute.
  • Threads do not have their own position in the channel list.

    • This means that :class:Thread does not have a position attribute.
  • :attr:Thread.created_at of threads created before 10 January 2022 is None.

  • :attr:Thread.members is of type List[:class:ThreadMember] rather than List[:class:Member]

    • Most of the time, this data is not provided and a call to :meth:Thread.fetch_members is needed.

For convenience, :class:Thread has a set of properties and methods that return the information about the parent channel:

  • :attr:Thread.category

  • :attr:Thread.category_id

  • :meth:Thread.is_news

  • :meth:Thread.is_nsfw

  • :meth:Thread.permissions_for

    • Note that this outputs the permissions of the parent channel and you might need to check for different permissions when trying to determine if a member can do something.

      Here are some notable examples:

      • A guild member can send messages in a text channel if they have :attr:~Permissions.send_messages permission in it.

        A guild member can send messages in a public thread if: - They have :attr:~Permissions.send_messages_in_threads permission in its parent channel. - The thread is not :attr:~Thread.locked.

        A guild member can send messages in a private thread if: - They have :attr:~Permissions.send_messages_in_threads permission in its parent channel. - They're either already a member of the thread or have a :attr:~Permissions.manage_threads permission in its parent channel. - The thread is not :attr:~Thread.locked.

      • A guild member can edit a text channel if they have :attr:~Permissions.manage_channels permission in it.

        A guild member can edit a thread if they have :attr:~Permissions.manage_threads permission in its parent channel.

        .. note::

        A thread's :attr:`~Thread.owner` can archive a (not-locked) thread and edit its :attr:`~Thread.name`
        and :attr:`~Thread.auto_archive_duration` without :attr:`~Permissions.manage_threads` permission.
        
      • A guild member can react with an emoji to messages in a text channel if: - They have :attr:~Permissions.read_message_history permission in it. - They have :attr:~Permissions.add_reactions permission in it or the message already has that emoji reaction.

        A guild member can react with an emoji to messages in a public thread if: - They have :attr:~Permissions.read_message_history permission in its parent channel. - They have :attr:~Permissions.add_reactions permission in its parent channel or the message already has that emoji reaction. - The thread is not :attr:~Thread.archived. Note that the guild member can unarchive a thread (if it's not :attr:~Thread.locked) to react to a message.

        A guild member can react with an emoji to messages in a private thread if: - They have :attr:~Permissions.read_message_history permission in its parent channel. - They have :attr:~Permissions.add_reactions permission in its parent channel or the message already has that emoji reaction. - They're either already a member of the thread or have a :attr:~Permissions.manage_threads permission in its parent channel. - The thread is not :attr:~Thread.archived. Note that the guild member can unarchive a thread (if it's not :attr:~Thread.locked) to react to a message.

The following changes have been made:

  • :attr:Message.channel may now be a :class:Thread.

  • :attr:Message.channel_mentions list may now contain a :class:Thread.

  • :attr:AuditLogEntry.target may now be a :class:Thread.

  • :attr:PartialMessage.channel may now be a :class:Thread.

  • :attr:Guild.get_channel does not return :class:Thread\s.

    • If you're looking to get a channel or thread, use :attr:Guild.get_channel_or_thread instead.
    • If you're only looking to get threads, use :attr:Guild.get_thread or :attr:TextChannel.get_thread instead.
  • channel parameter in :func:on_guild_channel_pins_update may now be a :class:Thread.

  • channel parameter in :func:on_typing may now be a :class:Thread.

  • :meth:Client.fetch_channel may now return :class:Thread.

  • :meth:Client.get_channel may now return :class:Thread.

  • :meth:Guild.fetch_channel may now return :class:Thread.

Removing In-Place Edits

Most of the model methods that previously edited the model in-place have been updated to no longer do this. Instead, these methods will now return a new instance of the newly updated model. This has been done to avoid the library running into race conditions between in-place edits and gateway events on model updates. See :issue:4098 for more information.

Quick example:

.. code:: python

# before
await member.edit(nick='new nick')
await member.send(f'Your new nick is {member.nick}')

# after
updated_member = await member.edit(nick='new nick')
await member.send(f'Your new nick is {updated_member.nick}')

The following have been changed:

  • :meth:CategoryChannel.edit

    • Note that this method will return None instead of :class:CategoryChannel if the edit was only positional.
  • :meth:Member.edit

    • Note that this method only returns the updated :class:Member when certain fields are updated.
  • :meth:StageChannel.edit

    • Note that this method will return None instead of :class:StageChannel if the edit was only positional.
  • :meth:TextChannel.edit

    • Note that this method will return None instead of :class:TextChannel if the edit was only positional.
  • :meth:VoiceChannel.edit

    • Note that this method will return None instead of :class:VoiceChannel if the edit was only positional.
  • :meth:ClientUser.edit

  • :meth:Emoji.edit

  • :meth:Guild.edit

  • :meth:Message.edit

  • :meth:Role.edit

  • :meth:Template.edit

  • :meth:Template.sync

  • :meth:Webhook.edit

  • :meth:Webhook.edit_message

  • :meth:WebhookMessage.edit

Sticker Changes

Discord has changed how their stickers work and as such, sticker support has been reworked.

The following breaking changes have been made:

  • Type of :attr:Message.stickers changed to List[:class:StickerItem].

    • To get the :class:Sticker from :class:StickerItem, use :meth:StickerItem.fetch or (only for stickers from guilds the bot is in) :meth:Client.get_sticker.
  • :attr:Sticker.format is now of :class:StickerFormatType type.

  • Sticker.tags has been removed.

    • Depending on type of the sticker, :attr:StandardSticker.tags or :attr:GuildSticker.emoji can be used instead.
  • Sticker.image and related methods have been removed.

  • Sticker.preview_image and related methods have been removed.

  • :attr:AuditLogDiff.type is now of Union[:class:ChannelType, :class:StickerType] type.

  • The old StickerType enum has been renamed to :class:StickerFormatType.

    • :class:StickerType now refers to a sticker type (official sticker vs guild-uploaded sticker) rather than its format type.

Integrations Changes

To support the new integration types, integration support has been reworked.

The following breaking changes have been made:

  • The old Integration class has been renamed to :class:StreamIntegration.
  • :meth:Guild.integrations now returns subclasses of the new :class:Integration class.

Presence Updates Now Have A Separate Event

Presence updates (changes in member's status and activity) now have a separate :func:on_presence_update event. :func:on_member_update event is now only called on member updates (changes in nickname, role, pending status, etc.).

From API perspective, these are separate events and as such, this change improves library's consistency with the API. Presence updates usually are 90% of all handled events so splitting these should benefit listeners that were only interested in member updates.

Quick example:

.. code:: python

# before
@client.event
async def on_member_update(self, before, after):
    if before.nick != after.nick:
        await nick_changed(before, after)
    if before.status != after.status:
        await status_changed(before, after)

# after
@client.event
async def on_member_update(self, before, after):
    if before.nick != after.nick:
        await nick_changed(before, after)

@client.event
async def on_presence_update(self, before, after):
    if before.status != after.status:
        await status_changed(before, after)

Moving Away From Custom AsyncIterator

Asynchronous iterators in v1.0 were implemented using a special class named AsyncIterator. v2.0 instead provides regular asynchronous iterators with no added utility methods.

This means that usage of the following utility methods is no longer possible:

  • AsyncIterator.next()

    Usage of an explicit async for loop should generally be preferred:

    .. code:: python

      # before
      it = channel.history()
      while True:
          try:
              message = await self.next()
          except discord.NoMoreItems:
              break
          print(f'Found message with ID {message.id}')
    
      # after
      async for message in channel.history():
          print(f'Found message with ID {message.id}')
    

    If you need to get next item from an iterator without a loop, you can use :func:anext (new in Python 3.10) or :meth:~object.__anext__ instead:

    .. code:: python

      # before
      it = channel.history()
      first = await it.next()
      if first.content == 'do not iterate':
          return
      async for message in it:
          ...
    
      # after
      it = channel.history()
      first = await anext(it)  # await it.__anext__() on Python<3.10
      if first.content == 'do not iterate':
          return
      async for message in it:
          ...
    
  • AsyncIterator.get()

    .. code:: python

      # before
      msg = await channel.history().get(author__name='Dave')
    
      # after
      msg = await discord.utils.get(channel.history(), author__name='Dave')
    
  • AsyncIterator.find()

    .. code:: python

      def predicate(event):
          return event.reason is not None
    
      # before
      event = await guild.audit_logs().find(predicate)
    
      # after
      event = await discord.utils.find(predicate, guild.audit_logs())
    
  • AsyncIterator.flatten()

    .. code:: python

      # before
      users = await reaction.users().flatten()
    
      # after
      users = [user async for user in reaction.users()]
    
  • AsyncIterator.chunk()

    .. code:: python

      # before
      async for leader, *users in reaction.users().chunk(3):
          ...
    
      # after
      async for leader, *users in discord.utils.as_chunks(reaction.users(), 3):
          ...
    
  • AsyncIterator.map()

    .. code:: python

      # before
      content_of_messages = []
      async for content in channel.history().map(lambda m: m.content):
          content_of_messages.append(content)
    
      # after
      content_of_messages = [message.content async for message in channel.history()]
    
  • AsyncIterator.filter()

    .. code:: python

      def predicate(message):
          return not message.author.bot
    
      # before
      user_messages = []
      async for message in channel.history().filter(lambda m: not m.author.bot):
          user_messages.append(message)
    
      # after
      user_messages = [message async for message in channel.history() if not m.author.bot]
    

To ease this transition, these changes have been made:

  • Added :func:utils.as_chunks as an alternative for AsyncIter.chunk.
  • Added support for :term:asynchronous iterator to :func:utils.find.
  • Added support for :term:asynchronous iterator to :func:utils.get.

The return type of the following methods has been changed to an :term:asynchronous iterator:

  • :meth:abc.Messageable.history
  • :meth:Client.fetch_guilds
  • :meth:Guild.audit_logs
  • :meth:Guild.fetch_members
  • :meth:Reaction.users

The NoMoreItems exception was removed as calling :func:anext or :meth:~object.__anext__ on an :term:asynchronous iterator will now raise :class:StopAsyncIteration.

Changing certain lists to be lazy sequences instead

In order to improve performance when calculating the length of certain lists, certain attributes were changed to return a sequence rather than a :class:list.

A sequence is similar to a :class:list except it is read-only. In order to get a list again you can call :class:list on the resulting sequence.

The following properties were changed to return a sequence instead of a list:

  • :attr:Client.guilds
  • :attr:Client.emojis
  • :attr:Client.private_channels
  • :attr:Guild.roles
  • :attr:Guild.channels
  • :attr:Guild.members

This change should be transparent, unless you are modifying the sequence by doing things such as list.append.

Embed Changes

Originally, embeds used a special sentinel to denote emptiness or remove an attribute from display. The Embed.Empty sentinel was made when Discord's embed design was in a nebulous state of flux. Since then, the embed design has stabilised and thus the sentinel is seen as legacy.

Therefore, Embed.Empty has been removed in favour of None.

Additionally, Embed.__eq__ has been implemented thus embeds becoming unhashable (e.g. using them in sets or dict keys).

.. code-block:: python

# before
embed = discord.Embed(title='foo')
embed.title = discord.Embed.Empty
embed == embed.copy() # False

# after
embed = discord.Embed(title='foo')
embed.title = None
embed == embed.copy() # True
{embed, embed} # Raises TypeError

Removal of InvalidArgument Exception

The custom InvalidArgument exception has been removed and functions and methods that raised it are now raising :class:TypeError and/or :class:ValueError instead.

The following methods have been changed:

  • :meth:Message.add_reaction
  • :meth:AutoShardedClient.change_presence
  • :meth:Client.change_presence
  • :meth:Reaction.clear
  • :meth:Message.clear_reaction
  • :meth:Guild.create_category
  • :meth:Guild.create_custom_emoji
  • :meth:Client.create_guild
  • :meth:Template.create_guild
  • :meth:StageChannel.create_instance
  • :meth:Guild.create_role
  • :meth:Guild.create_stage_channel
  • :meth:Guild.create_text_channel
  • :meth:Guild.create_voice_channel
  • :meth:TextChannel.create_webhook
  • :meth:Webhook.delete
  • :meth:WebhookMessage.delete
  • :meth:Webhook.delete_message
  • :meth:CategoryChannel.edit
  • :meth:ClientUser.edit
  • :meth:Guild.edit
  • :meth:Message.edit
  • :meth:Role.edit
  • :meth:StageChannel.edit
  • :meth:StageInstance.edit
  • :meth:StreamIntegration.edit
  • :meth:TextChannel.edit
  • :meth:VoiceChannel.edit
  • :meth:Webhook.edit
  • :meth:WebhookMessage.edit
  • :meth:Webhook.edit_message
  • :meth:Guild.edit_role_positions
  • :meth:Guild.estimate_pruned_members
  • :meth:TextChannel.follow
  • :meth:Webhook.from_url
  • :meth:abc.GuildChannel.move
  • :meth:Guild.prune_members
  • :meth:Message.remove_reaction
  • :meth:Message.reply
  • :meth:abc.Messageable.send
  • :meth:Webhook.send
  • :meth:abc.GuildChannel.set_permissions

Logging Changes

The library now provides a default logging configuration if using :meth:Client.run. To disable it, pass None to the log_handler keyword parameter. Since the library now provides a default logging configuration, certain methods were changed to no longer print to :data:sys.stderr but use the logger instead:

  • :meth:Client.on_error
  • :meth:discord.ext.tasks.Loop.error
  • :meth:discord.ext.commands.Bot.on_command_error
  • :meth:VoiceClient.play

For more information, check :doc:logging.

Text in Voice

In order to support text in voice functionality, a few changes had to be made:

  • :class:VoiceChannel is now :class:abc.Messageable so it can have messages sent and received.
  • :attr:Message.channel can now be :class:VoiceChannel.

In the future this may include :class:StageChannel when Discord implements it.

Removal of StoreChannel

Discord's API has removed store channels as of March 10th, 2022 <https://support-dev.discord.com/hc/en-us/articles/6309018858647>_. Therefore, the library has removed support for it as well.

This removes the following:

  • StoreChannel
  • commands.StoreChannelConverter
  • ChannelType.store

Change in Guild.bans endpoint

Due to a breaking API change by Discord, :meth:Guild.bans no longer returns a list of every ban in the guild but instead is paginated using an asynchronous iterator.

.. code-block:: python3

# before

bans = await guild.bans()

# after
async for ban in guild.bans(limit=1000):
    ...

Flag classes now have a custom bool() implementation

To allow library users to easily check whether an instance of a flag class has any flags enabled, using bool on them will now only return True if at least one flag is enabled.

This means that evaluating instances of the following classes in a bool context (such as if obj:) may no longer return True:

  • :class:Intents
  • :class:MemberCacheFlags
  • :class:MessageFlags
  • :class:Permissions
  • :class:PublicUserFlags
  • :class:SystemChannelFlags

Function Signature Changes

Parameters in the following methods are now all positional-only:

  • :meth:AutoShardedClient.get_shard
  • :meth:Client.get_channel
  • :meth:Client.fetch_channel
  • :meth:Guild.get_channel
  • :meth:Guild.fetch_channel
  • :meth:Client.get_emoji
  • :meth:Guild.fetch_emoji
  • :meth:Client.get_guild
  • :meth:Client.fetch_guild
  • :meth:Client.delete_invite
  • :meth:Guild.get_member
  • :meth:Guild.get_member_named
  • :meth:Guild.fetch_member
  • :meth:Client.get_user
  • :meth:Client.fetch_user
  • :meth:Guild.get_role
  • :meth:Client.fetch_webhook
  • :meth:Client.fetch_widget
  • :meth:Message.add_reaction
  • :meth:Client.on_error
  • :meth:abc.Messageable.fetch_message
  • :meth:abc.GuildChannel.permissions_for
  • :meth:DMChannel.get_partial_message
  • :meth:TextChannel.get_partial_message
  • :meth:TextChannel.delete_messages
  • :meth:Webhook.delete_message
  • :func:utils.find
  • :func:utils.snowflake_time

The following parameters are now positional-only:

  • iterable in :func:utils.get
  • event_method in :meth:Client.on_error
  • event in :meth:Client.wait_for
  • dt in :func:utils.time_snowflake

The following are now keyword-only:

  • Parameters in :meth:Reaction.users
  • Parameters in :meth:Client.create_guild
  • permissions, guild, redirect_uri, and scopes parameters in :func:utils.oauth_url
  • high in :func:utils.snowflake_time

The library now less often uses None as the default value for function/method parameters.

As a result, these parameters can no longer be None:

  • size, format, and static_format in :meth:Asset.replace
  • check in :meth:TextChannel.purge
  • icon and code in :meth:Client.create_guild
  • roles in :meth:Emoji.edit
  • topic, position and overwrites in :meth:Guild.create_text_channel
  • position and overwrites in :meth:Guild.create_voice_channel
  • topic, position and overwrites in :meth:Guild.create_stage_channel
  • position and overwrites in :meth:Guild.create_category
  • roles in :meth:Guild.prune_members
  • roles in :meth:Guild.estimate_pruned_members
  • description in :meth:Guild.create_template
  • roles in :meth:Guild.create_custom_emoji
  • before, after, oldest_first, user, and action in :meth:Guild.audit_logs
  • enable_emoticons in :meth:StreamIntegration.edit
  • mute, deafen, suppress, and roles in :meth:Member.edit
  • position in :meth:Role.edit
  • icon in :meth:Template.create_guild
  • name in :meth:Template.edit
  • permissions, guild, redirect_uri, scopes in :meth:utils.oauth_url
  • content, username, avatar_url, tts, file, files, embed, embeds, and allowed_mentions in :meth:Webhook.send

Allowed types for the following parameters have been changed:

  • rtc_region in :meth:Guild.create_voice_channel is now of type Optional[:class:str].
  • rtc_region in :meth:StageChannel.edit is now of type Optional[:class:str].
  • rtc_region in :meth:VoiceChannel.edit is now of type Optional[:class:str].
  • preferred_locale in :meth:Guild.edit is now of type :class:Locale.

Attribute Type Changes

The following changes have been made:

  • :attr:DMChannel.recipient may now be None.

  • :meth:Guild.vanity_invite may now be None. This has been done to fix an issue with the method returning a broken :class:Invite object.

  • :meth:Widget.fetch_invite may now be None.

  • :attr:Guild.shard_id is now 0 instead of None if :class:AutoShardedClient is not used.

  • :attr:Guild.mfa_level is now of type :class:MFALevel.

  • :attr:Guild.member_count is now of type Optional[:class:int].

  • :attr:AuditLogDiff.mfa_level is now of type :class:MFALevel.

  • :attr:AuditLogDiff.rtc_region is now of type :class:str.

  • :attr:StageChannel.rtc_region is now of type :class:str.

  • :attr:VoiceChannel.rtc_region is now of type :class:str.

  • :attr:ClientUser.avatar is now None when the default avatar is used.

    • If you want the avatar that a user has displayed, consider :attr:ClientUser.display_avatar.
  • :attr:Member.avatar is now None when the default avatar is used.

    • If you want the avatar that a member or user has displayed, consider :attr:Member.display_avatar or :attr:User.display_avatar.
  • :attr:User.avatar is now None when the default avatar is used.

    • If you want the avatar that a user has displayed, consider :attr:User.display_avatar.
  • :attr:Webhook.avatar is now None when the default avatar is used.

    • If you want the avatar that a webhook has displayed, consider :attr:Webhook.display_avatar.
  • :attr:AuditLogEntry.target may now be a :class:PartialMessageable.

  • :attr:PartialMessage.channel may now be a :class:PartialMessageable.

  • :attr:Guild.preferred_locale is now of type :class:Locale.

  • :attr:abc.GuildChannel.overwrites keys can now have :class:Object in them.

Removals

The following deprecated functionality have been removed:

  • Client.request_offline_members

    • Use :meth:Guild.chunk instead.
  • AutoShardedClient.request_offline_members

    • Use :meth:Guild.chunk instead.
  • Client.logout

    • Use :meth:Client.close instead.
  • fetch_offline_members parameter from :class:Client constructor

    • Use chunk_guild_at_startup instead.
  • Permissions.use_slash_commands and PermissionOverwrite.use_slash_commands

    • Use :attr:Permissions.use_application_commands and PermissionOverwrite.use_application_commands instead.

The following have been removed:

  • MemberCacheFlags.online

    • There is no replacement for this one. The current API version no longer provides enough data for this to be possible.
  • AppInfo.summary

    • There is no replacement for this one. The current API version no longer provides this field.
  • User.permissions_in and Member.permissions_in

    • Use :meth:abc.GuildChannel.permissions_for instead.
  • guild_subscriptions parameter from :class:Client constructor

    • The current API version no longer provides this functionality. Use intents parameter instead.
  • :class:VerificationLevel aliases:

    • VerificationLevel.table_flip - use :attr:VerificationLevel.high instead.
    • VerificationLevel.extreme - use :attr:VerificationLevel.highest instead.
    • VerificationLevel.double_table_flip - use :attr:VerificationLevel.highest instead.
    • VerificationLevel.very_high - use :attr:VerificationLevel.highest instead.
  • topic parameter from :meth:StageChannel.edit

    • The topic parameter must now be set via :meth:StageChannel.create_instance.
  • Reaction.custom_emoji

    • Use :meth:Reaction.is_custom_emoji instead.
  • AuditLogDiff.region

  • Guild.region

  • VoiceRegion

    • This has been marked deprecated by Discord and it was usually more or less out of date due to the pace they added them anyway.
  • region parameter from :meth:Client.create_guild

  • region parameter from :meth:Template.create_guild

  • region parameter from :meth:Guild.edit

  • on_private_channel_create event

    • Discord API no longer sends channel create event for DMs.
  • on_private_channel_delete event

    • Discord API no longer sends channel create event for DMs.
  • The undocumented private on_socket_response event

    • Consider using the newer documented :func:on_socket_event_type event instead.
  • abc.Messageable.trigger_typing

    • Use :meth:abc.Messageable.typing with await instead.

Miscellaneous Changes

The following changes have been made:

  • :func:on_socket_raw_receive is now only called if enable_debug_events is set on :class:Client.

  • :func:on_socket_raw_receive is now only called once the complete message is received and decompressed. The passed msg parameter is now always :class:str.

  • :func:on_socket_raw_send is now only called if enable_debug_events is set on :class:Client.

  • The documented return type for :meth:Guild.fetch_channels changed to Sequence[:class:abc.GuildChannel].

  • :func:utils.resolve_invite now returns a :class:ResolvedInvite class.

  • :func:utils.oauth_url now defaults to bot and applications.commands scopes when not given instead of just bot.

  • :meth:abc.Messageable.typing can no longer be used as a regular (non-async) context manager.

  • :attr:Intents.emojis is now an alias of :attr:Intents.emojis_and_stickers.

    This may affect code that iterates through (name, value) pairs in an instance of this class:

    .. code:: python

      # before
      friendly_names = {
          ...,
          'emojis': 'Emojis Intent',
          ...,
      }
      for name, value in discord.Intents.all():
          print(f'{friendly_names[name]}: {value}')
    
      # after
      friendly_names = {
          ...,
          'emojis_and_stickers': 'Emojis Intent',
          ...,
      }
      for name, value in discord.Intents.all():
          print(f'{friendly_names[name]}: {value}')
    
  • created_at is no longer part of :class:abc.Snowflake.

    All of the existing classes still keep this attribute. It is just no longer part of this protocol. This has been done because Discord reuses IDs (snowflakes) of some models in other models. For example, if :class:Thread is created from a message, its :attr:Thread.id is equivalent to the ID of that message and as such it doesn't contain information about creation time of the thread and :attr:Thread.created_at cannot be based on it.

  • :class:Embed's bool implementation now returns True when embed has any data set.

  • Calling :meth:Emoji.edit without roles argument no longer makes the emoji available to everyone.

    • To make the emoji available to everyone, pass an empty list to roles instead.
  • The old Colour.blurple has been renamed to :attr:Colour.og_blurple.

    • :attr:Colour.blurple refers to a different colour now.
  • :attr:Message.type is now set to :attr:MessageType.reply when a message is a reply.

    • This is caused by a difference in behavior in the current Discord API version.
  • :meth:Message.edit now merges object passed in allowed_mentions parameter with :attr:Client.allowed_mentions. If the parameter isn't provided, the defaults given by :attr:Client.allowed_mentions are used instead.

  • :meth:Permissions.stage_moderator now includes the :attr:Permissions.manage_channels permission and the :attr:Permissions.request_to_speak permission is no longer included.

  • :attr:File.filename will no longer be None, in situations where previously this was the case the filename is set to 'untitled'.

  • :attr:Message.application will no longer be a raw :class:dict of the API payload and now returns an instance of :class:MessageApplication.

:meth:VoiceProtocol.connect signature changes.

:meth:VoiceProtocol.connect will now be passed 2 keyword only arguments, self_deaf and self_mute. These indicate whether or not the client should join the voice chat being deafened or muted.

.. _migrating_2_0_commands:

Command Extension Changes

.. _migrating_2_0_commands_extension_cog_async:

Extension and Cog Loading / Unloading is Now Asynchronous


As an extension to the :ref:`asyncio changes <migrating_2_0_client_async_setup>` the loading and unloading of extensions and cogs is now asynchronous.

To accommodate this, the following changes have been made:

- The ``setup`` and ``teardown`` functions in extensions must now be coroutines.
- :meth:`ext.commands.Bot.load_extension` must now be awaited.
- :meth:`ext.commands.Bot.unload_extension` must now be awaited.
- :meth:`ext.commands.Bot.reload_extension` must now be awaited.
- :meth:`ext.commands.Bot.add_cog` must now be awaited.
- :meth:`ext.commands.Bot.remove_cog` must now be awaited.

Quick example of an extension setup function:

.. code:: python

    # before
    def setup(bot):
        bot.add_cog(MyCog(bot))

    # after
    async def setup(bot):
        await bot.add_cog(MyCog(bot))

Quick example of loading an extension:

.. code:: python

    # before
    bot.load_extension('my_extension')

    # after using setup_hook
    class MyBot(commands.Bot):
        async def setup_hook(self):
            await self.load_extension('my_extension')

    # after using async_with
    async def main():
        async with bot:
            await bot.load_extension('my_extension')
            await bot.start(TOKEN)

    asyncio.run(main())


Converters Are Now Generic Runtime Protocols
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:class:`~ext.commands.Converter` is now a :func:`runtime-checkable <typing.runtime_checkable>` :class:`typing.Protocol`.

This results in a change of the base metaclass used by these classes
which may affect user-created classes that inherit from :class:`~ext.commands.Converter`.

Quick example:

.. code:: python

    # before
    class SomeConverterMeta(type):
        ...

    class SomeConverter(commands.Converter, metaclass=SomeConverterMeta):
        ...

    # after
    class SomeConverterMeta(type(commands.Converter)):
        ...

    class SomeConverter(commands.Converter, metaclass=SomeConverterMeta):
        ...

In addition, :class:`~ext.commands.Converter` is now a :class:`typing.Generic` which (optionally) allows the users to
define their type hints more accurately.

Function Signature Changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Parameters in the following methods are now all positional-only:

- :meth:`ext.commands.when_mentioned`
- :meth:`ext.commands.Bot.on_command_error`
- :meth:`ext.commands.Bot.check`
- :meth:`ext.commands.Bot.check_once`
- :meth:`ext.commands.Bot.is_owner`
- :meth:`ext.commands.Bot.before_invoke`
- :meth:`ext.commands.Bot.after_invoke`
- :meth:`ext.commands.Bot.get_prefix`
- :meth:`ext.commands.Bot.invoke`
- :meth:`ext.commands.Bot.process_commands`
- :meth:`ext.commands.Command.is_on_cooldown`
- :meth:`ext.commands.Command.reset_cooldown`
- :meth:`ext.commands.Command.get_cooldown_retry_after`
- :meth:`ext.commands.Command.error`
- :meth:`ext.commands.Command.before_invoke`
- :meth:`ext.commands.Command.after_invoke`
- :meth:`ext.commands.Command.can_run`
- :meth:`ext.commands.check`
- :meth:`ext.commands.has_role`
- :meth:`ext.commands.bot_has_role`
- :meth:`ext.commands.before_invoke`
- :meth:`ext.commands.after_invoke`
- :meth:`ext.commands.HelpCommand.get_command_signature`
- :meth:`ext.commands.HelpCommand.remove_mentions`
- :meth:`ext.commands.HelpCommand.command_not_found`
- :meth:`ext.commands.HelpCommand.subcommand_not_found`
- :meth:`ext.commands.HelpCommand.get_max_size`
- :meth:`ext.commands.HelpCommand.send_error_message`
- :meth:`ext.commands.HelpCommand.on_help_command_error`
- :meth:`ext.commands.HelpCommand.send_bot_help`
- :meth:`ext.commands.HelpCommand.send_cog_help`
- :meth:`ext.commands.HelpCommand.send_group_help`
- :meth:`ext.commands.HelpCommand.send_command_help`
- :meth:`ext.commands.HelpCommand.prepare_help_command`
- :meth:`ext.commands.DefaultHelpCommand.shorten_text`
- :meth:`ext.commands.DefaultHelpCommand.add_command_formatting`
- :meth:`ext.commands.MinimalHelpCommand.get_command_signature`
- :meth:`ext.commands.MinimalHelpCommand.add_bot_commands_formatting`
- :meth:`ext.commands.MinimalHelpCommand.add_subcommand_formatting`
- :meth:`ext.commands.MinimalHelpCommand.add_aliases_formatting`
- :meth:`ext.commands.MinimalHelpCommand.add_command_formatting`

The following parameters are now positional-only:

- ``func`` in :meth:`ext.commands.Bot.check`
- ``func`` in :meth:`ext.commands.Bot.add_check`
- ``func`` in :meth:`ext.commands.Bot.remove_check`
- ``func`` in :meth:`ext.commands.Bot.check_once`
- ``func`` in :meth:`ext.commands.Bot.add_listener`
- ``func`` in :meth:`ext.commands.Bot.remove_listener`
- ``message`` in :meth:`ext.commands.Bot.get_context`
- ``func`` in :meth:`ext.commands.Command.add_check`
- ``func`` in :meth:`ext.commands.Command.remove_check`
- ``context`` in :meth:`ext.commands.Command.__call__`
- ``commands`` in :meth:`ext.commands.HelpCommand.filter_commands`
- ``ctx`` in :meth:`ext.commands.HelpCommand.command_callback`
- ``func`` in :meth:`ext.commands.HelpCommand.add_check`
- ``func`` in :meth:`ext.commands.HelpCommand.remove_check`
- ``commands`` in :meth:`ext.commands.DefaultHelpCommand.add_indented_commands`
- ``cog`` in :meth:`ext.commands.Bot.add_cog`
- ``name`` in :meth:`ext.commands.Bot.get_cog`
- ``name`` in :meth:`ext.commands.Bot.remove_cog`
- ``command`` in :meth:`ext.commands.Context.invoke`
- ``command`` in :meth:`ext.commands.GroupMixin.add_command`
- ``name`` in :meth:`ext.commands.GroupMixin.get_command`
- ``name`` in :meth:`ext.commands.GroupMixin.remove_command`

The following parameters have been removed:

- ``self_bot`` from :class:`~ext.commands.Bot`

    - This has been done due to the :ref:`migrating_2_0_userbot_removal` changes.

The library now less often uses ``None`` as the default value for function/method parameters.

As a result, these parameters can no longer be ``None``:

- ``name`` in :meth:`ext.commands.Bot.add_listener`
- ``name`` in :meth:`ext.commands.Bot.remove_listener`
- ``name`` in :meth:`ext.commands.Bot.listen`
- ``name`` in :meth:`ext.commands.Cog.listener`
- ``name`` in :meth:`ext.commands.Command`
- ``name`` and ``cls`` in :meth:`ext.commands.command`
- ``name`` and ``cls`` in :meth:`ext.commands.group`

Removals
~~~~~~~~~~

The following attributes have been removed:

- ``original`` from the :exc:`~ext.commands.ExtensionNotFound`
- ``type`` from the :class:`~ext.commands.Cooldown` class
  that was provided by the :attr:`ext.commands.CommandOnCooldown.cooldown` attribute

    - Use :attr:`ext.commands.CommandOnCooldown.type` instead.

- ``clean_prefix`` from the :class:`~ext.commands.HelpCommand`

    - Use :attr:`ext.commands.Context.clean_prefix` instead.

Miscellaneous Changes
~~~~~~~~~~~~~~~~~~~~~~

- :meth:`ext.commands.Bot.add_cog` is now raising :exc:`ClientException` when a cog with the same name is already loaded.

    - To override a cog, the new ``override`` parameter can be used.

- When passing a callable to ``type`` argument of :meth:`~ext.commands.cooldown`,
  it now needs to accept :class:`~ext.commands.Context` rather than :class:`Message` as its only argument.
- Metaclass of :class:`~ext.commands.Context` changed from :class:`abc.ABCMeta` to :class:`type`.
- Changed type of :attr:`ext.commands.Command.clean_params` from :class:`collections.OrderedDict` to :class:`dict`.
  As the latter is guaranteed to preserve insertion order since Python 3.7.
- :attr:`ext.commands.ChannelNotReadable.argument` may now be a :class:`Thread` due to the :ref:`migrating_2_0_thread_support` changes.
- :attr:`ext.commands.NSFWChannelRequired.channel` may now be a :class:`Thread` due to the :ref:`migrating_2_0_thread_support` changes.
- :attr:`ext.commands.Context.channel` may now be a :class:`Thread` due to the :ref:`migrating_2_0_thread_support` changes.
- :attr:`ext.commands.Context.channel` may now be a :class:`PartialMessageable`.
- ``MissingPermissions.missing_perms`` has been renamed to :attr:`ext.commands.MissingPermissions.missing_permissions`.
- ``BotMissingPermissions.missing_perms`` has been renamed to :attr:`ext.commands.BotMissingPermissions.missing_permissions`.
- :meth:`ext.commands.Cog.cog_load` has been added as part of the :ref:`migrating_2_0_commands_extension_cog_async` changes.
- :meth:`ext.commands.Cog.cog_unload` may now be a :term:`coroutine` due to the :ref:`migrating_2_0_commands_extension_cog_async` changes.
- :attr:`ext.commands.Command.clean_params` type now uses a custom :class:`inspect.Parameter` to handle defaults.

.. _migrating_2_0_tasks:

Tasks Extension Changes
-------------------------

- Calling :meth:`ext.tasks.Loop.stop` in :meth:`~ext.tasks.Loop.before_loop` now stops the first iteration from running.
- Calling :meth:`ext.tasks.Loop.change_interval` now changes the interval for the sleep time right away,
  rather than on the next loop iteration.
- ``loop`` parameter in :func:`ext.tasks.loop` can no longer be ``None``.

Migrating to v1.0
======================

The contents of that migration has been moved to :ref:`migrating_1_0`.