diff --git a/docs/migrating.rst b/docs/migrating.rst index a6df471a0..5e34cd2aa 100644 --- a/docs/migrating.rst +++ b/docs/migrating.rst @@ -1,5 +1,1200 @@ .. 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 `_ +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 + +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 ` 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 ` :class:`~typing.Protocol`\s: + +- :class:`abc.Snowflake` +- :class:`abc.User` +- :class:`abc.PrivateChannel` + +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` + +``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 +- ``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 `. +- ``Guild.is_icon_animated`` has been replaced with :meth:`Guild.icon.is_animated `. +- ``Guild.icon_url`` has been replaced with :attr:`Guild.icon`. +- ``Guild.icon_url_as`` has been replaced with :meth:`Guild.icon.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 `) + - ``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 `) + +- :attr:`AppInfo.icon` + + - ``AppInfo.icon`` (replaced by :attr:`AppInfo.icon.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 `) + +- :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 `) + - ``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 `) + +- :attr:`Guild.banner` + + - ``Guild.banner`` (replaced by :attr:`Guild.banner.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 `) + +- :attr:`Guild.discovery_splash` + + - ``Guild.discovery_splash`` (replaced by :attr:`Guild.discovery_splash.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 `) + +- :attr:`Guild.icon` + + - ``Guild.icon`` (replaced by :attr:`Guild.icon.key `) + - ``Guild.is_icon_animated`` (replaced by :meth:`Guild.icon.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 `) + +- :attr:`Guild.splash` + + - ``Guild.splash`` (replaced by :attr:`Guild.splash.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 `) + +- :attr:`Member.avatar` + + - ``Member.avatar`` (replaced by :attr:`Member.avatar.key `) + - ``Member.is_avatar_animated`` (replaced by :meth:`Member.avatar.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 `) + +- :attr:`Member.default_avatar` + + - ``Member.default_avatar`` (replaced by :attr:`Member.default_avatar.key `) + - ``Member.default_avatar_url`` (replaced by :attr:`Member.default_avatar`) + - ``Member.default_avatar_url_as`` (replaced by :meth:`Member.default_avatar.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 `) + - ``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 `) + +- :attr:`PartialInviteGuild.icon` + + - ``PartialInviteGuild.icon`` (replaced by :attr:`PartialInviteGuild.icon.key `) + - ``PartialInviteGuild.is_icon_animated`` (replaced by :meth:`PartialInviteGuild.icon.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 `) + +- :attr:`PartialInviteGuild.splash` + + - ``PartialInviteGuild.splash`` (replaced by :attr:`PartialInviteGuild.splash.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 `) + +- :attr:`Team.icon` + + - ``Team.icon`` (replaced by :attr:`Team.icon.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 `) + +- :attr:`User.avatar` + + - ``User.avatar`` (replaced by :attr:`User.avatar.key `) + - ``User.is_avatar_animated`` (replaced by :meth:`User.avatar.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 `) + +- :attr:`User.default_avatar` + + - ``User.default_avatar`` (replaced by :attr:`User.default_avatar.key `) + - ``User.default_avatar_url`` (replaced by :attr:`User.default_avatar`) + - ``User.default_avatar_url_as`` (replaced by :meth:`User.default_avatar.replace `) + +- :attr:`Webhook.avatar` + + - ``Webhook.avatar`` (replaced by :attr:`Webhook.avatar.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 `) + +.. _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:`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:`StoreChannel.edit` + + - Note that this method will return ``None`` instead of :class:`StoreChannel` 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` + +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:`StoreChannel.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` + +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:`Guild.get_stage_instance` +- :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:`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` +- :meth:`utils.find` + +The following parameters are now positional-only: + +- ``iterable`` in :meth:`utils.get` + +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 :meth:`utils.oauth_url` + +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` +- ``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`]. + +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. +- :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:`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`. + +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. + +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. + +Miscellanous 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. +- :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. + +.. _migrating_2_0_commands: + +Command Extension Changes +--------------------------- + +Converters Are Now Generic Runtime Protocols +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:class:`~ext.commands.Converter` is now a :func:`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 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following parameters are now positional-only: + +- ``func`` in :meth:`ext.commands.Bot.add_check` +- ``func`` in :meth:`ext.commands.Bot.remove_check` +- ``func`` in :meth:`ext.commands.Command.add_check` +- ``func`` in :meth:`ext.commands.Command.remove_check` +- ``func`` in :meth:`ext.commands.HelpCommand.add_check` +- ``func`` in :meth:`ext.commands.HelpCommand.remove_check` +- ``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. + +Miscellanous 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. + +- 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`. + +.. _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_1_0: Migrating to v1.0