diff --git a/.apiversion b/.apiversion
index c3cae12b..0359f243 100644
--- a/.apiversion
+++ b/.apiversion
@@ -1 +1 @@
-9.3
+9.4
diff --git a/.butcher/enums/ButtonStyle.yml b/.butcher/enums/ButtonStyle.yml
new file mode 100644
index 00000000..12833dde
--- /dev/null
+++ b/.butcher/enums/ButtonStyle.yml
@@ -0,0 +1,12 @@
+name: ButtonStyle
+description: |
+ This object represents a button style (inline- or reply-keyboard).
+
+ Sources:
+ * https://core.telegram.org/bots/api#inlinekeyboardbutton
+ * https://core.telegram.org/bots/api#keyboardbutton
+
+parse:
+ entity: InlineKeyboardButton
+ attribute: style
+ regexp: "'([a-z]+)'"
diff --git a/.butcher/methods/createForumTopic/entity.json b/.butcher/methods/createForumTopic/entity.json
index c10317c1..e3f744b3 100644
--- a/.butcher/methods/createForumTopic/entity.json
+++ b/.butcher/methods/createForumTopic/entity.json
@@ -7,9 +7,9 @@
"object": {
"anchor": "createforumtopic",
"name": "createForumTopic",
- "description": "Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns information about the created topic as a ForumTopic object.",
- "html_description": "
Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns information about the created topic as a ForumTopic object.
",
- "rst_description": "Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator rights. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.",
+ "description": "Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator right. Returns information about the created topic as a ForumTopic object.",
+ "html_description": "Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator right. Returns information about the created topic as a ForumTopic object.
",
+ "rst_description": "Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator right. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.",
"annotations": [
{
"type": "Integer or String",
diff --git a/.butcher/methods/getUserProfileAudios/entity.json b/.butcher/methods/getUserProfileAudios/entity.json
new file mode 100644
index 00000000..89fa4466
--- /dev/null
+++ b/.butcher/methods/getUserProfileAudios/entity.json
@@ -0,0 +1,41 @@
+{
+ "meta": {},
+ "group": {
+ "title": "Available methods",
+ "anchor": "available-methods"
+ },
+ "object": {
+ "anchor": "getuserprofileaudios",
+ "name": "getUserProfileAudios",
+ "description": "Use this method to get a list of profile audios for a user. Returns a UserProfileAudios object.",
+ "html_description": "Use this method to get a list of profile audios for a user. Returns a UserProfileAudios object.
",
+ "rst_description": "Use this method to get a list of profile audios for a user. Returns a :class:`aiogram.types.user_profile_audios.UserProfileAudios` object.",
+ "annotations": [
+ {
+ "type": "Integer",
+ "required": true,
+ "description": "Unique identifier of the target user",
+ "html_description": "Unique identifier of the target user | ",
+ "rst_description": "Unique identifier of the target user\n",
+ "name": "user_id"
+ },
+ {
+ "type": "Integer",
+ "required": false,
+ "description": "Sequential number of the first audio to be returned. By default, all audios are returned.",
+ "html_description": "Sequential number of the first audio to be returned. By default, all audios are returned. | ",
+ "rst_description": "Sequential number of the first audio to be returned. By default, all audios are returned.\n",
+ "name": "offset"
+ },
+ {
+ "type": "Integer",
+ "required": false,
+ "description": "Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100.",
+ "html_description": "Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100. | ",
+ "rst_description": "Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100.\n",
+ "name": "limit"
+ }
+ ],
+ "category": "methods"
+ }
+}
diff --git a/.butcher/methods/removeMyProfilePhoto/entity.json b/.butcher/methods/removeMyProfilePhoto/entity.json
new file mode 100644
index 00000000..e24d04bc
--- /dev/null
+++ b/.butcher/methods/removeMyProfilePhoto/entity.json
@@ -0,0 +1,16 @@
+{
+ "meta": {},
+ "group": {
+ "title": "Available methods",
+ "anchor": "available-methods"
+ },
+ "object": {
+ "anchor": "removemyprofilephoto",
+ "name": "removeMyProfilePhoto",
+ "description": "Removes the profile photo of the bot. Requires no parameters. Returns True on success.",
+ "html_description": "Removes the profile photo of the bot. Requires no parameters. Returns True on success.
",
+ "rst_description": "Removes the profile photo of the bot. Requires no parameters. Returns :code:`True` on success.",
+ "annotations": [],
+ "category": "methods"
+ }
+}
diff --git a/.butcher/methods/setMyProfilePhoto/entity.json b/.butcher/methods/setMyProfilePhoto/entity.json
new file mode 100644
index 00000000..53f4ab44
--- /dev/null
+++ b/.butcher/methods/setMyProfilePhoto/entity.json
@@ -0,0 +1,25 @@
+{
+ "meta": {},
+ "group": {
+ "title": "Available methods",
+ "anchor": "available-methods"
+ },
+ "object": {
+ "anchor": "setmyprofilephoto",
+ "name": "setMyProfilePhoto",
+ "description": "Changes the profile photo of the bot. Returns True on success.",
+ "html_description": "Changes the profile photo of the bot. Returns True on success.
",
+ "rst_description": "Changes the profile photo of the bot. Returns :code:`True` on success.",
+ "annotations": [
+ {
+ "type": "InputProfilePhoto",
+ "required": true,
+ "description": "The new profile photo to set",
+ "html_description": "The new profile photo to set | ",
+ "rst_description": "The new profile photo to set\n",
+ "name": "photo"
+ }
+ ],
+ "category": "methods"
+ }
+}
diff --git a/.butcher/schema/schema.json b/.butcher/schema/schema.json
index d492782d..0fca3099 100644
--- a/.butcher/schema/schema.json
+++ b/.butcher/schema/schema.json
@@ -1,7 +1,7 @@
{
"api": {
- "version": "9.3",
- "release_date": "2025-12-31"
+ "version": "9.4",
+ "release_date": "2026-02-09"
},
"items": [
{
@@ -552,6 +552,14 @@
"rst_description": "*Optional*. :code:`True`, if the bot has forum topic mode enabled in private chats. Returned only in :class:`aiogram.methods.get_me.GetMe`.\n",
"name": "has_topics_enabled",
"required": false
+ },
+ {
+ "type": "Boolean",
+ "description": "True, if the bot allows users to create and delete topics in private chats. Returned only in getMe.",
+ "html_description": "Optional. True, if the bot allows users to create and delete topics in private chats. Returned only in getMe. | ",
+ "rst_description": "*Optional*. :code:`True`, if the bot allows users to create and delete topics in private chats. Returned only in :class:`aiogram.methods.get_me.GetMe`.\n",
+ "name": "allows_users_to_create_topics",
+ "required": false
}
],
"category": "types"
@@ -1021,6 +1029,14 @@
"name": "rating",
"required": false
},
+ {
+ "type": "Audio",
+ "description": "For private chats, the first audio added to the profile of the user",
+ "html_description": "Optional. For private chats, the first audio added to the profile of the user | ",
+ "rst_description": "*Optional*. For private chats, the first audio added to the profile of the user\n",
+ "name": "first_profile_audio",
+ "required": false
+ },
{
"type": "UniqueGiftColors",
"description": "The color scheme based on a unique gift that must be used for the chat's name, message replies and link previews",
@@ -1479,6 +1495,22 @@
"name": "left_chat_member",
"required": false
},
+ {
+ "type": "ChatOwnerLeft",
+ "description": "Service message: chat owner has left",
+ "html_description": "Optional. Service message: chat owner has left | ",
+ "rst_description": "*Optional*. Service message: chat owner has left\n",
+ "name": "chat_owner_left",
+ "required": false
+ },
+ {
+ "type": "ChatOwnerChanged",
+ "description": "Service message: chat owner has changed",
+ "html_description": "Optional. Service message: chat owner has changed | ",
+ "rst_description": "*Optional*. Service message: chat owner has changed\n",
+ "name": "chat_owner_changed",
+ "required": false
+ },
{
"type": "String",
"description": "A chat title was changed to this value",
@@ -2794,6 +2826,64 @@
],
"category": "types"
},
+ {
+ "anchor": "videoquality",
+ "name": "VideoQuality",
+ "description": "This object represents a video file of a specific quality.",
+ "html_description": "This object represents a video file of a specific quality.
",
+ "rst_description": "This object represents a video file of a specific quality.",
+ "annotations": [
+ {
+ "type": "String",
+ "description": "Identifier for this file, which can be used to download or reuse the file",
+ "html_description": "Identifier for this file, which can be used to download or reuse the file | ",
+ "rst_description": "Identifier for this file, which can be used to download or reuse the file\n",
+ "name": "file_id",
+ "required": true
+ },
+ {
+ "type": "String",
+ "description": "Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.",
+ "html_description": "Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | ",
+ "rst_description": "Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.\n",
+ "name": "file_unique_id",
+ "required": true
+ },
+ {
+ "type": "Integer",
+ "description": "Video width",
+ "html_description": "Video width | ",
+ "rst_description": "Video width\n",
+ "name": "width",
+ "required": true
+ },
+ {
+ "type": "Integer",
+ "description": "Video height",
+ "html_description": "Video height | ",
+ "rst_description": "Video height\n",
+ "name": "height",
+ "required": true
+ },
+ {
+ "type": "String",
+ "description": "Codec that was used to encode the video, for example, 'h264', 'h265', or 'av01'",
+ "html_description": "Codec that was used to encode the video, for example, “h264”, “h265”, or “av01” | ",
+ "rst_description": "Codec that was used to encode the video, for example, 'h264', 'h265', or 'av01'\n",
+ "name": "codec",
+ "required": true
+ },
+ {
+ "type": "Integer",
+ "description": "File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.",
+ "html_description": "Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. | ",
+ "rst_description": "*Optional*. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.\n",
+ "name": "file_size",
+ "required": false
+ }
+ ],
+ "category": "types"
+ },
{
"anchor": "video",
"name": "Video",
@@ -2865,6 +2955,14 @@
"name": "start_timestamp",
"required": false
},
+ {
+ "type": "Array of VideoQuality",
+ "description": "List of available qualities of the video",
+ "html_description": "Optional. List of available qualities of the video | ",
+ "rst_description": "*Optional*. List of available qualities of the video\n",
+ "name": "qualities",
+ "required": false
+ },
{
"type": "String",
"description": "Original filename as defined by the sender",
@@ -5184,6 +5282,32 @@
],
"category": "types"
},
+ {
+ "anchor": "userprofileaudios",
+ "name": "UserProfileAudios",
+ "description": "This object represents the audios displayed on a user's profile.",
+ "html_description": "This object represents the audios displayed on a user's profile.
",
+ "rst_description": "This object represents the audios displayed on a user's profile.",
+ "annotations": [
+ {
+ "type": "Integer",
+ "description": "Total number of profile audios for the target user",
+ "html_description": "Total number of profile audios for the target user | ",
+ "rst_description": "Total number of profile audios for the target user\n",
+ "name": "total_count",
+ "required": true
+ },
+ {
+ "type": "Array of Audio",
+ "description": "Requested profile audios",
+ "html_description": "Requested profile audios | ",
+ "rst_description": "Requested profile audios\n",
+ "name": "audios",
+ "required": true
+ }
+ ],
+ "category": "types"
+ },
{
"anchor": "file",
"name": "File",
@@ -5305,18 +5429,34 @@
{
"anchor": "keyboardbutton",
"name": "KeyboardButton",
- "description": "This object represents one button of the reply keyboard. At most one of the optional fields must be used to specify type of the button. For simple text buttons, String can be used instead of this object to specify the button text.\nNote: request_users and request_chat options will only work in Telegram versions released after 3 February, 2023. Older clients will display unsupported message.",
- "html_description": "This object represents one button of the reply keyboard. At most one of the optional fields must be used to specify type of the button. For simple text buttons, String can be used instead of this object to specify the button text.
Note: request_users and request_chat options will only work in Telegram versions released after 3 February, 2023. Older clients will display unsupported message.
",
- "rst_description": "This object represents one button of the reply keyboard. At most one of the optional fields must be used to specify type of the button. For simple text buttons, *String* can be used instead of this object to specify the button text.\n**Note:** *request_users* and *request_chat* options will only work in Telegram versions released after 3 February, 2023. Older clients will display *unsupported message*.",
+ "description": "This object represents one button of the reply keyboard. At most one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button. For simple text buttons, String can be used instead of this object to specify the button text.",
+ "html_description": "This object represents one button of the reply keyboard. At most one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button. For simple text buttons, String can be used instead of this object to specify the button text.
",
+ "rst_description": "This object represents one button of the reply keyboard. At most one of the fields other than *text*, *icon_custom_emoji_id*, and *style* must be used to specify the type of the button. For simple text buttons, *String* can be used instead of this object to specify the button text.",
"annotations": [
{
"type": "String",
- "description": "Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed",
- "html_description": "Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed | ",
- "rst_description": "Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed\n",
+ "description": "Text of the button. If none of the fields other than text, icon_custom_emoji_id, and style are used, it will be sent as a message when the button is pressed",
+ "html_description": "Text of the button. If none of the fields other than text, icon_custom_emoji_id, and style are used, it will be sent as a message when the button is pressed | ",
+ "rst_description": "Text of the button. If none of the fields other than *text*, *icon_custom_emoji_id*, and *style* are used, it will be sent as a message when the button is pressed\n",
"name": "text",
"required": true
},
+ {
+ "type": "String",
+ "description": "Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.",
+ "html_description": "Optional. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription. | ",
+ "rst_description": "*Optional*. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on `Fragment `_ or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.\n",
+ "name": "icon_custom_emoji_id",
+ "required": false
+ },
+ {
+ "type": "String",
+ "description": "Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.",
+ "html_description": "Optional. Style of the button. Must be one of “danger” (red), “success” (green) or “primary” (blue). If omitted, then an app-specific style is used. | ",
+ "rst_description": "*Optional*. Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.\n",
+ "name": "style",
+ "required": false
+ },
{
"type": "KeyboardButtonRequestUsers",
"description": "If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a 'users_shared' service message. Available in private chats only.",
@@ -5597,9 +5737,9 @@
{
"anchor": "inlinekeyboardbutton",
"name": "InlineKeyboardButton",
- "description": "This object represents one button of an inline keyboard. Exactly one of the optional fields must be used to specify type of the button.",
- "html_description": "This object represents one button of an inline keyboard. Exactly one of the optional fields must be used to specify type of the button.
",
- "rst_description": "This object represents one button of an inline keyboard. Exactly one of the optional fields must be used to specify type of the button.",
+ "description": "This object represents one button of an inline keyboard. Exactly one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button.",
+ "html_description": "This object represents one button of an inline keyboard. Exactly one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button.
",
+ "rst_description": "This object represents one button of an inline keyboard. Exactly one of the fields other than *text*, *icon_custom_emoji_id*, and *style* must be used to specify the type of the button.",
"annotations": [
{
"type": "String",
@@ -5609,6 +5749,22 @@
"name": "text",
"required": true
},
+ {
+ "type": "String",
+ "description": "Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.",
+ "html_description": "Optional. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription. | ",
+ "rst_description": "*Optional*. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on `Fragment `_ or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.\n",
+ "name": "icon_custom_emoji_id",
+ "required": false
+ },
+ {
+ "type": "String",
+ "description": "Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.",
+ "html_description": "Optional. Style of the button. Must be one of “danger” (red), “success” (green) or “primary” (blue). If omitted, then an app-specific style is used. | ",
+ "rst_description": "*Optional*. Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.\n",
+ "name": "style",
+ "required": false
+ },
{
"type": "String",
"description": "HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id= can be used to mention a user by their identifier without using a username, if this is allowed by their privacy settings.",
@@ -7884,11 +8040,19 @@
},
{
"type": "Integer",
- "description": "The number of unique gifts that receive this model for every 1000 gifts upgraded",
- "html_description": "The number of unique gifts that receive this model for every 1000 gifts upgraded | ",
- "rst_description": "The number of unique gifts that receive this model for every 1000 gifts upgraded\n",
+ "description": "The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts.",
+ "html_description": "The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts. | ",
+ "rst_description": "The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts.\n",
"name": "rarity_per_mille",
"required": true
+ },
+ {
+ "type": "String",
+ "description": "Rarity of the model if it is a crafted model. Currently, can be 'uncommon', 'rare', 'epic', or 'legendary'.",
+ "html_description": "Optional. Rarity of the model if it is a crafted model. Currently, can be “uncommon”, “rare”, “epic”, or “legendary”. | ",
+ "rst_description": "*Optional*. Rarity of the model if it is a crafted model. Currently, can be 'uncommon', 'rare', 'epic', or 'legendary'.\n",
+ "name": "rarity",
+ "required": false
}
],
"category": "types"
@@ -8132,6 +8296,14 @@
"name": "is_premium",
"required": false
},
+ {
+ "type": "True",
+ "description": "True, if the gift was used to craft another gift and isn't available anymore",
+ "html_description": "Optional. True, if the gift was used to craft another gift and isn't available anymore | ",
+ "rst_description": "*Optional*. :code:`True`, if the gift was used to craft another gift and isn't available anymore\n",
+ "name": "is_burned",
+ "required": false
+ },
{
"type": "True",
"description": "True, if the gift is assigned from the TON blockchain and can't be resold or transferred in Telegram",
@@ -9193,6 +9365,42 @@
],
"category": "types"
},
+ {
+ "anchor": "chatownerleft",
+ "name": "ChatOwnerLeft",
+ "description": "Describes a service message about the chat owner leaving the chat.",
+ "html_description": "Describes a service message about the chat owner leaving the chat.
",
+ "rst_description": "Describes a service message about the chat owner leaving the chat.",
+ "annotations": [
+ {
+ "type": "User",
+ "description": "The user which will be the new owner of the chat if the previous owner does not return to the chat",
+ "html_description": "Optional. The user which will be the new owner of the chat if the previous owner does not return to the chat | ",
+ "rst_description": "*Optional*. The user which will be the new owner of the chat if the previous owner does not return to the chat\n",
+ "name": "new_owner",
+ "required": false
+ }
+ ],
+ "category": "types"
+ },
+ {
+ "anchor": "chatownerchanged",
+ "name": "ChatOwnerChanged",
+ "description": "Describes a service message about an ownership change in the chat.",
+ "html_description": "Describes a service message about an ownership change in the chat.
",
+ "rst_description": "Describes a service message about an ownership change in the chat.",
+ "annotations": [
+ {
+ "type": "User",
+ "description": "The new owner of the chat",
+ "html_description": "The new owner of the chat | ",
+ "rst_description": "The new owner of the chat\n",
+ "name": "new_owner",
+ "required": true
+ }
+ ],
+ "category": "types"
+ },
{
"anchor": "userchatboosts",
"name": "UserChatBoosts",
@@ -13040,6 +13248,40 @@
],
"category": "methods"
},
+ {
+ "anchor": "getuserprofileaudios",
+ "name": "getUserProfileAudios",
+ "description": "Use this method to get a list of profile audios for a user. Returns a UserProfileAudios object.",
+ "html_description": "Use this method to get a list of profile audios for a user. Returns a UserProfileAudios object.
",
+ "rst_description": "Use this method to get a list of profile audios for a user. Returns a :class:`aiogram.types.user_profile_audios.UserProfileAudios` object.",
+ "annotations": [
+ {
+ "type": "Integer",
+ "required": true,
+ "description": "Unique identifier of the target user",
+ "html_description": "Unique identifier of the target user | ",
+ "rst_description": "Unique identifier of the target user\n",
+ "name": "user_id"
+ },
+ {
+ "type": "Integer",
+ "required": false,
+ "description": "Sequential number of the first audio to be returned. By default, all audios are returned.",
+ "html_description": "Sequential number of the first audio to be returned. By default, all audios are returned. | ",
+ "rst_description": "Sequential number of the first audio to be returned. By default, all audios are returned.\n",
+ "name": "offset"
+ },
+ {
+ "type": "Integer",
+ "required": false,
+ "description": "Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100.",
+ "html_description": "Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100. | ",
+ "rst_description": "Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100.\n",
+ "name": "limit"
+ }
+ ],
+ "category": "methods"
+ },
{
"anchor": "setuseremojistatus",
"name": "setUserEmojiStatus",
@@ -14116,9 +14358,9 @@
{
"anchor": "createforumtopic",
"name": "createForumTopic",
- "description": "Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns information about the created topic as a ForumTopic object.",
- "html_description": "Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns information about the created topic as a ForumTopic object.
",
- "rst_description": "Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator rights. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.",
+ "description": "Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator right. Returns information about the created topic as a ForumTopic object.",
+ "html_description": "Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator right. Returns information about the created topic as a ForumTopic object.
",
+ "rst_description": "Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator right. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.",
"annotations": [
{
"type": "Integer or String",
@@ -14729,6 +14971,33 @@
],
"category": "methods"
},
+ {
+ "anchor": "setmyprofilephoto",
+ "name": "setMyProfilePhoto",
+ "description": "Changes the profile photo of the bot. Returns True on success.",
+ "html_description": "Changes the profile photo of the bot. Returns True on success.
",
+ "rst_description": "Changes the profile photo of the bot. Returns :code:`True` on success.",
+ "annotations": [
+ {
+ "type": "InputProfilePhoto",
+ "required": true,
+ "description": "The new profile photo to set",
+ "html_description": "The new profile photo to set | ",
+ "rst_description": "The new profile photo to set\n",
+ "name": "photo"
+ }
+ ],
+ "category": "methods"
+ },
+ {
+ "anchor": "removemyprofilephoto",
+ "name": "removeMyProfilePhoto",
+ "description": "Removes the profile photo of the bot. Requires no parameters. Returns True on success.",
+ "html_description": "Removes the profile photo of the bot. Requires no parameters. Returns True on success.
",
+ "rst_description": "Removes the profile photo of the bot. Requires no parameters. Returns :code:`True` on success.",
+ "annotations": [],
+ "category": "methods"
+ },
{
"anchor": "setchatmenubutton",
"name": "setChatMenuButton",
@@ -22596,9 +22865,9 @@
{
"anchor": "gamehighscore",
"name": "GameHighScore",
- "description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\nIf you've got any questions, please check out our Bot FAQ",
- "html_description": "This object represents one row of the high scores table for a game.
And that's about all we've got for now.
\nIf you've got any questions, please check out our Bot FAQ »
",
- "rst_description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\n\nIf you've got any questions, please check out our `https://core.telegram.org/bots/faq `_ **Bot FAQ »**",
+ "description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\nIf you've got any questions, please check out our Bot FAQ\n-",
+ "html_description": "This object represents one row of the high scores table for a game.
And that's about all we've got for now.
\nIf you've got any questions, please check out our Bot FAQ »
\n-
",
+ "rst_description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\n\nIf you've got any questions, please check out our `https://core.telegram.org/bots/faq `_ **Bot FAQ »**\n\n-",
"annotations": [
{
"type": "Integer",
diff --git a/.butcher/types/ChatFullInfo/entity.json b/.butcher/types/ChatFullInfo/entity.json
index 937ed037..3627d65d 100644
--- a/.butcher/types/ChatFullInfo/entity.json
+++ b/.butcher/types/ChatFullInfo/entity.json
@@ -395,6 +395,14 @@
"name": "rating",
"required": false
},
+ {
+ "type": "Audio",
+ "description": "For private chats, the first audio added to the profile of the user",
+ "html_description": "Optional. For private chats, the first audio added to the profile of the user | ",
+ "rst_description": "*Optional*. For private chats, the first audio added to the profile of the user\n",
+ "name": "first_profile_audio",
+ "required": false
+ },
{
"type": "UniqueGiftColors",
"description": "The color scheme based on a unique gift that must be used for the chat's name, message replies and link previews",
diff --git a/.butcher/types/ChatOwnerChanged/entity.json b/.butcher/types/ChatOwnerChanged/entity.json
new file mode 100644
index 00000000..083be3fa
--- /dev/null
+++ b/.butcher/types/ChatOwnerChanged/entity.json
@@ -0,0 +1,25 @@
+{
+ "meta": {},
+ "group": {
+ "title": "Available types",
+ "anchor": "available-types"
+ },
+ "object": {
+ "anchor": "chatownerchanged",
+ "name": "ChatOwnerChanged",
+ "description": "Describes a service message about an ownership change in the chat.",
+ "html_description": "Describes a service message about an ownership change in the chat.
",
+ "rst_description": "Describes a service message about an ownership change in the chat.",
+ "annotations": [
+ {
+ "type": "User",
+ "description": "The new owner of the chat",
+ "html_description": "The new owner of the chat | ",
+ "rst_description": "The new owner of the chat\n",
+ "name": "new_owner",
+ "required": true
+ }
+ ],
+ "category": "types"
+ }
+}
diff --git a/.butcher/types/ChatOwnerLeft/entity.json b/.butcher/types/ChatOwnerLeft/entity.json
new file mode 100644
index 00000000..bbeac660
--- /dev/null
+++ b/.butcher/types/ChatOwnerLeft/entity.json
@@ -0,0 +1,25 @@
+{
+ "meta": {},
+ "group": {
+ "title": "Available types",
+ "anchor": "available-types"
+ },
+ "object": {
+ "anchor": "chatownerleft",
+ "name": "ChatOwnerLeft",
+ "description": "Describes a service message about the chat owner leaving the chat.",
+ "html_description": "Describes a service message about the chat owner leaving the chat.
",
+ "rst_description": "Describes a service message about the chat owner leaving the chat.",
+ "annotations": [
+ {
+ "type": "User",
+ "description": "The user which will be the new owner of the chat if the previous owner does not return to the chat",
+ "html_description": "Optional. The user which will be the new owner of the chat if the previous owner does not return to the chat | ",
+ "rst_description": "*Optional*. The user which will be the new owner of the chat if the previous owner does not return to the chat\n",
+ "name": "new_owner",
+ "required": false
+ }
+ ],
+ "category": "types"
+ }
+}
diff --git a/.butcher/types/GameHighScore/entity.json b/.butcher/types/GameHighScore/entity.json
index ce3f52d2..21a8a5e7 100644
--- a/.butcher/types/GameHighScore/entity.json
+++ b/.butcher/types/GameHighScore/entity.json
@@ -7,9 +7,9 @@
"object": {
"anchor": "gamehighscore",
"name": "GameHighScore",
- "description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\nIf you've got any questions, please check out our Bot FAQ",
- "html_description": "This object represents one row of the high scores table for a game.
And that's about all we've got for now.
\nIf you've got any questions, please check out our Bot FAQ »
",
- "rst_description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\n\nIf you've got any questions, please check out our `https://core.telegram.org/bots/faq `_ **Bot FAQ »**",
+ "description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\nIf you've got any questions, please check out our Bot FAQ\n-",
+ "html_description": "This object represents one row of the high scores table for a game.
And that's about all we've got for now.
\nIf you've got any questions, please check out our Bot FAQ »
\n-
",
+ "rst_description": "This object represents one row of the high scores table for a game.\nAnd that's about all we've got for now.\n\nIf you've got any questions, please check out our `https://core.telegram.org/bots/faq `_ **Bot FAQ »**\n\n-",
"annotations": [
{
"type": "Integer",
diff --git a/.butcher/types/InlineKeyboardButton/entity.json b/.butcher/types/InlineKeyboardButton/entity.json
index 6279c8e6..15e52808 100644
--- a/.butcher/types/InlineKeyboardButton/entity.json
+++ b/.butcher/types/InlineKeyboardButton/entity.json
@@ -7,9 +7,9 @@
"object": {
"anchor": "inlinekeyboardbutton",
"name": "InlineKeyboardButton",
- "description": "This object represents one button of an inline keyboard. Exactly one of the optional fields must be used to specify type of the button.",
- "html_description": "This object represents one button of an inline keyboard. Exactly one of the optional fields must be used to specify type of the button.
",
- "rst_description": "This object represents one button of an inline keyboard. Exactly one of the optional fields must be used to specify type of the button.",
+ "description": "This object represents one button of an inline keyboard. Exactly one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button.",
+ "html_description": "This object represents one button of an inline keyboard. Exactly one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button.
",
+ "rst_description": "This object represents one button of an inline keyboard. Exactly one of the fields other than *text*, *icon_custom_emoji_id*, and *style* must be used to specify the type of the button.",
"annotations": [
{
"type": "String",
@@ -19,6 +19,22 @@
"name": "text",
"required": true
},
+ {
+ "type": "String",
+ "description": "Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.",
+ "html_description": "Optional. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription. | ",
+ "rst_description": "*Optional*. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on `Fragment `_ or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.\n",
+ "name": "icon_custom_emoji_id",
+ "required": false
+ },
+ {
+ "type": "String",
+ "description": "Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.",
+ "html_description": "Optional. Style of the button. Must be one of “danger” (red), “success” (green) or “primary” (blue). If omitted, then an app-specific style is used. | ",
+ "rst_description": "*Optional*. Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.\n",
+ "name": "style",
+ "required": false
+ },
{
"type": "String",
"description": "HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id= can be used to mention a user by their identifier without using a username, if this is allowed by their privacy settings.",
diff --git a/.butcher/types/KeyboardButton/entity.json b/.butcher/types/KeyboardButton/entity.json
index d0eff8d0..16771a3b 100644
--- a/.butcher/types/KeyboardButton/entity.json
+++ b/.butcher/types/KeyboardButton/entity.json
@@ -7,18 +7,34 @@
"object": {
"anchor": "keyboardbutton",
"name": "KeyboardButton",
- "description": "This object represents one button of the reply keyboard. At most one of the optional fields must be used to specify type of the button. For simple text buttons, String can be used instead of this object to specify the button text.\nNote: request_users and request_chat options will only work in Telegram versions released after 3 February, 2023. Older clients will display unsupported message.",
- "html_description": "This object represents one button of the reply keyboard. At most one of the optional fields must be used to specify type of the button. For simple text buttons, String can be used instead of this object to specify the button text.
Note: request_users and request_chat options will only work in Telegram versions released after 3 February, 2023. Older clients will display unsupported message.
",
- "rst_description": "This object represents one button of the reply keyboard. At most one of the optional fields must be used to specify type of the button. For simple text buttons, *String* can be used instead of this object to specify the button text.\n**Note:** *request_users* and *request_chat* options will only work in Telegram versions released after 3 February, 2023. Older clients will display *unsupported message*.",
+ "description": "This object represents one button of the reply keyboard. At most one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button. For simple text buttons, String can be used instead of this object to specify the button text.",
+ "html_description": "This object represents one button of the reply keyboard. At most one of the fields other than text, icon_custom_emoji_id, and style must be used to specify the type of the button. For simple text buttons, String can be used instead of this object to specify the button text.
",
+ "rst_description": "This object represents one button of the reply keyboard. At most one of the fields other than *text*, *icon_custom_emoji_id*, and *style* must be used to specify the type of the button. For simple text buttons, *String* can be used instead of this object to specify the button text.",
"annotations": [
{
"type": "String",
- "description": "Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed",
- "html_description": "Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed | ",
- "rst_description": "Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed\n",
+ "description": "Text of the button. If none of the fields other than text, icon_custom_emoji_id, and style are used, it will be sent as a message when the button is pressed",
+ "html_description": "Text of the button. If none of the fields other than text, icon_custom_emoji_id, and style are used, it will be sent as a message when the button is pressed | ",
+ "rst_description": "Text of the button. If none of the fields other than *text*, *icon_custom_emoji_id*, and *style* are used, it will be sent as a message when the button is pressed\n",
"name": "text",
"required": true
},
+ {
+ "type": "String",
+ "description": "Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.",
+ "html_description": "Optional. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription. | ",
+ "rst_description": "*Optional*. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on `Fragment `_ or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.\n",
+ "name": "icon_custom_emoji_id",
+ "required": false
+ },
+ {
+ "type": "String",
+ "description": "Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.",
+ "html_description": "Optional. Style of the button. Must be one of “danger” (red), “success” (green) or “primary” (blue). If omitted, then an app-specific style is used. | ",
+ "rst_description": "*Optional*. Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used.\n",
+ "name": "style",
+ "required": false
+ },
{
"type": "KeyboardButtonRequestUsers",
"description": "If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a 'users_shared' service message. Available in private chats only.",
diff --git a/.butcher/types/Message/entity.json b/.butcher/types/Message/entity.json
index d4dd8bc0..594442fc 100644
--- a/.butcher/types/Message/entity.json
+++ b/.butcher/types/Message/entity.json
@@ -443,6 +443,22 @@
"name": "left_chat_member",
"required": false
},
+ {
+ "type": "ChatOwnerLeft",
+ "description": "Service message: chat owner has left",
+ "html_description": "Optional. Service message: chat owner has left | ",
+ "rst_description": "*Optional*. Service message: chat owner has left\n",
+ "name": "chat_owner_left",
+ "required": false
+ },
+ {
+ "type": "ChatOwnerChanged",
+ "description": "Service message: chat owner has changed",
+ "html_description": "Optional. Service message: chat owner has changed | ",
+ "rst_description": "*Optional*. Service message: chat owner has changed\n",
+ "name": "chat_owner_changed",
+ "required": false
+ },
{
"type": "String",
"description": "A chat title was changed to this value",
diff --git a/.butcher/types/UniqueGift/entity.json b/.butcher/types/UniqueGift/entity.json
index 9d5f4a50..4b4427ae 100644
--- a/.butcher/types/UniqueGift/entity.json
+++ b/.butcher/types/UniqueGift/entity.json
@@ -75,6 +75,14 @@
"name": "is_premium",
"required": false
},
+ {
+ "type": "True",
+ "description": "True, if the gift was used to craft another gift and isn't available anymore",
+ "html_description": "Optional. True, if the gift was used to craft another gift and isn't available anymore | ",
+ "rst_description": "*Optional*. :code:`True`, if the gift was used to craft another gift and isn't available anymore\n",
+ "name": "is_burned",
+ "required": false
+ },
{
"type": "True",
"description": "True, if the gift is assigned from the TON blockchain and can't be resold or transferred in Telegram",
diff --git a/.butcher/types/UniqueGiftModel/entity.json b/.butcher/types/UniqueGiftModel/entity.json
index 4ff33770..6573026a 100644
--- a/.butcher/types/UniqueGiftModel/entity.json
+++ b/.butcher/types/UniqueGiftModel/entity.json
@@ -29,11 +29,19 @@
},
{
"type": "Integer",
- "description": "The number of unique gifts that receive this model for every 1000 gifts upgraded",
- "html_description": "The number of unique gifts that receive this model for every 1000 gifts upgraded | ",
- "rst_description": "The number of unique gifts that receive this model for every 1000 gifts upgraded\n",
+ "description": "The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts.",
+ "html_description": "The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts. | ",
+ "rst_description": "The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts.\n",
"name": "rarity_per_mille",
"required": true
+ },
+ {
+ "type": "String",
+ "description": "Rarity of the model if it is a crafted model. Currently, can be 'uncommon', 'rare', 'epic', or 'legendary'.",
+ "html_description": "Optional. Rarity of the model if it is a crafted model. Currently, can be “uncommon”, “rare”, “epic”, or “legendary”. | ",
+ "rst_description": "*Optional*. Rarity of the model if it is a crafted model. Currently, can be 'uncommon', 'rare', 'epic', or 'legendary'.\n",
+ "name": "rarity",
+ "required": false
}
],
"category": "types"
diff --git a/.butcher/types/User/aliases.yml b/.butcher/types/User/aliases.yml
index 85d2188d..51e3e370 100644
--- a/.butcher/types/User/aliases.yml
+++ b/.butcher/types/User/aliases.yml
@@ -2,3 +2,7 @@ get_profile_photos:
method: getUserProfilePhotos
fill:
user_id: self.id
+get_profile_audios:
+ method: getUserProfileAudios
+ fill:
+ user_id: self.id
diff --git a/.butcher/types/User/entity.json b/.butcher/types/User/entity.json
index b444dbb3..cead909a 100644
--- a/.butcher/types/User/entity.json
+++ b/.butcher/types/User/entity.json
@@ -122,6 +122,14 @@
"rst_description": "*Optional*. :code:`True`, if the bot has forum topic mode enabled in private chats. Returned only in :class:`aiogram.methods.get_me.GetMe`.\n",
"name": "has_topics_enabled",
"required": false
+ },
+ {
+ "type": "Boolean",
+ "description": "True, if the bot allows users to create and delete topics in private chats. Returned only in getMe.",
+ "html_description": "Optional. True, if the bot allows users to create and delete topics in private chats. Returned only in getMe. | ",
+ "rst_description": "*Optional*. :code:`True`, if the bot allows users to create and delete topics in private chats. Returned only in :class:`aiogram.methods.get_me.GetMe`.\n",
+ "name": "allows_users_to_create_topics",
+ "required": false
}
],
"category": "types"
diff --git a/.butcher/types/UserProfileAudios/entity.json b/.butcher/types/UserProfileAudios/entity.json
new file mode 100644
index 00000000..77e373ce
--- /dev/null
+++ b/.butcher/types/UserProfileAudios/entity.json
@@ -0,0 +1,33 @@
+{
+ "meta": {},
+ "group": {
+ "title": "Available types",
+ "anchor": "available-types"
+ },
+ "object": {
+ "anchor": "userprofileaudios",
+ "name": "UserProfileAudios",
+ "description": "This object represents the audios displayed on a user's profile.",
+ "html_description": "This object represents the audios displayed on a user's profile.
",
+ "rst_description": "This object represents the audios displayed on a user's profile.",
+ "annotations": [
+ {
+ "type": "Integer",
+ "description": "Total number of profile audios for the target user",
+ "html_description": "Total number of profile audios for the target user | ",
+ "rst_description": "Total number of profile audios for the target user\n",
+ "name": "total_count",
+ "required": true
+ },
+ {
+ "type": "Array of Audio",
+ "description": "Requested profile audios",
+ "html_description": "Requested profile audios | ",
+ "rst_description": "Requested profile audios\n",
+ "name": "audios",
+ "required": true
+ }
+ ],
+ "category": "types"
+ }
+}
diff --git a/.butcher/types/Video/entity.json b/.butcher/types/Video/entity.json
index d62323ee..41ec3bb6 100644
--- a/.butcher/types/Video/entity.json
+++ b/.butcher/types/Video/entity.json
@@ -75,6 +75,14 @@
"name": "start_timestamp",
"required": false
},
+ {
+ "type": "Array of VideoQuality",
+ "description": "List of available qualities of the video",
+ "html_description": "Optional. List of available qualities of the video | ",
+ "rst_description": "*Optional*. List of available qualities of the video\n",
+ "name": "qualities",
+ "required": false
+ },
{
"type": "String",
"description": "Original filename as defined by the sender",
diff --git a/.butcher/types/VideoQuality/entity.json b/.butcher/types/VideoQuality/entity.json
new file mode 100644
index 00000000..0ba0b78a
--- /dev/null
+++ b/.butcher/types/VideoQuality/entity.json
@@ -0,0 +1,65 @@
+{
+ "meta": {},
+ "group": {
+ "title": "Available types",
+ "anchor": "available-types"
+ },
+ "object": {
+ "anchor": "videoquality",
+ "name": "VideoQuality",
+ "description": "This object represents a video file of a specific quality.",
+ "html_description": "This object represents a video file of a specific quality.
",
+ "rst_description": "This object represents a video file of a specific quality.",
+ "annotations": [
+ {
+ "type": "String",
+ "description": "Identifier for this file, which can be used to download or reuse the file",
+ "html_description": "Identifier for this file, which can be used to download or reuse the file | ",
+ "rst_description": "Identifier for this file, which can be used to download or reuse the file\n",
+ "name": "file_id",
+ "required": true
+ },
+ {
+ "type": "String",
+ "description": "Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.",
+ "html_description": "Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file. | ",
+ "rst_description": "Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.\n",
+ "name": "file_unique_id",
+ "required": true
+ },
+ {
+ "type": "Integer",
+ "description": "Video width",
+ "html_description": "Video width | ",
+ "rst_description": "Video width\n",
+ "name": "width",
+ "required": true
+ },
+ {
+ "type": "Integer",
+ "description": "Video height",
+ "html_description": "Video height | ",
+ "rst_description": "Video height\n",
+ "name": "height",
+ "required": true
+ },
+ {
+ "type": "String",
+ "description": "Codec that was used to encode the video, for example, 'h264', 'h265', or 'av01'",
+ "html_description": "Codec that was used to encode the video, for example, “h264”, “h265”, or “av01” | ",
+ "rst_description": "Codec that was used to encode the video, for example, 'h264', 'h265', or 'av01'\n",
+ "name": "codec",
+ "required": true
+ },
+ {
+ "type": "Integer",
+ "description": "File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.",
+ "html_description": "Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value. | ",
+ "rst_description": "*Optional*. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.\n",
+ "name": "file_size",
+ "required": false
+ }
+ ],
+ "category": "types"
+ }
+}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 122d6b1d..2937cb60 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -72,7 +72,7 @@ jobs:
run: |
uv run ruff check --output-format=github aiogram examples
uv run mypy aiogram
- uv run black --check --diff aiogram tests
+ uv run ruff format --check --diff aiogram tests scripts examples
- name: Setup redis
if: ${{ env.IS_WINDOWS == 'false' }}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 97742762..891f4221 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -13,13 +13,10 @@ repos:
- id: "check-toml"
- id: "check-json"
- - repo: https://github.com/psf/black
- rev: 25.9.0
- hooks:
- - id: black
- files: &files '^(aiogram|tests|examples)'
-
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 'v0.14.0'
hooks:
- id: ruff
+ files: &files '^(aiogram|tests|examples)'
+ - id: ruff-format
+ files: *files
diff --git a/CHANGES.rst b/CHANGES.rst
index 4369caa7..be4cb56e 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -16,6 +16,88 @@ Changelog
.. towncrier release notes start
+3.25.0 (2026-02-10)
+====================
+
+Features
+--------
+
+- Add full_name property to Contact and corresponding tests
+ `#1758 `_
+- Updated to `Bot API 9.4 (February 9, 2026) `_
+
+ **New Features:**
+
+ - Bots with Premium subscriptions can now use custom emoji directly in messages to private, group, and supergroup chats
+ - Bots can create topics in private chats via the :class:`aiogram.methods.create_forum_topic.CreateForumTopic` method
+ - Bots can prevent users from creating/deleting topics in private chats through BotFather settings
+
+ **New Fields:**
+
+ - Added :code:`allows_users_to_create_topics` field to :class:`aiogram.types.user.User` class - indicates whether the user allows others to create topics in chats with them
+ - Added :code:`icon_custom_emoji_id` field to :class:`aiogram.types.keyboard_button.KeyboardButton` and :class:`aiogram.types.inline_keyboard_button.InlineKeyboardButton` classes - allows displaying custom emoji icons on buttons
+ - Added :code:`style` field to :class:`aiogram.types.keyboard_button.KeyboardButton` and :class:`aiogram.types.inline_keyboard_button.InlineKeyboardButton` classes - changes button color/style
+ - Added :code:`chat_owner_left` field to :class:`aiogram.types.message.Message` class - service message indicating chat owner has left (type: :class:`aiogram.types.chat_owner_left.ChatOwnerLeft`)
+ - Added :code:`chat_owner_changed` field to :class:`aiogram.types.message.Message` class - service message indicating chat ownership has transferred (type: :class:`aiogram.types.chat_owner_changed.ChatOwnerChanged`)
+ - Added :code:`qualities` field to :class:`aiogram.types.video.Video` class - list of available video quality options (type: :code:`list[`:class:`aiogram.types.video_quality.VideoQuality`:code:`]`)
+ - Added :code:`first_profile_audio` field to :class:`aiogram.types.chat_full_info.ChatFullInfo` class - user's first profile audio
+ - Added :code:`rarity` field to :class:`aiogram.types.unique_gift_model.UniqueGiftModel` class
+ - Added :code:`is_burned` field to :class:`aiogram.types.unique_gift.UniqueGift` class
+
+ **New Methods:**
+
+ - Added :class:`aiogram.methods.set_my_profile_photo.SetMyProfilePhoto` method - allows bots to set their profile photo
+ - Added :class:`aiogram.methods.remove_my_profile_photo.RemoveMyProfilePhoto` method - allows bots to remove their profile photo
+ - Added :class:`aiogram.methods.get_user_profile_audios.GetUserProfileAudios` method - retrieves a user's profile audio list
+ - Added :meth:`aiogram.types.user.User.get_profile_audios` shortcut - creates a prefilled :class:`aiogram.methods.get_user_profile_audios.GetUserProfileAudios` request with :code:`user_id`
+
+ **New Types:**
+
+ - Added :class:`aiogram.types.chat_owner_left.ChatOwnerLeft` type - describes a service message about the chat owner leaving the chat
+ - Added :class:`aiogram.types.chat_owner_changed.ChatOwnerChanged` type - describes a service message about an ownership change in the chat
+ - Added :class:`aiogram.types.video_quality.VideoQuality` type - describes available video quality options
+ - Added :class:`aiogram.types.user_profile_audios.UserProfileAudios` type - represents the collection of audios displayed on a user's profile
+
+ `#1761 `_
+
+
+Bugfixes
+--------
+
+- Fixed scene handling for ``channel_post`` and ``edited_channel_post`` when Scenes are registered but FSM state is unavailable, and added channel-scoped FSM context support for ``CHAT``/``CHAT_TOPIC`` strategies.
+ `#1743 `_
+
+
+Misc
+----
+
+- Migrated from Black and isort to Ruff for code formatting and linting, a modern, blazingly fast formatter and linter written in Rust.
+
+ Enabled additional ruff rule sets.
+
+ **For end users:**
+
+ No changes required. This is purely a development tooling change that doesn't affect the library API or behavior.
+
+ **For contributors:**
+
+ - Use ``make reformat`` or ``uv run ruff format`` to format code (replaces ``black`` and ``isort``)
+ - Use ``make lint`` to check code quality (now includes formatting, linting, and type checking)
+ - Pre-commit hooks automatically updated to use ``ruff`` and ``ruff-format``
+ - CI/CD pipelines updated to use ruff in GitHub Actions workflows
+
+ **Benefits:**
+
+ - 10-100x faster formatting and linting compared to Black + isort + flake8
+ - Single tool for formatting, import sorting, and linting
+ - More comprehensive code quality checks out of the box
+ - Auto-fixes for many common issues (33 issues auto-fixed during migration)
+ - Better integration with modern Python development workflows
+
+ This change improves the developer experience and code quality while maintaining the same code style standards.
+ `#1750 `_
+
+
3.24.0 (2026-01-02)
====================
diff --git a/Makefile b/Makefile
index 0c6ad5a3..a92db1f8 100644
--- a/Makefile
+++ b/Makefile
@@ -37,15 +37,14 @@ install: clean
.PHONY: lint
lint:
- uv run isort --check-only $(code_dir)
- uv run black --check --diff $(code_dir)
+ uv run ruff format --check --diff $(package_dir)
uv run ruff check --show-fixes --preview $(package_dir) $(examples_dir)
uv run mypy $(package_dir)
.PHONY: reformat
reformat:
- uv run black $(code_dir)
- uv run isort $(code_dir)
+ uv run ruff format $(code_dir)
+ uv run ruff check --fix $(code_dir)
# =================================================================================================
# Tests
diff --git a/README.rst b/README.rst
index 52322104..c3b660d3 100644
--- a/README.rst
+++ b/README.rst
@@ -52,7 +52,7 @@ Features
- Asynchronous (`asyncio docs `_, :pep:`492`)
- Has type hints (:pep:`484`) and can be used with `mypy `_
- Supports `PyPy `_
-- Supports `Telegram Bot API 9.3 `_ and gets fast updates to the latest versions of the Bot API
+- Supports `Telegram Bot API 9.4 `_ and gets fast updates to the latest versions of the Bot API
- Telegram Bot API integration code was `autogenerated `_ and can be easily re-generated when API gets updated
- Updates router (Blueprints)
- Has Finite State Machine
diff --git a/aiogram/__meta__.py b/aiogram/__meta__.py
index 6f449426..0c33f1a9 100644
--- a/aiogram/__meta__.py
+++ b/aiogram/__meta__.py
@@ -1,2 +1,2 @@
-__version__ = "3.24.0"
-__api_version__ = "9.3"
+__version__ = "3.25.0"
+__api_version__ = "9.4"
diff --git a/aiogram/client/bot.py b/aiogram/client/bot.py
index fd5ba841..d10429f6 100644
--- a/aiogram/client/bot.py
+++ b/aiogram/client/bot.py
@@ -2,12 +2,11 @@ from __future__ import annotations
import io
import pathlib
+from collections.abc import AsyncGenerator, AsyncIterator
from contextlib import asynccontextmanager
from types import TracebackType
from typing import (
Any,
- AsyncGenerator,
- AsyncIterator,
BinaryIO,
TypeVar,
cast,
@@ -93,6 +92,7 @@ from ..methods import (
GetUpdates,
GetUserChatBoosts,
GetUserGifts,
+ GetUserProfileAudios,
GetUserProfilePhotos,
GetWebhookInfo,
GiftPremiumSubscription,
@@ -106,6 +106,7 @@ from ..methods import (
RefundStarPayment,
RemoveBusinessAccountProfilePhoto,
RemoveChatVerification,
+ RemoveMyProfilePhoto,
RemoveUserVerification,
ReopenForumTopic,
ReopenGeneralForumTopic,
@@ -155,6 +156,7 @@ from ..methods import (
SetMyDefaultAdministratorRights,
SetMyDescription,
SetMyName,
+ SetMyProfilePhoto,
SetMyShortDescription,
SetPassportDataErrors,
SetStickerEmojiList,
@@ -242,6 +244,7 @@ from ..types import (
Update,
User,
UserChatBoosts,
+ UserProfileAudios,
UserProfilePhotos,
WebhookInfo,
)
@@ -283,9 +286,9 @@ class Bot:
# Few arguments are completely removed in 3.7.0 version
# Temporary solution to raise an error if user passed these arguments
# with explanation how to fix it
- parse_mode = kwargs.get("parse_mode", None)
- link_preview_is_disabled = kwargs.get("disable_web_page_preview", None)
- protect_content = kwargs.get("protect_content", None)
+ parse_mode = kwargs.get("parse_mode")
+ link_preview_is_disabled = kwargs.get("disable_web_page_preview")
+ protect_content = kwargs.get("protect_content")
if (
parse_mode is not None
or link_preview_is_disabled is not None
@@ -310,7 +313,7 @@ class Bot:
self.__token = token
self._me: User | None = None
- async def __aenter__(self) -> "Bot":
+ async def __aenter__(self) -> Bot:
return self
async def __aexit__(
@@ -912,7 +915,7 @@ class Bot:
request_timeout: int | None = None,
) -> ForumTopic:
"""
- Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator rights. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.
+ Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator right. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.
Source: https://core.telegram.org/bots/api#createforumtopic
@@ -5843,3 +5846,65 @@ class Bot:
entities=entities,
)
return await self(call, request_timeout=request_timeout)
+
+ async def get_user_profile_audios(
+ self,
+ user_id: int,
+ offset: int | None = None,
+ limit: int | None = None,
+ request_timeout: int | None = None,
+ ) -> UserProfileAudios:
+ """
+ Use this method to get a list of profile audios for a user. Returns a :class:`aiogram.types.user_profile_audios.UserProfileAudios` object.
+
+ Source: https://core.telegram.org/bots/api#getuserprofileaudios
+
+ :param user_id: Unique identifier of the target user
+ :param offset: Sequential number of the first audio to be returned. By default, all audios are returned.
+ :param limit: Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100.
+ :param request_timeout: Request timeout
+ :return: Returns a :class:`aiogram.types.user_profile_audios.UserProfileAudios` object.
+ """
+
+ call = GetUserProfileAudios(
+ user_id=user_id,
+ offset=offset,
+ limit=limit,
+ )
+ return await self(call, request_timeout=request_timeout)
+
+ async def remove_my_profile_photo(
+ self,
+ request_timeout: int | None = None,
+ ) -> bool:
+ """
+ Removes the profile photo of the bot. Requires no parameters. Returns :code:`True` on success.
+
+ Source: https://core.telegram.org/bots/api#removemyprofilephoto
+
+ :param request_timeout: Request timeout
+ :return: Returns :code:`True` on success.
+ """
+
+ call = RemoveMyProfilePhoto()
+ return await self(call, request_timeout=request_timeout)
+
+ async def set_my_profile_photo(
+ self,
+ photo: InputProfilePhotoUnion,
+ request_timeout: int | None = None,
+ ) -> bool:
+ """
+ Changes the profile photo of the bot. Returns :code:`True` on success.
+
+ Source: https://core.telegram.org/bots/api#setmyprofilephoto
+
+ :param photo: The new profile photo to set
+ :param request_timeout: Request timeout
+ :return: Returns :code:`True` on success.
+ """
+
+ call = SetMyProfilePhoto(
+ photo=photo,
+ )
+ return await self(call, request_timeout=request_timeout)
diff --git a/aiogram/client/session/aiohttp.py b/aiogram/client/session/aiohttp.py
index 7b032c0e..c1d0b291 100644
--- a/aiogram/client/session/aiohttp.py
+++ b/aiogram/client/session/aiohttp.py
@@ -58,7 +58,7 @@ def _prepare_connector(chain_or_plain: _ProxyType) -> tuple[type[TCPConnector],
# since tuple is Iterable(compatible with _ProxyChain) object, we assume that
# user wants chained proxies if tuple is a pair of string(url) and BasicAuth
if isinstance(chain_or_plain, str) or (
- isinstance(chain_or_plain, tuple) and len(chain_or_plain) == 2
+ isinstance(chain_or_plain, tuple) and len(chain_or_plain) == 2 # noqa: PLR2004
):
chain_or_plain = cast(_ProxyBasic, chain_or_plain)
return ProxyConnector, _retrieve_basic(chain_or_plain)
@@ -170,10 +170,10 @@ class AiohttpSession(BaseSession):
timeout=self.timeout if timeout is None else timeout,
) as resp:
raw_result = await resp.text()
- except asyncio.TimeoutError:
- raise TelegramNetworkError(method=method, message="Request timeout error")
+ except asyncio.TimeoutError as e:
+ raise TelegramNetworkError(method=method, message="Request timeout error") from e
except ClientError as e:
- raise TelegramNetworkError(method=method, message=f"{type(e).__name__}: {e}")
+ raise TelegramNetworkError(method=method, message=f"{type(e).__name__}: {e}") from e
response = self.check_response(
bot=bot,
method=method,
diff --git a/aiogram/client/session/base.py b/aiogram/client/session/base.py
index a5eea81e..08c428bd 100644
--- a/aiogram/client/session/base.py
+++ b/aiogram/client/session/base.py
@@ -90,14 +90,14 @@ class BaseSession(abc.ABC):
# in due to decoder can be customized and raise any exception
msg = "Failed to decode object"
- raise ClientDecodeError(msg, e, content)
+ raise ClientDecodeError(msg, e, content) from e
try:
response_type = Response[method.__returning__] # type: ignore
response = response_type.model_validate(json_data, context={"bot": bot})
except ValidationError as e:
msg = "Failed to deserialize object"
- raise ClientDecodeError(msg, e, json_data)
+ raise ClientDecodeError(msg, e, json_data) from e
if HTTPStatus.OK <= status_code <= HTTPStatus.IM_USED and response.ok:
return response
diff --git a/aiogram/dispatcher/flags.py b/aiogram/dispatcher/flags.py
index f7b6a76d..f96d5213 100644
--- a/aiogram/dispatcher/flags.py
+++ b/aiogram/dispatcher/flags.py
@@ -21,10 +21,10 @@ class FlagDecorator:
flag: Flag
@classmethod
- def _with_flag(cls, flag: Flag) -> "FlagDecorator":
+ def _with_flag(cls, flag: Flag) -> FlagDecorator:
return cls(flag)
- def _with_value(self, value: Any) -> "FlagDecorator":
+ def _with_value(self, value: Any) -> FlagDecorator:
new_flag = Flag(self.flag.name, value)
return self._with_flag(new_flag)
@@ -33,11 +33,11 @@ class FlagDecorator:
pass
@overload
- def __call__(self, value: Any, /) -> "FlagDecorator":
+ def __call__(self, value: Any, /) -> FlagDecorator:
pass
@overload
- def __call__(self, **kwargs: Any) -> "FlagDecorator":
+ def __call__(self, **kwargs: Any) -> FlagDecorator:
pass
def __call__(
diff --git a/aiogram/enums/__init__.py b/aiogram/enums/__init__.py
index aa9d6b4a..1a6e5326 100644
--- a/aiogram/enums/__init__.py
+++ b/aiogram/enums/__init__.py
@@ -1,4 +1,5 @@
from .bot_command_scope_type import BotCommandScopeType
+from .button_style import ButtonStyle
from .chat_action import ChatAction
from .chat_boost_source_type import ChatBoostSourceType
from .chat_member_status import ChatMemberStatus
@@ -36,6 +37,7 @@ from .update_type import UpdateType
__all__ = (
"BotCommandScopeType",
+ "ButtonStyle",
"ChatAction",
"ChatBoostSourceType",
"ChatMemberStatus",
diff --git a/aiogram/enums/button_style.py b/aiogram/enums/button_style.py
new file mode 100644
index 00000000..8f633e96
--- /dev/null
+++ b/aiogram/enums/button_style.py
@@ -0,0 +1,15 @@
+from enum import Enum
+
+
+class ButtonStyle(str, Enum):
+ """
+ This object represents a button style (inline- or reply-keyboard).
+
+ Sources:
+ * https://core.telegram.org/bots/api#inlinekeyboardbutton
+ * https://core.telegram.org/bots/api#keyboardbutton
+ """
+
+ DANGER = "danger"
+ SUCCESS = "success"
+ PRIMARY = "primary"
diff --git a/aiogram/enums/content_type.py b/aiogram/enums/content_type.py
index 0c1c4b29..b2a555d4 100644
--- a/aiogram/enums/content_type.py
+++ b/aiogram/enums/content_type.py
@@ -28,6 +28,8 @@ class ContentType(str, Enum):
LOCATION = "location"
NEW_CHAT_MEMBERS = "new_chat_members"
LEFT_CHAT_MEMBER = "left_chat_member"
+ CHAT_OWNER_LEFT = "chat_owner_left"
+ CHAT_OWNER_CHANGED = "chat_owner_changed"
NEW_CHAT_TITLE = "new_chat_title"
NEW_CHAT_PHOTO = "new_chat_photo"
DELETE_CHAT_PHOTO = "delete_chat_photo"
diff --git a/aiogram/filters/chat_member_updated.py b/aiogram/filters/chat_member_updated.py
index dc86fe21..c9f18ea3 100644
--- a/aiogram/filters/chat_member_updated.py
+++ b/aiogram/filters/chat_member_updated.py
@@ -37,7 +37,7 @@ class _MemberStatusMarker:
def __or__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
- ) -> "_MemberStatusGroupMarker":
+ ) -> _MemberStatusGroupMarker:
if isinstance(other, _MemberStatusMarker):
return _MemberStatusGroupMarker(self, other)
if isinstance(other, _MemberStatusGroupMarker):
@@ -53,7 +53,7 @@ class _MemberStatusMarker:
def __rshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
- ) -> "_MemberStatusTransition":
+ ) -> _MemberStatusTransition:
old = _MemberStatusGroupMarker(self)
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=old, new=_MemberStatusGroupMarker(other))
@@ -68,7 +68,7 @@ class _MemberStatusMarker:
def __lshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
- ) -> "_MemberStatusTransition":
+ ) -> _MemberStatusTransition:
new = _MemberStatusGroupMarker(self)
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=_MemberStatusGroupMarker(other), new=new)
@@ -118,7 +118,7 @@ class _MemberStatusGroupMarker:
def __rshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
- ) -> "_MemberStatusTransition":
+ ) -> _MemberStatusTransition:
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=self, new=_MemberStatusGroupMarker(other))
if isinstance(other, _MemberStatusGroupMarker):
@@ -132,7 +132,7 @@ class _MemberStatusGroupMarker:
def __lshift__(
self,
other: _MemberStatusMarker | _MemberStatusGroupMarker,
- ) -> "_MemberStatusTransition":
+ ) -> _MemberStatusTransition:
if isinstance(other, _MemberStatusMarker):
return _MemberStatusTransition(old=_MemberStatusGroupMarker(other), new=self)
if isinstance(other, _MemberStatusGroupMarker):
diff --git a/aiogram/filters/command.py b/aiogram/filters/command.py
index cab50969..498cd8c7 100644
--- a/aiogram/filters/command.py
+++ b/aiogram/filters/command.py
@@ -123,14 +123,15 @@ class Command(Filter):
result.update(command.magic_result)
return result
- def extract_command(self, text: str) -> CommandObject:
+ @classmethod
+ def extract_command(cls, text: str) -> CommandObject:
# First step: separate command with arguments
# "/command@mention arg1 arg2" -> "/command@mention", ["arg1 arg2"]
try:
full_command, *args = text.split(maxsplit=1)
- except ValueError:
+ except ValueError as e:
msg = "not enough values to unpack"
- raise CommandException(msg)
+ raise CommandException(msg) from e
# Separate command into valuable parts
# "/command@mention" -> "/", ("command", "@", "mention")
@@ -292,6 +293,6 @@ class CommandStart(Command):
args = decode_payload(args)
except UnicodeDecodeError as e:
msg = f"Failed to decode Base64: {e}"
- raise CommandException(msg)
+ raise CommandException(msg) from e
return replace(command, args=args)
return command
diff --git a/aiogram/fsm/middleware.py b/aiogram/fsm/middleware.py
index 41fd993f..effa3f02 100644
--- a/aiogram/fsm/middleware.py
+++ b/aiogram/fsm/middleware.py
@@ -70,6 +70,9 @@ class FSMContextMiddleware(BaseMiddleware):
) -> FSMContext | None:
if chat_id is None:
chat_id = user_id
+ elif user_id is None and self.strategy in {FSMStrategy.CHAT, FSMStrategy.CHAT_TOPIC}:
+ # CHAT/CHAT_TOPIC are chat-scoped, so missing user_id can fallback to chat_id.
+ user_id = chat_id
if chat_id is not None and user_id is not None:
chat_id, user_id, thread_id = apply_strategy(
diff --git a/aiogram/fsm/scene.py b/aiogram/fsm/scene.py
index 42b038c5..da0a52d2 100644
--- a/aiogram/fsm/scene.py
+++ b/aiogram/fsm/scene.py
@@ -120,7 +120,7 @@ class ObserverDecorator:
handlers = getattr(target, "__aiogram_handler__", None)
if not handlers:
handlers = []
- setattr(target, "__aiogram_handler__", handlers)
+ target.__aiogram_handler__ = handlers # type: ignore[union-attr]
handlers.append(
HandlerContainer(
@@ -137,7 +137,7 @@ class ObserverDecorator:
action = getattr(target, "__aiogram_action__", None)
if action is None:
action = defaultdict(dict)
- setattr(target, "__aiogram_action__", action)
+ target.__aiogram_action__ = action # type: ignore[attr-defined]
action[self.action][self.name] = CallableObject(target)
def __call__(self, target: CallbackType) -> CallbackType:
@@ -248,8 +248,16 @@ class SceneHandlerWrapper:
event: TelegramObject,
**kwargs: Any,
) -> Any:
- state: FSMContext = kwargs["state"]
- scenes: ScenesManager = kwargs["scenes"]
+ try:
+ state: FSMContext = kwargs["state"]
+ scenes: ScenesManager = kwargs["scenes"]
+ except KeyError as error:
+ missing_key = error.args[0]
+ msg = (
+ f"Scene context key {missing_key!r} is not available. "
+ "Ensure FSM is enabled and pipeline is intact."
+ )
+ raise SceneException(msg) from None
event_update: Update = kwargs["event_update"]
scene = self.scene(
wizard=SceneWizard(
@@ -768,12 +776,15 @@ class SceneRegistry:
data: dict[str, Any],
) -> Any:
assert isinstance(event, Update), "Event must be an Update instance"
+ state = data.get("state")
+ if state is None:
+ return await handler(event, data)
data["scenes"] = ScenesManager(
registry=self,
update_type=event.event_type,
event=event.event,
- state=data["state"],
+ state=state,
data=data,
)
return await handler(event, data)
@@ -784,12 +795,16 @@ class SceneRegistry:
event: TelegramObject,
data: dict[str, Any],
) -> Any:
+ state = data.get("state")
+ if state is None:
+ return await handler(event, data)
+
update: Update = data["event_update"]
data["scenes"] = ScenesManager(
registry=self,
update_type=update.event_type,
event=event,
- state=data["state"],
+ state=state,
data=data,
)
return await handler(event, data)
@@ -865,7 +880,7 @@ class SceneRegistry:
return self._scenes[scene]
except KeyError:
msg = f"Scene {scene!r} is not registered"
- raise SceneException(msg)
+ raise SceneException(msg) from None
@dataclass
diff --git a/aiogram/methods/__init__.py b/aiogram/methods/__init__.py
index 9c1ebccd..786e53e5 100644
--- a/aiogram/methods/__init__.py
+++ b/aiogram/methods/__init__.py
@@ -74,6 +74,7 @@ from .get_sticker_set import GetStickerSet
from .get_updates import GetUpdates
from .get_user_chat_boosts import GetUserChatBoosts
from .get_user_gifts import GetUserGifts
+from .get_user_profile_audios import GetUserProfileAudios
from .get_user_profile_photos import GetUserProfilePhotos
from .get_webhook_info import GetWebhookInfo
from .gift_premium_subscription import GiftPremiumSubscription
@@ -87,6 +88,7 @@ from .read_business_message import ReadBusinessMessage
from .refund_star_payment import RefundStarPayment
from .remove_business_account_profile_photo import RemoveBusinessAccountProfilePhoto
from .remove_chat_verification import RemoveChatVerification
+from .remove_my_profile_photo import RemoveMyProfilePhoto
from .remove_user_verification import RemoveUserVerification
from .reopen_forum_topic import ReopenForumTopic
from .reopen_general_forum_topic import ReopenGeneralForumTopic
@@ -136,6 +138,7 @@ from .set_my_commands import SetMyCommands
from .set_my_default_administrator_rights import SetMyDefaultAdministratorRights
from .set_my_description import SetMyDescription
from .set_my_name import SetMyName
+from .set_my_profile_photo import SetMyProfilePhoto
from .set_my_short_description import SetMyShortDescription
from .set_passport_data_errors import SetPassportDataErrors
from .set_sticker_emoji_list import SetStickerEmojiList
@@ -238,6 +241,7 @@ __all__ = (
"GetUpdates",
"GetUserChatBoosts",
"GetUserGifts",
+ "GetUserProfileAudios",
"GetUserProfilePhotos",
"GetWebhookInfo",
"GiftPremiumSubscription",
@@ -251,6 +255,7 @@ __all__ = (
"RefundStarPayment",
"RemoveBusinessAccountProfilePhoto",
"RemoveChatVerification",
+ "RemoveMyProfilePhoto",
"RemoveUserVerification",
"ReopenForumTopic",
"ReopenGeneralForumTopic",
@@ -302,6 +307,7 @@ __all__ = (
"SetMyDefaultAdministratorRights",
"SetMyDescription",
"SetMyName",
+ "SetMyProfilePhoto",
"SetMyShortDescription",
"SetPassportDataErrors",
"SetStickerEmojiList",
diff --git a/aiogram/methods/base.py b/aiogram/methods/base.py
index ca97dd7d..fdc1e3f0 100644
--- a/aiogram/methods/base.py
+++ b/aiogram/methods/base.py
@@ -1,11 +1,11 @@
from __future__ import annotations
from abc import ABC, abstractmethod
+from collections.abc import Generator
from typing import (
TYPE_CHECKING,
Any,
ClassVar,
- Generator,
Generic,
TypeVar,
)
diff --git a/aiogram/methods/create_forum_topic.py b/aiogram/methods/create_forum_topic.py
index 0d7940a3..242b67cb 100644
--- a/aiogram/methods/create_forum_topic.py
+++ b/aiogram/methods/create_forum_topic.py
@@ -8,7 +8,7 @@ from .base import TelegramMethod
class CreateForumTopic(TelegramMethod[ForumTopic]):
"""
- Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator rights. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.
+ Use this method to create a topic in a forum supergroup chat or a private chat with a user. In the case of a supergroup chat the bot must be an administrator in the chat for this to work and must have the *can_manage_topics* administrator right. Returns information about the created topic as a :class:`aiogram.types.forum_topic.ForumTopic` object.
Source: https://core.telegram.org/bots/api#createforumtopic
"""
diff --git a/aiogram/methods/get_user_profile_audios.py b/aiogram/methods/get_user_profile_audios.py
new file mode 100644
index 00000000..ce77de40
--- /dev/null
+++ b/aiogram/methods/get_user_profile_audios.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..types import UserProfileAudios
+from .base import TelegramMethod
+
+
+class GetUserProfileAudios(TelegramMethod[UserProfileAudios]):
+ """
+ Use this method to get a list of profile audios for a user. Returns a :class:`aiogram.types.user_profile_audios.UserProfileAudios` object.
+
+ Source: https://core.telegram.org/bots/api#getuserprofileaudios
+ """
+
+ __returning__ = UserProfileAudios
+ __api_method__ = "getUserProfileAudios"
+
+ user_id: int
+ """Unique identifier of the target user"""
+ offset: int | None = None
+ """Sequential number of the first audio to be returned. By default, all audios are returned."""
+ limit: int | None = None
+ """Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100."""
+
+ if TYPE_CHECKING:
+ # DO NOT EDIT MANUALLY!!!
+ # This section was auto-generated via `butcher`
+
+ def __init__(
+ __pydantic__self__,
+ *,
+ user_id: int,
+ offset: int | None = None,
+ limit: int | None = None,
+ **__pydantic_kwargs: Any,
+ ) -> None:
+ # DO NOT EDIT MANUALLY!!!
+ # This method was auto-generated via `butcher`
+ # Is needed only for type checking and IDE support without any additional plugins
+
+ super().__init__(user_id=user_id, offset=offset, limit=limit, **__pydantic_kwargs)
diff --git a/aiogram/methods/remove_my_profile_photo.py b/aiogram/methods/remove_my_profile_photo.py
new file mode 100644
index 00000000..c59dca32
--- /dev/null
+++ b/aiogram/methods/remove_my_profile_photo.py
@@ -0,0 +1,14 @@
+from __future__ import annotations
+
+from .base import TelegramMethod
+
+
+class RemoveMyProfilePhoto(TelegramMethod[bool]):
+ """
+ Removes the profile photo of the bot. Requires no parameters. Returns :code:`True` on success.
+
+ Source: https://core.telegram.org/bots/api#removemyprofilephoto
+ """
+
+ __returning__ = bool
+ __api_method__ = "removeMyProfilePhoto"
diff --git a/aiogram/methods/set_my_profile_photo.py b/aiogram/methods/set_my_profile_photo.py
new file mode 100644
index 00000000..4f72ec61
--- /dev/null
+++ b/aiogram/methods/set_my_profile_photo.py
@@ -0,0 +1,33 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from ..types import InputProfilePhotoUnion
+from .base import TelegramMethod
+
+
+class SetMyProfilePhoto(TelegramMethod[bool]):
+ """
+ Changes the profile photo of the bot. Returns :code:`True` on success.
+
+ Source: https://core.telegram.org/bots/api#setmyprofilephoto
+ """
+
+ __returning__ = bool
+ __api_method__ = "setMyProfilePhoto"
+
+ photo: InputProfilePhotoUnion
+ """The new profile photo to set"""
+
+ if TYPE_CHECKING:
+ # DO NOT EDIT MANUALLY!!!
+ # This section was auto-generated via `butcher`
+
+ def __init__(
+ __pydantic__self__, *, photo: InputProfilePhotoUnion, **__pydantic_kwargs: Any
+ ) -> None:
+ # DO NOT EDIT MANUALLY!!!
+ # This method was auto-generated via `butcher`
+ # Is needed only for type checking and IDE support without any additional plugins
+
+ super().__init__(photo=photo, **__pydantic_kwargs)
diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py
index 6fe65642..90e59515 100644
--- a/aiogram/types/__init__.py
+++ b/aiogram/types/__init__.py
@@ -1,4 +1,4 @@
-from typing import List, Literal, Optional, Union
+from typing import Literal, Optional, Union
from .accepted_gift_types import AcceptedGiftTypes
from .affiliate_info import AffiliateInfo
@@ -387,6 +387,8 @@ __all__ = (
"ChatMemberRestricted",
"ChatMemberUnion",
"ChatMemberUpdated",
+ "ChatOwnerChanged",
+ "ChatOwnerLeft",
"ChatPermissions",
"ChatPhoto",
"ChatShared",
@@ -627,6 +629,7 @@ __all__ = (
"Update",
"User",
"UserChatBoosts",
+ "UserProfileAudios",
"UserProfilePhotos",
"UserRating",
"UserShared",
@@ -638,6 +641,7 @@ __all__ = (
"VideoChatScheduled",
"VideoChatStarted",
"VideoNote",
+ "VideoQuality",
"Voice",
"WebAppData",
"WebAppInfo",
@@ -646,6 +650,10 @@ __all__ = (
)
from ..client.default import Default as _Default
+from .chat_owner_changed import ChatOwnerChanged
+from .chat_owner_left import ChatOwnerLeft
+from .user_profile_audios import UserProfileAudios
+from .video_quality import VideoQuality
# Load typing forward refs for every TelegramObject
for _entity_name in __all__:
@@ -654,7 +662,7 @@ for _entity_name in __all__:
continue
_entity.model_rebuild(
_types_namespace={
- "List": List,
+ "List": list,
"Optional": Optional,
"Union": Union,
"Literal": Literal,
diff --git a/aiogram/types/chat_full_info.py b/aiogram/types/chat_full_info.py
index 9505aebf..93fb0a00 100644
--- a/aiogram/types/chat_full_info.py
+++ b/aiogram/types/chat_full_info.py
@@ -9,6 +9,7 @@ from .custom import DateTime
if TYPE_CHECKING:
from .accepted_gift_types import AcceptedGiftTypes
+ from .audio import Audio
from .birthdate import Birthdate
from .business_intro import BusinessIntro
from .business_location import BusinessLocation
@@ -125,6 +126,8 @@ class ChatFullInfo(Chat):
"""*Optional*. For supergroups, the location to which the supergroup is connected"""
rating: UserRating | None = None
"""*Optional*. For private chats, the rating of the user if any"""
+ first_profile_audio: Audio | None = None
+ """*Optional*. For private chats, the first audio added to the profile of the user"""
unique_gift_colors: UniqueGiftColors | None = None
"""*Optional*. The color scheme based on a unique gift that must be used for the chat's name, message replies and link previews"""
paid_message_star_count: int | None = None
@@ -190,6 +193,7 @@ class ChatFullInfo(Chat):
linked_chat_id: int | None = None,
location: ChatLocation | None = None,
rating: UserRating | None = None,
+ first_profile_audio: Audio | None = None,
unique_gift_colors: UniqueGiftColors | None = None,
paid_message_star_count: int | None = None,
can_send_gift: bool | None = None,
@@ -248,6 +252,7 @@ class ChatFullInfo(Chat):
linked_chat_id=linked_chat_id,
location=location,
rating=rating,
+ first_profile_audio=first_profile_audio,
unique_gift_colors=unique_gift_colors,
paid_message_star_count=paid_message_star_count,
can_send_gift=can_send_gift,
diff --git a/aiogram/types/chat_owner_changed.py b/aiogram/types/chat_owner_changed.py
new file mode 100644
index 00000000..1b9bc00f
--- /dev/null
+++ b/aiogram/types/chat_owner_changed.py
@@ -0,0 +1,30 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from .base import TelegramObject
+
+if TYPE_CHECKING:
+ from .user import User
+
+
+class ChatOwnerChanged(TelegramObject):
+ """
+ Describes a service message about an ownership change in the chat.
+
+ Source: https://core.telegram.org/bots/api#chatownerchanged
+ """
+
+ new_owner: User
+ """The new owner of the chat"""
+
+ if TYPE_CHECKING:
+ # DO NOT EDIT MANUALLY!!!
+ # This section was auto-generated via `butcher`
+
+ def __init__(__pydantic__self__, *, new_owner: User, **__pydantic_kwargs: Any) -> None:
+ # DO NOT EDIT MANUALLY!!!
+ # This method was auto-generated via `butcher`
+ # Is needed only for type checking and IDE support without any additional plugins
+
+ super().__init__(new_owner=new_owner, **__pydantic_kwargs)
diff --git a/aiogram/types/chat_owner_left.py b/aiogram/types/chat_owner_left.py
new file mode 100644
index 00000000..8c9af97d
--- /dev/null
+++ b/aiogram/types/chat_owner_left.py
@@ -0,0 +1,32 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from .base import TelegramObject
+
+if TYPE_CHECKING:
+ from .user import User
+
+
+class ChatOwnerLeft(TelegramObject):
+ """
+ Describes a service message about the chat owner leaving the chat.
+
+ Source: https://core.telegram.org/bots/api#chatownerleft
+ """
+
+ new_owner: User | None = None
+ """*Optional*. The user which will be the new owner of the chat if the previous owner does not return to the chat"""
+
+ if TYPE_CHECKING:
+ # DO NOT EDIT MANUALLY!!!
+ # This section was auto-generated via `butcher`
+
+ def __init__(
+ __pydantic__self__, *, new_owner: User | None = None, **__pydantic_kwargs: Any
+ ) -> None:
+ # DO NOT EDIT MANUALLY!!!
+ # This method was auto-generated via `butcher`
+ # Is needed only for type checking and IDE support without any additional plugins
+
+ super().__init__(new_owner=new_owner, **__pydantic_kwargs)
diff --git a/aiogram/types/contact.py b/aiogram/types/contact.py
index acc53765..9e7677a4 100644
--- a/aiogram/types/contact.py
+++ b/aiogram/types/contact.py
@@ -49,3 +49,9 @@ class Contact(TelegramObject):
vcard=vcard,
**__pydantic_kwargs,
)
+
+ @property
+ def full_name(self) -> str:
+ if self.last_name:
+ return f"{self.first_name} {self.last_name}"
+ return self.first_name
diff --git a/aiogram/types/custom.py b/aiogram/types/custom.py
index 8617fa64..660c0be7 100644
--- a/aiogram/types/custom.py
+++ b/aiogram/types/custom.py
@@ -1,8 +1,8 @@
import sys
from datetime import datetime, timezone
+from typing import Annotated
from pydantic import PlainSerializer
-from typing_extensions import Annotated
if sys.platform == "win32": # pragma: no cover
diff --git a/aiogram/types/game_high_score.py b/aiogram/types/game_high_score.py
index 5364be6e..e8fdcf40 100644
--- a/aiogram/types/game_high_score.py
+++ b/aiogram/types/game_high_score.py
@@ -15,6 +15,8 @@ class GameHighScore(TelegramObject):
If you've got any questions, please check out our `https://core.telegram.org/bots/faq `_ **Bot FAQ »**
+ -
+
Source: https://core.telegram.org/bots/api#gamehighscore
"""
diff --git a/aiogram/types/inaccessible_message.py b/aiogram/types/inaccessible_message.py
index 30c48a70..8b8a9f1f 100644
--- a/aiogram/types/inaccessible_message.py
+++ b/aiogram/types/inaccessible_message.py
@@ -130,9 +130,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMessage(
chat_id=self.chat.id,
@@ -208,9 +208,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMessage(
chat_id=self.chat.id,
@@ -298,9 +298,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAnimation(
chat_id=self.chat.id,
@@ -391,9 +391,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAnimation(
chat_id=self.chat.id,
@@ -483,9 +483,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAudio(
chat_id=self.chat.id,
@@ -571,9 +571,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAudio(
chat_id=self.chat.id,
@@ -652,9 +652,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendContact(
chat_id=self.chat.id,
@@ -727,9 +727,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendContact(
chat_id=self.chat.id,
@@ -808,9 +808,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDocument(
chat_id=self.chat.id,
@@ -889,9 +889,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDocument(
chat_id=self.chat.id,
@@ -958,9 +958,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendGame(
chat_id=self.chat.id,
@@ -1018,9 +1018,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendGame(
chat_id=self.chat.id,
@@ -1122,9 +1122,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendInvoice(
chat_id=self.chat.id,
@@ -1245,9 +1245,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendInvoice(
chat_id=self.chat.id,
@@ -1342,9 +1342,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendLocation(
chat_id=self.chat.id,
@@ -1423,9 +1423,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendLocation(
chat_id=self.chat.id,
@@ -1492,9 +1492,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMediaGroup(
chat_id=self.chat.id,
@@ -1552,9 +1552,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMediaGroup(
chat_id=self.chat.id,
@@ -1628,9 +1628,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPhoto(
chat_id=self.chat.id,
@@ -1709,9 +1709,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPhoto(
chat_id=self.chat.id,
@@ -1804,9 +1804,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPoll(
chat_id=self.chat.id,
@@ -1903,9 +1903,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPoll(
chat_id=self.chat.id,
@@ -1982,9 +1982,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDice(
chat_id=self.chat.id,
@@ -2048,9 +2048,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDice(
chat_id=self.chat.id,
@@ -2118,9 +2118,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendSticker(
chat_id=self.chat.id,
@@ -2187,9 +2187,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendSticker(
chat_id=self.chat.id,
@@ -2270,9 +2270,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVenue(
chat_id=self.chat.id,
@@ -2357,9 +2357,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVenue(
chat_id=self.chat.id,
@@ -2456,9 +2456,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideo(
chat_id=self.chat.id,
@@ -2558,9 +2558,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideo(
chat_id=self.chat.id,
@@ -2644,9 +2644,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideoNote(
chat_id=self.chat.id,
@@ -2719,9 +2719,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideoNote(
chat_id=self.chat.id,
@@ -2798,9 +2798,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVoice(
chat_id=self.chat.id,
@@ -2876,9 +2876,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVoice(
chat_id=self.chat.id,
@@ -2954,9 +2954,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPaidMedia(
chat_id=self.chat.id,
@@ -3031,9 +3031,9 @@ class InaccessibleMessage(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPaidMedia(
chat_id=self.chat.id,
diff --git a/aiogram/types/inline_keyboard_button.py b/aiogram/types/inline_keyboard_button.py
index bc4e934f..f85418cd 100644
--- a/aiogram/types/inline_keyboard_button.py
+++ b/aiogram/types/inline_keyboard_button.py
@@ -14,13 +14,17 @@ if TYPE_CHECKING:
class InlineKeyboardButton(MutableTelegramObject):
"""
- This object represents one button of an inline keyboard. Exactly one of the optional fields must be used to specify type of the button.
+ This object represents one button of an inline keyboard. Exactly one of the fields other than *text*, *icon_custom_emoji_id*, and *style* must be used to specify the type of the button.
Source: https://core.telegram.org/bots/api#inlinekeyboardbutton
"""
text: str
"""Label text on the button"""
+ icon_custom_emoji_id: str | None = None
+ """*Optional*. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on `Fragment `_ or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription."""
+ style: str | None = None
+ """*Optional*. Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used."""
url: str | None = None
"""*Optional*. HTTP or tg:// URL to be opened when the button is pressed. Links :code:`tg://user?id=` can be used to mention a user by their identifier without using a username, if this is allowed by their privacy settings."""
callback_data: str | None = None
@@ -50,6 +54,8 @@ class InlineKeyboardButton(MutableTelegramObject):
__pydantic__self__,
*,
text: str,
+ icon_custom_emoji_id: str | None = None,
+ style: str | None = None,
url: str | None = None,
callback_data: str | None = None,
web_app: WebAppInfo | None = None,
@@ -68,6 +74,8 @@ class InlineKeyboardButton(MutableTelegramObject):
super().__init__(
text=text,
+ icon_custom_emoji_id=icon_custom_emoji_id,
+ style=style,
url=url,
callback_data=callback_data,
web_app=web_app,
diff --git a/aiogram/types/input_file.py b/aiogram/types/input_file.py
index 07cc0765..b8df230e 100644
--- a/aiogram/types/input_file.py
+++ b/aiogram/types/input_file.py
@@ -3,8 +3,9 @@ from __future__ import annotations
import io
import os
from abc import ABC, abstractmethod
+from collections.abc import AsyncGenerator
from pathlib import Path
-from typing import TYPE_CHECKING, Any, AsyncGenerator
+from typing import TYPE_CHECKING, Any
import aiofiles
@@ -33,7 +34,7 @@ class InputFile(ABC):
self.chunk_size = chunk_size
@abstractmethod
- async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]: # pragma: no cover
+ async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]: # pragma: no cover
yield b""
@@ -72,7 +73,7 @@ class BufferedInputFile(InputFile):
data = f.read()
return cls(data, filename=filename, chunk_size=chunk_size)
- async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]:
+ async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]:
buffer = io.BytesIO(self.data)
while chunk := buffer.read(self.chunk_size):
yield chunk
@@ -99,7 +100,7 @@ class FSInputFile(InputFile):
self.path = path
- async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]:
+ async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]:
async with aiofiles.open(self.path, "rb") as f:
while chunk := await f.read(self.chunk_size):
yield chunk
@@ -135,7 +136,7 @@ class URLInputFile(InputFile):
self.timeout = timeout
self.bot = bot
- async def read(self, bot: "Bot") -> AsyncGenerator[bytes, None]:
+ async def read(self, bot: Bot) -> AsyncGenerator[bytes, None]:
bot = self.bot or bot
stream = bot.session.stream_content(
url=self.url,
diff --git a/aiogram/types/keyboard_button.py b/aiogram/types/keyboard_button.py
index 0635476e..f0c4af22 100644
--- a/aiogram/types/keyboard_button.py
+++ b/aiogram/types/keyboard_button.py
@@ -16,14 +16,17 @@ if TYPE_CHECKING:
class KeyboardButton(MutableTelegramObject):
"""
- This object represents one button of the reply keyboard. At most one of the optional fields must be used to specify type of the button. For simple text buttons, *String* can be used instead of this object to specify the button text.
- **Note:** *request_users* and *request_chat* options will only work in Telegram versions released after 3 February, 2023. Older clients will display *unsupported message*.
+ This object represents one button of the reply keyboard. At most one of the fields other than *text*, *icon_custom_emoji_id*, and *style* must be used to specify the type of the button. For simple text buttons, *String* can be used instead of this object to specify the button text.
Source: https://core.telegram.org/bots/api#keyboardbutton
"""
text: str
- """Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed"""
+ """Text of the button. If none of the fields other than *text*, *icon_custom_emoji_id*, and *style* are used, it will be sent as a message when the button is pressed"""
+ icon_custom_emoji_id: str | None = None
+ """*Optional*. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on `Fragment `_ or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription."""
+ style: str | None = None
+ """*Optional*. Style of the button. Must be one of 'danger' (red), 'success' (green) or 'primary' (blue). If omitted, then an app-specific style is used."""
request_users: KeyboardButtonRequestUsers | None = None
"""*Optional*. If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a 'users_shared' service message. Available in private chats only."""
request_chat: KeyboardButtonRequestChat | None = None
@@ -52,6 +55,8 @@ class KeyboardButton(MutableTelegramObject):
__pydantic__self__,
*,
text: str,
+ icon_custom_emoji_id: str | None = None,
+ style: str | None = None,
request_users: KeyboardButtonRequestUsers | None = None,
request_chat: KeyboardButtonRequestChat | None = None,
request_contact: bool | None = None,
@@ -67,6 +72,8 @@ class KeyboardButton(MutableTelegramObject):
super().__init__(
text=text,
+ icon_custom_emoji_id=icon_custom_emoji_id,
+ style=style,
request_users=request_users,
request_chat=request_chat,
request_contact=request_contact,
diff --git a/aiogram/types/message.py b/aiogram/types/message.py
index f740e224..b35fe0fb 100644
--- a/aiogram/types/message.py
+++ b/aiogram/types/message.py
@@ -55,6 +55,8 @@ if TYPE_CHECKING:
from .chat_background import ChatBackground
from .chat_boost_added import ChatBoostAdded
from .chat_id_union import ChatIdUnion
+ from .chat_owner_changed import ChatOwnerChanged
+ from .chat_owner_left import ChatOwnerLeft
from .chat_shared import ChatShared
from .checklist import Checklist
from .checklist_tasks_added import ChecklistTasksAdded
@@ -245,6 +247,10 @@ class Message(MaybeInaccessibleMessage):
"""*Optional*. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)"""
left_chat_member: User | None = None
"""*Optional*. A member was removed from the group, information about them (this member may be the bot itself)"""
+ chat_owner_left: ChatOwnerLeft | None = None
+ """*Optional*. Service message: chat owner has left"""
+ chat_owner_changed: ChatOwnerChanged | None = None
+ """*Optional*. Service message: chat owner has changed"""
new_chat_title: str | None = None
"""*Optional*. A chat title was changed to this value"""
new_chat_photo: list[PhotoSize] | None = None
@@ -440,6 +446,8 @@ class Message(MaybeInaccessibleMessage):
location: Location | None = None,
new_chat_members: list[User] | None = None,
left_chat_member: User | None = None,
+ chat_owner_left: ChatOwnerLeft | None = None,
+ chat_owner_changed: ChatOwnerChanged | None = None,
new_chat_title: str | None = None,
new_chat_photo: list[PhotoSize] | None = None,
delete_chat_photo: bool | None = None,
@@ -557,6 +565,8 @@ class Message(MaybeInaccessibleMessage):
location=location,
new_chat_members=new_chat_members,
left_chat_member=left_chat_member,
+ chat_owner_left=chat_owner_left,
+ chat_owner_changed=chat_owner_changed,
new_chat_title=new_chat_title,
new_chat_photo=new_chat_photo,
delete_chat_photo=delete_chat_photo,
@@ -650,6 +660,10 @@ class Message(MaybeInaccessibleMessage):
return ContentType.NEW_CHAT_MEMBERS
if self.left_chat_member:
return ContentType.LEFT_CHAT_MEMBER
+ if self.chat_owner_left:
+ return ContentType.CHAT_OWNER_LEFT
+ if self.chat_owner_changed:
+ return ContentType.CHAT_OWNER_CHANGED
if self.invoice:
return ContentType.INVOICE
if self.successful_payment:
@@ -851,9 +865,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAnimation(
chat_id=self.chat.id,
@@ -944,9 +958,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAnimation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAnimation(
chat_id=self.chat.id,
@@ -1032,9 +1046,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAudio(
chat_id=self.chat.id,
@@ -1120,9 +1134,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendAudio
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendAudio(
chat_id=self.chat.id,
@@ -1197,9 +1211,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendContact(
chat_id=self.chat.id,
@@ -1272,9 +1286,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendContact
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendContact(
chat_id=self.chat.id,
@@ -1349,9 +1363,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDocument(
chat_id=self.chat.id,
@@ -1430,9 +1444,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDocument
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDocument(
chat_id=self.chat.id,
@@ -1495,9 +1509,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendGame(
chat_id=self.chat.id,
@@ -1555,9 +1569,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendGame
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendGame(
chat_id=self.chat.id,
@@ -1657,9 +1671,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendInvoice(
chat_id=self.chat.id,
@@ -1783,9 +1797,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendInvoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendInvoice(
chat_id=self.chat.id,
@@ -1877,9 +1891,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendLocation(
chat_id=self.chat.id,
@@ -1958,9 +1972,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendLocation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendLocation(
chat_id=self.chat.id,
@@ -2023,9 +2037,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMediaGroup(
chat_id=self.chat.id,
@@ -2083,9 +2097,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMediaGroup
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMediaGroup(
chat_id=self.chat.id,
@@ -2153,9 +2167,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMessage(
chat_id=self.chat.id,
@@ -2231,9 +2245,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendMessage(
chat_id=self.chat.id,
@@ -2309,9 +2323,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPhoto(
chat_id=self.chat.id,
@@ -2390,9 +2404,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPhoto
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPhoto(
chat_id=self.chat.id,
@@ -2481,9 +2495,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPoll(
chat_id=self.chat.id,
@@ -2580,9 +2594,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPoll
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPoll(
chat_id=self.chat.id,
@@ -2655,9 +2669,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDice(
chat_id=self.chat.id,
@@ -2721,9 +2735,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendDice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendDice(
chat_id=self.chat.id,
@@ -2787,9 +2801,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendSticker(
chat_id=self.chat.id,
@@ -2856,9 +2870,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendSticker
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendSticker(
chat_id=self.chat.id,
@@ -2935,9 +2949,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVenue(
chat_id=self.chat.id,
@@ -3022,9 +3036,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVenue
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVenue(
chat_id=self.chat.id,
@@ -3117,9 +3131,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideo(
chat_id=self.chat.id,
@@ -3219,9 +3233,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideo
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideo(
chat_id=self.chat.id,
@@ -3301,9 +3315,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideoNote(
chat_id=self.chat.id,
@@ -3376,9 +3390,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVideoNote
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVideoNote(
chat_id=self.chat.id,
@@ -3451,9 +3465,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVoice(
chat_id=self.chat.id,
@@ -3529,9 +3543,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendVoice
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendVoice(
chat_id=self.chat.id,
@@ -3808,9 +3822,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import CopyMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return CopyMessage(
from_chat_id=self.chat.id,
@@ -3872,9 +3886,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageText
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return EditMessageText(
chat_id=self.chat.id,
@@ -3928,9 +3942,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import ForwardMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return ForwardMessage(
from_chat_id=self.chat.id,
@@ -3975,9 +3989,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageMedia
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return EditMessageMedia(
chat_id=self.chat.id,
@@ -4016,9 +4030,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageReplyMarkup
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return EditMessageReplyMarkup(
chat_id=self.chat.id,
@@ -4055,9 +4069,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageReplyMarkup
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return EditMessageReplyMarkup(
chat_id=self.chat.id,
@@ -4107,9 +4121,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageLiveLocation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return EditMessageLiveLocation(
chat_id=self.chat.id,
@@ -4153,9 +4167,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import StopMessageLiveLocation
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return StopMessageLiveLocation(
chat_id=self.chat.id,
@@ -4201,9 +4215,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import EditMessageCaption
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return EditMessageCaption(
chat_id=self.chat.id,
@@ -4261,9 +4275,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import DeleteMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return DeleteMessage(
chat_id=self.chat.id,
@@ -4297,9 +4311,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import PinChatMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return PinChatMessage(
chat_id=self.chat.id,
@@ -4332,9 +4346,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import UnpinChatMessage
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return UnpinChatMessage(
chat_id=self.chat.id,
@@ -4353,7 +4367,7 @@ class Message(MaybeInaccessibleMessage):
:param include_thread_id: if set, adds chat thread id to URL and returns like https://t.me/username/thread_id/message_id
:return: string with full message URL
"""
- if self.chat.type in ("private", "group"):
+ if self.chat.type in {"private", "group"}:
return None
chat_value = (
@@ -4397,9 +4411,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SetMessageReaction
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SetMessageReaction(
chat_id=self.chat.id,
@@ -4461,9 +4475,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPaidMedia(
chat_id=self.chat.id,
@@ -4536,9 +4550,9 @@ class Message(MaybeInaccessibleMessage):
from aiogram.methods import SendPaidMedia
- assert (
- self.chat is not None
- ), "This method can be used only if chat is present in the message."
+ assert self.chat is not None, (
+ "This method can be used only if chat is present in the message."
+ )
return SendPaidMedia(
chat_id=self.chat.id,
diff --git a/aiogram/types/unique_gift.py b/aiogram/types/unique_gift.py
index 586e79a6..1b23337c 100644
--- a/aiogram/types/unique_gift.py
+++ b/aiogram/types/unique_gift.py
@@ -35,6 +35,8 @@ class UniqueGift(TelegramObject):
"""Backdrop of the gift"""
is_premium: bool | None = None
"""*Optional*. :code:`True`, if the original regular gift was exclusively purchaseable by Telegram Premium subscribers"""
+ is_burned: bool | None = None
+ """*Optional*. :code:`True`, if the gift was used to craft another gift and isn't available anymore"""
is_from_blockchain: bool | None = None
"""*Optional*. :code:`True`, if the gift is assigned from the TON blockchain and can't be resold or transferred in Telegram"""
colors: UniqueGiftColors | None = None
@@ -57,6 +59,7 @@ class UniqueGift(TelegramObject):
symbol: UniqueGiftSymbol,
backdrop: UniqueGiftBackdrop,
is_premium: bool | None = None,
+ is_burned: bool | None = None,
is_from_blockchain: bool | None = None,
colors: UniqueGiftColors | None = None,
publisher_chat: Chat | None = None,
@@ -75,6 +78,7 @@ class UniqueGift(TelegramObject):
symbol=symbol,
backdrop=backdrop,
is_premium=is_premium,
+ is_burned=is_burned,
is_from_blockchain=is_from_blockchain,
colors=colors,
publisher_chat=publisher_chat,
diff --git a/aiogram/types/unique_gift_model.py b/aiogram/types/unique_gift_model.py
index 7a87f032..17faedfd 100644
--- a/aiogram/types/unique_gift_model.py
+++ b/aiogram/types/unique_gift_model.py
@@ -20,7 +20,9 @@ class UniqueGiftModel(TelegramObject):
sticker: Sticker
"""The sticker that represents the unique gift"""
rarity_per_mille: int
- """The number of unique gifts that receive this model for every 1000 gifts upgraded"""
+ """The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts."""
+ rarity: str | None = None
+ """*Optional*. Rarity of the model if it is a crafted model. Currently, can be 'uncommon', 'rare', 'epic', or 'legendary'."""
if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!!
@@ -32,6 +34,7 @@ class UniqueGiftModel(TelegramObject):
name: str,
sticker: Sticker,
rarity_per_mille: int,
+ rarity: str | None = None,
**__pydantic_kwargs: Any,
) -> None:
# DO NOT EDIT MANUALLY!!!
@@ -39,5 +42,9 @@ class UniqueGiftModel(TelegramObject):
# Is needed only for type checking and IDE support without any additional plugins
super().__init__(
- name=name, sticker=sticker, rarity_per_mille=rarity_per_mille, **__pydantic_kwargs
+ name=name,
+ sticker=sticker,
+ rarity_per_mille=rarity_per_mille,
+ rarity=rarity,
+ **__pydantic_kwargs,
)
diff --git a/aiogram/types/user.py b/aiogram/types/user.py
index 0dfb7ee2..7f03504c 100644
--- a/aiogram/types/user.py
+++ b/aiogram/types/user.py
@@ -7,7 +7,7 @@ from ..utils.link import create_tg_link
from .base import TelegramObject
if TYPE_CHECKING:
- from ..methods import GetUserProfilePhotos
+ from ..methods import GetUserProfileAudios, GetUserProfilePhotos
class User(TelegramObject):
@@ -45,6 +45,8 @@ class User(TelegramObject):
"""*Optional*. :code:`True`, if the bot has a main Web App. Returned only in :class:`aiogram.methods.get_me.GetMe`."""
has_topics_enabled: bool | None = None
"""*Optional*. :code:`True`, if the bot has forum topic mode enabled in private chats. Returned only in :class:`aiogram.methods.get_me.GetMe`."""
+ allows_users_to_create_topics: bool | None = None
+ """*Optional*. :code:`True`, if the bot allows users to create and delete topics in private chats. Returned only in :class:`aiogram.methods.get_me.GetMe`."""
if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!!
@@ -67,6 +69,7 @@ class User(TelegramObject):
can_connect_to_business: bool | None = None,
has_main_web_app: bool | None = None,
has_topics_enabled: bool | None = None,
+ allows_users_to_create_topics: bool | None = None,
**__pydantic_kwargs: Any,
) -> None:
# DO NOT EDIT MANUALLY!!!
@@ -88,6 +91,7 @@ class User(TelegramObject):
can_connect_to_business=can_connect_to_business,
has_main_web_app=has_main_web_app,
has_topics_enabled=has_topics_enabled,
+ allows_users_to_create_topics=allows_users_to_create_topics,
**__pydantic_kwargs,
)
@@ -142,3 +146,35 @@ class User(TelegramObject):
limit=limit,
**kwargs,
).as_(self._bot)
+
+ def get_profile_audios(
+ self,
+ offset: int | None = None,
+ limit: int | None = None,
+ **kwargs: Any,
+ ) -> GetUserProfileAudios:
+ """
+ Shortcut for method :class:`aiogram.methods.get_user_profile_audios.GetUserProfileAudios`
+ will automatically fill method attributes:
+
+ - :code:`user_id`
+
+ Use this method to get a list of profile audios for a user. Returns a :class:`aiogram.types.user_profile_audios.UserProfileAudios` object.
+
+ Source: https://core.telegram.org/bots/api#getuserprofileaudios
+
+ :param offset: Sequential number of the first audio to be returned. By default, all audios are returned.
+ :param limit: Limits the number of audios to be retrieved. Values between 1-100 are accepted. Defaults to 100.
+ :return: instance of method :class:`aiogram.methods.get_user_profile_audios.GetUserProfileAudios`
+ """
+ # DO NOT EDIT MANUALLY!!!
+ # This method was auto-generated via `butcher`
+
+ from aiogram.methods import GetUserProfileAudios
+
+ return GetUserProfileAudios(
+ user_id=self.id,
+ offset=offset,
+ limit=limit,
+ **kwargs,
+ ).as_(self._bot)
diff --git a/aiogram/types/user_profile_audios.py b/aiogram/types/user_profile_audios.py
new file mode 100644
index 00000000..a8f7eb9a
--- /dev/null
+++ b/aiogram/types/user_profile_audios.py
@@ -0,0 +1,34 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from .base import TelegramObject
+
+if TYPE_CHECKING:
+ from .audio import Audio
+
+
+class UserProfileAudios(TelegramObject):
+ """
+ This object represents the audios displayed on a user's profile.
+
+ Source: https://core.telegram.org/bots/api#userprofileaudios
+ """
+
+ total_count: int
+ """Total number of profile audios for the target user"""
+ audios: list[Audio]
+ """Requested profile audios"""
+
+ if TYPE_CHECKING:
+ # DO NOT EDIT MANUALLY!!!
+ # This section was auto-generated via `butcher`
+
+ def __init__(
+ __pydantic__self__, *, total_count: int, audios: list[Audio], **__pydantic_kwargs: Any
+ ) -> None:
+ # DO NOT EDIT MANUALLY!!!
+ # This method was auto-generated via `butcher`
+ # Is needed only for type checking and IDE support without any additional plugins
+
+ super().__init__(total_count=total_count, audios=audios, **__pydantic_kwargs)
diff --git a/aiogram/types/video.py b/aiogram/types/video.py
index a13c9c96..1674f6c2 100644
--- a/aiogram/types/video.py
+++ b/aiogram/types/video.py
@@ -7,6 +7,7 @@ from .base import TelegramObject
if TYPE_CHECKING:
from .photo_size import PhotoSize
+ from .video_quality import VideoQuality
class Video(TelegramObject):
@@ -32,6 +33,8 @@ class Video(TelegramObject):
"""*Optional*. Available sizes of the cover of the video in the message"""
start_timestamp: datetime.datetime | None = None
"""*Optional*. Timestamp in seconds from which the video will play in the message"""
+ qualities: list[VideoQuality] | None = None
+ """*Optional*. List of available qualities of the video"""
file_name: str | None = None
"""*Optional*. Original filename as defined by the sender"""
mime_type: str | None = None
@@ -54,6 +57,7 @@ class Video(TelegramObject):
thumbnail: PhotoSize | None = None,
cover: list[PhotoSize] | None = None,
start_timestamp: datetime.datetime | None = None,
+ qualities: list[VideoQuality] | None = None,
file_name: str | None = None,
mime_type: str | None = None,
file_size: int | None = None,
@@ -72,6 +76,7 @@ class Video(TelegramObject):
thumbnail=thumbnail,
cover=cover,
start_timestamp=start_timestamp,
+ qualities=qualities,
file_name=file_name,
mime_type=mime_type,
file_size=file_size,
diff --git a/aiogram/types/video_quality.py b/aiogram/types/video_quality.py
new file mode 100644
index 00000000..9b17e79c
--- /dev/null
+++ b/aiogram/types/video_quality.py
@@ -0,0 +1,55 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any
+
+from .base import TelegramObject
+
+
+class VideoQuality(TelegramObject):
+ """
+ This object represents a video file of a specific quality.
+
+ Source: https://core.telegram.org/bots/api#videoquality
+ """
+
+ file_id: str
+ """Identifier for this file, which can be used to download or reuse the file"""
+ file_unique_id: str
+ """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file."""
+ width: int
+ """Video width"""
+ height: int
+ """Video height"""
+ codec: str
+ """Codec that was used to encode the video, for example, 'h264', 'h265', or 'av01'"""
+ file_size: int | None = None
+ """*Optional*. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value."""
+
+ if TYPE_CHECKING:
+ # DO NOT EDIT MANUALLY!!!
+ # This section was auto-generated via `butcher`
+
+ def __init__(
+ __pydantic__self__,
+ *,
+ file_id: str,
+ file_unique_id: str,
+ width: int,
+ height: int,
+ codec: str,
+ file_size: int | None = None,
+ **__pydantic_kwargs: Any,
+ ) -> None:
+ # DO NOT EDIT MANUALLY!!!
+ # This method was auto-generated via `butcher`
+ # Is needed only for type checking and IDE support without any additional plugins
+
+ super().__init__(
+ file_id=file_id,
+ file_unique_id=file_unique_id,
+ width=width,
+ height=height,
+ codec=codec,
+ file_size=file_size,
+ **__pydantic_kwargs,
+ )
diff --git a/aiogram/utils/deep_linking.py b/aiogram/utils/deep_linking.py
index 4892ca5a..1de7581e 100644
--- a/aiogram/utils/deep_linking.py
+++ b/aiogram/utils/deep_linking.py
@@ -22,6 +22,7 @@ if TYPE_CHECKING:
from aiogram import Bot
BAD_PATTERN = re.compile(r"[^a-zA-Z0-9-_]")
+DEEPLINK_PAYLOAD_LENGTH = 64
async def create_start_link(
@@ -145,8 +146,8 @@ def create_deep_link(
)
raise ValueError(msg)
- if len(payload) > 64:
- msg = "Payload must be up to 64 characters long."
+ if len(payload) > DEEPLINK_PAYLOAD_LENGTH:
+ msg = f"Payload must be up to {DEEPLINK_PAYLOAD_LENGTH} characters long."
raise ValueError(msg)
if not app_name:
diff --git a/aiogram/utils/mixins.py b/aiogram/utils/mixins.py
index 15d94cd9..70d90cc4 100644
--- a/aiogram/utils/mixins.py
+++ b/aiogram/utils/mixins.py
@@ -15,7 +15,7 @@ class DataMixin:
data: dict[str, Any] | None = getattr(self, "_data", None)
if data is None:
data = {}
- setattr(self, "_data", data)
+ self._data = data
return data
def __getitem__(self, key: str) -> Any:
diff --git a/docs/api/enums/button_style.rst b/docs/api/enums/button_style.rst
new file mode 100644
index 00000000..d9ac08ca
--- /dev/null
+++ b/docs/api/enums/button_style.rst
@@ -0,0 +1,9 @@
+###########
+ButtonStyle
+###########
+
+
+.. automodule:: aiogram.enums.button_style
+ :members:
+ :member-order: bysource
+ :undoc-members: True
diff --git a/docs/api/enums/index.rst b/docs/api/enums/index.rst
index be84c782..8082a207 100644
--- a/docs/api/enums/index.rst
+++ b/docs/api/enums/index.rst
@@ -11,6 +11,7 @@ Here is list of all available enums:
:maxdepth: 1
bot_command_scope_type
+ button_style
chat_action
chat_boost_source_type
chat_member_status
diff --git a/docs/api/methods/get_user_profile_audios.rst b/docs/api/methods/get_user_profile_audios.rst
new file mode 100644
index 00000000..f9567228
--- /dev/null
+++ b/docs/api/methods/get_user_profile_audios.rst
@@ -0,0 +1,38 @@
+####################
+getUserProfileAudios
+####################
+
+Returns: :obj:`UserProfileAudios`
+
+.. automodule:: aiogram.methods.get_user_profile_audios
+ :members:
+ :member-order: bysource
+ :undoc-members: True
+ :exclude-members: model_config,model_fields
+
+
+Usage
+=====
+
+As bot method
+-------------
+
+.. code-block::
+
+ result: UserProfileAudios = await bot.get_user_profile_audios(...)
+
+
+Method as object
+----------------
+
+Imports:
+
+- :code:`from aiogram.methods.get_user_profile_audios import GetUserProfileAudios`
+- alias: :code:`from aiogram.methods import GetUserProfileAudios`
+
+With specific bot
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: python
+
+ result: UserProfileAudios = await bot(GetUserProfileAudios(...))
diff --git a/docs/api/methods/index.rst b/docs/api/methods/index.rst
index a93ed67d..bbd6303a 100644
--- a/docs/api/methods/index.rst
+++ b/docs/api/methods/index.rst
@@ -82,6 +82,7 @@ Available methods
get_my_short_description
get_user_chat_boosts
get_user_gifts
+ get_user_profile_audios
get_user_profile_photos
gift_premium_subscription
hide_general_forum_topic
@@ -93,6 +94,7 @@ Available methods
read_business_message
remove_business_account_profile_photo
remove_chat_verification
+ remove_my_profile_photo
remove_user_verification
reopen_forum_topic
reopen_general_forum_topic
@@ -135,6 +137,7 @@ Available methods
set_my_default_administrator_rights
set_my_description
set_my_name
+ set_my_profile_photo
set_my_short_description
set_user_emoji_status
transfer_business_account_stars
diff --git a/docs/api/methods/remove_my_profile_photo.rst b/docs/api/methods/remove_my_profile_photo.rst
new file mode 100644
index 00000000..19d0d251
--- /dev/null
+++ b/docs/api/methods/remove_my_profile_photo.rst
@@ -0,0 +1,45 @@
+####################
+removeMyProfilePhoto
+####################
+
+Returns: :obj:`bool`
+
+.. automodule:: aiogram.methods.remove_my_profile_photo
+ :members:
+ :member-order: bysource
+ :undoc-members: True
+ :exclude-members: model_config,model_fields
+
+
+Usage
+=====
+
+As bot method
+-------------
+
+.. code-block::
+
+ result: bool = await bot.remove_my_profile_photo(...)
+
+
+Method as object
+----------------
+
+Imports:
+
+- :code:`from aiogram.methods.remove_my_profile_photo import RemoveMyProfilePhoto`
+- alias: :code:`from aiogram.methods import RemoveMyProfilePhoto`
+
+With specific bot
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: python
+
+ result: bool = await bot(RemoveMyProfilePhoto(...))
+
+As reply into Webhook in handler
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: python
+
+ return RemoveMyProfilePhoto(...)
diff --git a/docs/api/methods/set_my_profile_photo.rst b/docs/api/methods/set_my_profile_photo.rst
new file mode 100644
index 00000000..c2290414
--- /dev/null
+++ b/docs/api/methods/set_my_profile_photo.rst
@@ -0,0 +1,45 @@
+#################
+setMyProfilePhoto
+#################
+
+Returns: :obj:`bool`
+
+.. automodule:: aiogram.methods.set_my_profile_photo
+ :members:
+ :member-order: bysource
+ :undoc-members: True
+ :exclude-members: model_config,model_fields
+
+
+Usage
+=====
+
+As bot method
+-------------
+
+.. code-block::
+
+ result: bool = await bot.set_my_profile_photo(...)
+
+
+Method as object
+----------------
+
+Imports:
+
+- :code:`from aiogram.methods.set_my_profile_photo import SetMyProfilePhoto`
+- alias: :code:`from aiogram.methods import SetMyProfilePhoto`
+
+With specific bot
+~~~~~~~~~~~~~~~~~
+
+.. code-block:: python
+
+ result: bool = await bot(SetMyProfilePhoto(...))
+
+As reply into Webhook in handler
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. code-block:: python
+
+ return SetMyProfilePhoto(...)
diff --git a/docs/api/types/chat_owner_changed.rst b/docs/api/types/chat_owner_changed.rst
new file mode 100644
index 00000000..b87b0f84
--- /dev/null
+++ b/docs/api/types/chat_owner_changed.rst
@@ -0,0 +1,10 @@
+################
+ChatOwnerChanged
+################
+
+
+.. automodule:: aiogram.types.chat_owner_changed
+ :members:
+ :member-order: bysource
+ :undoc-members: True
+ :exclude-members: model_config,model_fields
diff --git a/docs/api/types/chat_owner_left.rst b/docs/api/types/chat_owner_left.rst
new file mode 100644
index 00000000..1cca68c3
--- /dev/null
+++ b/docs/api/types/chat_owner_left.rst
@@ -0,0 +1,10 @@
+#############
+ChatOwnerLeft
+#############
+
+
+.. automodule:: aiogram.types.chat_owner_left
+ :members:
+ :member-order: bysource
+ :undoc-members: True
+ :exclude-members: model_config,model_fields
diff --git a/docs/api/types/index.rst b/docs/api/types/index.rst
index e48d11b5..8cfa8a45 100644
--- a/docs/api/types/index.rst
+++ b/docs/api/types/index.rst
@@ -67,6 +67,8 @@ Available types
chat_member_owner
chat_member_restricted
chat_member_updated
+ chat_owner_changed
+ chat_owner_left
chat_permissions
chat_photo
chat_shared
@@ -199,6 +201,7 @@ Available types
unique_gift_symbol
user
user_chat_boosts
+ user_profile_audios
user_profile_photos
user_rating
user_shared
@@ -210,6 +213,7 @@ Available types
video_chat_scheduled
video_chat_started
video_note
+ video_quality
voice
web_app_data
web_app_info
diff --git a/docs/api/types/user_profile_audios.rst b/docs/api/types/user_profile_audios.rst
new file mode 100644
index 00000000..2c4b12e0
--- /dev/null
+++ b/docs/api/types/user_profile_audios.rst
@@ -0,0 +1,10 @@
+#################
+UserProfileAudios
+#################
+
+
+.. automodule:: aiogram.types.user_profile_audios
+ :members:
+ :member-order: bysource
+ :undoc-members: True
+ :exclude-members: model_config,model_fields
diff --git a/docs/api/types/video_quality.rst b/docs/api/types/video_quality.rst
new file mode 100644
index 00000000..a77a9cf3
--- /dev/null
+++ b/docs/api/types/video_quality.rst
@@ -0,0 +1,10 @@
+############
+VideoQuality
+############
+
+
+.. automodule:: aiogram.types.video_quality
+ :members:
+ :member-order: bysource
+ :undoc-members: True
+ :exclude-members: model_config,model_fields
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 558601ab..4b9f6c1f 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -148,8 +148,8 @@ When using :code:`uv`, prefix commands with :code:`uv run` to execute them in th
.. code-block:: bash
# Format code
- uv run black aiogram tests examples
- uv run isort aiogram tests examples
+ uv run ruff format aiogram tests scripts examples
+ uv run ruff check --fix aiogram tests scripts examples
# Run tests
uv run pytest tests
@@ -180,22 +180,22 @@ implementing new features or experimenting.
Format the code (code-style)
----------------------------
-Note that this project is Black-formatted, so you should follow that code-style,
-too be sure You're correctly doing this let's reformat the code automatically:
+Note that this project uses Ruff for formatting and linting, so you should follow that code-style.
+To be sure you're correctly doing this, let's reformat the code automatically:
Using traditional approach:
.. code-block:: bash
- black aiogram tests examples
- isort aiogram tests examples
+ ruff format aiogram tests scripts examples
+ ruff check --fix aiogram tests scripts examples
Or with uv:
.. code-block:: bash
- uv run black aiogram tests examples
- uv run isort aiogram tests examples
+ uv run ruff format aiogram tests scripts examples
+ uv run ruff check --fix aiogram tests scripts examples
Or simply use Makefile:
diff --git a/docs/migration_2_to_3.rst b/docs/migration_2_to_3.rst
index 1a4fc84a..ca7367a9 100644
--- a/docs/migration_2_to_3.rst
+++ b/docs/migration_2_to_3.rst
@@ -94,9 +94,61 @@ Bot API
However, all errors are classified by HTTP status codes, and for each method,
only one type of error can be associated with a given code.
Therefore, in most cases, you should check only the error type (by status code)
- without inspecting the error message.
+ without inspecting the error message. More details can be found in the
+ :ref:`exceptions section » `.
+Exceptions
+==========
+
+Mapping (v2 -> v3)
+-------------------
+
+- RetryAfter -> :class:`TelegramRetryAfter` (:mod:`aiogram.exceptions`)
+ - Important attribute in v3: ``retry_after`` (int).
+
+- ChatMigrated / MigrateToChat -> :class:`TelegramMigrateToChat`
+ - Important attribute in v3: ``migrate_to_chat_id`` (int).
+
+- ClientDecodeError -> :class:`ClientDecodeError`
+ - Important attributes in v3: ``original`` (Exception) and ``data`` (response body).
+
+- BadRequest -> :class:`TelegramBadRequest`
+- Unauthorized -> :class:`TelegramUnauthorizedError`
+- Forbidden -> :class:`TelegramForbiddenError`
+- NotFound -> :class:`TelegramNotFound`
+- Conflict -> :class:`TelegramConflictError`
+- ServerError -> :class:`TelegramServerError`
+- NetworkError -> :class:`TelegramNetworkError`
+- EntityTooLarge -> :class:`TelegramEntityTooLarge`
+
+
+Exceptions removed in v3 (from v2)
+----------------------------------
+
+The list below contains common exception names that appeared in aiogram v2 but
+are not defined as separate classes in the v3 codebase. For each v2 name, a
+recommended v3 replacement (or handling) is provided — keep your migration
+logic simple and rely on the v3 exception classes and their attributes.
+
+- MessageNotModified -> :class:`TelegramBadRequest`
+- MessageToEditNotFound -> :class:`TelegramNotFound`
+- MessageToDeleteNotFound -> :class:`TelegramNotFound`
+- MessageCantBeDeleted -> :class:`TelegramForbiddenError` / :class:`TelegramBadRequest`
+- CantParseEntities -> :class:`TelegramBadRequest`
+- MessageIsTooLong -> :class:`TelegramEntityTooLarge`
+- MessageIdentifierNotFound -> :class:`TelegramNotFound`
+- UserDeactivated -> :class:`TelegramForbiddenError`
+- CantInitiateConversation -> :class:`TelegramBadRequest`
+- StickerSetNameInvalid -> :class:`TelegramBadRequest`
+- ChatAdminRequired -> :class:`TelegramForbiddenError`
+
+Use these replacements when migrating exception handling from v2 to v3. If
+you relied on catching very specific v2 exception classes, replace those
+handlers with the corresponding v3 class above (or catch a broader v3 class
+such as :class:`TelegramBadRequest` / :class:`TelegramAPIError`) and inspect
+available attributes (see "Mapping (v2 -> v3)") for any required details.
+
Middlewares
===========
diff --git a/examples/multi_file_bot/bot.py b/examples/multi_file_bot/bot.py
index 0f1ba083..709500be 100644
--- a/examples/multi_file_bot/bot.py
+++ b/examples/multi_file_bot/bot.py
@@ -2,12 +2,11 @@ import asyncio
import logging
from os import getenv
-from handlers.echo import echo_router
-from handlers.start import start_router
-
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums import ParseMode
+from handlers.echo import echo_router
+from handlers.start import start_router
# Bot token can be obtained via https://t.me/BotFather
TOKEN = getenv("BOT_TOKEN")
diff --git a/examples/multibot.py b/examples/multibot.py
index e8de6794..2dc2de1a 100644
--- a/examples/multibot.py
+++ b/examples/multibot.py
@@ -4,7 +4,6 @@ from os import getenv
from typing import Any
from aiohttp import web
-from finite_state_machine import form_router
from aiogram import Bot, Dispatcher, F, Router
from aiogram.client.session.aiohttp import AiohttpSession
@@ -19,6 +18,7 @@ from aiogram.webhook.aiohttp_server import (
TokenBasedRequestHandler,
setup_application,
)
+from finite_state_machine import form_router
main_router = Router()
diff --git a/examples/web_app/main.py b/examples/web_app/main.py
index fbbbcff1..1e1938c6 100644
--- a/examples/web_app/main.py
+++ b/examples/web_app/main.py
@@ -4,14 +4,14 @@ from os import getenv
from aiohttp.web import run_app
from aiohttp.web_app import Application
-from handlers import my_router
-from routes import check_data_handler, demo_handler, send_message_handler
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.enums.parse_mode import ParseMode
from aiogram.types import MenuButtonWebApp, WebAppInfo
from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application
+from handlers import my_router
+from routes import check_data_handler, demo_handler, send_message_handler
TOKEN = getenv("BOT_TOKEN")
diff --git a/pyproject.toml b/pyproject.toml
index 122ae13f..ca0c8c4d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -94,8 +94,6 @@ docs = [
[dependency-groups]
dev = [
- "black~=25.9",
- "isort~=7.0",
"ruff~=0.14",
"mypy==1.10.1",
"toml~=0.10.2",
@@ -139,16 +137,38 @@ exclude = [
[tool.ruff.lint]
select = [
# "C", # TODO: mccabe - code complecity
- "C4",
- "E",
- "F",
- "Q",
- "RET",
- "T10",
- "T20",
+ "A", # flake8-annotations
+ "B", # flake8-bugbear
+ "C4", # flake8-comprehensions
+ "DTZ", # flake8-datetimez
+ "E", # pycodestyle errors
+ "F", # pyflakes
+ "I", # isort
+ "PERF", # perflint
+ "PL", # pylint
+ "Q", # flake8-quotes
+ "RET", # flake8-return
+ "SIM", # flake8-simplify
+ "T10", # flake8-debugger
+ "T20", # flake8-print
+ "UP", # pyupgrade
]
ignore = [
- "F401",
+ "F401", # unused-import (handled by other tools)
+ "A002", # builtin-argument-shadowing (common in API: type, id, format, etc.)
+ "A005", # stdlib-module-shadowing (Module `types` shadows a Python standard-library module)
+ "B008", # function-call-in-default-argument (Pydantic Field() defaults)
+ "PLC0415", # import-outside-top-level (needed for circular imports)
+ "PLC1901", # compare-to-empty-string (sometimes more explicit is better)
+ "PLR0904", # too-many-public-methods (Bot class has many API methods)
+ "PLR0911", # too-many-return-statements
+ "PLR0912", # too-many-branches
+ "PLR0913", # too-many-arguments (Telegram API methods have many params)
+ "PLR0915", # too-many-statements
+ "PLR0917", # too-many-positional-arguments (Telegram API design)
+ "PLR6301", # no-self-use (sometimes methods are intentionally not static)
+ "PLW2901", # redefined-loop-name (intentional pattern in this codebase)
+ "PLW3201", # bad-dunder-method-name (custom dunders for framework design)
]
[tool.ruff.lint.isort]
@@ -164,6 +184,20 @@ known-first-party = [
"aiogram/types/*" = ["E501"]
"aiogram/methods/*" = ["E501"]
"aiogram/enums/*" = ["E501"]
+"tests/**" = [
+ "PLR0124",
+ "PLR2004",
+ "DTZ005",
+ "DTZ006",
+ "A001",
+ "A004",
+ "B018",
+ "B020",
+ "B904",
+ "E501",
+ "F821",
+ "UP",
+]
[tool.pytest.ini_options]
@@ -174,7 +208,6 @@ testpaths = [
filterwarnings = [
"error",
"ignore::pytest.PytestUnraisableExceptionWarning",
-
# Remove when uvloop fixes the issue
# https://github.com/MagicStack/uvloop/issues/703
# https://github.com/MagicStack/uvloop/pull/705
@@ -235,23 +268,10 @@ module = [
ignore_missing_imports = true
disallow_untyped_defs = true
-[tool.black]
-line-length = 99
-target-version = ['py310', 'py311', 'py312', 'py313']
-exclude = '''
-(
- \.eggs
- | \.git
- | \.tox
- | build
- | dist
- | venv
- | docs
-)
-'''
-
-[tool.isort]
-profile = "black"
+[tool.ruff.format]
+quote-style = "double"
+indent-style = "space"
+line-ending = "auto"
[tool.towncrier]
package = "aiogram"
diff --git a/scripts/bump_version.py b/scripts/bump_version.py
index 92485406..3dc586c7 100644
--- a/scripts/bump_version.py
+++ b/scripts/bump_version.py
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
"""Version bumping script for aiogram (replaces hatch version)."""
+
import re
import sys
from pathlib import Path
@@ -44,9 +45,9 @@ def bump_version(part: str) -> str:
if __name__ == "__main__":
- if len(sys.argv) != 2:
- print("Usage: python scripts/bump_version.py [major|minor|patch|to:X.Y.Z]")
+ if len(sys.argv) != 2: # noqa: PLR2004
+ print("Usage: python scripts/bump_version.py [major|minor|patch|to:X.Y.Z]") # noqa: T201
sys.exit(1)
new_version = bump_version(sys.argv[1])
- print(f"Bumped version to {new_version}")
+ print(f"Bumped version to {new_version}") # noqa: T201
diff --git a/scripts/bump_versions.py b/scripts/bump_versions.py
index 2cd37642..7174f422 100644
--- a/scripts/bump_versions.py
+++ b/scripts/bump_versions.py
@@ -45,8 +45,7 @@ def get_telegram_api_version() -> str:
def replace_line(content: str, pattern: re.Pattern, new_value: str) -> str:
- result = pattern.sub(f"\\g<1>{new_value}\\g<2>", content)
- return result
+ return pattern.sub(f"\\g<1>{new_value}\\g<2>", content)
def write_package_meta(api_version: str) -> None:
@@ -55,7 +54,7 @@ def write_package_meta(api_version: str) -> None:
content = replace_line(content, API_VERSION, api_version)
- print(f"Write {path}")
+ print(f"Write {path}") # noqa: T201
path.write_text(content)
@@ -64,7 +63,7 @@ def write_readme(api_version: str) -> None:
content = path.read_text()
content = replace_line(content, API_VERSION_BADGE, api_version)
content = replace_line(content, API_VERSION_LINE, api_version)
- print(f"Write {path}")
+ print(f"Write {path}") # noqa: T201
path.write_text(content)
@@ -72,14 +71,14 @@ def write_docs_index(api_version: str) -> None:
path = Path.cwd() / "docs" / "index.rst"
content = path.read_text()
content = replace_line(content, API_VERSION_BADGE, api_version)
- print(f"Write {path}")
+ print(f"Write {path}") # noqa: T201
path.write_text(content)
def main():
api_version = get_telegram_api_version()
- print(f"Telegram Bot API version: {api_version}")
+ print(f"Telegram Bot API version: {api_version}") # noqa: T201
write_package_meta(api_version=api_version)
write_readme(api_version=api_version)
write_docs_index(api_version=api_version)
diff --git a/tests/conftest.py b/tests/conftest.py
index 5034cde5..9632a5c1 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -144,8 +144,7 @@ async def memory_storage():
@pytest.fixture()
async def redis_isolation(redis_storage):
- isolation = redis_storage.create_isolation()
- return isolation
+ return redis_storage.create_isolation()
@pytest.fixture()
diff --git a/tests/deprecated.py b/tests/deprecated.py
index f030e515..138ef190 100644
--- a/tests/deprecated.py
+++ b/tests/deprecated.py
@@ -1,5 +1,4 @@
from contextlib import contextmanager
-from typing import Type
import pytest
from packaging import version
@@ -10,8 +9,8 @@ import aiogram
@contextmanager
def check_deprecated(
max_version: str,
- exception: Type[Exception],
- warning: Type[Warning] = DeprecationWarning,
+ exception: type[Exception],
+ warning: type[Warning] = DeprecationWarning,
) -> None:
"""
Should be used for modules that are being deprecated or already removed from aiogram
diff --git a/tests/mocked_bot.py b/tests/mocked_bot.py
index cd137aee..b7007029 100644
--- a/tests/mocked_bot.py
+++ b/tests/mocked_bot.py
@@ -1,5 +1,6 @@
from collections import deque
-from typing import TYPE_CHECKING, Any, AsyncGenerator, Deque, Dict, Optional, Type
+from collections.abc import AsyncGenerator
+from typing import TYPE_CHECKING, Any
from aiogram import Bot
from aiogram.client.session.base import BaseSession
@@ -10,9 +11,9 @@ from aiogram.types import UNSET_PARSE_MODE, ResponseParameters, User
class MockedSession(BaseSession):
def __init__(self):
- super(MockedSession, self).__init__()
- self.responses: Deque[Response[TelegramType]] = deque()
- self.requests: Deque[TelegramMethod[TelegramType]] = deque()
+ super().__init__()
+ self.responses: deque[Response[TelegramType]] = deque()
+ self.requests: deque[TelegramMethod[TelegramType]] = deque()
self.closed = True
def add_result(self, response: Response[TelegramType]) -> Response[TelegramType]:
@@ -29,7 +30,7 @@ class MockedSession(BaseSession):
self,
bot: Bot,
method: TelegramMethod[TelegramType],
- timeout: Optional[int] = UNSET_PARSE_MODE,
+ timeout: int | None = UNSET_PARSE_MODE,
) -> TelegramType:
self.closed = False
self.requests.append(method)
@@ -45,7 +46,7 @@ class MockedSession(BaseSession):
async def stream_content(
self,
url: str,
- headers: Optional[Dict[str, Any]] = None,
+ headers: dict[str, Any] | None = None,
timeout: int = 30,
chunk_size: int = 65536,
raise_for_status: bool = True,
@@ -58,9 +59,7 @@ class MockedBot(Bot):
session: MockedSession
def __init__(self, **kwargs):
- super(MockedBot, self).__init__(
- kwargs.pop("token", "42:TEST"), session=MockedSession(), **kwargs
- )
+ super().__init__(kwargs.pop("token", "42:TEST"), session=MockedSession(), **kwargs)
self._me = User(
id=self.id,
is_bot=True,
@@ -72,13 +71,13 @@ class MockedBot(Bot):
def add_result_for(
self,
- method: Type[TelegramMethod[TelegramType]],
+ method: type[TelegramMethod[TelegramType]],
ok: bool,
result: TelegramType = None,
- description: Optional[str] = None,
+ description: str | None = None,
error_code: int = 200,
- migrate_to_chat_id: Optional[int] = None,
- retry_after: Optional[int] = None,
+ migrate_to_chat_id: int | None = None,
+ retry_after: int | None = None,
) -> Response[TelegramType]:
response = Response[method.__returning__]( # type: ignore
ok=ok,
diff --git a/tests/test_api/test_client/test_session/test_aiohttp_session.py b/tests/test_api/test_client/test_session/test_aiohttp_session.py
index bc0239df..a10b6a2e 100644
--- a/tests/test_api/test_client/test_session/test_aiohttp_session.py
+++ b/tests/test_api/test_client/test_session/test_aiohttp_session.py
@@ -1,12 +1,8 @@
import asyncio
+from collections.abc import AsyncGenerator, AsyncIterable
from typing import (
Any,
AsyncContextManager,
- AsyncGenerator,
- AsyncIterable,
- Dict,
- List,
- Union,
)
from unittest.mock import AsyncMock, patch
@@ -115,10 +111,10 @@ class TestAiohttpSession:
str_: str
int_: int
bool_: bool
- unset_: Union[str, Default] = Default("parse_mode")
+ unset_: str | Default = Default("parse_mode")
null_: None
- list_: List[str]
- dict_: Dict[str, Any]
+ list_: list[str]
+ dict_: dict[str, Any]
session = AiohttpSession()
form = session.build_form_data(
diff --git a/tests/test_api/test_client/test_session/test_base_session.py b/tests/test_api/test_client/test_session/test_base_session.py
index 6bca619c..46d6bbc7 100644
--- a/tests/test_api/test_client/test_session/test_base_session.py
+++ b/tests/test_api/test_client/test_session/test_base_session.py
@@ -1,6 +1,7 @@
import datetime
import json
-from typing import Any, AsyncContextManager, AsyncGenerator, Dict, Optional
+from collections.abc import AsyncGenerator
+from typing import Any, AsyncContextManager
from unittest.mock import AsyncMock, patch
import pytest
@@ -39,7 +40,7 @@ class CustomSession(BaseSession):
self,
token: str,
method: TelegramMethod[TelegramType],
- timeout: Optional[int] = UNSET_PARSE_MODE,
+ timeout: int | None = UNSET_PARSE_MODE,
) -> None: # type: ignore
assert isinstance(token, str)
assert isinstance(method, TelegramMethod)
@@ -47,7 +48,7 @@ class CustomSession(BaseSession):
async def stream_content(
self,
url: str,
- headers: Optional[Dict[str, Any]] = None,
+ headers: dict[str, Any] | None = None,
timeout: int = 30,
chunk_size: int = 65536,
raise_for_status: bool = True,
diff --git a/tests/test_api/test_methods/test_add_sticker_to_set.py b/tests/test_api/test_methods/test_add_sticker_to_set.py
index 84b7a9a8..bf388bf4 100644
--- a/tests/test_api/test_methods/test_add_sticker_to_set.py
+++ b/tests/test_api/test_methods/test_add_sticker_to_set.py
@@ -15,5 +15,5 @@ class TestAddStickerToSet:
sticker="file id", format=StickerFormat.STATIC, emoji_list=[":)"]
),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_answer_callback_query.py b/tests/test_api/test_methods/test_answer_callback_query.py
index 11e01b1e..8565237f 100644
--- a/tests/test_api/test_methods/test_answer_callback_query.py
+++ b/tests/test_api/test_methods/test_answer_callback_query.py
@@ -7,5 +7,5 @@ class TestAnswerCallbackQuery:
prepare_result = bot.add_result_for(AnswerCallbackQuery, ok=True, result=True)
response: bool = await bot.answer_callback_query(callback_query_id="cq id", text="OK")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_answer_inline_query.py b/tests/test_api/test_methods/test_answer_inline_query.py
index f634ff1a..da9d8c0a 100644
--- a/tests/test_api/test_methods/test_answer_inline_query.py
+++ b/tests/test_api/test_methods/test_answer_inline_query.py
@@ -17,5 +17,5 @@ class TestAnswerInlineQuery:
)
],
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_answer_pre_checkout_query.py b/tests/test_api/test_methods/test_answer_pre_checkout_query.py
index 09887aff..c31b1633 100644
--- a/tests/test_api/test_methods/test_answer_pre_checkout_query.py
+++ b/tests/test_api/test_methods/test_answer_pre_checkout_query.py
@@ -9,5 +9,5 @@ class TestAnswerPreCheckoutQuery:
response: bool = await bot.answer_pre_checkout_query(
pre_checkout_query_id="query id", ok=True
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_answer_shipping_query.py b/tests/test_api/test_methods/test_answer_shipping_query.py
index 3d8b2a68..5c1d8550 100644
--- a/tests/test_api/test_methods/test_answer_shipping_query.py
+++ b/tests/test_api/test_methods/test_answer_shipping_query.py
@@ -7,5 +7,5 @@ class TestAnswerShippingQuery:
prepare_result = bot.add_result_for(AnswerShippingQuery, ok=True, result=True)
response: bool = await bot.answer_shipping_query(shipping_query_id="query id", ok=True)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_answer_web_app_query.py b/tests/test_api/test_methods/test_answer_web_app_query.py
index a42640a8..f38d0938 100644
--- a/tests/test_api/test_methods/test_answer_web_app_query.py
+++ b/tests/test_api/test_methods/test_answer_web_app_query.py
@@ -15,5 +15,5 @@ class TestAnswerWebAppQuery:
thumbnail_url="test",
),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_approve_chat_join_request.py b/tests/test_api/test_methods/test_approve_chat_join_request.py
index f4e035b7..cea8c17f 100755
--- a/tests/test_api/test_methods/test_approve_chat_join_request.py
+++ b/tests/test_api/test_methods/test_approve_chat_join_request.py
@@ -10,5 +10,5 @@ class TestApproveChatJoinRequest:
chat_id=-42,
user_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_approve_suggested_post.py b/tests/test_api/test_methods/test_approve_suggested_post.py
index e652c47d..8d84f5fe 100644
--- a/tests/test_api/test_methods/test_approve_suggested_post.py
+++ b/tests/test_api/test_methods/test_approve_suggested_post.py
@@ -10,5 +10,5 @@ class TestApproveSuggestedPost:
chat_id=-42,
message_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_ban_chat_member.py b/tests/test_api/test_methods/test_ban_chat_member.py
index 1e81a01b..6809a596 100644
--- a/tests/test_api/test_methods/test_ban_chat_member.py
+++ b/tests/test_api/test_methods/test_ban_chat_member.py
@@ -7,5 +7,5 @@ class TestKickChatMember:
prepare_result = bot.add_result_for(BanChatMember, ok=True, result=True)
response: bool = await bot.ban_chat_member(chat_id=-42, user_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_ban_chat_sender_chat.py b/tests/test_api/test_methods/test_ban_chat_sender_chat.py
index 237516f5..feefbc8d 100755
--- a/tests/test_api/test_methods/test_ban_chat_sender_chat.py
+++ b/tests/test_api/test_methods/test_ban_chat_sender_chat.py
@@ -10,5 +10,5 @@ class TestBanChatSenderChat:
chat_id=-42,
sender_chat_id=-1337,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_close.py b/tests/test_api/test_methods/test_close.py
index 6944ca2a..fe2602e2 100644
--- a/tests/test_api/test_methods/test_close.py
+++ b/tests/test_api/test_methods/test_close.py
@@ -7,5 +7,5 @@ class TestClose:
prepare_result = bot.add_result_for(Close, ok=True, result=True)
response: bool = await bot.close()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_close_forum_topic.py b/tests/test_api/test_methods/test_close_forum_topic.py
index 44644e31..527ed511 100644
--- a/tests/test_api/test_methods/test_close_forum_topic.py
+++ b/tests/test_api/test_methods/test_close_forum_topic.py
@@ -10,5 +10,5 @@ class TestCloseForumTopic:
chat_id=42,
message_thread_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_close_general_forum_topic.py b/tests/test_api/test_methods/test_close_general_forum_topic.py
index f73e3b47..45133499 100644
--- a/tests/test_api/test_methods/test_close_general_forum_topic.py
+++ b/tests/test_api/test_methods/test_close_general_forum_topic.py
@@ -7,5 +7,5 @@ class TestCloseGeneralForumTopic:
prepare_result = bot.add_result_for(CloseGeneralForumTopic, ok=True, result=True)
response: bool = await bot.close_general_forum_topic(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_convert_gift_to_stars.py b/tests/test_api/test_methods/test_convert_gift_to_stars.py
index 4d9182b5..37fb4aa1 100644
--- a/tests/test_api/test_methods/test_convert_gift_to_stars.py
+++ b/tests/test_api/test_methods/test_convert_gift_to_stars.py
@@ -9,5 +9,5 @@ class TestConvertGiftToStars:
response: bool = await bot.convert_gift_to_stars(
business_connection_id="test_connection_id", owned_gift_id="test_gift_id"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_copy_message.py b/tests/test_api/test_methods/test_copy_message.py
index 0b9a762a..f9425998 100644
--- a/tests/test_api/test_methods/test_copy_message.py
+++ b/tests/test_api/test_methods/test_copy_message.py
@@ -12,5 +12,5 @@ class TestCopyMessage:
from_chat_id=42,
message_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_create_chat_invite_link.py b/tests/test_api/test_methods/test_create_chat_invite_link.py
index 5e8216d3..8247d8fc 100644
--- a/tests/test_api/test_methods/test_create_chat_invite_link.py
+++ b/tests/test_api/test_methods/test_create_chat_invite_link.py
@@ -20,5 +20,5 @@ class TestCreateChatInviteLink:
response: ChatInviteLink = await bot.create_chat_invite_link(
chat_id=-42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_create_chat_subscruption_invite_link.py b/tests/test_api/test_methods/test_create_chat_subscruption_invite_link.py
index 66b002eb..7d8b4d87 100644
--- a/tests/test_api/test_methods/test_create_chat_subscruption_invite_link.py
+++ b/tests/test_api/test_methods/test_create_chat_subscruption_invite_link.py
@@ -24,5 +24,5 @@ class TestCreateChatSubscriptionInviteLink:
subscription_period=timedelta(days=30),
subscription_price=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_create_forum_topic.py b/tests/test_api/test_methods/test_create_forum_topic.py
index e39882c2..23e7ea87 100644
--- a/tests/test_api/test_methods/test_create_forum_topic.py
+++ b/tests/test_api/test_methods/test_create_forum_topic.py
@@ -15,5 +15,5 @@ class TestCreateForumTopic:
chat_id=42,
name="test",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_create_invoice_link.py b/tests/test_api/test_methods/test_create_invoice_link.py
index 97112036..9de395a4 100644
--- a/tests/test_api/test_methods/test_create_invoice_link.py
+++ b/tests/test_api/test_methods/test_create_invoice_link.py
@@ -17,5 +17,5 @@ class TestCreateInvoiceLink:
currency="BTC",
prices=[LabeledPrice(label="Test", amount=1)],
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_create_new_sticker_set.py b/tests/test_api/test_methods/test_create_new_sticker_set.py
index cdddc933..d5bfbd65 100644
--- a/tests/test_api/test_methods/test_create_new_sticker_set.py
+++ b/tests/test_api/test_methods/test_create_new_sticker_set.py
@@ -20,5 +20,5 @@ class TestCreateNewStickerSet:
],
sticker_format=StickerFormat.STATIC,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_decline_chat_join_request.py b/tests/test_api/test_methods/test_decline_chat_join_request.py
index 9da77257..5a5cf4a5 100755
--- a/tests/test_api/test_methods/test_decline_chat_join_request.py
+++ b/tests/test_api/test_methods/test_decline_chat_join_request.py
@@ -10,5 +10,5 @@ class TestDeclineChatJoinRequest:
chat_id=-42,
user_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_decline_suggested_post.py b/tests/test_api/test_methods/test_decline_suggested_post.py
index 6edb190b..8509c943 100644
--- a/tests/test_api/test_methods/test_decline_suggested_post.py
+++ b/tests/test_api/test_methods/test_decline_suggested_post.py
@@ -10,5 +10,5 @@ class TestDeclineSuggestedPost:
chat_id=-42,
message_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_business_messages.py b/tests/test_api/test_methods/test_delete_business_messages.py
index 060dcf94..6f39e599 100644
--- a/tests/test_api/test_methods/test_delete_business_messages.py
+++ b/tests/test_api/test_methods/test_delete_business_messages.py
@@ -9,5 +9,5 @@ class TestDeleteBusinessMessages:
response: bool = await bot.delete_business_messages(
business_connection_id="test_connection_id", message_ids=[1, 2, 3]
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_chat_photo.py b/tests/test_api/test_methods/test_delete_chat_photo.py
index dd9b0b9c..954bc9fc 100644
--- a/tests/test_api/test_methods/test_delete_chat_photo.py
+++ b/tests/test_api/test_methods/test_delete_chat_photo.py
@@ -7,5 +7,5 @@ class TestDeleteChatPhoto:
prepare_result = bot.add_result_for(DeleteChatPhoto, ok=True, result=True)
response: bool = await bot.delete_chat_photo(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_chat_sticker_set.py b/tests/test_api/test_methods/test_delete_chat_sticker_set.py
index 5de60460..611fb5e9 100644
--- a/tests/test_api/test_methods/test_delete_chat_sticker_set.py
+++ b/tests/test_api/test_methods/test_delete_chat_sticker_set.py
@@ -7,5 +7,5 @@ class TestDeleteChatStickerSet:
prepare_result = bot.add_result_for(DeleteChatStickerSet, ok=True, result=True)
response: bool = await bot.delete_chat_sticker_set(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_forum_topic.py b/tests/test_api/test_methods/test_delete_forum_topic.py
index ad14ba61..9af1b271 100644
--- a/tests/test_api/test_methods/test_delete_forum_topic.py
+++ b/tests/test_api/test_methods/test_delete_forum_topic.py
@@ -10,5 +10,5 @@ class TestDeleteForumTopic:
chat_id=42,
message_thread_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_message.py b/tests/test_api/test_methods/test_delete_message.py
index 67c9d767..e8531e34 100644
--- a/tests/test_api/test_methods/test_delete_message.py
+++ b/tests/test_api/test_methods/test_delete_message.py
@@ -7,5 +7,5 @@ class TestDeleteMessage:
prepare_result = bot.add_result_for(DeleteMessage, ok=True, result=True)
response: bool = await bot.delete_message(chat_id=42, message_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_my_commands.py b/tests/test_api/test_methods/test_delete_my_commands.py
index 9aff0d71..c771c4e2 100644
--- a/tests/test_api/test_methods/test_delete_my_commands.py
+++ b/tests/test_api/test_methods/test_delete_my_commands.py
@@ -7,5 +7,5 @@ class TestKickChatMember:
prepare_result = bot.add_result_for(DeleteMyCommands, ok=True, result=True)
response: bool = await bot.delete_my_commands()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_sticker_from_set.py b/tests/test_api/test_methods/test_delete_sticker_from_set.py
index 2bc0311f..a8e7b438 100644
--- a/tests/test_api/test_methods/test_delete_sticker_from_set.py
+++ b/tests/test_api/test_methods/test_delete_sticker_from_set.py
@@ -7,5 +7,5 @@ class TestDeleteStickerFromSet:
prepare_result = bot.add_result_for(DeleteStickerFromSet, ok=True, result=True)
response: bool = await bot.delete_sticker_from_set(sticker="sticker id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_sticker_set.py b/tests/test_api/test_methods/test_delete_sticker_set.py
index 1b864c75..2f7fd06d 100644
--- a/tests/test_api/test_methods/test_delete_sticker_set.py
+++ b/tests/test_api/test_methods/test_delete_sticker_set.py
@@ -7,5 +7,5 @@ class TestDeleteStickerSet:
prepare_result = bot.add_result_for(DeleteStickerSet, ok=True, result=True)
response: bool = await bot.delete_sticker_set(name="test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_story.py b/tests/test_api/test_methods/test_delete_story.py
index c205cc20..4d1295e2 100644
--- a/tests/test_api/test_methods/test_delete_story.py
+++ b/tests/test_api/test_methods/test_delete_story.py
@@ -9,5 +9,5 @@ class TestDeleteStory:
response: bool = await bot.delete_story(
business_connection_id="test_connection_id", story_id=42
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_delete_webhook.py b/tests/test_api/test_methods/test_delete_webhook.py
index 85c93d57..713302da 100644
--- a/tests/test_api/test_methods/test_delete_webhook.py
+++ b/tests/test_api/test_methods/test_delete_webhook.py
@@ -7,5 +7,5 @@ class TestDeleteWebhook:
prepare_result = bot.add_result_for(DeleteWebhook, ok=True, result=True)
response: bool = await bot.delete_webhook()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_chat_invite_link.py b/tests/test_api/test_methods/test_edit_chat_invite_link.py
index 9f1c0822..f79514c5 100644
--- a/tests/test_api/test_methods/test_edit_chat_invite_link.py
+++ b/tests/test_api/test_methods/test_edit_chat_invite_link.py
@@ -20,5 +20,5 @@ class TestEditChatInviteLink:
response: ChatInviteLink = await bot.edit_chat_invite_link(
chat_id=-42, invite_link="https://t.me/username", member_limit=1
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_chat_subscruption_invite_link.py b/tests/test_api/test_methods/test_edit_chat_subscruption_invite_link.py
index 7edbb032..c5853734 100644
--- a/tests/test_api/test_methods/test_edit_chat_subscruption_invite_link.py
+++ b/tests/test_api/test_methods/test_edit_chat_subscruption_invite_link.py
@@ -22,5 +22,5 @@ class TestEditChatSubscriptionInviteLink:
invite_link="https://t.me/username/test",
name="test",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_forum_topic.py b/tests/test_api/test_methods/test_edit_forum_topic.py
index e00cda20..d68945b8 100644
--- a/tests/test_api/test_methods/test_edit_forum_topic.py
+++ b/tests/test_api/test_methods/test_edit_forum_topic.py
@@ -12,5 +12,5 @@ class TestEditForumTopic:
name="test",
icon_custom_emoji_id="0",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_general_forum_topic.py b/tests/test_api/test_methods/test_edit_general_forum_topic.py
index 6d08f809..ff4e8493 100644
--- a/tests/test_api/test_methods/test_edit_general_forum_topic.py
+++ b/tests/test_api/test_methods/test_edit_general_forum_topic.py
@@ -7,5 +7,5 @@ class TestCloseGeneralForumTopic:
prepare_result = bot.add_result_for(EditGeneralForumTopic, ok=True, result=True)
response: bool = await bot.edit_general_forum_topic(chat_id=42, name="Test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_message_caption.py b/tests/test_api/test_methods/test_edit_message_caption.py
index 2aba0502..8713c673 100644
--- a/tests/test_api/test_methods/test_edit_message_caption.py
+++ b/tests/test_api/test_methods/test_edit_message_caption.py
@@ -1,5 +1,4 @@
import datetime
-from typing import Union
from aiogram.methods import EditMessageCaption
from aiogram.types import Chat, Message
@@ -19,6 +18,6 @@ class TestEditMessageCaption:
),
)
- response: Union[Message, bool] = await bot.edit_message_caption()
- request = bot.get_request()
+ response: Message | bool = await bot.edit_message_caption()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_message_checklist.py b/tests/test_api/test_methods/test_edit_message_checklist.py
index e154cf6f..e3c4dd02 100644
--- a/tests/test_api/test_methods/test_edit_message_checklist.py
+++ b/tests/test_api/test_methods/test_edit_message_checklist.py
@@ -31,5 +31,5 @@ class TestEditMessageChecklist:
message_id=42,
checklist=checklist,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_message_live_location.py b/tests/test_api/test_methods/test_edit_message_live_location.py
index 2ab8372e..89c6075e 100644
--- a/tests/test_api/test_methods/test_edit_message_live_location.py
+++ b/tests/test_api/test_methods/test_edit_message_live_location.py
@@ -1,5 +1,3 @@
-from typing import Union
-
from aiogram.methods import EditMessageLiveLocation
from aiogram.types import Message
from tests.mocked_bot import MockedBot
@@ -9,8 +7,8 @@ class TestEditMessageLiveLocation:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageLiveLocation, ok=True, result=True)
- response: Union[Message, bool] = await bot.edit_message_live_location(
+ response: Message | bool = await bot.edit_message_live_location(
latitude=3.141592, longitude=3.141592
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_message_media.py b/tests/test_api/test_methods/test_edit_message_media.py
index c4c1cfd5..562588f3 100644
--- a/tests/test_api/test_methods/test_edit_message_media.py
+++ b/tests/test_api/test_methods/test_edit_message_media.py
@@ -1,5 +1,3 @@
-from typing import Union
-
from aiogram.methods import EditMessageMedia
from aiogram.types import BufferedInputFile, InputMediaPhoto, Message
from tests.mocked_bot import MockedBot
@@ -9,8 +7,8 @@ class TestEditMessageMedia:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageMedia, ok=True, result=True)
- response: Union[Message, bool] = await bot.edit_message_media(
+ response: Message | bool = await bot.edit_message_media(
media=InputMediaPhoto(media=BufferedInputFile(b"", "photo.png"))
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_message_reply_markup.py b/tests/test_api/test_methods/test_edit_message_reply_markup.py
index 2e2e97e3..2a4438e6 100644
--- a/tests/test_api/test_methods/test_edit_message_reply_markup.py
+++ b/tests/test_api/test_methods/test_edit_message_reply_markup.py
@@ -1,5 +1,3 @@
-from typing import Union
-
from aiogram.methods import EditMessageReplyMarkup
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from tests.mocked_bot import MockedBot
@@ -9,7 +7,7 @@ class TestEditMessageReplyMarkup:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageReplyMarkup, ok=True, result=True)
- response: Union[Message, bool] = await bot.edit_message_reply_markup(
+ response: Message | bool = await bot.edit_message_reply_markup(
chat_id=42,
inline_message_id="inline message id",
reply_markup=InlineKeyboardMarkup(
@@ -18,5 +16,5 @@ class TestEditMessageReplyMarkup:
]
),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_message_text.py b/tests/test_api/test_methods/test_edit_message_text.py
index f4f33f4c..6c22624b 100644
--- a/tests/test_api/test_methods/test_edit_message_text.py
+++ b/tests/test_api/test_methods/test_edit_message_text.py
@@ -1,5 +1,3 @@
-from typing import Union
-
from aiogram.methods import EditMessageText
from aiogram.types import Message
from tests.mocked_bot import MockedBot
@@ -9,8 +7,8 @@ class TestEditMessageText:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(EditMessageText, ok=True, result=True)
- response: Union[Message, bool] = await bot.edit_message_text(
+ response: Message | bool = await bot.edit_message_text(
chat_id=42, inline_message_id="inline message id", text="text"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_story.py b/tests/test_api/test_methods/test_edit_story.py
index 30555974..2203b5e5 100644
--- a/tests/test_api/test_methods/test_edit_story.py
+++ b/tests/test_api/test_methods/test_edit_story.py
@@ -1,5 +1,3 @@
-import datetime
-
from aiogram.methods import EditStory
from aiogram.types import Chat, InputStoryContentPhoto, Story
from tests.mocked_bot import MockedBot
@@ -22,5 +20,5 @@ class TestEditStory:
content=InputStoryContentPhoto(type="photo", photo="test_photo"),
caption="Test caption",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_edit_user_star_subscription.py b/tests/test_api/test_methods/test_edit_user_star_subscription.py
index c534e781..695df9af 100644
--- a/tests/test_api/test_methods/test_edit_user_star_subscription.py
+++ b/tests/test_api/test_methods/test_edit_user_star_subscription.py
@@ -11,5 +11,5 @@ class TestEditUserStarSubscription:
telegram_payment_charge_id="telegram_payment_charge_id",
is_canceled=False,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_export_chat_invite_link.py b/tests/test_api/test_methods/test_export_chat_invite_link.py
index 1d0de7ec..56714362 100644
--- a/tests/test_api/test_methods/test_export_chat_invite_link.py
+++ b/tests/test_api/test_methods/test_export_chat_invite_link.py
@@ -9,5 +9,5 @@ class TestExportChatInviteLink:
)
response: str = await bot.export_chat_invite_link(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_available_gifts.py b/tests/test_api/test_methods/test_get_available_gifts.py
index 3576eee5..a3db3936 100644
--- a/tests/test_api/test_methods/test_get_available_gifts.py
+++ b/tests/test_api/test_methods/test_get_available_gifts.py
@@ -28,5 +28,5 @@ class TestGetAvailableGifts:
)
response: Gifts = await bot.get_available_gifts()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_business_account_gifts.py b/tests/test_api/test_methods/test_get_business_account_gifts.py
index 697e3b6c..712911cb 100644
--- a/tests/test_api/test_methods/test_get_business_account_gifts.py
+++ b/tests/test_api/test_methods/test_get_business_account_gifts.py
@@ -37,5 +37,5 @@ class TestGetBusinessAccountGifts:
business_connection_id="test_connection_id",
limit=10,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_business_account_star_balance.py b/tests/test_api/test_methods/test_get_business_account_star_balance.py
index 5540fa44..67bdb19e 100644
--- a/tests/test_api/test_methods/test_get_business_account_star_balance.py
+++ b/tests/test_api/test_methods/test_get_business_account_star_balance.py
@@ -17,5 +17,5 @@ class TestGetBusinessAccountStarBalance:
response: StarAmount = await bot.get_business_account_star_balance(
business_connection_id="test_connection_id",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_business_connection.py b/tests/test_api/test_methods/test_get_business_connection.py
index 22776cb4..99bf1ba0 100644
--- a/tests/test_api/test_methods/test_get_business_connection.py
+++ b/tests/test_api/test_methods/test_get_business_connection.py
@@ -20,5 +20,5 @@ class TestGetBusinessConnection:
response: BusinessConnection = await bot.get_business_connection(
business_connection_id="test"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_chat.py b/tests/test_api/test_methods/test_get_chat.py
index bc0eb8cd..f02aa2e0 100644
--- a/tests/test_api/test_methods/test_get_chat.py
+++ b/tests/test_api/test_methods/test_get_chat.py
@@ -25,5 +25,5 @@ class TestGetChat:
)
response: ChatFullInfo = await bot.get_chat(chat_id=-42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_chat_administrators.py b/tests/test_api/test_methods/test_get_chat_administrators.py
index f5992a87..6026fa8c 100644
--- a/tests/test_api/test_methods/test_get_chat_administrators.py
+++ b/tests/test_api/test_methods/test_get_chat_administrators.py
@@ -1,5 +1,3 @@
-from typing import List
-
from aiogram.methods import GetChatAdministrators
from aiogram.types import ChatMember, ChatMemberOwner, User
from tests.mocked_bot import MockedBot
@@ -16,6 +14,6 @@ class TestGetChatAdministrators:
)
],
)
- response: List[ChatMember] = await bot.get_chat_administrators(chat_id=-42)
- request = bot.get_request()
+ response: list[ChatMember] = await bot.get_chat_administrators(chat_id=-42)
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_chat_gifts.py b/tests/test_api/test_methods/test_get_chat_gifts.py
index 26c83b47..90111ac9 100644
--- a/tests/test_api/test_methods/test_get_chat_gifts.py
+++ b/tests/test_api/test_methods/test_get_chat_gifts.py
@@ -37,5 +37,5 @@ class TestGetChatGifts:
chat_id=42,
limit=10,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_chat_member.py b/tests/test_api/test_methods/test_get_chat_member.py
index 5d0490a5..091f3391 100644
--- a/tests/test_api/test_methods/test_get_chat_member.py
+++ b/tests/test_api/test_methods/test_get_chat_member.py
@@ -13,5 +13,5 @@ class TestGetChatMember:
),
)
response = await bot.get_chat_member(chat_id=-42, user_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_chat_member_count.py b/tests/test_api/test_methods/test_get_chat_member_count.py
index d2af7e2f..bec7ec16 100644
--- a/tests/test_api/test_methods/test_get_chat_member_count.py
+++ b/tests/test_api/test_methods/test_get_chat_member_count.py
@@ -7,5 +7,5 @@ class TestGetChatMembersCount:
prepare_result = bot.add_result_for(GetChatMemberCount, ok=True, result=42)
response: int = await bot.get_chat_member_count(chat_id=-42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_chat_menu_button.py b/tests/test_api/test_methods/test_get_chat_menu_button.py
index 361ebc14..dbfb0285 100644
--- a/tests/test_api/test_methods/test_get_chat_menu_button.py
+++ b/tests/test_api/test_methods/test_get_chat_menu_button.py
@@ -8,5 +8,5 @@ class TestGetChatMenuButton:
prepare_result = bot.add_result_for(GetChatMenuButton, ok=True, result=MenuButtonDefault())
response: MenuButton = await bot.get_chat_menu_button()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_custom_emoji_stickers.py b/tests/test_api/test_methods/test_get_custom_emoji_stickers.py
index 20775c75..c29a0852 100644
--- a/tests/test_api/test_methods/test_get_custom_emoji_stickers.py
+++ b/tests/test_api/test_methods/test_get_custom_emoji_stickers.py
@@ -1,5 +1,3 @@
-from typing import List
-
from aiogram.methods import GetCustomEmojiStickers
from aiogram.types import Sticker
from tests.mocked_bot import MockedBot
@@ -24,8 +22,8 @@ class TestGetCustomEmojiStickers:
],
)
- response: List[Sticker] = await bot.get_custom_emoji_stickers(
+ response: list[Sticker] = await bot.get_custom_emoji_stickers(
custom_emoji_ids=["1", "2"],
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_file.py b/tests/test_api/test_methods/test_get_file.py
index 808681b4..71a85e2c 100644
--- a/tests/test_api/test_methods/test_get_file.py
+++ b/tests/test_api/test_methods/test_get_file.py
@@ -10,5 +10,5 @@ class TestGetFile:
)
response: File = await bot.get_file(file_id="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_forum_topic_icon_stickers.py b/tests/test_api/test_methods/test_get_forum_topic_icon_stickers.py
index df0ed2b2..4e798113 100644
--- a/tests/test_api/test_methods/test_get_forum_topic_icon_stickers.py
+++ b/tests/test_api/test_methods/test_get_forum_topic_icon_stickers.py
@@ -1,5 +1,3 @@
-from typing import List
-
from aiogram.methods import GetForumTopicIconStickers
from aiogram.types import Sticker
from tests.mocked_bot import MockedBot
@@ -9,6 +7,6 @@ class TestGetForumTopicIconStickers:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(GetForumTopicIconStickers, ok=True, result=[])
- response: List[Sticker] = await bot.get_forum_topic_icon_stickers()
- request = bot.get_request()
+ response: list[Sticker] = await bot.get_forum_topic_icon_stickers()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_game_high_scores.py b/tests/test_api/test_methods/test_get_game_high_scores.py
index 189c05a8..075a2e66 100644
--- a/tests/test_api/test_methods/test_get_game_high_scores.py
+++ b/tests/test_api/test_methods/test_get_game_high_scores.py
@@ -1,5 +1,3 @@
-from typing import List
-
from aiogram.methods import GetGameHighScores
from aiogram.types import GameHighScore, User
from tests.mocked_bot import MockedBot
@@ -17,6 +15,6 @@ class TestGetGameHighScores:
],
)
- response: List[GameHighScore] = await bot.get_game_high_scores(user_id=42)
- request = bot.get_request()
+ response: list[GameHighScore] = await bot.get_game_high_scores(user_id=42)
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_me.py b/tests/test_api/test_methods/test_get_me.py
index eac5557d..ab86adce 100644
--- a/tests/test_api/test_methods/test_get_me.py
+++ b/tests/test_api/test_methods/test_get_me.py
@@ -9,7 +9,7 @@ class TestGetMe:
GetMe, ok=True, result=User(id=42, is_bot=False, first_name="User")
)
response: User = await bot.get_me()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
async def test_me_property(self, bot: MockedBot):
diff --git a/tests/test_api/test_methods/test_get_my_commands.py b/tests/test_api/test_methods/test_get_my_commands.py
index 45a69a20..7b6633f7 100644
--- a/tests/test_api/test_methods/test_get_my_commands.py
+++ b/tests/test_api/test_methods/test_get_my_commands.py
@@ -1,5 +1,3 @@
-from typing import List
-
from aiogram.methods import GetMyCommands
from aiogram.types import BotCommand
from tests.mocked_bot import MockedBot
@@ -9,6 +7,6 @@ class TestGetMyCommands:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(GetMyCommands, ok=True, result=None)
- response: List[BotCommand] = await bot.get_my_commands()
- request = bot.get_request()
+ response: list[BotCommand] = await bot.get_my_commands()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_my_default_administrator_rights.py b/tests/test_api/test_methods/test_get_my_default_administrator_rights.py
index da76a4b2..75136c05 100644
--- a/tests/test_api/test_methods/test_get_my_default_administrator_rights.py
+++ b/tests/test_api/test_methods/test_get_my_default_administrator_rights.py
@@ -24,5 +24,5 @@ class TestGetMyDefaultAdministratorRights:
)
response: ChatAdministratorRights = await bot.get_my_default_administrator_rights()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_my_description.py b/tests/test_api/test_methods/test_get_my_description.py
index 061d0837..9c08631a 100644
--- a/tests/test_api/test_methods/test_get_my_description.py
+++ b/tests/test_api/test_methods/test_get_my_description.py
@@ -10,5 +10,5 @@ class TestGetMyDescription:
)
response: BotDescription = await bot.get_my_description()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_my_short_description.py b/tests/test_api/test_methods/test_get_my_short_description.py
index f0d4a3ac..7f155a45 100644
--- a/tests/test_api/test_methods/test_get_my_short_description.py
+++ b/tests/test_api/test_methods/test_get_my_short_description.py
@@ -10,5 +10,5 @@ class TestGetMyShortDescription:
)
response: BotShortDescription = await bot.get_my_short_description()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_my_star_balance.py b/tests/test_api/test_methods/test_get_my_star_balance.py
index 7c9edd21..22434e38 100644
--- a/tests/test_api/test_methods/test_get_my_star_balance.py
+++ b/tests/test_api/test_methods/test_get_my_star_balance.py
@@ -14,5 +14,5 @@ class TestGetMyStarBalance:
)
response: StarAmount = await bot.get_my_star_balance()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_star_transactions.py b/tests/test_api/test_methods/test_get_star_transactions.py
index d7d37374..9429f12f 100644
--- a/tests/test_api/test_methods/test_get_star_transactions.py
+++ b/tests/test_api/test_methods/test_get_star_transactions.py
@@ -3,7 +3,6 @@ from datetime import datetime
from aiogram.enums import TransactionPartnerUserTransactionTypeEnum
from aiogram.methods import GetStarTransactions
from aiogram.types import (
- File,
StarTransaction,
StarTransactions,
TransactionPartnerUser,
@@ -45,5 +44,5 @@ class TestGetStarTransactions:
)
response: StarTransactions = await bot.get_star_transactions(limit=10, offset=0)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_sticker_set.py b/tests/test_api/test_methods/test_get_sticker_set.py
index 8b643b25..4881e6c5 100644
--- a/tests/test_api/test_methods/test_get_sticker_set.py
+++ b/tests/test_api/test_methods/test_get_sticker_set.py
@@ -29,5 +29,5 @@ class TestGetStickerSet:
)
response: StickerSet = await bot.get_sticker_set(name="test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_updates.py b/tests/test_api/test_methods/test_get_updates.py
index f9249e03..7033c616 100644
--- a/tests/test_api/test_methods/test_get_updates.py
+++ b/tests/test_api/test_methods/test_get_updates.py
@@ -1,5 +1,3 @@
-from typing import List
-
from aiogram.methods import GetUpdates
from aiogram.types import Update
from tests.mocked_bot import MockedBot
@@ -9,6 +7,6 @@ class TestGetUpdates:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(GetUpdates, ok=True, result=[Update(update_id=42)])
- response: List[Update] = await bot.get_updates()
- request = bot.get_request()
+ response: list[Update] = await bot.get_updates()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_url.py b/tests/test_api/test_methods/test_get_url.py
index 0c5cddc5..87e25487 100644
--- a/tests/test_api/test_methods/test_get_url.py
+++ b/tests/test_api/test_methods/test_get_url.py
@@ -1,5 +1,4 @@
import datetime
-from typing import Optional
import pytest
@@ -31,9 +30,9 @@ class TestGetMessageUrl:
bot: MockedBot,
chat_type: str,
chat_id: int,
- chat_username: Optional[str],
+ chat_username: str | None,
force_private: bool,
- expected_result: Optional[str],
+ expected_result: str | None,
):
fake_chat = Chat(id=chat_id, username=chat_username, type=chat_type)
fake_message_id = 10
@@ -80,11 +79,11 @@ class TestGetMessageUrl:
def test_get_url_if_topic_message(
self,
bot: MockedBot,
- chat_username: Optional[str],
+ chat_username: str | None,
force_private: bool,
include_thread_id: bool,
- fake_thread_id_topic: Optional[int],
- expected_result: Optional[str],
+ fake_thread_id_topic: int | None,
+ expected_result: str | None,
):
fake_message_id = 10
fake_chat_id = -1001234567890
diff --git a/tests/test_api/test_methods/test_get_user_gifts.py b/tests/test_api/test_methods/test_get_user_gifts.py
index 38430c0e..b9b6c076 100644
--- a/tests/test_api/test_methods/test_get_user_gifts.py
+++ b/tests/test_api/test_methods/test_get_user_gifts.py
@@ -37,5 +37,5 @@ class TestGetUserGifts:
user_id=42,
limit=10,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_user_profile_audios.py b/tests/test_api/test_methods/test_get_user_profile_audios.py
new file mode 100644
index 00000000..b4f08905
--- /dev/null
+++ b/tests/test_api/test_methods/test_get_user_profile_audios.py
@@ -0,0 +1,19 @@
+from aiogram.methods import GetUserProfileAudios
+from aiogram.types import Audio, UserProfileAudios
+from tests.mocked_bot import MockedBot
+
+
+class TestGetUserProfileAudios:
+ async def test_bot_method(self, bot: MockedBot):
+ prepare_result = bot.add_result_for(
+ GetUserProfileAudios,
+ ok=True,
+ result=UserProfileAudios(
+ total_count=1,
+ audios=[Audio(file_id="file_id", file_unique_id="file_unique_id", duration=120)],
+ ),
+ )
+
+ response: UserProfileAudios = await bot.get_user_profile_audios(user_id=42)
+ bot.get_request()
+ assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_user_profile_photos.py b/tests/test_api/test_methods/test_get_user_profile_photos.py
index 1b9fad5d..df61db63 100644
--- a/tests/test_api/test_methods/test_get_user_profile_photos.py
+++ b/tests/test_api/test_methods/test_get_user_profile_photos.py
@@ -17,5 +17,5 @@ class TestGetUserProfilePhotos:
)
response: UserProfilePhotos = await bot.get_user_profile_photos(user_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_get_webhook_info.py b/tests/test_api/test_methods/test_get_webhook_info.py
index 20ef27e0..29f392ef 100644
--- a/tests/test_api/test_methods/test_get_webhook_info.py
+++ b/tests/test_api/test_methods/test_get_webhook_info.py
@@ -14,5 +14,5 @@ class TestGetWebhookInfo:
)
response: WebhookInfo = await bot.get_webhook_info()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_gift_premium_subscription.py b/tests/test_api/test_methods/test_gift_premium_subscription.py
index c3bcc948..bd60ec13 100644
--- a/tests/test_api/test_methods/test_gift_premium_subscription.py
+++ b/tests/test_api/test_methods/test_gift_premium_subscription.py
@@ -12,5 +12,5 @@ class TestGiftPremiumSubscription:
star_count=1000,
text="Enjoy your premium subscription!",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_hide_general_forum_topic.py b/tests/test_api/test_methods/test_hide_general_forum_topic.py
index 8c6163aa..259fe03b 100644
--- a/tests/test_api/test_methods/test_hide_general_forum_topic.py
+++ b/tests/test_api/test_methods/test_hide_general_forum_topic.py
@@ -7,5 +7,5 @@ class TestHideGeneralForumTopic:
prepare_result = bot.add_result_for(HideGeneralForumTopic, ok=True, result=True)
response: bool = await bot.hide_general_forum_topic(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_leave_chat.py b/tests/test_api/test_methods/test_leave_chat.py
index 56c87bac..fc4b33d2 100644
--- a/tests/test_api/test_methods/test_leave_chat.py
+++ b/tests/test_api/test_methods/test_leave_chat.py
@@ -7,5 +7,5 @@ class TestLeaveChat:
prepare_result = bot.add_result_for(LeaveChat, ok=True, result=True)
response: bool = await bot.leave_chat(chat_id=-42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_log_out.py b/tests/test_api/test_methods/test_log_out.py
index 2c23dede..0f1462e1 100644
--- a/tests/test_api/test_methods/test_log_out.py
+++ b/tests/test_api/test_methods/test_log_out.py
@@ -7,5 +7,5 @@ class TestLogOut:
prepare_result = bot.add_result_for(LogOut, ok=True, result=True)
response: bool = await bot.log_out()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_pin_chat_message.py b/tests/test_api/test_methods/test_pin_chat_message.py
index 7ce4e613..f9f44fff 100644
--- a/tests/test_api/test_methods/test_pin_chat_message.py
+++ b/tests/test_api/test_methods/test_pin_chat_message.py
@@ -7,5 +7,5 @@ class TestPinChatMessage:
prepare_result = bot.add_result_for(PinChatMessage, ok=True, result=True)
response: bool = await bot.pin_chat_message(chat_id=-42, message_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_post_story.py b/tests/test_api/test_methods/test_post_story.py
index 0dd74cc4..3a37e373 100644
--- a/tests/test_api/test_methods/test_post_story.py
+++ b/tests/test_api/test_methods/test_post_story.py
@@ -1,5 +1,3 @@
-import datetime
-
from aiogram.methods import PostStory
from aiogram.types import Chat, InputStoryContentPhoto, Story
from tests.mocked_bot import MockedBot
@@ -22,5 +20,5 @@ class TestPostStory:
active_period=6 * 3600, # 6 hours
caption="Test story caption",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_promote_chat_member.py b/tests/test_api/test_methods/test_promote_chat_member.py
index 93a3a1d0..ee3b7f4e 100644
--- a/tests/test_api/test_methods/test_promote_chat_member.py
+++ b/tests/test_api/test_methods/test_promote_chat_member.py
@@ -7,5 +7,5 @@ class TestPromoteChatMember:
prepare_result = bot.add_result_for(PromoteChatMember, ok=True, result=True)
response: bool = await bot.promote_chat_member(chat_id=-42, user_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_read_business_message.py b/tests/test_api/test_methods/test_read_business_message.py
index 73db813f..0f3b2db7 100644
--- a/tests/test_api/test_methods/test_read_business_message.py
+++ b/tests/test_api/test_methods/test_read_business_message.py
@@ -9,5 +9,5 @@ class TestReadBusinessMessage:
response: bool = await bot.read_business_message(
business_connection_id="test_connection_id", chat_id=123456789, message_id=42
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_refund_star_payment.py b/tests/test_api/test_methods/test_refund_star_payment.py
index a39e55ca..989d1cc4 100644
--- a/tests/test_api/test_methods/test_refund_star_payment.py
+++ b/tests/test_api/test_methods/test_refund_star_payment.py
@@ -10,5 +10,5 @@ class TestRefundStarPayment:
user_id=42,
telegram_payment_charge_id="12345",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_remove_business_account_profile_photo.py b/tests/test_api/test_methods/test_remove_business_account_profile_photo.py
index d951f9e5..8ba1b691 100644
--- a/tests/test_api/test_methods/test_remove_business_account_profile_photo.py
+++ b/tests/test_api/test_methods/test_remove_business_account_profile_photo.py
@@ -11,5 +11,5 @@ class TestRemoveBusinessAccountProfilePhoto:
response: bool = await bot.remove_business_account_profile_photo(
business_connection_id="test_connection_id", is_public=True
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_remove_chat_verification.py b/tests/test_api/test_methods/test_remove_chat_verification.py
index 29186300..242daadb 100644
--- a/tests/test_api/test_methods/test_remove_chat_verification.py
+++ b/tests/test_api/test_methods/test_remove_chat_verification.py
@@ -1,5 +1,4 @@
-from aiogram.methods import RemoveChatVerification, VerifyChat
-from aiogram.types import Poll
+from aiogram.methods import RemoveChatVerification
from tests.mocked_bot import MockedBot
@@ -8,5 +7,5 @@ class TestRemoveChatVerification:
prepare_result = bot.add_result_for(RemoveChatVerification, ok=True, result=True)
response: bool = await bot.remove_chat_verification(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_remove_my_profile_photo.py b/tests/test_api/test_methods/test_remove_my_profile_photo.py
new file mode 100644
index 00000000..9be7b4b1
--- /dev/null
+++ b/tests/test_api/test_methods/test_remove_my_profile_photo.py
@@ -0,0 +1,11 @@
+from aiogram.methods import RemoveMyProfilePhoto
+from tests.mocked_bot import MockedBot
+
+
+class TestRemoveMyProfilePhoto:
+ async def test_bot_method(self, bot: MockedBot):
+ prepare_result = bot.add_result_for(RemoveMyProfilePhoto, ok=True, result=True)
+
+ response: bool = await bot.remove_my_profile_photo()
+ bot.get_request()
+ assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_remove_user_verification.py b/tests/test_api/test_methods/test_remove_user_verification.py
index 4d405c82..d5e32484 100644
--- a/tests/test_api/test_methods/test_remove_user_verification.py
+++ b/tests/test_api/test_methods/test_remove_user_verification.py
@@ -1,5 +1,4 @@
-from aiogram.methods import RemoveUserVerification, VerifyChat
-from aiogram.types import Poll
+from aiogram.methods import RemoveUserVerification
from tests.mocked_bot import MockedBot
@@ -8,5 +7,5 @@ class TestRemoveUserVerification:
prepare_result = bot.add_result_for(RemoveUserVerification, ok=True, result=True)
response: bool = await bot.remove_user_verification(user_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_reopen_forum_topic.py b/tests/test_api/test_methods/test_reopen_forum_topic.py
index 35cd6f4c..b71c5484 100644
--- a/tests/test_api/test_methods/test_reopen_forum_topic.py
+++ b/tests/test_api/test_methods/test_reopen_forum_topic.py
@@ -10,5 +10,5 @@ class TestReopenForumTopic:
chat_id=42,
message_thread_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_reopen_general_forum_topic.py b/tests/test_api/test_methods/test_reopen_general_forum_topic.py
index e7c08b89..8e35855f 100644
--- a/tests/test_api/test_methods/test_reopen_general_forum_topic.py
+++ b/tests/test_api/test_methods/test_reopen_general_forum_topic.py
@@ -7,5 +7,5 @@ class TestReopenGeneralForumTopic:
prepare_result = bot.add_result_for(ReopenGeneralForumTopic, ok=True, result=True)
response: bool = await bot.reopen_general_forum_topic(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_replace_sticker_in_set.py b/tests/test_api/test_methods/test_replace_sticker_in_set.py
index e556919d..50e066bc 100644
--- a/tests/test_api/test_methods/test_replace_sticker_in_set.py
+++ b/tests/test_api/test_methods/test_replace_sticker_in_set.py
@@ -17,5 +17,5 @@ class TestReplaceStickerInSet:
emoji_list=["test"],
),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_repost_story.py b/tests/test_api/test_methods/test_repost_story.py
index f00b9f11..0fab2c2f 100644
--- a/tests/test_api/test_methods/test_repost_story.py
+++ b/tests/test_api/test_methods/test_repost_story.py
@@ -20,5 +20,5 @@ class TestRepostStory:
from_story_id=456,
active_period=6 * 3600,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_restrict_chat_member.py b/tests/test_api/test_methods/test_restrict_chat_member.py
index e845b400..96d7a676 100644
--- a/tests/test_api/test_methods/test_restrict_chat_member.py
+++ b/tests/test_api/test_methods/test_restrict_chat_member.py
@@ -10,5 +10,5 @@ class TestRestrictChatMember:
response: bool = await bot.restrict_chat_member(
chat_id=-42, user_id=42, permissions=ChatPermissions()
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_revoke_chat_invite_link.py b/tests/test_api/test_methods/test_revoke_chat_invite_link.py
index cbaab593..cbc51e2c 100644
--- a/tests/test_api/test_methods/test_revoke_chat_invite_link.py
+++ b/tests/test_api/test_methods/test_revoke_chat_invite_link.py
@@ -21,5 +21,5 @@ class TestRevokeChatInviteLink:
chat_id=-42,
invite_link="https://t.me/username",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_save_prepared_inline_message.py b/tests/test_api/test_methods/test_save_prepared_inline_message.py
index 2a1d1f4d..6cf5b9f8 100644
--- a/tests/test_api/test_methods/test_save_prepared_inline_message.py
+++ b/tests/test_api/test_methods/test_save_prepared_inline_message.py
@@ -30,5 +30,5 @@ class TestSavePreparedInlineMessage:
),
),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_animation.py b/tests/test_api/test_methods/test_send_animation.py
index 569d663d..9f08eb8c 100644
--- a/tests/test_api/test_methods/test_send_animation.py
+++ b/tests/test_api/test_methods/test_send_animation.py
@@ -21,5 +21,5 @@ class TestSendAnimation:
)
response: Message = await bot.send_animation(chat_id=42, animation="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_audio.py b/tests/test_api/test_methods/test_send_audio.py
index 63cdaf10..8ba52624 100644
--- a/tests/test_api/test_methods/test_send_audio.py
+++ b/tests/test_api/test_methods/test_send_audio.py
@@ -19,5 +19,5 @@ class TestSendAudio:
)
response: Message = await bot.send_audio(chat_id=42, audio="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_chat_action.py b/tests/test_api/test_methods/test_send_chat_action.py
index 22f2805d..95835f7b 100644
--- a/tests/test_api/test_methods/test_send_chat_action.py
+++ b/tests/test_api/test_methods/test_send_chat_action.py
@@ -8,5 +8,5 @@ class TestSendChatAction:
prepare_result = bot.add_result_for(SendChatAction, ok=True, result=True)
response: bool = await bot.send_chat_action(chat_id=42, action=ChatAction.TYPING)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_checklist.py b/tests/test_api/test_methods/test_send_checklist.py
index 40c279f3..b4677672 100644
--- a/tests/test_api/test_methods/test_send_checklist.py
+++ b/tests/test_api/test_methods/test_send_checklist.py
@@ -30,5 +30,5 @@ class TestSendChecklist:
chat_id=42,
checklist=checklist,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_contact.py b/tests/test_api/test_methods/test_send_contact.py
index e3f14496..8cd1f1fc 100644
--- a/tests/test_api/test_methods/test_send_contact.py
+++ b/tests/test_api/test_methods/test_send_contact.py
@@ -21,5 +21,5 @@ class TestSendContact:
response: Message = await bot.send_contact(
chat_id=42, phone_number="911", first_name="911"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_dice.py b/tests/test_api/test_methods/test_send_dice.py
index 66daf7d8..00dcbdb9 100644
--- a/tests/test_api/test_methods/test_send_dice.py
+++ b/tests/test_api/test_methods/test_send_dice.py
@@ -8,5 +8,5 @@ class TestSendDice:
prepare_result = bot.add_result_for(SendDice, ok=True, result=None)
response: Message = await bot.send_dice(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_document.py b/tests/test_api/test_methods/test_send_document.py
index 9a96a6ac..17dd9cf1 100644
--- a/tests/test_api/test_methods/test_send_document.py
+++ b/tests/test_api/test_methods/test_send_document.py
@@ -19,5 +19,5 @@ class TestSendDocument:
)
response: Message = await bot.send_document(chat_id=42, document="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_game.py b/tests/test_api/test_methods/test_send_game.py
index 99621444..dfe8d10f 100644
--- a/tests/test_api/test_methods/test_send_game.py
+++ b/tests/test_api/test_methods/test_send_game.py
@@ -25,5 +25,5 @@ class TestSendGame:
)
response: Message = await bot.send_game(chat_id=42, game_short_name="game")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_gift.py b/tests/test_api/test_methods/test_send_gift.py
index 5654d49d..6acd477d 100644
--- a/tests/test_api/test_methods/test_send_gift.py
+++ b/tests/test_api/test_methods/test_send_gift.py
@@ -7,5 +7,5 @@ class TestSendGift:
prepare_result = bot.add_result_for(SendGift, ok=True, result=True)
response: bool = await bot.send_gift(user_id=42, gift_id="gift_id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_invoice.py b/tests/test_api/test_methods/test_send_invoice.py
index b65ffd89..a079fe14 100644
--- a/tests/test_api/test_methods/test_send_invoice.py
+++ b/tests/test_api/test_methods/test_send_invoice.py
@@ -34,5 +34,5 @@ class TestSendInvoice:
currency="BTC",
prices=[LabeledPrice(amount=1, label="test")],
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_location.py b/tests/test_api/test_methods/test_send_location.py
index be17dbec..c6db81a5 100644
--- a/tests/test_api/test_methods/test_send_location.py
+++ b/tests/test_api/test_methods/test_send_location.py
@@ -19,5 +19,5 @@ class TestSendLocation:
)
response: Message = await bot.send_location(chat_id=42, latitude=3.14, longitude=3.14)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_media_group.py b/tests/test_api/test_methods/test_send_media_group.py
index 1ac4cf42..63d8f7ab 100644
--- a/tests/test_api/test_methods/test_send_media_group.py
+++ b/tests/test_api/test_methods/test_send_media_group.py
@@ -1,5 +1,4 @@
import datetime
-from typing import List
from aiogram.methods import SendMediaGroup
from aiogram.types import (
@@ -45,12 +44,12 @@ class TestSendMediaGroup:
],
)
- response: List[Message] = await bot.send_media_group(
+ response: list[Message] = await bot.send_media_group(
chat_id=42,
media=[
InputMediaPhoto(media="file id"),
InputMediaVideo(media=BufferedInputFile(b"", "video.mp4")),
],
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_message.py b/tests/test_api/test_methods/test_send_message.py
index f16267c2..3a612148 100644
--- a/tests/test_api/test_methods/test_send_message.py
+++ b/tests/test_api/test_methods/test_send_message.py
@@ -19,7 +19,7 @@ class TestSendMessage:
)
response: Message = await bot.send_message(chat_id=42, text="test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
async def test_force_reply(self):
diff --git a/tests/test_api/test_methods/test_send_message_draft.py b/tests/test_api/test_methods/test_send_message_draft.py
index cdb775fd..5edf790b 100644
--- a/tests/test_api/test_methods/test_send_message_draft.py
+++ b/tests/test_api/test_methods/test_send_message_draft.py
@@ -15,5 +15,5 @@ class TestSendMessageDraft:
draft_id=1,
text="test draft",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_paid_media.py b/tests/test_api/test_methods/test_send_paid_media.py
index 37ff2717..0039d88a 100755
--- a/tests/test_api/test_methods/test_send_paid_media.py
+++ b/tests/test_api/test_methods/test_send_paid_media.py
@@ -39,5 +39,5 @@ class TestSendPaidMedia:
response: Message = await bot.send_paid_media(
chat_id=-42, star_count=1, media=[InputPaidMediaPhoto(media="file_id")]
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_photo.py b/tests/test_api/test_methods/test_send_photo.py
index f1ccc638..07eb2080 100644
--- a/tests/test_api/test_methods/test_send_photo.py
+++ b/tests/test_api/test_methods/test_send_photo.py
@@ -21,5 +21,5 @@ class TestSendPhoto:
)
response: Message = await bot.send_photo(chat_id=42, photo="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_poll.py b/tests/test_api/test_methods/test_send_poll.py
index d070643d..27288b51 100644
--- a/tests/test_api/test_methods/test_send_poll.py
+++ b/tests/test_api/test_methods/test_send_poll.py
@@ -34,5 +34,5 @@ class TestSendPoll:
response: Message = await bot.send_poll(
chat_id=42, question="Q?", options=["A", "B"], correct_option_id=0, type="quiz"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_sticker.py b/tests/test_api/test_methods/test_send_sticker.py
index 9e553e4c..add81076 100644
--- a/tests/test_api/test_methods/test_send_sticker.py
+++ b/tests/test_api/test_methods/test_send_sticker.py
@@ -27,5 +27,5 @@ class TestSendSticker:
)
response: Message = await bot.send_sticker(chat_id=42, sticker="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_venue.py b/tests/test_api/test_methods/test_send_venue.py
index 3b910851..8eb733af 100644
--- a/tests/test_api/test_methods/test_send_venue.py
+++ b/tests/test_api/test_methods/test_send_venue.py
@@ -31,5 +31,5 @@ class TestSendVenue:
address="Under the stairs, 4 Privet Drive, "
"Little Whinging, Surrey, England, Great Britain",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_video.py b/tests/test_api/test_methods/test_send_video.py
index 31bec569..3e412084 100644
--- a/tests/test_api/test_methods/test_send_video.py
+++ b/tests/test_api/test_methods/test_send_video.py
@@ -21,5 +21,5 @@ class TestSendVideo:
)
response: Message = await bot.send_video(chat_id=42, video="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_video_note.py b/tests/test_api/test_methods/test_send_video_note.py
index f78fdce7..6bc52ddf 100644
--- a/tests/test_api/test_methods/test_send_video_note.py
+++ b/tests/test_api/test_methods/test_send_video_note.py
@@ -25,5 +25,5 @@ class TestSendVideoNote:
video_note="file id",
thumbnail=BufferedInputFile(b"", "file.png"),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_send_voice.py b/tests/test_api/test_methods/test_send_voice.py
index 11ff9d85..40e36df0 100644
--- a/tests/test_api/test_methods/test_send_voice.py
+++ b/tests/test_api/test_methods/test_send_voice.py
@@ -19,5 +19,5 @@ class TestSendVoice:
)
response: Message = await bot.send_voice(chat_id=42, voice="file id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_business_account_bio.py b/tests/test_api/test_methods/test_set_business_account_bio.py
index 5041122b..e2cde8bc 100644
--- a/tests/test_api/test_methods/test_set_business_account_bio.py
+++ b/tests/test_api/test_methods/test_set_business_account_bio.py
@@ -10,5 +10,5 @@ class TestSetBusinessAccountBio:
business_connection_id="test_connection_id",
bio="This is a test bio for the business account",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_business_account_gift_settings.py b/tests/test_api/test_methods/test_set_business_account_gift_settings.py
index 9aaa7201..bd98a675 100644
--- a/tests/test_api/test_methods/test_set_business_account_gift_settings.py
+++ b/tests/test_api/test_methods/test_set_business_account_gift_settings.py
@@ -18,5 +18,5 @@ class TestSetBusinessAccountGiftSettings:
premium_subscription=True,
),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_business_account_name.py b/tests/test_api/test_methods/test_set_business_account_name.py
index 5098ad34..77a437b1 100644
--- a/tests/test_api/test_methods/test_set_business_account_name.py
+++ b/tests/test_api/test_methods/test_set_business_account_name.py
@@ -11,5 +11,5 @@ class TestSetBusinessAccountName:
first_name="Test Business",
last_name="Account Name",
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_business_account_profile_photo.py b/tests/test_api/test_methods/test_set_business_account_profile_photo.py
index 99808985..b7dbec3c 100644
--- a/tests/test_api/test_methods/test_set_business_account_profile_photo.py
+++ b/tests/test_api/test_methods/test_set_business_account_profile_photo.py
@@ -12,5 +12,5 @@ class TestSetBusinessAccountProfilePhoto:
photo=InputProfilePhotoStatic(photo="test_photo_file_id"),
is_public=True,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_business_account_username.py b/tests/test_api/test_methods/test_set_business_account_username.py
index db912ca3..a7b050f5 100644
--- a/tests/test_api/test_methods/test_set_business_account_username.py
+++ b/tests/test_api/test_methods/test_set_business_account_username.py
@@ -9,5 +9,5 @@ class TestSetBusinessAccountUsername:
response: bool = await bot.set_business_account_username(
business_connection_id="test_connection_id", username="test_business_username"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py b/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py
index 7ac13d36..9aabcbb5 100644
--- a/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py
+++ b/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py
@@ -9,5 +9,5 @@ class TestSetChatTitle:
response: bool = await bot.set_chat_administrator_custom_title(
chat_id=-42, user_id=42, custom_title="test chat"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_chat_description.py b/tests/test_api/test_methods/test_set_chat_description.py
index 7f4d8404..95385606 100644
--- a/tests/test_api/test_methods/test_set_chat_description.py
+++ b/tests/test_api/test_methods/test_set_chat_description.py
@@ -7,5 +7,5 @@ class TestSetChatDescription:
prepare_result = bot.add_result_for(SetChatDescription, ok=True, result=True)
response: bool = await bot.set_chat_description(chat_id=-42, description="awesome chat")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_chat_menu_button.py b/tests/test_api/test_methods/test_set_chat_menu_button.py
index 86d482cf..5ab8c51f 100644
--- a/tests/test_api/test_methods/test_set_chat_menu_button.py
+++ b/tests/test_api/test_methods/test_set_chat_menu_button.py
@@ -7,5 +7,5 @@ class TestSetChatMenuButton:
prepare_result = bot.add_result_for(SetChatMenuButton, ok=True, result=True)
response: bool = await bot.set_chat_menu_button()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_chat_permissions.py b/tests/test_api/test_methods/test_set_chat_permissions.py
index 11cb8743..1cb41c91 100644
--- a/tests/test_api/test_methods/test_set_chat_permissions.py
+++ b/tests/test_api/test_methods/test_set_chat_permissions.py
@@ -10,5 +10,5 @@ class TestSetChatPermissions:
response: bool = await bot.set_chat_permissions(
chat_id=-42, permissions=ChatPermissions(can_send_messages=False)
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_chat_photo.py b/tests/test_api/test_methods/test_set_chat_photo.py
index d69aacea..8a4057a7 100644
--- a/tests/test_api/test_methods/test_set_chat_photo.py
+++ b/tests/test_api/test_methods/test_set_chat_photo.py
@@ -10,5 +10,5 @@ class TestSetChatPhoto:
response: bool = await bot.set_chat_photo(
chat_id=-42, photo=BufferedInputFile(b"", filename="file.png")
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_chat_sticker_set.py b/tests/test_api/test_methods/test_set_chat_sticker_set.py
index 78e692bc..44f776ac 100644
--- a/tests/test_api/test_methods/test_set_chat_sticker_set.py
+++ b/tests/test_api/test_methods/test_set_chat_sticker_set.py
@@ -7,5 +7,5 @@ class TestSetChatStickerSet:
prepare_result = bot.add_result_for(SetChatStickerSet, ok=True, result=True)
response: bool = await bot.set_chat_sticker_set(chat_id=-42, sticker_set_name="test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_chat_title.py b/tests/test_api/test_methods/test_set_chat_title.py
index c9a9c087..056b0f2b 100644
--- a/tests/test_api/test_methods/test_set_chat_title.py
+++ b/tests/test_api/test_methods/test_set_chat_title.py
@@ -7,5 +7,5 @@ class TestSetChatTitle:
prepare_result = bot.add_result_for(SetChatTitle, ok=True, result=True)
response: bool = await bot.set_chat_title(chat_id=-42, title="test chat")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_custom_emoji_sticker_set_thumbnail.py b/tests/test_api/test_methods/test_set_custom_emoji_sticker_set_thumbnail.py
index b78e8c84..4b92229f 100644
--- a/tests/test_api/test_methods/test_set_custom_emoji_sticker_set_thumbnail.py
+++ b/tests/test_api/test_methods/test_set_custom_emoji_sticker_set_thumbnail.py
@@ -11,5 +11,5 @@ class TestSetCustomEmojiStickerSetThumbnail:
response: bool = await bot.set_custom_emoji_sticker_set_thumbnail(
name="test", custom_emoji_id="custom id"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_game_score.py b/tests/test_api/test_methods/test_set_game_score.py
index c2f1a2fa..ea1db0c1 100644
--- a/tests/test_api/test_methods/test_set_game_score.py
+++ b/tests/test_api/test_methods/test_set_game_score.py
@@ -1,5 +1,3 @@
-from typing import Union
-
from aiogram.methods import SetGameScore
from aiogram.types import Message
from tests.mocked_bot import MockedBot
@@ -9,8 +7,8 @@ class TestSetGameScore:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(SetGameScore, ok=True, result=True)
- response: Union[Message, bool] = await bot.set_game_score(
+ response: Message | bool = await bot.set_game_score(
user_id=42, score=100500, inline_message_id="inline message"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_my_commands.py b/tests/test_api/test_methods/test_set_my_commands.py
index 6a42aeed..fa5fe3c3 100644
--- a/tests/test_api/test_methods/test_set_my_commands.py
+++ b/tests/test_api/test_methods/test_set_my_commands.py
@@ -9,5 +9,5 @@ class TestSetMyCommands:
response: bool = await bot.set_my_commands(
commands=[],
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_my_default_administrator_rights.py b/tests/test_api/test_methods/test_set_my_default_administrator_rights.py
index 157bdd38..3f42071e 100644
--- a/tests/test_api/test_methods/test_set_my_default_administrator_rights.py
+++ b/tests/test_api/test_methods/test_set_my_default_administrator_rights.py
@@ -7,5 +7,5 @@ class TestSetMyDefaultAdministratorRights:
prepare_result = bot.add_result_for(SetMyDefaultAdministratorRights, ok=True, result=True)
response: bool = await bot.set_my_default_administrator_rights()
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_my_description.py b/tests/test_api/test_methods/test_set_my_description.py
index 2c0dc6f3..a2134cbc 100644
--- a/tests/test_api/test_methods/test_set_my_description.py
+++ b/tests/test_api/test_methods/test_set_my_description.py
@@ -7,5 +7,5 @@ class TestSetMyDescription:
prepare_result = bot.add_result_for(SetMyDescription, ok=True, result=True)
response: bool = await bot.set_my_description(description="Test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_my_profile_photo.py b/tests/test_api/test_methods/test_set_my_profile_photo.py
new file mode 100644
index 00000000..2bf596f5
--- /dev/null
+++ b/tests/test_api/test_methods/test_set_my_profile_photo.py
@@ -0,0 +1,14 @@
+from aiogram.methods import SetMyProfilePhoto
+from aiogram.types import InputProfilePhotoStatic
+from tests.mocked_bot import MockedBot
+
+
+class TestSetMyProfilePhoto:
+ async def test_bot_method(self, bot: MockedBot):
+ prepare_result = bot.add_result_for(SetMyProfilePhoto, ok=True, result=True)
+
+ response: bool = await bot.set_my_profile_photo(
+ photo=InputProfilePhotoStatic(photo="file_id")
+ )
+ bot.get_request()
+ assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_my_short_description.py b/tests/test_api/test_methods/test_set_my_short_description.py
index 6bebc09b..729b2670 100644
--- a/tests/test_api/test_methods/test_set_my_short_description.py
+++ b/tests/test_api/test_methods/test_set_my_short_description.py
@@ -7,5 +7,5 @@ class TestSetMyShortDescription:
prepare_result = bot.add_result_for(SetMyShortDescription, ok=True, result=True)
response: bool = await bot.set_my_short_description(short_description="Test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_passport_data_errors.py b/tests/test_api/test_methods/test_set_passport_data_errors.py
index c3afe7de..e21b48ec 100644
--- a/tests/test_api/test_methods/test_set_passport_data_errors.py
+++ b/tests/test_api/test_methods/test_set_passport_data_errors.py
@@ -17,5 +17,5 @@ class TestSetPassportDataErrors:
)
],
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_sticker_emoji_list.py b/tests/test_api/test_methods/test_set_sticker_emoji_list.py
index 82acb7db..97266d8e 100644
--- a/tests/test_api/test_methods/test_set_sticker_emoji_list.py
+++ b/tests/test_api/test_methods/test_set_sticker_emoji_list.py
@@ -7,5 +7,5 @@ class TestSetStickerEmojiList:
prepare_result = bot.add_result_for(SetStickerEmojiList, ok=True, result=True)
response: bool = await bot.set_sticker_emoji_list(sticker="sticker id", emoji_list=["X"])
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_sticker_keywords.py b/tests/test_api/test_methods/test_set_sticker_keywords.py
index 4c279aab..21f70b2c 100644
--- a/tests/test_api/test_methods/test_set_sticker_keywords.py
+++ b/tests/test_api/test_methods/test_set_sticker_keywords.py
@@ -7,5 +7,5 @@ class TestSetStickerKeywords:
prepare_result = bot.add_result_for(SetStickerKeywords, ok=True, result=True)
response: bool = await bot.set_sticker_keywords(sticker="sticker id", keywords=["X"])
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_sticker_mask_position.py b/tests/test_api/test_methods/test_set_sticker_mask_position.py
index ac9f9e0a..3f842ceb 100644
--- a/tests/test_api/test_methods/test_set_sticker_mask_position.py
+++ b/tests/test_api/test_methods/test_set_sticker_mask_position.py
@@ -7,5 +7,5 @@ class TestSetStickerEmojiList:
prepare_result = bot.add_result_for(SetStickerMaskPosition, ok=True, result=True)
response: bool = await bot.set_sticker_mask_position(sticker="sticker id")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_sticker_position_in_set.py b/tests/test_api/test_methods/test_set_sticker_position_in_set.py
index 2640de82..2646d24d 100644
--- a/tests/test_api/test_methods/test_set_sticker_position_in_set.py
+++ b/tests/test_api/test_methods/test_set_sticker_position_in_set.py
@@ -7,5 +7,5 @@ class TestSetStickerPositionInSet:
prepare_result = bot.add_result_for(SetStickerPositionInSet, ok=True, result=True)
response: bool = await bot.set_sticker_position_in_set(sticker="sticker", position=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_sticker_set_thumbnail.py b/tests/test_api/test_methods/test_set_sticker_set_thumbnail.py
index 5d4b745b..45dc9700 100644
--- a/tests/test_api/test_methods/test_set_sticker_set_thumbnail.py
+++ b/tests/test_api/test_methods/test_set_sticker_set_thumbnail.py
@@ -10,5 +10,5 @@ class TestSetStickerSetThumbnail:
response: bool = await bot.set_sticker_set_thumbnail(
name="test", format=StickerFormat.STATIC, user_id=42
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_sticker_set_title.py b/tests/test_api/test_methods/test_set_sticker_set_title.py
index a4eb9d09..6c0b7d21 100644
--- a/tests/test_api/test_methods/test_set_sticker_set_title.py
+++ b/tests/test_api/test_methods/test_set_sticker_set_title.py
@@ -7,5 +7,5 @@ class TestSetStickerSetTitle:
prepare_result = bot.add_result_for(SetStickerSetTitle, ok=True, result=True)
response: bool = await bot.set_sticker_set_title(name="test", title="Test")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_user_emoji_status.py b/tests/test_api/test_methods/test_set_user_emoji_status.py
index 4e661eaf..1a03d5f7 100644
--- a/tests/test_api/test_methods/test_set_user_emoji_status.py
+++ b/tests/test_api/test_methods/test_set_user_emoji_status.py
@@ -13,5 +13,5 @@ class TestSetUserEmojiStatus:
emoji_status_custom_emoji_id="emoji_status_custom_emoji_id",
emoji_status_expiration_date=datetime.now() + timedelta(days=1),
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_set_webhook.py b/tests/test_api/test_methods/test_set_webhook.py
index 37f75a9c..dd9f6902 100644
--- a/tests/test_api/test_methods/test_set_webhook.py
+++ b/tests/test_api/test_methods/test_set_webhook.py
@@ -7,5 +7,5 @@ class TestSetWebhook:
prepare_result = bot.add_result_for(SetWebhook, ok=True, result=True)
response: bool = await bot.set_webhook(url="https://example.com")
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_stop_message_live_location.py b/tests/test_api/test_methods/test_stop_message_live_location.py
index b0b7f397..46792924 100644
--- a/tests/test_api/test_methods/test_stop_message_live_location.py
+++ b/tests/test_api/test_methods/test_stop_message_live_location.py
@@ -1,5 +1,3 @@
-from typing import Union
-
from aiogram.methods import StopMessageLiveLocation
from aiogram.types import Message
from tests.mocked_bot import MockedBot
@@ -9,8 +7,8 @@ class TestStopMessageLiveLocation:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(StopMessageLiveLocation, ok=True, result=True)
- response: Union[Message, bool] = await bot.stop_message_live_location(
+ response: Message | bool = await bot.stop_message_live_location(
inline_message_id="inline message id"
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_stop_poll.py b/tests/test_api/test_methods/test_stop_poll.py
index 5a89e1ed..64d0406c 100644
--- a/tests/test_api/test_methods/test_stop_poll.py
+++ b/tests/test_api/test_methods/test_stop_poll.py
@@ -22,5 +22,5 @@ class TestStopPoll:
)
response: Poll = await bot.stop_poll(chat_id=42, message_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_transfer_business_account_stars.py b/tests/test_api/test_methods/test_transfer_business_account_stars.py
index c45b87c6..d8253006 100644
--- a/tests/test_api/test_methods/test_transfer_business_account_stars.py
+++ b/tests/test_api/test_methods/test_transfer_business_account_stars.py
@@ -9,5 +9,5 @@ class TestTransferBusinessAccountStars:
response: bool = await bot.transfer_business_account_stars(
business_connection_id="test_connection_id", star_count=100
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_transfer_gift.py b/tests/test_api/test_methods/test_transfer_gift.py
index 8d2221d1..5de5db02 100644
--- a/tests/test_api/test_methods/test_transfer_gift.py
+++ b/tests/test_api/test_methods/test_transfer_gift.py
@@ -12,5 +12,5 @@ class TestTransferGift:
new_owner_chat_id=123456789,
star_count=50,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_unban_chat_member.py b/tests/test_api/test_methods/test_unban_chat_member.py
index 23f922ad..3a2acfee 100644
--- a/tests/test_api/test_methods/test_unban_chat_member.py
+++ b/tests/test_api/test_methods/test_unban_chat_member.py
@@ -7,5 +7,5 @@ class TestUnbanChatMember:
prepare_result = bot.add_result_for(UnbanChatMember, ok=True, result=True)
response: bool = await bot.unban_chat_member(chat_id=-42, user_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_unban_chat_sender_chat.py b/tests/test_api/test_methods/test_unban_chat_sender_chat.py
index e23c1b13..0ca89e0a 100755
--- a/tests/test_api/test_methods/test_unban_chat_sender_chat.py
+++ b/tests/test_api/test_methods/test_unban_chat_sender_chat.py
@@ -10,5 +10,5 @@ class TestUnbanChatSenderChat:
chat_id=-42,
sender_chat_id=-1337,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_unhide_general_forum_topic.py b/tests/test_api/test_methods/test_unhide_general_forum_topic.py
index da830cd6..9de88ad6 100644
--- a/tests/test_api/test_methods/test_unhide_general_forum_topic.py
+++ b/tests/test_api/test_methods/test_unhide_general_forum_topic.py
@@ -7,5 +7,5 @@ class TestUnhideGeneralForumTopic:
prepare_result = bot.add_result_for(UnhideGeneralForumTopic, ok=True, result=True)
response: bool = await bot.unhide_general_forum_topic(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_unpin_all_chat_messages.py b/tests/test_api/test_methods/test_unpin_all_chat_messages.py
index 64ce1d4c..ba92871d 100644
--- a/tests/test_api/test_methods/test_unpin_all_chat_messages.py
+++ b/tests/test_api/test_methods/test_unpin_all_chat_messages.py
@@ -9,5 +9,5 @@ class TestUnpinAllChatMessages:
response: bool = await bot.unpin_all_chat_messages(
chat_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_unpin_all_forum_topic_messages.py b/tests/test_api/test_methods/test_unpin_all_forum_topic_messages.py
index 045fd926..74171470 100644
--- a/tests/test_api/test_methods/test_unpin_all_forum_topic_messages.py
+++ b/tests/test_api/test_methods/test_unpin_all_forum_topic_messages.py
@@ -10,5 +10,5 @@ class TestUnpinAllForumTopicMessages:
chat_id=42,
message_thread_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_unpin_all_general_forum_topic_messages.py b/tests/test_api/test_methods/test_unpin_all_general_forum_topic_messages.py
index 09a3658e..21325a8f 100644
--- a/tests/test_api/test_methods/test_unpin_all_general_forum_topic_messages.py
+++ b/tests/test_api/test_methods/test_unpin_all_general_forum_topic_messages.py
@@ -11,5 +11,5 @@ class TestUnpinAllForumTopicMessages:
response: bool = await bot.unpin_all_general_forum_topic_messages(
chat_id=42,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_unpin_chat_message.py b/tests/test_api/test_methods/test_unpin_chat_message.py
index fdf1622d..8506a021 100644
--- a/tests/test_api/test_methods/test_unpin_chat_message.py
+++ b/tests/test_api/test_methods/test_unpin_chat_message.py
@@ -7,5 +7,5 @@ class TestUnpinChatMessage:
prepare_result = bot.add_result_for(UnpinChatMessage, ok=True, result=True)
response: bool = await bot.unpin_chat_message(chat_id=-42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_upgrade_gift.py b/tests/test_api/test_methods/test_upgrade_gift.py
index f88f8dff..4461b677 100644
--- a/tests/test_api/test_methods/test_upgrade_gift.py
+++ b/tests/test_api/test_methods/test_upgrade_gift.py
@@ -12,5 +12,5 @@ class TestUpgradeGift:
keep_original_details=True,
star_count=100,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_upload_sticker_file.py b/tests/test_api/test_methods/test_upload_sticker_file.py
index 0823772c..31819054 100644
--- a/tests/test_api/test_methods/test_upload_sticker_file.py
+++ b/tests/test_api/test_methods/test_upload_sticker_file.py
@@ -15,5 +15,5 @@ class TestUploadStickerFile:
sticker=BufferedInputFile(b"", "file.png"),
sticker_format=StickerFormat.STATIC,
)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_verify_chat.py b/tests/test_api/test_methods/test_verify_chat.py
index 1473b976..17401621 100644
--- a/tests/test_api/test_methods/test_verify_chat.py
+++ b/tests/test_api/test_methods/test_verify_chat.py
@@ -1,5 +1,4 @@
from aiogram.methods import VerifyChat
-from aiogram.types import Poll
from tests.mocked_bot import MockedBot
@@ -8,5 +7,5 @@ class TestVerifyChat:
prepare_result = bot.add_result_for(VerifyChat, ok=True, result=True)
response: bool = await bot.verify_chat(chat_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_methods/test_verify_user.py b/tests/test_api/test_methods/test_verify_user.py
index 9bb502ca..b69471a3 100644
--- a/tests/test_api/test_methods/test_verify_user.py
+++ b/tests/test_api/test_methods/test_verify_user.py
@@ -1,5 +1,4 @@
-from aiogram.methods import VerifyChat, VerifyUser
-from aiogram.types import Poll
+from aiogram.methods import VerifyUser
from tests.mocked_bot import MockedBot
@@ -8,5 +7,5 @@ class TestVerifyUser:
prepare_result = bot.add_result_for(VerifyUser, ok=True, result=True)
response: bool = await bot.verify_user(user_id=42)
- request = bot.get_request()
+ bot.get_request()
assert response == prepare_result.result
diff --git a/tests/test_api/test_types/test_callback_query.py b/tests/test_api/test_types/test_callback_query.py
index f3716fe1..ed45d2b1 100644
--- a/tests/test_api/test_types/test_callback_query.py
+++ b/tests/test_api/test_types/test_callback_query.py
@@ -8,7 +8,7 @@ class TestCallbackQuery:
id="id", from_user=User(id=42, is_bot=False, first_name="name"), chat_instance="chat"
)
- kwargs = dict(text="foo", show_alert=True, url="https://foo.bar/", cache_time=123)
+ kwargs = {"text": "foo", "show_alert": True, "url": "https://foo.bar/", "cache_time": 123}
api_method = callback_query.answer(**kwargs)
diff --git a/tests/test_api/test_types/test_chat.py b/tests/test_api/test_types/test_chat.py
index 14cc3dc2..360b2ee1 100644
--- a/tests/test_api/test_types/test_chat.py
+++ b/tests/test_api/test_types/test_chat.py
@@ -1,5 +1,3 @@
-from typing import Optional
-
from pytest import mark, param
from aiogram.enums import ChatAction
@@ -199,9 +197,9 @@ class TestChat:
)
def test_full_name(
self,
- first: Optional[str],
- last: Optional[str],
- title: Optional[str],
+ first: str | None,
+ last: str | None,
+ title: str | None,
chat_type: str,
result: str,
):
diff --git a/tests/test_api/test_types/test_chat_join_request.py b/tests/test_api/test_types/test_chat_join_request.py
index ec442cb1..3bb1fc3f 100644
--- a/tests/test_api/test_types/test_chat_join_request.py
+++ b/tests/test_api/test_types/test_chat_join_request.py
@@ -1,5 +1,5 @@
import datetime
-from typing import Any, Dict, Type, Union
+from typing import Any
import pytest
@@ -59,45 +59,49 @@ class TestChatJoinRequest:
@pytest.mark.parametrize(
"alias_for_method,kwargs,method_class",
[
- ["answer_animation", dict(animation="animation"), SendAnimation],
- ["answer_audio", dict(audio="audio"), SendAudio],
- ["answer_contact", dict(phone_number="+000000000000", first_name="Test"), SendContact],
- ["answer_document", dict(document="document"), SendDocument],
- ["answer_game", dict(game_short_name="game"), SendGame],
+ ["answer_animation", {"animation": "animation"}, SendAnimation],
+ ["answer_audio", {"audio": "audio"}, SendAudio],
+ [
+ "answer_contact",
+ {"phone_number": "+000000000000", "first_name": "Test"},
+ SendContact,
+ ],
+ ["answer_document", {"document": "document"}, SendDocument],
+ ["answer_game", {"game_short_name": "game"}, SendGame],
[
"answer_invoice",
- dict(
- title="title",
- description="description",
- payload="payload",
- provider_token="provider_token",
- start_parameter="start_parameter",
- currency="currency",
- prices=[],
- ),
+ {
+ "title": "title",
+ "description": "description",
+ "payload": "payload",
+ "provider_token": "provider_token",
+ "start_parameter": "start_parameter",
+ "currency": "currency",
+ "prices": [],
+ },
SendInvoice,
],
- ["answer_location", dict(latitude=0.42, longitude=0.42), SendLocation],
- ["answer_media_group", dict(media=[]), SendMediaGroup],
- ["answer", dict(text="test"), SendMessage],
- ["answer_photo", dict(photo="photo"), SendPhoto],
- ["answer_poll", dict(question="Q?", options=[]), SendPoll],
- ["answer_dice", dict(), SendDice],
- ["answer_sticker", dict(sticker="sticker"), SendSticker],
- ["answer_sticker", dict(sticker="sticker"), SendSticker],
+ ["answer_location", {"latitude": 0.42, "longitude": 0.42}, SendLocation],
+ ["answer_media_group", {"media": []}, SendMediaGroup],
+ ["answer", {"text": "test"}, SendMessage],
+ ["answer_photo", {"photo": "photo"}, SendPhoto],
+ ["answer_poll", {"question": "Q?", "options": []}, SendPoll],
+ ["answer_dice", {}, SendDice],
+ ["answer_sticker", {"sticker": "sticker"}, SendSticker],
+ ["answer_sticker", {"sticker": "sticker"}, SendSticker],
[
"answer_venue",
- dict(
- latitude=0.42,
- longitude=0.42,
- title="title",
- address="address",
- ),
+ {
+ "latitude": 0.42,
+ "longitude": 0.42,
+ "title": "title",
+ "address": "address",
+ },
SendVenue,
],
- ["answer_video", dict(video="video"), SendVideo],
- ["answer_video_note", dict(video_note="video_note"), SendVideoNote],
- ["answer_voice", dict(voice="voice"), SendVoice],
+ ["answer_video", {"video": "video"}, SendVideo],
+ ["answer_video_note", {"video_note": "video_note"}, SendVideoNote],
+ ["answer_voice", {"voice": "voice"}, SendVoice],
],
)
@pytest.mark.parametrize("suffix", ["", "_pm"])
@@ -105,27 +109,25 @@ class TestChatJoinRequest:
self,
alias_for_method: str,
suffix: str,
- kwargs: Dict[str, Any],
- method_class: Type[
- Union[
- SendAnimation,
- SendAudio,
- SendContact,
- SendDocument,
- SendGame,
- SendInvoice,
- SendLocation,
- SendMediaGroup,
- SendMessage,
- SendPhoto,
- SendPoll,
- SendSticker,
- SendSticker,
- SendVenue,
- SendVideo,
- SendVideoNote,
- SendVoice,
- ]
+ kwargs: dict[str, Any],
+ method_class: type[
+ SendAnimation
+ | SendAudio
+ | SendContact
+ | SendDocument
+ | SendGame
+ | SendInvoice
+ | SendLocation
+ | SendMediaGroup
+ | SendMessage
+ | SendPhoto
+ | SendPoll
+ | SendSticker
+ | SendSticker
+ | SendVenue
+ | SendVideo
+ | SendVideoNote
+ | SendVoice
],
):
event = ChatJoinRequest(
diff --git a/tests/test_api/test_types/test_chat_member_updated.py b/tests/test_api/test_types/test_chat_member_updated.py
index 2d9c9a94..a48e7a0d 100644
--- a/tests/test_api/test_types/test_chat_member_updated.py
+++ b/tests/test_api/test_types/test_chat_member_updated.py
@@ -1,5 +1,5 @@
import datetime
-from typing import Any, Dict, Type, Union
+from typing import Any
import pytest
@@ -35,71 +35,73 @@ class TestChatMemberUpdated:
@pytest.mark.parametrize(
"alias_for_method,kwargs,method_class",
[
- ["answer_animation", dict(animation="animation"), SendAnimation],
- ["answer_audio", dict(audio="audio"), SendAudio],
- ["answer_contact", dict(phone_number="+000000000000", first_name="Test"), SendContact],
- ["answer_document", dict(document="document"), SendDocument],
- ["answer_game", dict(game_short_name="game"), SendGame],
+ ["answer_animation", {"animation": "animation"}, SendAnimation],
+ ["answer_audio", {"audio": "audio"}, SendAudio],
+ [
+ "answer_contact",
+ {"phone_number": "+000000000000", "first_name": "Test"},
+ SendContact,
+ ],
+ ["answer_document", {"document": "document"}, SendDocument],
+ ["answer_game", {"game_short_name": "game"}, SendGame],
[
"answer_invoice",
- dict(
- title="title",
- description="description",
- payload="payload",
- provider_token="provider_token",
- start_parameter="start_parameter",
- currency="currency",
- prices=[],
- ),
+ {
+ "title": "title",
+ "description": "description",
+ "payload": "payload",
+ "provider_token": "provider_token",
+ "start_parameter": "start_parameter",
+ "currency": "currency",
+ "prices": [],
+ },
SendInvoice,
],
- ["answer_location", dict(latitude=0.42, longitude=0.42), SendLocation],
- ["answer_media_group", dict(media=[]), SendMediaGroup],
- ["answer", dict(text="test"), SendMessage],
- ["answer_photo", dict(photo="photo"), SendPhoto],
- ["answer_poll", dict(question="Q?", options=[]), SendPoll],
- ["answer_dice", dict(), SendDice],
- ["answer_sticker", dict(sticker="sticker"), SendSticker],
- ["answer_sticker", dict(sticker="sticker"), SendSticker],
+ ["answer_location", {"latitude": 0.42, "longitude": 0.42}, SendLocation],
+ ["answer_media_group", {"media": []}, SendMediaGroup],
+ ["answer", {"text": "test"}, SendMessage],
+ ["answer_photo", {"photo": "photo"}, SendPhoto],
+ ["answer_poll", {"question": "Q?", "options": []}, SendPoll],
+ ["answer_dice", {}, SendDice],
+ ["answer_sticker", {"sticker": "sticker"}, SendSticker],
+ ["answer_sticker", {"sticker": "sticker"}, SendSticker],
[
"answer_venue",
- dict(
- latitude=0.42,
- longitude=0.42,
- title="title",
- address="address",
- ),
+ {
+ "latitude": 0.42,
+ "longitude": 0.42,
+ "title": "title",
+ "address": "address",
+ },
SendVenue,
],
- ["answer_video", dict(video="video"), SendVideo],
- ["answer_video_note", dict(video_note="video_note"), SendVideoNote],
- ["answer_voice", dict(voice="voice"), SendVoice],
+ ["answer_video", {"video": "video"}, SendVideo],
+ ["answer_video_note", {"video_note": "video_note"}, SendVideoNote],
+ ["answer_voice", {"voice": "voice"}, SendVoice],
],
)
def test_answer_aliases(
self,
alias_for_method: str,
- kwargs: Dict[str, Any],
- method_class: Type[
- Union[
- SendAnimation,
- SendAudio,
- SendContact,
- SendDocument,
- SendGame,
- SendInvoice,
- SendLocation,
- SendMediaGroup,
- SendMessage,
- SendPhoto,
- SendPoll,
- SendSticker,
- SendSticker,
- SendVenue,
- SendVideo,
- SendVideoNote,
- SendVoice,
- ]
+ kwargs: dict[str, Any],
+ method_class: type[
+ SendAnimation
+ | SendAudio
+ | SendContact
+ | SendDocument
+ | SendGame
+ | SendInvoice
+ | SendLocation
+ | SendMediaGroup
+ | SendMessage
+ | SendPhoto
+ | SendPoll
+ | SendSticker
+ | SendSticker
+ | SendVenue
+ | SendVideo
+ | SendVideoNote
+ | SendVoice
],
):
user = User(id=42, is_bot=False, first_name="Test")
diff --git a/tests/test_api/test_types/test_contact.py b/tests/test_api/test_types/test_contact.py
new file mode 100644
index 00000000..bc555809
--- /dev/null
+++ b/tests/test_api/test_types/test_contact.py
@@ -0,0 +1,20 @@
+import pytest
+
+from aiogram.types import Contact
+
+
+class TestContact:
+ @pytest.mark.parametrize(
+ "first,last,result",
+ [
+ ["User", None, "User"],
+ ["", None, ""],
+ [" ", None, " "],
+ ["User", "Name", "User Name"],
+ ["User", " ", "User "],
+ [" ", " ", " "],
+ ],
+ )
+ def test_full_name(self, first: str, last: str, result: bool):
+ contact = Contact(phone_number="911", first_name=first, last_name=last)
+ assert contact.full_name == result
diff --git a/tests/test_api/test_types/test_inaccessible_message.py b/tests/test_api/test_types/test_inaccessible_message.py
index 5a7ee111..4719957d 100644
--- a/tests/test_api/test_types/test_inaccessible_message.py
+++ b/tests/test_api/test_types/test_inaccessible_message.py
@@ -1,4 +1,4 @@
-from typing import Any, Dict, Type, Union
+from typing import Any
import pytest
@@ -49,46 +49,46 @@ class TestMessage:
@pytest.mark.parametrize(
"alias_for_method,kwargs,method_class",
[
- ["animation", dict(animation="animation"), SendAnimation],
- ["audio", dict(audio="audio"), SendAudio],
- ["contact", dict(phone_number="+000000000000", first_name="Test"), SendContact],
- ["document", dict(document="document"), SendDocument],
- ["game", dict(game_short_name="game"), SendGame],
+ ["animation", {"animation": "animation"}, SendAnimation],
+ ["audio", {"audio": "audio"}, SendAudio],
+ ["contact", {"phone_number": "+000000000000", "first_name": "Test"}, SendContact],
+ ["document", {"document": "document"}, SendDocument],
+ ["game", {"game_short_name": "game"}, SendGame],
[
"invoice",
- dict(
- title="title",
- description="description",
- payload="payload",
- provider_token="provider_token",
- start_parameter="start_parameter",
- currency="currency",
- prices=[],
- ),
+ {
+ "title": "title",
+ "description": "description",
+ "payload": "payload",
+ "provider_token": "provider_token",
+ "start_parameter": "start_parameter",
+ "currency": "currency",
+ "prices": [],
+ },
SendInvoice,
],
- ["location", dict(latitude=0.42, longitude=0.42), SendLocation],
- ["media_group", dict(media=[]), SendMediaGroup],
- ["", dict(text="test"), SendMessage],
- ["photo", dict(photo="photo"), SendPhoto],
- ["poll", dict(question="Q?", options=[]), SendPoll],
- ["dice", dict(), SendDice],
- ["sticker", dict(sticker="sticker"), SendSticker],
- ["sticker", dict(sticker="sticker"), SendSticker],
+ ["location", {"latitude": 0.42, "longitude": 0.42}, SendLocation],
+ ["media_group", {"media": []}, SendMediaGroup],
+ ["", {"text": "test"}, SendMessage],
+ ["photo", {"photo": "photo"}, SendPhoto],
+ ["poll", {"question": "Q?", "options": []}, SendPoll],
+ ["dice", {}, SendDice],
+ ["sticker", {"sticker": "sticker"}, SendSticker],
+ ["sticker", {"sticker": "sticker"}, SendSticker],
[
"venue",
- dict(
- latitude=0.42,
- longitude=0.42,
- title="title",
- address="address",
- ),
+ {
+ "latitude": 0.42,
+ "longitude": 0.42,
+ "title": "title",
+ "address": "address",
+ },
SendVenue,
],
- ["video", dict(video="video"), SendVideo],
- ["video_note", dict(video_note="video_note"), SendVideoNote],
- ["voice", dict(voice="voice"), SendVoice],
- ["paid_media", dict(media=[], star_count=42), SendPaidMedia],
+ ["video", {"video": "video"}, SendVideo],
+ ["video_note", {"video_note": "video_note"}, SendVideoNote],
+ ["voice", {"voice": "voice"}, SendVoice],
+ ["paid_media", {"media": [], "star_count": 42}, SendPaidMedia],
],
)
@pytest.mark.parametrize("alias_type", ["reply", "answer"])
@@ -96,28 +96,26 @@ class TestMessage:
self,
alias_for_method: str,
alias_type: str,
- kwargs: Dict[str, Any],
- method_class: Type[
- Union[
- SendAnimation,
- SendAudio,
- SendContact,
- SendDocument,
- SendGame,
- SendInvoice,
- SendLocation,
- SendMediaGroup,
- SendMessage,
- SendPhoto,
- SendPoll,
- SendSticker,
- SendSticker,
- SendVenue,
- SendVideo,
- SendVideoNote,
- SendVoice,
- SendPaidMedia,
- ]
+ kwargs: dict[str, Any],
+ method_class: type[
+ SendAnimation
+ | SendAudio
+ | SendContact
+ | SendDocument
+ | SendGame
+ | SendInvoice
+ | SendLocation
+ | SendMediaGroup
+ | SendMessage
+ | SendPhoto
+ | SendPoll
+ | SendSticker
+ | SendSticker
+ | SendVenue
+ | SendVideo
+ | SendVideoNote
+ | SendVoice
+ | SendPaidMedia
],
):
message = InaccessibleMessage(
diff --git a/tests/test_api/test_types/test_inline_query.py b/tests/test_api/test_types/test_inline_query.py
index c822649d..088f0cc5 100644
--- a/tests/test_api/test_types/test_inline_query.py
+++ b/tests/test_api/test_types/test_inline_query.py
@@ -11,13 +11,13 @@ class TestInlineQuery:
offset="",
)
- kwargs = dict(
- results=[],
- cache_time=123,
- next_offset="123",
- switch_pm_text="foo",
- switch_pm_parameter="foo",
- )
+ kwargs = {
+ "results": [],
+ "cache_time": 123,
+ "next_offset": "123",
+ "switch_pm_text": "foo",
+ "switch_pm_parameter": "foo",
+ }
api_method = inline_query.answer(**kwargs)
diff --git a/tests/test_api/test_types/test_message.py b/tests/test_api/test_types/test_message.py
index f211f300..51d1790d 100644
--- a/tests/test_api/test_types/test_message.py
+++ b/tests/test_api/test_types/test_message.py
@@ -1,5 +1,5 @@
import datetime
-from typing import Any, Dict, Optional, Type, Union
+from typing import Any
import pytest
@@ -46,6 +46,8 @@ from aiogram.types import (
Chat,
ChatBackground,
ChatBoostAdded,
+ ChatOwnerChanged,
+ ChatOwnerLeft,
ChatShared,
Checklist,
ChecklistTask,
@@ -253,6 +255,31 @@ TEST_MESSAGE_LEFT_CHAT_MEMBER = Message(
chat=Chat(id=42, type="private"),
from_user=User(id=42, is_bot=False, first_name="Test"),
)
+TEST_MESSAGE_CHAT_OWNER_LEFT = Message(
+ message_id=42,
+ date=datetime.datetime.now(),
+ chat_owner_left=ChatOwnerLeft(
+ new_owner=User(id=43, is_bot=False, first_name="NewOwner"),
+ ),
+ chat=Chat(id=42, type="private"),
+ from_user=User(id=42, is_bot=False, first_name="Test"),
+)
+TEST_MESSAGE_CHAT_OWNER_LEFT_NO_SUCCESSOR = Message(
+ message_id=42,
+ date=datetime.datetime.now(),
+ chat_owner_left=ChatOwnerLeft(),
+ chat=Chat(id=42, type="private"),
+ from_user=User(id=42, is_bot=False, first_name="Test"),
+)
+TEST_MESSAGE_CHAT_OWNER_CHANGED = Message(
+ message_id=42,
+ date=datetime.datetime.now(),
+ chat_owner_changed=ChatOwnerChanged(
+ new_owner=User(id=43, is_bot=False, first_name="NewOwner"),
+ ),
+ chat=Chat(id=42, type="private"),
+ from_user=User(id=42, is_bot=False, first_name="Test"),
+)
TEST_MESSAGE_INVOICE = Message(
message_id=42,
date=datetime.datetime.now(),
@@ -849,6 +876,8 @@ MESSAGES_AND_CONTENT_TYPES = [
[TEST_MESSAGE_LOCATION, ContentType.LOCATION],
[TEST_MESSAGE_NEW_CHAT_MEMBERS, ContentType.NEW_CHAT_MEMBERS],
[TEST_MESSAGE_LEFT_CHAT_MEMBER, ContentType.LEFT_CHAT_MEMBER],
+ [TEST_MESSAGE_CHAT_OWNER_LEFT, ContentType.CHAT_OWNER_LEFT],
+ [TEST_MESSAGE_CHAT_OWNER_CHANGED, ContentType.CHAT_OWNER_CHANGED],
[TEST_MESSAGE_INVOICE, ContentType.INVOICE],
[TEST_MESSAGE_SUCCESSFUL_PAYMENT, ContentType.SUCCESSFUL_PAYMENT],
[TEST_MESSAGE_CONNECTED_WEBSITE, ContentType.CONNECTED_WEBSITE],
@@ -930,6 +959,8 @@ MESSAGES_AND_COPY_METHODS = [
[TEST_MESSAGE_STORY, ForwardMessage],
[TEST_MESSAGE_NEW_CHAT_MEMBERS, None],
[TEST_MESSAGE_LEFT_CHAT_MEMBER, None],
+ [TEST_MESSAGE_CHAT_OWNER_LEFT, None],
+ [TEST_MESSAGE_CHAT_OWNER_CHANGED, None],
[TEST_MESSAGE_INVOICE, None],
[TEST_MESSAGE_SUCCESSFUL_PAYMENT, None],
[TEST_MESSAGE_CONNECTED_WEBSITE, None],
@@ -989,7 +1020,7 @@ MESSAGES_AND_COPY_METHODS = [
class TestAllMessageTypesTested:
@pytest.fixture(scope="function")
def known_content_types(self):
- content_types = {t for t in ContentType}
+ content_types = set(ContentType)
content_types.remove(ContentType.ANY)
return content_types
@@ -1023,6 +1054,11 @@ class TestMessage:
def test_content_type(self, message: Message, content_type: str):
assert message.content_type == content_type
+ def test_chat_owner_left_no_successor(self):
+ assert (
+ TEST_MESSAGE_CHAT_OWNER_LEFT_NO_SUCCESSOR.content_type == ContentType.CHAT_OWNER_LEFT
+ )
+
def test_as_reply_parameters(self):
message = Message(
message_id=42, chat=Chat(id=42, type="private"), date=datetime.datetime.now()
@@ -1034,46 +1070,46 @@ class TestMessage:
@pytest.mark.parametrize(
"alias_for_method,kwargs,method_class",
[
- ["animation", dict(animation="animation"), SendAnimation],
- ["audio", dict(audio="audio"), SendAudio],
- ["contact", dict(phone_number="+000000000000", first_name="Test"), SendContact],
- ["document", dict(document="document"), SendDocument],
- ["game", dict(game_short_name="game"), SendGame],
+ ["animation", {"animation": "animation"}, SendAnimation],
+ ["audio", {"audio": "audio"}, SendAudio],
+ ["contact", {"phone_number": "+000000000000", "first_name": "Test"}, SendContact],
+ ["document", {"document": "document"}, SendDocument],
+ ["game", {"game_short_name": "game"}, SendGame],
[
"invoice",
- dict(
- title="title",
- description="description",
- payload="payload",
- provider_token="provider_token",
- start_parameter="start_parameter",
- currency="currency",
- prices=[],
- ),
+ {
+ "title": "title",
+ "description": "description",
+ "payload": "payload",
+ "provider_token": "provider_token",
+ "start_parameter": "start_parameter",
+ "currency": "currency",
+ "prices": [],
+ },
SendInvoice,
],
- ["location", dict(latitude=0.42, longitude=0.42), SendLocation],
- ["media_group", dict(media=[]), SendMediaGroup],
- ["", dict(text="test"), SendMessage],
- ["photo", dict(photo="photo"), SendPhoto],
- ["poll", dict(question="Q?", options=[]), SendPoll],
- ["dice", dict(), SendDice],
- ["sticker", dict(sticker="sticker"), SendSticker],
- ["sticker", dict(sticker="sticker"), SendSticker],
+ ["location", {"latitude": 0.42, "longitude": 0.42}, SendLocation],
+ ["media_group", {"media": []}, SendMediaGroup],
+ ["", {"text": "test"}, SendMessage],
+ ["photo", {"photo": "photo"}, SendPhoto],
+ ["poll", {"question": "Q?", "options": []}, SendPoll],
+ ["dice", {}, SendDice],
+ ["sticker", {"sticker": "sticker"}, SendSticker],
+ ["sticker", {"sticker": "sticker"}, SendSticker],
[
"venue",
- dict(
- latitude=0.42,
- longitude=0.42,
- title="title",
- address="address",
- ),
+ {
+ "latitude": 0.42,
+ "longitude": 0.42,
+ "title": "title",
+ "address": "address",
+ },
SendVenue,
],
- ["video", dict(video="video"), SendVideo],
- ["video_note", dict(video_note="video_note"), SendVideoNote],
- ["voice", dict(voice="voice"), SendVoice],
- ["paid_media", dict(media=[], star_count=42), SendPaidMedia],
+ ["video", {"video": "video"}, SendVideo],
+ ["video_note", {"video_note": "video_note"}, SendVideoNote],
+ ["voice", {"voice": "voice"}, SendVoice],
+ ["paid_media", {"media": [], "star_count": 42}, SendPaidMedia],
],
)
@pytest.mark.parametrize("alias_type", ["reply", "answer"])
@@ -1081,28 +1117,26 @@ class TestMessage:
self,
alias_for_method: str,
alias_type: str,
- kwargs: Dict[str, Any],
- method_class: Type[
- Union[
- SendAnimation,
- SendAudio,
- SendContact,
- SendDocument,
- SendGame,
- SendInvoice,
- SendLocation,
- SendMediaGroup,
- SendMessage,
- SendPhoto,
- SendPoll,
- SendSticker,
- SendSticker,
- SendVenue,
- SendVideo,
- SendVideoNote,
- SendVoice,
- SendPaidMedia,
- ]
+ kwargs: dict[str, Any],
+ method_class: type[
+ SendAnimation
+ | SendAudio
+ | SendContact
+ | SendDocument
+ | SendGame
+ | SendInvoice
+ | SendLocation
+ | SendMediaGroup
+ | SendMessage
+ | SendPhoto
+ | SendPoll
+ | SendSticker
+ | SendSticker
+ | SendVenue
+ | SendVideo
+ | SendVideoNote
+ | SendVoice
+ | SendPaidMedia
],
):
message = Message(
@@ -1145,7 +1179,7 @@ class TestMessage:
def test_send_copy(
self,
message: Message,
- expected_method: Optional[Type[TelegramMethod]],
+ expected_method: type[TelegramMethod] | None,
):
if expected_method is None:
with pytest.raises(TypeError, match="This type of message can't be copied."):
@@ -1181,8 +1215,8 @@ class TestMessage:
def test_send_copy_custom_parse_mode(
self,
message: Message,
- expected_method: Optional[Type[TelegramMethod]],
- custom_parse_mode: Optional[str],
+ expected_method: type[TelegramMethod] | None,
+ custom_parse_mode: str | None,
):
method = message.send_copy(
chat_id=42,
diff --git a/tests/test_api/test_types/test_pre_checkout_query.py b/tests/test_api/test_types/test_pre_checkout_query.py
index ef6c9865..a003c274 100644
--- a/tests/test_api/test_types/test_pre_checkout_query.py
+++ b/tests/test_api/test_types/test_pre_checkout_query.py
@@ -12,7 +12,7 @@ class TestPreCheckoutQuery:
invoice_payload="payload",
)
- kwargs = dict(ok=True, error_message="foo")
+ kwargs = {"ok": True, "error_message": "foo"}
api_method = pre_checkout_query.answer(**kwargs)
diff --git a/tests/test_api/test_types/test_reply_keyboard_remove.py b/tests/test_api/test_types/test_reply_keyboard_remove.py
index 1d252cc5..1e14fd2c 100644
--- a/tests/test_api/test_types/test_reply_keyboard_remove.py
+++ b/tests/test_api/test_types/test_reply_keyboard_remove.py
@@ -1,5 +1,3 @@
-from typing import Dict
-
import pytest
from aiogram.types import ReplyKeyboardRemove
@@ -11,13 +9,13 @@ class TestReplyKeyboardRemove:
"""
def test_remove_keyboard_default_is_true(self):
- assert (
- ReplyKeyboardRemove.model_fields["remove_keyboard"].default is True
- ), "Remove keyboard has incorrect default value!"
+ assert ReplyKeyboardRemove.model_fields["remove_keyboard"].default is True, (
+ "Remove keyboard has incorrect default value!"
+ )
@pytest.mark.parametrize(
"kwargs,expected",
[[{}, True], [{"remove_keyboard": True}, True]],
)
- def test_remove_keyboard_values(self, kwargs: Dict[str, bool], expected: bool):
+ def test_remove_keyboard_values(self, kwargs: dict[str, bool], expected: bool):
assert ReplyKeyboardRemove(**kwargs).remove_keyboard is expected
diff --git a/tests/test_api/test_types/test_shipping_query.py b/tests/test_api/test_types/test_shipping_query.py
index 76668b15..aa3537c5 100644
--- a/tests/test_api/test_types/test_shipping_query.py
+++ b/tests/test_api/test_types/test_shipping_query.py
@@ -28,7 +28,7 @@ class TestInlineQuery:
ShippingOption(id="id", title="foo", prices=[LabeledPrice(label="foo", amount=123)])
]
- kwargs = dict(ok=True, shipping_options=shipping_options, error_message="foo")
+ kwargs = {"ok": True, "shipping_options": shipping_options, "error_message": "foo"}
api_method = shipping_query.answer(**kwargs)
diff --git a/tests/test_api/test_types/test_user.py b/tests/test_api/test_types/test_user.py
index 9f28175b..610fcd9c 100644
--- a/tests/test_api/test_types/test_user.py
+++ b/tests/test_api/test_types/test_user.py
@@ -56,3 +56,9 @@ class TestUser:
method = user.get_profile_photos(description="test")
assert method.user_id == user.id
+
+ def test_get_profile_audios(self):
+ user = User(id=42, is_bot=False, first_name="Test", last_name="User")
+
+ method = user.get_profile_audios(description="test")
+ assert method.user_id == user.id
diff --git a/tests/test_api/test_types/test_video_quality.py b/tests/test_api/test_types/test_video_quality.py
new file mode 100644
index 00000000..20144911
--- /dev/null
+++ b/tests/test_api/test_types/test_video_quality.py
@@ -0,0 +1,61 @@
+import pytest
+
+from aiogram.types import Video, VideoQuality
+
+
+@pytest.fixture()
+def video_quality():
+ return VideoQuality(
+ file_id="abc123",
+ file_unique_id="unique123",
+ width=1920,
+ height=1080,
+ codec="h264",
+ )
+
+
+class TestVideoQuality:
+ def test_instantiation(self, video_quality: VideoQuality):
+ assert video_quality.file_id == "abc123"
+ assert video_quality.file_unique_id == "unique123"
+ assert video_quality.width == 1920
+ assert video_quality.height == 1080
+ assert video_quality.codec == "h264"
+ assert video_quality.file_size is None
+
+ def test_instantiation_with_file_size(self):
+ file_size = 1048576
+ vq = VideoQuality(
+ file_id="abc123",
+ file_unique_id="unique123",
+ width=1920,
+ height=1080,
+ codec="h265",
+ file_size=file_size,
+ )
+ assert vq.file_size == file_size
+
+ def test_video_with_qualities(self, video_quality: VideoQuality):
+ file_size = 524288
+ video = Video(
+ file_id="video123",
+ file_unique_id="unique_video123",
+ width=1920,
+ height=1080,
+ duration=120,
+ qualities=[
+ video_quality,
+ VideoQuality(
+ file_id="q2",
+ file_unique_id="uq2",
+ width=1280,
+ height=720,
+ codec="h264",
+ file_size=file_size,
+ ),
+ ],
+ )
+ assert video.qualities is not None
+ assert len(video.qualities) == 2
+ assert video.qualities[0].width == 1920
+ assert video.qualities[1].file_size == file_size
diff --git a/tests/test_dispatcher/test_dispatcher.py b/tests/test_dispatcher/test_dispatcher.py
index 0b50a2ba..ca7d4092 100644
--- a/tests/test_dispatcher/test_dispatcher.py
+++ b/tests/test_dispatcher/test_dispatcher.py
@@ -6,7 +6,7 @@ import warnings
from asyncio import Event
from collections import Counter
from contextlib import suppress
-from typing import Any, Optional
+from typing import Any
from unittest.mock import AsyncMock, patch
import pytest
@@ -199,7 +199,7 @@ class TestDispatcher:
async def test_process_update_empty(self, bot: MockedBot):
dispatcher = Dispatcher()
- with pytest.warns(RuntimeWarning, match="Detected unknown update type") as record:
+ with pytest.warns(RuntimeWarning, match="Detected unknown update type"):
result = await dispatcher._process_update(bot=bot, update=Update(update_id=42))
assert not result
@@ -819,7 +819,7 @@ class TestDispatcher:
with (
patch(
"aiogram.dispatcher.dispatcher.Dispatcher._process_update", new_callable=AsyncMock
- ) as mocked_process_update,
+ ),
patch(
"aiogram.dispatcher.dispatcher.Dispatcher._listen_updates"
) as patched_listen_updates,
@@ -913,7 +913,7 @@ class TestDispatcher:
patch(
"aiogram.dispatcher.dispatcher.Dispatcher._process_update",
side_effect=mock_process_update,
- ) as mocked_process_update,
+ ),
patch(
"aiogram.dispatcher.dispatcher.Dispatcher._listen_updates"
) as patched_listen_updates,
diff --git a/tests/test_dispatcher/test_event/test_handler.py b/tests/test_dispatcher/test_event/test_handler.py
index 7105a058..5b5bc4c9 100644
--- a/tests/test_dispatcher/test_event/test_handler.py
+++ b/tests/test_dispatcher/test_event/test_handler.py
@@ -1,5 +1,6 @@
import functools
-from typing import Any, Callable, Dict, Set, Union
+from collections.abc import Callable
+from typing import Any
import pytest
from magic_filter import F as A
@@ -29,7 +30,7 @@ async def callback4(foo: int, *, bar: int, baz: int):
class TestFilter(Filter):
- async def __call__(self, foo: int, bar: int, baz: int) -> Union[bool, Dict[str, Any]]:
+ async def __call__(self, foo: int, bar: int, baz: int) -> bool | dict[str, Any]:
return locals()
@@ -61,7 +62,7 @@ class TestCallableObject:
pytest.param(SyncCallable(), {"foo", "bar", "baz"}),
],
)
- def test_init_args_spec(self, callback: Callable, args: Set[str]):
+ def test_init_args_spec(self, callback: Callable, args: set[str]):
obj = CallableObject(callback)
assert set(obj.params) == args
@@ -125,7 +126,7 @@ class TestCallableObject:
],
)
def test_prepare_kwargs(
- self, callback: Callable, kwargs: Dict[str, Any], result: Dict[str, Any]
+ self, callback: Callable, kwargs: dict[str, Any], result: dict[str, Any]
):
obj = CallableObject(callback)
assert obj._prepare_kwargs(kwargs) == result
@@ -147,7 +148,6 @@ class TestFilterObject:
def test_post_init(self):
case = F.test
filter_obj = FilterObject(callback=case)
- print(filter_obj.callback)
assert filter_obj.callback == case.resolve
diff --git a/tests/test_dispatcher/test_event/test_telegram.py b/tests/test_dispatcher/test_event/test_telegram.py
index 713aabb8..36c13ee4 100644
--- a/tests/test_dispatcher/test_event/test_telegram.py
+++ b/tests/test_dispatcher/test_event/test_telegram.py
@@ -1,6 +1,6 @@
import datetime
import functools
-from typing import Any, Dict, NoReturn, Optional, Union
+from typing import Any, NoReturn
import pytest
from pydantic import BaseModel
@@ -31,7 +31,7 @@ async def pipe_handler(*args, **kwargs):
class MyFilter1(Filter, BaseModel):
test: str
- async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
+ async def __call__(self, *args: Any, **kwargs: Any) -> bool | dict[str, Any]:
return True
@@ -44,16 +44,16 @@ class MyFilter3(MyFilter1):
class OptionalFilter(Filter, BaseModel):
- optional: Optional[str]
+ optional: str | None
- async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
+ async def __call__(self, *args: Any, **kwargs: Any) -> bool | dict[str, Any]:
return True
class DefaultFilter(Filter, BaseModel):
default: str = "Default"
- async def __call__(self, *args: Any, **kwargs: Any) -> Union[bool, Dict[str, Any]]:
+ async def __call__(self, *args: Any, **kwargs: Any) -> bool | dict[str, Any]:
return True
diff --git a/tests/test_filters/test_base.py b/tests/test_filters/test_base.py
index 36d5bbee..131cf1b0 100644
--- a/tests/test_filters/test_base.py
+++ b/tests/test_filters/test_base.py
@@ -1,4 +1,4 @@
-from typing import Awaitable
+from collections.abc import Awaitable
from unittest.mock import AsyncMock, patch
from aiogram.filters import Filter
diff --git a/tests/test_filters/test_callback_data.py b/tests/test_filters/test_callback_data.py
index 1bc50cae..79ca5929 100644
--- a/tests/test_filters/test_callback_data.py
+++ b/tests/test_filters/test_callback_data.py
@@ -107,20 +107,20 @@ class TestCallbackData:
def test_pack_optional(self):
class MyCallback1(CallbackData, prefix="test1"):
foo: str
- bar: Optional[int] = None
+ bar: int | None = None
assert MyCallback1(foo="spam").pack() == "test1:spam:"
assert MyCallback1(foo="spam", bar=42).pack() == "test1:spam:42"
class MyCallback2(CallbackData, prefix="test2"):
- foo: Optional[str] = None
+ foo: str | None = None
bar: int
assert MyCallback2(bar=42).pack() == "test2::42"
assert MyCallback2(foo="spam", bar=42).pack() == "test2:spam:42"
class MyCallback3(CallbackData, prefix="test3"):
- foo: Optional[str] = "experiment"
+ foo: str | None = "experiment"
bar: int
assert MyCallback3(bar=42).pack() == "test3:experiment:42"
@@ -141,28 +141,28 @@ class TestCallbackData:
class MyCallback1(CallbackData, prefix="test1"):
foo: str
- bar: Optional[int] = None
+ bar: int | None = None
assert MyCallback1.unpack("test1:spam:") == MyCallback1(foo="spam")
assert MyCallback1.unpack("test1:spam:42") == MyCallback1(foo="spam", bar=42)
class MyCallback2(CallbackData, prefix="test2"):
- foo: Optional[str] = None
+ foo: str | None = None
bar: int
assert MyCallback2.unpack("test2::42") == MyCallback2(bar=42)
assert MyCallback2.unpack("test2:spam:42") == MyCallback2(foo="spam", bar=42)
class MyCallback3(CallbackData, prefix="test3"):
- foo: Optional[str] = "experiment"
+ foo: str | None = "experiment"
bar: int
assert MyCallback3.unpack("test3:experiment:42") == MyCallback3(bar=42)
assert MyCallback3.unpack("test3:spam:42") == MyCallback3(foo="spam", bar=42)
class MyCallback4(CallbackData, prefix="test4"):
- foo: Optional[str] = ""
- bar: Optional[str] = None
+ foo: str | None = ""
+ bar: str | None = None
assert MyCallback4.unpack("test4::") == MyCallback4(foo="", bar=None)
assert MyCallback4.unpack("test4::") == MyCallback4()
diff --git a/tests/test_filters/test_chat_member_updated.py b/tests/test_filters/test_chat_member_updated.py
index 2a608c8f..c88b705e 100644
--- a/tests/test_filters/test_chat_member_updated.py
+++ b/tests/test_filters/test_chat_member_updated.py
@@ -1,5 +1,4 @@
from datetime import datetime
-from typing import Optional
import pytest
@@ -27,7 +26,7 @@ from aiogram.types import (
class ChatMemberCustom(ChatMember):
status: str
- is_member: Optional[bool] = None
+ is_member: bool | None = None
class TestMemberStatusMarker:
diff --git a/tests/test_filters/test_state.py b/tests/test_filters/test_state.py
index 16124ef9..de7ad72d 100644
--- a/tests/test_filters/test_state.py
+++ b/tests/test_filters/test_state.py
@@ -56,7 +56,7 @@ class TestStateFilter:
assert SG.state == copy(SG.state)
assert SG.state == "SG:state"
- assert "SG:state" == SG.state
+ assert SG.state == "SG:state"
assert State() == State()
assert SG.state != 1
diff --git a/tests/test_fsm/storage/test_key_builder.py b/tests/test_fsm/storage/test_key_builder.py
index f62ac505..43df2a0e 100644
--- a/tests/test_fsm/storage/test_key_builder.py
+++ b/tests/test_fsm/storage/test_key_builder.py
@@ -1,4 +1,4 @@
-from typing import Literal, Optional
+from typing import Literal
import pytest
@@ -67,7 +67,7 @@ class TestDefaultKeyBuilder:
async def test_generate_key(
self,
key_builder: DefaultKeyBuilder,
- field: Optional[Literal["data", "state", "lock"]],
+ field: Literal["data", "state", "lock"] | None,
result: str,
):
key = StorageKey(
diff --git a/tests/test_fsm/test_middleware.py b/tests/test_fsm/test_middleware.py
new file mode 100644
index 00000000..82ef93b2
--- /dev/null
+++ b/tests/test_fsm/test_middleware.py
@@ -0,0 +1,90 @@
+from aiogram.fsm.middleware import FSMContextMiddleware
+from aiogram.fsm.storage.memory import DisabledEventIsolation, MemoryStorage
+from aiogram.fsm.strategy import FSMStrategy
+from tests.mocked_bot import MockedBot
+
+CHANNEL_ID = -1001234567890
+THREAD_ID = 42
+
+
+def create_middleware(strategy: FSMStrategy) -> FSMContextMiddleware:
+ return FSMContextMiddleware(
+ storage=MemoryStorage(),
+ events_isolation=DisabledEventIsolation(),
+ strategy=strategy,
+ )
+
+
+class TestFSMContextMiddleware:
+ def test_resolve_context_for_channel_in_chat_strategy(self):
+ bot = MockedBot()
+ middleware = create_middleware(FSMStrategy.CHAT)
+
+ context = middleware.resolve_context(
+ bot=bot,
+ chat_id=CHANNEL_ID,
+ user_id=None,
+ )
+
+ assert context is not None
+ assert context.key.chat_id == CHANNEL_ID
+ assert context.key.user_id == CHANNEL_ID
+
+ def test_resolve_context_with_missing_user_in_chat_topic_strategy_uses_chat_id_for_user_id(
+ self,
+ ):
+ """
+ When user_id is absent (e.g., channel-like updates), chat-scoped strategies
+ should still build a stable FSM key by mirroring chat_id into user_id.
+ """
+ bot = MockedBot()
+ middleware = create_middleware(FSMStrategy.CHAT_TOPIC)
+
+ context = middleware.resolve_context(
+ bot=bot,
+ chat_id=CHANNEL_ID,
+ user_id=None,
+ thread_id=THREAD_ID,
+ )
+
+ assert context is not None
+ assert context.key.chat_id == CHANNEL_ID
+ assert context.key.user_id == CHANNEL_ID
+ assert context.key.thread_id == THREAD_ID
+
+ def test_resolve_context_for_channel_in_user_in_chat_strategy(self):
+ bot = MockedBot()
+ middleware = create_middleware(FSMStrategy.USER_IN_CHAT)
+
+ context = middleware.resolve_context(
+ bot=bot,
+ chat_id=CHANNEL_ID,
+ user_id=None,
+ )
+
+ assert context is None
+
+ def test_resolve_context_for_channel_in_global_user_strategy(self):
+ bot = MockedBot()
+ middleware = create_middleware(FSMStrategy.GLOBAL_USER)
+
+ context = middleware.resolve_context(
+ bot=bot,
+ chat_id=CHANNEL_ID,
+ user_id=None,
+ )
+
+ assert context is None
+
+ def test_resolve_context_for_channel_in_user_in_topic_strategy(self):
+ bot = MockedBot()
+ middleware = create_middleware(FSMStrategy.USER_IN_TOPIC)
+
+ context = middleware.resolve_context(
+ bot=bot,
+ chat_id=CHANNEL_ID,
+ user_id=None,
+ thread_id=THREAD_ID,
+ )
+
+ assert context is None
diff --git a/tests/test_fsm/test_scene.py b/tests/test_fsm/test_scene.py
index 58c2a23f..3a9944b0 100644
--- a/tests/test_fsm/test_scene.py
+++ b/tests/test_fsm/test_scene.py
@@ -309,6 +309,34 @@ class TestSceneHandlerWrapper:
# Check whether result is correct
assert result == 42
+ async def test_scene_handler_wrapper_call_without_scene_context(self):
+ class MyScene(Scene):
+ pass
+
+ async def handler_mock(*args, **kwargs):
+ return 42
+
+ event_update_mock = Update(
+ update_id=42,
+ message=Message(
+ message_id=42,
+ text="test",
+ date=datetime.now(),
+ chat=Chat(
+ type="private",
+ id=42,
+ ),
+ ),
+ )
+
+ scene_handler_wrapper = SceneHandlerWrapper(MyScene, handler_mock)
+
+ with pytest.raises(
+ SceneException,
+ match="Scene context key 'state' is not available. Ensure FSM is enabled and pipeline is intact.",
+ ):
+ await scene_handler_wrapper(event_update_mock, event_update=event_update_mock)
+
def test_scene_handler_wrapper_str(self):
class MyScene(Scene):
pass
@@ -1558,6 +1586,27 @@ class TestSceneRegistry:
handler.assert_called_once_with(event, data)
assert result == handler.return_value
+ async def test_scene_registry_update_middleware_without_state(self):
+ router = Router()
+ registry = SceneRegistry(router)
+ handler = AsyncMock(spec=NextMiddlewareType)
+ event = Update(
+ update_id=42,
+ message=Message(
+ message_id=42,
+ text="test",
+ date=datetime.now(),
+ chat=Chat(id=42, type="private"),
+ ),
+ )
+ data = {}
+
+ result = await registry._update_middleware(handler, event, data)
+
+ assert "scenes" not in data
+ handler.assert_called_once_with(event, data)
+ assert result == handler.return_value
+
async def test_scene_registry_update_middleware_not_update(self, bot: MockedBot):
router = Router()
registry = SceneRegistry(router)
@@ -1604,6 +1653,24 @@ class TestSceneRegistry:
handler.assert_called_once_with(event, data)
assert result == handler.return_value
+ async def test_scene_registry_middleware_without_state(self):
+ router = Router()
+ registry = SceneRegistry(router)
+ handler = AsyncMock(spec=NextMiddlewareType)
+ event = Message(
+ message_id=42,
+ text="test",
+ date=datetime.now(),
+ chat=Chat(id=42, type="private"),
+ )
+ data = {}
+
+ result = await registry._middleware(handler, event, data)
+
+ assert "scenes" not in data
+ handler.assert_called_once_with(event, data)
+ assert result == handler.return_value
+
class TestSceneInheritance:
def test_inherit_handlers(self):
diff --git a/tests/test_issues/test_1672_middleware_data_in_scene.py b/tests/test_issues/test_1672_middleware_data_in_scene.py
index 13885dcb..0935c3f9 100644
--- a/tests/test_issues/test_1672_middleware_data_in_scene.py
+++ b/tests/test_issues/test_1672_middleware_data_in_scene.py
@@ -1,5 +1,6 @@
+from collections.abc import Awaitable, Callable
from datetime import datetime
-from typing import Any, Awaitable, Callable, Dict
+from typing import Any
from unittest.mock import AsyncMock
import pytest
@@ -25,9 +26,9 @@ class EchoScene(Scene, state="test"):
class TestMiddleware(BaseMiddleware):
async def __call__(
self,
- handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
+ handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
- data: Dict[str, Any],
+ data: dict[str, Any],
) -> Any:
data["test_context"] = "Custom context here"
return await handler(event, data)
diff --git a/tests/test_issues/test_1743_channel_post_with_scenes.py b/tests/test_issues/test_1743_channel_post_with_scenes.py
new file mode 100644
index 00000000..cdaf58a7
--- /dev/null
+++ b/tests/test_issues/test_1743_channel_post_with_scenes.py
@@ -0,0 +1,118 @@
+from datetime import datetime
+
+import pytest
+
+from aiogram import Dispatcher, F, Router
+from aiogram.fsm.context import FSMContext
+from aiogram.fsm.scene import Scene, SceneRegistry, on
+from aiogram.fsm.strategy import FSMStrategy
+from aiogram.types import Chat, Message, Update
+from tests.mocked_bot import MockedBot
+
+CHANNEL_ID = -1001234567890
+
+
+class BrowseScene(Scene, state="browse"):
+ pass
+
+
+class ChannelStateScene(Scene, state="channel_state"):
+ @on.channel_post()
+ async def save_message_id(
+ self,
+ message: Message,
+ state: FSMContext,
+ ) -> int:
+ await state.update_data(last_message_id=message.message_id)
+ return message.message_id
+
+
+@pytest.mark.parametrize("update_type", ["channel_post", "edited_channel_post"])
+async def test_channel_events_with_scenes_do_not_require_fsm_state(
+ bot: MockedBot,
+ update_type: str,
+):
+ dispatcher = Dispatcher()
+ channel_router = Router()
+
+ if update_type == "channel_post":
+ channel_router.channel_post.filter(F.chat.id == CHANNEL_ID)
+
+ @channel_router.channel_post()
+ async def on_channel_post(message: Message):
+ return message.message_id
+ else:
+ channel_router.edited_channel_post.filter(F.chat.id == CHANNEL_ID)
+
+ @channel_router.edited_channel_post()
+ async def on_edited_channel_post(message: Message):
+ return message.message_id
+
+ dispatcher.include_router(channel_router)
+ SceneRegistry(dispatcher).add(BrowseScene)
+
+ message = Message(
+ message_id=1,
+ date=datetime.now(),
+ chat=Chat(id=CHANNEL_ID, type="channel"),
+ text="test",
+ )
+
+ kwargs = {"update_id": 1, update_type: message}
+ update = Update(**kwargs)
+
+ result = await dispatcher.feed_update(bot, update)
+ assert result == 1
+
+
+async def test_channel_scene_has_fsm_state_with_chat_strategy(bot: MockedBot):
+ dispatcher = Dispatcher(fsm_strategy=FSMStrategy.CHAT)
+ router = Router()
+
+ @router.channel_post((F.chat.id == CHANNEL_ID) & (F.text == "enter"))
+ async def enter_channel_state(message: Message, state: FSMContext):
+ await state.set_state(ChannelStateScene.__scene_config__.state)
+ return message.message_id
+
+ dispatcher.include_router(router)
+ SceneRegistry(dispatcher).add(ChannelStateScene)
+
+ initial_update = Update(
+ update_id=1,
+ channel_post=Message(
+ message_id=10,
+ date=datetime.now(),
+ chat=Chat(id=CHANNEL_ID, type="channel"),
+ text="enter",
+ ),
+ )
+ await dispatcher.feed_update(bot, initial_update)
+
+ active_state = await dispatcher.fsm.storage.get_state(
+ key=dispatcher.fsm.get_context(
+ bot=bot,
+ chat_id=CHANNEL_ID,
+ user_id=CHANNEL_ID,
+ ).key
+ )
+ assert active_state == ChannelStateScene.__scene_config__.state
+
+ scene_update = Update(
+ update_id=2,
+ channel_post=Message(
+ message_id=11,
+ date=datetime.now(),
+ chat=Chat(id=CHANNEL_ID, type="channel"),
+ text="scene",
+ ),
+ )
+ result = await dispatcher.feed_update(bot, scene_update)
+ assert result == 11
+ state_data = await dispatcher.fsm.storage.get_data(
+ key=dispatcher.fsm.get_context(
+ bot=bot,
+ chat_id=CHANNEL_ID,
+ user_id=CHANNEL_ID,
+ ).key
+ )
+ assert state_data["last_message_id"] == 11
diff --git a/tests/test_utils/test_backoff.py b/tests/test_utils/test_backoff.py
index 57e7269f..13a0f256 100644
--- a/tests/test_utils/test_backoff.py
+++ b/tests/test_utils/test_backoff.py
@@ -9,10 +9,15 @@ class TestBackoffConfig:
@pytest.mark.parametrize(
"kwargs",
[
- dict(min_delay=1.0, max_delay=1.0, factor=2.0, jitter=0.1), # equals min and max
- dict(min_delay=1.0, max_delay=1.0, factor=1.0, jitter=0.1), # factor == 1
- dict(min_delay=1.0, max_delay=2.0, factor=0.5, jitter=0.1), # factor < 1
- dict(min_delay=2.0, max_delay=1.0, factor=2.0, jitter=0.1), # min > max
+ {
+ "min_delay": 1.0,
+ "max_delay": 1.0,
+ "factor": 2.0,
+ "jitter": 0.1,
+ }, # equals min and max
+ {"min_delay": 1.0, "max_delay": 1.0, "factor": 1.0, "jitter": 0.1}, # factor == 1
+ {"min_delay": 1.0, "max_delay": 2.0, "factor": 0.5, "jitter": 0.1}, # factor < 1
+ {"min_delay": 2.0, "max_delay": 1.0, "factor": 2.0, "jitter": 0.1}, # min > max
],
)
def test_incorrect_post_init(self, kwargs):
@@ -21,7 +26,7 @@ class TestBackoffConfig:
@pytest.mark.parametrize(
"kwargs",
- [dict(min_delay=1.0, max_delay=2.0, factor=1.2, jitter=0.1)],
+ [{"min_delay": 1.0, "max_delay": 2.0, "factor": 1.2, "jitter": 0.1}],
)
def test_correct_post_init(self, kwargs):
assert BackoffConfig(**kwargs)
diff --git a/tests/test_utils/test_chat_member.py b/tests/test_utils/test_chat_member.py
index 2f0f9eef..8a42600c 100644
--- a/tests/test_utils/test_chat_member.py
+++ b/tests/test_utils/test_chat_member.py
@@ -1,5 +1,4 @@
from datetime import datetime
-from typing import Type
import pytest
@@ -90,6 +89,6 @@ CHAT_MEMBER_RESTRICTED = ChatMemberRestricted(
(CHAT_MEMBER_RESTRICTED, ChatMemberRestricted),
],
)
-def test_chat_member_resolution(data: dict, resolved_type: Type[ChatMember]) -> None:
+def test_chat_member_resolution(data: dict, resolved_type: type[ChatMember]) -> None:
chat_member = ChatMemberAdapter.validate_python(data)
assert isinstance(chat_member, resolved_type)
diff --git a/tests/test_utils/test_dataclass.py b/tests/test_utils/test_dataclass.py
index d714362d..9beeadb4 100644
--- a/tests/test_utils/test_dataclass.py
+++ b/tests/test_utils/test_dataclass.py
@@ -31,7 +31,6 @@ class TestDataclassKwargs:
)
def test_dataclass_kwargs(self, py_version, expected):
with patch("sys.version_info", py_version):
-
assert (
dataclass_kwargs(
init=True,
diff --git a/tests/test_utils/test_i18n.py b/tests/test_utils/test_i18n.py
index 016b0e23..d6dc2444 100644
--- a/tests/test_utils/test_i18n.py
+++ b/tests/test_utils/test_i18n.py
@@ -1,5 +1,4 @@
-from pathlib import Path
-from typing import Any, Dict
+from typing import Any
import pytest
@@ -81,7 +80,7 @@ class TestI18nCore:
["it", {"singular": "test", "plural": "test2", "n": 2}, "test2"],
],
)
- def test_gettext(self, i18n: I18n, locale: str, case: Dict[str, Any], result: str):
+ def test_gettext(self, i18n: I18n, locale: str, case: dict[str, Any], result: str):
if locale is not None:
i18n.current_locale = locale
with i18n.context():
diff --git a/tests/test_utils/test_keyboard.py b/tests/test_utils/test_keyboard.py
index 1bc7063a..e80fed8e 100644
--- a/tests/test_utils/test_keyboard.py
+++ b/tests/test_utils/test_keyboard.py
@@ -207,21 +207,21 @@ class TestKeyboardBuilder:
markup = builder.export()
assert len(markup) == len(shape)
- for row, expected_size in zip(markup, shape):
+ for row, expected_size in zip(markup, shape, strict=False):
assert len(row) == expected_size
@pytest.mark.parametrize(
"builder_type,kwargs,expected",
[
- [ReplyKeyboardBuilder, dict(text="test"), KeyboardButton(text="test")],
+ [ReplyKeyboardBuilder, {"text": "test"}, KeyboardButton(text="test")],
[
InlineKeyboardBuilder,
- dict(text="test", callback_data="callback"),
+ {"text": "test", "callback_data": "callback"},
InlineKeyboardButton(text="test", callback_data="callback"),
],
[
InlineKeyboardBuilder,
- dict(text="test", callback_data=MyCallback(value="test")),
+ {"text": "test", "callback_data": MyCallback(value="test")},
InlineKeyboardButton(text="test", callback_data="test:test"),
],
],
diff --git a/tests/test_utils/test_link.py b/tests/test_utils/test_link.py
index f0276703..c2ac5514 100644
--- a/tests/test_utils/test_link.py
+++ b/tests/test_utils/test_link.py
@@ -1,5 +1,5 @@
from itertools import product
-from typing import Any, Dict
+from typing import Any
from urllib.parse import parse_qs
import pytest
@@ -18,7 +18,7 @@ class TestLink:
"base,params,result",
[["user", {"id": 42}, "tg://user?id=42"]],
)
- def test_create_tg_link(self, base: str, params: Dict[str, Any], result: str):
+ def test_create_tg_link(self, base: str, params: dict[str, Any], result: str):
assert create_tg_link(base, **params) == result
@pytest.mark.parametrize(
@@ -28,7 +28,7 @@ class TestLink:
["username", {"start": "test"}, "https://t.me/username?start=test"],
],
)
- def test_create_telegram_link(self, base: str, params: Dict[str, Any], result: str):
+ def test_create_telegram_link(self, base: str, params: dict[str, Any], result: str):
assert create_telegram_link(base, **params) == result
def test_fragment(self):
@@ -70,8 +70,8 @@ class TestCreateChannelBotLink:
}
variants = product([True, False], repeat=len(params))
- for index, variants in enumerate(variants):
- kwargs = {k: v for k, v in zip(params, variants) if v}
+ for _index, variants in enumerate(variants):
+ kwargs = {k: v for k, v in zip(params, variants, strict=False) if v}
if not kwargs:
# Variant without additional arguments is already covered
continue
diff --git a/tests/test_utils/test_markdown.py b/tests/test_utils/test_markdown.py
index 786cc321..7759ab4c 100644
--- a/tests/test_utils/test_markdown.py
+++ b/tests/test_utils/test_markdown.py
@@ -1,4 +1,5 @@
-from typing import Any, Callable, Optional, Tuple
+from collections.abc import Callable
+from typing import Any
import pytest
@@ -75,6 +76,6 @@ class TestMarkdown:
],
)
def test_formatter(
- self, func: Callable[[Any], Any], args: Tuple[str], sep: Optional[str], result: str
+ self, func: Callable[[Any], Any], args: tuple[str], sep: str | None, result: str
):
assert func(*args, **({"sep": sep} if sep is not None else {})) == result # type: ignore
diff --git a/tests/test_utils/test_text_decorations.py b/tests/test_utils/test_text_decorations.py
index 50623447..b4ccb5e8 100644
--- a/tests/test_utils/test_text_decorations.py
+++ b/tests/test_utils/test_text_decorations.py
@@ -1,5 +1,3 @@
-from typing import List, Optional
-
import pytest
from aiogram.types import MessageEntity, User
@@ -304,7 +302,7 @@ class TestTextDecoration:
self,
decorator: TextDecoration,
text: str,
- entities: Optional[List[MessageEntity]],
+ entities: list[MessageEntity] | None,
result: str,
):
assert decorator.unparse(text, entities) == result
diff --git a/tests/test_webhook/test_aiohttp_server.py b/tests/test_webhook/test_aiohttp_server.py
index b6ee3b8b..881e8fc7 100644
--- a/tests/test_webhook/test_aiohttp_server.py
+++ b/tests/test_webhook/test_aiohttp_server.py
@@ -2,7 +2,7 @@ import asyncio
import time
from asyncio import Event
from dataclasses import dataclass
-from typing import Any, Dict
+from typing import Any
from unittest.mock import AsyncMock, patch
import pytest
@@ -251,7 +251,7 @@ class TestTokenBasedRequestHandler:
@dataclass
class FakeRequest:
- match_info: Dict[str, Any]
+ match_info: dict[str, Any]
bot1 = await handler.resolve_bot(request=FakeRequest(match_info={"bot_token": "42:TEST"}))
assert bot1.id == 42
diff --git a/uv.lock b/uv.lock
index 809545e0..913926a4 100644
--- a/uv.lock
+++ b/uv.lock
@@ -84,8 +84,6 @@ signature = [
[package.dev-dependencies]
dev = [
- { name = "black" },
- { name = "isort" },
{ name = "motor-types" },
{ name = "mypy" },
{ name = "packaging" },
@@ -139,8 +137,6 @@ provides-extras = ["cli", "docs", "fast", "i18n", "mongo", "proxy", "redis", "si
[package.metadata.requires-dev]
dev = [
- { name = "black", specifier = "~=25.9" },
- { name = "isort", specifier = "~=7.0" },
{ name = "motor-types", specifier = "==1.0.0b4" },
{ name = "mypy", specifier = "==1.10.1" },
{ name = "packaging", specifier = "~=25.0" },
@@ -425,50 +421,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" },
]
-[[package]]
-name = "black"
-version = "25.12.0"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "click" },
- { name = "mypy-extensions" },
- { name = "packaging" },
- { name = "pathspec" },
- { name = "platformdirs" },
- { name = "pytokens" },
- { name = "tomli", marker = "python_full_version < '3.11'" },
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/c4/d9/07b458a3f1c525ac392b5edc6b191ff140b596f9d77092429417a54e249d/black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7", size = 659264, upload-time = "2025-12-08T01:40:52.501Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/37/d5/8d3145999d380e5d09bb00b0f7024bf0a8ccb5c07b5648e9295f02ec1d98/black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8", size = 1895720, upload-time = "2025-12-08T01:46:58.197Z" },
- { url = "https://files.pythonhosted.org/packages/06/97/7acc85c4add41098f4f076b21e3e4e383ad6ed0a3da26b2c89627241fc11/black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a", size = 1727193, upload-time = "2025-12-08T01:52:26.674Z" },
- { url = "https://files.pythonhosted.org/packages/24/f0/fdf0eb8ba907ddeb62255227d29d349e8256ef03558fbcadfbc26ecfe3b2/black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea", size = 1774506, upload-time = "2025-12-08T01:46:25.721Z" },
- { url = "https://files.pythonhosted.org/packages/e4/f5/9203a78efe00d13336786b133c6180a9303d46908a9aa72d1104ca214222/black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f", size = 1416085, upload-time = "2025-12-08T01:46:06.073Z" },
- { url = "https://files.pythonhosted.org/packages/ba/cc/7a6090e6b081c3316282c05c546e76affdce7bf7a3b7d2c3a2a69438bd01/black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da", size = 1226038, upload-time = "2025-12-08T01:45:29.388Z" },
- { url = "https://files.pythonhosted.org/packages/60/ad/7ac0d0e1e0612788dbc48e62aef8a8e8feffac7eb3d787db4e43b8462fa8/black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a", size = 1877003, upload-time = "2025-12-08T01:43:29.967Z" },
- { url = "https://files.pythonhosted.org/packages/e8/dd/a237e9f565f3617a88b49284b59cbca2a4f56ebe68676c1aad0ce36a54a7/black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be", size = 1712639, upload-time = "2025-12-08T01:52:46.756Z" },
- { url = "https://files.pythonhosted.org/packages/12/80/e187079df1ea4c12a0c63282ddd8b81d5107db6d642f7d7b75a6bcd6fc21/black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b", size = 1758143, upload-time = "2025-12-08T01:45:29.137Z" },
- { url = "https://files.pythonhosted.org/packages/93/b5/3096ccee4f29dc2c3aac57274326c4d2d929a77e629f695f544e159bfae4/black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5", size = 1420698, upload-time = "2025-12-08T01:45:53.379Z" },
- { url = "https://files.pythonhosted.org/packages/7e/39/f81c0ffbc25ffbe61c7d0385bf277e62ffc3e52f5ee668d7369d9854fadf/black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655", size = 1229317, upload-time = "2025-12-08T01:46:35.606Z" },
- { url = "https://files.pythonhosted.org/packages/d1/bd/26083f805115db17fda9877b3c7321d08c647df39d0df4c4ca8f8450593e/black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a", size = 1924178, upload-time = "2025-12-08T01:49:51.048Z" },
- { url = "https://files.pythonhosted.org/packages/89/6b/ea00d6651561e2bdd9231c4177f4f2ae19cc13a0b0574f47602a7519b6ca/black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783", size = 1742643, upload-time = "2025-12-08T01:49:59.09Z" },
- { url = "https://files.pythonhosted.org/packages/6d/f3/360fa4182e36e9875fabcf3a9717db9d27a8d11870f21cff97725c54f35b/black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59", size = 1800158, upload-time = "2025-12-08T01:44:27.301Z" },
- { url = "https://files.pythonhosted.org/packages/f8/08/2c64830cb6616278067e040acca21d4f79727b23077633953081c9445d61/black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892", size = 1426197, upload-time = "2025-12-08T01:45:51.198Z" },
- { url = "https://files.pythonhosted.org/packages/d4/60/a93f55fd9b9816b7432cf6842f0e3000fdd5b7869492a04b9011a133ee37/black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43", size = 1237266, upload-time = "2025-12-08T01:45:10.556Z" },
- { url = "https://files.pythonhosted.org/packages/c8/52/c551e36bc95495d2aa1a37d50566267aa47608c81a53f91daa809e03293f/black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5", size = 1923809, upload-time = "2025-12-08T01:46:55.126Z" },
- { url = "https://files.pythonhosted.org/packages/a0/f7/aac9b014140ee56d247e707af8db0aae2e9efc28d4a8aba92d0abd7ae9d1/black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f", size = 1742384, upload-time = "2025-12-08T01:49:37.022Z" },
- { url = "https://files.pythonhosted.org/packages/74/98/38aaa018b2ab06a863974c12b14a6266badc192b20603a81b738c47e902e/black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf", size = 1798761, upload-time = "2025-12-08T01:46:05.386Z" },
- { url = "https://files.pythonhosted.org/packages/16/3a/a8ac542125f61574a3f015b521ca83b47321ed19bb63fe6d7560f348bfe1/black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d", size = 1429180, upload-time = "2025-12-08T01:45:34.903Z" },
- { url = "https://files.pythonhosted.org/packages/e6/2d/bdc466a3db9145e946762d52cd55b1385509d9f9004fec1c97bdc8debbfb/black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce", size = 1239350, upload-time = "2025-12-08T01:46:09.458Z" },
- { url = "https://files.pythonhosted.org/packages/35/46/1d8f2542210c502e2ae1060b2e09e47af6a5e5963cb78e22ec1a11170b28/black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5", size = 1917015, upload-time = "2025-12-08T01:53:27.987Z" },
- { url = "https://files.pythonhosted.org/packages/41/37/68accadf977672beb8e2c64e080f568c74159c1aaa6414b4cd2aef2d7906/black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f", size = 1741830, upload-time = "2025-12-08T01:54:36.861Z" },
- { url = "https://files.pythonhosted.org/packages/ac/76/03608a9d8f0faad47a3af3a3c8c53af3367f6c0dd2d23a84710456c7ac56/black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f", size = 1791450, upload-time = "2025-12-08T01:44:52.581Z" },
- { url = "https://files.pythonhosted.org/packages/06/99/b2a4bd7dfaea7964974f947e1c76d6886d65fe5d24f687df2d85406b2609/black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83", size = 1452042, upload-time = "2025-12-08T01:46:13.188Z" },
- { url = "https://files.pythonhosted.org/packages/b2/7c/d9825de75ae5dd7795d007681b752275ea85a1c5d83269b4b9c754c2aaab/black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b", size = 1267446, upload-time = "2025-12-08T01:46:14.497Z" },
- { url = "https://files.pythonhosted.org/packages/68/11/21331aed19145a952ad28fca2756a1433ee9308079bd03bd898e903a2e53/black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828", size = 206191, upload-time = "2025-12-08T01:40:50.963Z" },
-]
-
[[package]]
name = "certifi"
version = "2025.11.12"
@@ -1175,15 +1127,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
]
-[[package]]
-name = "isort"
-version = "7.0.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049, upload-time = "2025-10-11T13:30:59.107Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" },
-]
-
[[package]]
name = "jinja2"
version = "3.1.6"
@@ -1530,15 +1473,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
-[[package]]
-name = "pathspec"
-version = "0.12.1"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
-]
-
[[package]]
name = "platformdirs"
version = "4.5.1"
@@ -2172,15 +2106,6 @@ asyncio = [
{ name = "async-timeout", marker = "python_full_version < '3.11'" },
]
-[[package]]
-name = "pytokens"
-version = "0.3.0"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" },
-]
-
[[package]]
name = "pytz"
version = "2025.2"