diff --git a/.apiversion b/.apiversion index 0359f243..c026ac82 100644 --- a/.apiversion +++ b/.apiversion @@ -1 +1 @@ -9.4 +9.6 diff --git a/.butcher/enums/ContentType.yml b/.butcher/enums/ContentType.yml index 8d70ad16..8f7ec557 100644 --- a/.butcher/enums/ContentType.yml +++ b/.butcher/enums/ContentType.yml @@ -39,6 +39,7 @@ extract: - reply_to_story - business_connection_id - sender_business_bot + - sender_tag - is_from_offline - has_media_spoiler - effect_id @@ -48,3 +49,4 @@ extract: - reply_to_checklist_task_id - suggested_post_info - is_paid_post + - reply_to_poll_option_id diff --git a/.butcher/methods/answerWebAppQuery/entity.json b/.butcher/methods/answerWebAppQuery/entity.json index cff0b491..82ae4124 100644 --- a/.butcher/methods/answerWebAppQuery/entity.json +++ b/.butcher/methods/answerWebAppQuery/entity.json @@ -1,8 +1,8 @@ { "meta": {}, "group": { - "title": "Inline mode", - "anchor": "inline-mode" + "title": "Available methods", + "anchor": "available-methods" }, "object": { "anchor": "answerwebappquery", diff --git a/.butcher/methods/editMessageChecklist/entity.json b/.butcher/methods/editMessageChecklist/entity.json index 9b5912c5..1c75a0bd 100644 --- a/.butcher/methods/editMessageChecklist/entity.json +++ b/.butcher/methods/editMessageChecklist/entity.json @@ -47,8 +47,8 @@ "type": "InlineKeyboardMarkup", "required": false, "description": "A JSON-serialized object for the new inline keyboard for the message", - "html_description": "A JSON-serialized object for the new inline keyboard for the message", - "rst_description": "A JSON-serialized object for the new inline keyboard for the message\n", + "html_description": "A JSON-serialized object for the new inline keyboard for the message", + "rst_description": "A JSON-serialized object for the new `inline keyboard `_ for the message\n", "name": "reply_markup" } ], diff --git a/.butcher/methods/getManagedBotToken/entity.json b/.butcher/methods/getManagedBotToken/entity.json new file mode 100644 index 00000000..bf831d7a --- /dev/null +++ b/.butcher/methods/getManagedBotToken/entity.json @@ -0,0 +1,25 @@ +{ + "meta": {}, + "group": { + "title": "Available methods", + "anchor": "available-methods" + }, + "object": { + "anchor": "getmanagedbottoken", + "name": "getManagedBotToken", + "description": "Use this method to get the token of a managed bot. Returns the token as String on success.", + "html_description": "

Use this method to get the token of a managed bot. Returns the token as String on success.

", + "rst_description": "Use this method to get the token of a managed bot. Returns the token as *String* on success.", + "annotations": [ + { + "type": "Integer", + "required": true, + "description": "User identifier of the managed bot whose token will be returned", + "html_description": "User identifier of the managed bot whose token will be returned", + "rst_description": "User identifier of the managed bot whose token will be returned\n", + "name": "user_id" + } + ], + "category": "methods" + } +} diff --git a/.butcher/methods/giftPremiumSubscription/entity.json b/.butcher/methods/giftPremiumSubscription/entity.json index b2275147..1a1f7867 100644 --- a/.butcher/methods/giftPremiumSubscription/entity.json +++ b/.butcher/methods/giftPremiumSubscription/entity.json @@ -46,17 +46,17 @@ { "type": "String", "required": false, - "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_parse_mode" }, { "type": "Array of MessageEntity", "required": false, - "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_entities" } ], diff --git a/.butcher/methods/promoteChatMember/entity.json b/.butcher/methods/promoteChatMember/entity.json index 4f0e4480..5495c781 100644 --- a/.butcher/methods/promoteChatMember/entity.json +++ b/.butcher/methods/promoteChatMember/entity.json @@ -154,6 +154,14 @@ "html_description": "Pass True if the administrator can manage direct messages within the channel and decline suggested posts; for channels only", "rst_description": "Pass :code:`True` if the administrator can manage direct messages within the channel and decline suggested posts; for channels only\n", "name": "can_manage_direct_messages" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True if the administrator can edit the tags of regular members; for groups and supergroups only", + "html_description": "Pass True if the administrator can edit the tags of regular members; for groups and supergroups only", + "rst_description": "Pass :code:`True` if the administrator can edit the tags of regular members; for groups and supergroups only\n", + "name": "can_manage_tags" } ], "category": "methods" diff --git a/.butcher/methods/replaceManagedBotToken/entity.json b/.butcher/methods/replaceManagedBotToken/entity.json new file mode 100644 index 00000000..050f0958 --- /dev/null +++ b/.butcher/methods/replaceManagedBotToken/entity.json @@ -0,0 +1,25 @@ +{ + "meta": {}, + "group": { + "title": "Available methods", + "anchor": "available-methods" + }, + "object": { + "anchor": "replacemanagedbottoken", + "name": "replaceManagedBotToken", + "description": "Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as String on success.", + "html_description": "

Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as String on success.

", + "rst_description": "Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as *String* on success.", + "annotations": [ + { + "type": "Integer", + "required": true, + "description": "User identifier of the managed bot whose token will be replaced", + "html_description": "User identifier of the managed bot whose token will be replaced", + "rst_description": "User identifier of the managed bot whose token will be replaced\n", + "name": "user_id" + } + ], + "category": "methods" + } +} diff --git a/.butcher/methods/savePreparedInlineMessage/entity.json b/.butcher/methods/savePreparedInlineMessage/entity.json index dc1caa96..fc6ee1b6 100644 --- a/.butcher/methods/savePreparedInlineMessage/entity.json +++ b/.butcher/methods/savePreparedInlineMessage/entity.json @@ -1,8 +1,8 @@ { "meta": {}, "group": { - "title": "Inline mode", - "anchor": "inline-mode" + "title": "Available methods", + "anchor": "available-methods" }, "object": { "anchor": "savepreparedinlinemessage", diff --git a/.butcher/methods/savePreparedKeyboardButton/entity.json b/.butcher/methods/savePreparedKeyboardButton/entity.json new file mode 100644 index 00000000..b31dcd00 --- /dev/null +++ b/.butcher/methods/savePreparedKeyboardButton/entity.json @@ -0,0 +1,33 @@ +{ + "meta": {}, + "group": { + "title": "Available methods", + "anchor": "available-methods" + }, + "object": { + "anchor": "savepreparedkeyboardbutton", + "name": "savePreparedKeyboardButton", + "description": "Stores a keyboard button that can be used by a user within a Mini App. Returns a PreparedKeyboardButton object.", + "html_description": "

Stores a keyboard button that can be used by a user within a Mini App. Returns a PreparedKeyboardButton object.

", + "rst_description": "Stores a keyboard button that can be used by a user within a Mini App. Returns a :class:`aiogram.types.prepared_keyboard_button.PreparedKeyboardButton` object.", + "annotations": [ + { + "type": "Integer", + "required": true, + "description": "Unique identifier of the target user that can use the button", + "html_description": "Unique identifier of the target user that can use the button", + "rst_description": "Unique identifier of the target user that can use the button\n", + "name": "user_id" + }, + { + "type": "KeyboardButton", + "required": true, + "description": "A JSON-serialized object describing the button to be saved. The button must be of the type request_users, request_chat, or request_managed_bot", + "html_description": "A JSON-serialized object describing the button to be saved. The button must be of the type request_users, request_chat, or request_managed_bot", + "rst_description": "A JSON-serialized object describing the button to be saved. The button must be of the type *request_users*, *request_chat*, or *request_managed_bot*\n", + "name": "button" + } + ], + "category": "methods" + } +} diff --git a/.butcher/methods/sendChecklist/entity.json b/.butcher/methods/sendChecklist/entity.json index 119ee882..31161ac7 100644 --- a/.butcher/methods/sendChecklist/entity.json +++ b/.butcher/methods/sendChecklist/entity.json @@ -71,8 +71,8 @@ "type": "InlineKeyboardMarkup", "required": false, "description": "A JSON-serialized object for an inline keyboard", - "html_description": "A JSON-serialized object for an inline keyboard", - "rst_description": "A JSON-serialized object for an inline keyboard\n", + "html_description": "A JSON-serialized object for an inline keyboard", + "rst_description": "A JSON-serialized object for an `inline keyboard `_\n", "name": "reply_markup" } ], diff --git a/.butcher/methods/sendGift/entity.json b/.butcher/methods/sendGift/entity.json index 1c428fc4..f8125fe1 100644 --- a/.butcher/methods/sendGift/entity.json +++ b/.butcher/methods/sendGift/entity.json @@ -54,17 +54,17 @@ { "type": "String", "required": false, - "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_parse_mode" }, { "type": "Array of MessageEntity", "required": false, - "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_entities" } ], diff --git a/.butcher/methods/sendMessageDraft/entity.json b/.butcher/methods/sendMessageDraft/entity.json index 5d64e874..b84bc368 100644 --- a/.butcher/methods/sendMessageDraft/entity.json +++ b/.butcher/methods/sendMessageDraft/entity.json @@ -7,9 +7,9 @@ "object": { "anchor": "sendmessagedraft", "name": "sendMessageDraft", - "description": "Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns True on success.", - "html_description": "

Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns True on success.

", - "rst_description": "Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns :code:`True` on success.", + "description": "Use this method to stream a partial message to a user while the message is being generated. Returns True on success.", + "html_description": "

Use this method to stream a partial message to a user while the message is being generated. Returns True on success.

", + "rst_description": "Use this method to stream a partial message to a user while the message is being generated. Returns :code:`True` on success.", "annotations": [ { "type": "Integer", diff --git a/.butcher/methods/sendPoll/default.yml b/.butcher/methods/sendPoll/default.yml index 26ac273c..de1aec36 100644 --- a/.butcher/methods/sendPoll/default.yml +++ b/.butcher/methods/sendPoll/default.yml @@ -1,3 +1,4 @@ explanation_parse_mode: parse_mode question_parse_mode: parse_mode +description_parse_mode: parse_mode protect_content: protect_content diff --git a/.butcher/methods/sendPoll/entity.json b/.butcher/methods/sendPoll/entity.json index 44e010c7..39afab83 100644 --- a/.butcher/methods/sendPoll/entity.json +++ b/.butcher/methods/sendPoll/entity.json @@ -86,18 +86,50 @@ { "type": "Boolean", "required": false, - "description": "True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False", - "html_description": "True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False", - "rst_description": ":code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False`\n", + "description": "Pass True, if the poll allows multiple answers, defaults to False", + "html_description": "Pass True, if the poll allows multiple answers, defaults to False", + "rst_description": "Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False`\n", "name": "allows_multiple_answers" }, { - "type": "Integer", + "type": "Boolean", "required": false, - "description": "0-based identifier of the correct answer option, required for polls in quiz mode", - "html_description": "0-based identifier of the correct answer option, required for polls in quiz mode", - "rst_description": "0-based identifier of the correct answer option, required for polls in quiz mode\n", - "name": "correct_option_id" + "description": "Pass True, if the poll allows to change chosen answer options, defaults to False for quizzes and to True for regular polls", + "html_description": "Pass True, if the poll allows to change chosen answer options, defaults to False for quizzes and to True for regular polls", + "rst_description": "Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls\n", + "name": "allows_revoting" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True, if the poll options must be shown in random order", + "html_description": "Pass True, if the poll options must be shown in random order", + "rst_description": "Pass :code:`True`, if the poll options must be shown in random order\n", + "name": "shuffle_options" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes", + "html_description": "Pass True, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes", + "rst_description": "Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes\n", + "name": "allow_adding_options" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True, if poll results must be shown only after the poll closes", + "html_description": "Pass True, if poll results must be shown only after the poll closes", + "rst_description": "Pass :code:`True`, if poll results must be shown only after the poll closes\n", + "name": "hide_results_until_closes" + }, + { + "type": "Array of Integer", + "required": false, + "description": "A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode", + "html_description": "A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode", + "rst_description": "A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode\n", + "name": "correct_option_ids" }, { "type": "String", @@ -126,17 +158,17 @@ { "type": "Integer", "required": false, - "description": "Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date.", - "html_description": "Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date.", - "rst_description": "Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*.\n", + "description": "Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with close_date.", + "html_description": "Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with close_date.", + "rst_description": "Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*.\n", "name": "open_period" }, { "type": "Integer", "required": false, - "description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with open_period.", - "html_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with open_period.", - "rst_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*.\n", + "description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with open_period.", + "html_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with open_period.", + "rst_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*.\n", "name": "close_date" }, { @@ -147,6 +179,30 @@ "rst_description": "Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview.\n", "name": "is_closed" }, + { + "type": "String", + "required": false, + "description": "Description of the poll to be sent, 0-1024 characters after entities parsing", + "html_description": "Description of the poll to be sent, 0-1024 characters after entities parsing", + "rst_description": "Description of the poll to be sent, 0-1024 characters after entities parsing\n", + "name": "description" + }, + { + "type": "String", + "required": false, + "description": "Mode for parsing entities in the poll description. See formatting options for more details.", + "html_description": "Mode for parsing entities in the poll description. See formatting options for more details.", + "rst_description": "Mode for parsing entities in the poll description. See `formatting options `_ for more details.\n", + "name": "description_parse_mode" + }, + { + "type": "Array of MessageEntity", + "required": false, + "description": "A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of description_parse_mode", + "html_description": "A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of description_parse_mode", + "rst_description": "A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode*\n", + "name": "description_entities" + }, { "type": "Boolean", "required": false, @@ -207,6 +263,18 @@ "release_date": "2023-12-29" } }, + { + "type": "Integer", + "required": false, + "description": "0-based identifier of the correct answer option, required for polls in quiz mode", + "html_description": "0-based identifier of the correct answer option, required for polls in quiz mode", + "rst_description": "0-based identifier of the correct answer option, required for polls in quiz mode\n", + "name": "correct_option_id", + "deprecated": { + "version": "9.6", + "release_date": "2026-04-03" + } + }, { "type": "Integer", "required": false, diff --git a/.butcher/methods/setChatMemberTag/entity.json b/.butcher/methods/setChatMemberTag/entity.json new file mode 100644 index 00000000..5de6b59f --- /dev/null +++ b/.butcher/methods/setChatMemberTag/entity.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "group": { + "title": "Available methods", + "anchor": "available-methods" + }, + "object": { + "anchor": "setchatmembertag", + "name": "setChatMemberTag", + "description": "Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the can_manage_tags administrator right. Returns True on success.", + "html_description": "

Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the can_manage_tags administrator right. Returns True on success.

", + "rst_description": "Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the *can_manage_tags* administrator right. Returns :code:`True` on success.", + "annotations": [ + { + "type": "Integer or String", + "required": true, + "description": "Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)", + "html_description": "Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)", + "rst_description": "Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)\n", + "name": "chat_id" + }, + { + "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": "String", + "required": false, + "description": "New tag for the member; 0-16 characters, emoji are not allowed", + "html_description": "New tag for the member; 0-16 characters, emoji are not allowed", + "rst_description": "New tag for the member; 0-16 characters, emoji are not allowed\n", + "name": "tag" + } + ], + "category": "methods" + } +} diff --git a/.butcher/schema/schema.json b/.butcher/schema/schema.json index 0fca3099..1d64c596 100644 --- a/.butcher/schema/schema.json +++ b/.butcher/schema/schema.json @@ -1,7 +1,7 @@ { "api": { - "version": "9.4", - "release_date": "2026-02-09" + "version": "9.6", + "release_date": "2026-04-03" }, "items": [ { @@ -206,6 +206,14 @@ "rst_description": "*Optional*. A boost was removed from a chat. The bot must be an administrator in the chat to receive these updates.\n", "name": "removed_chat_boost", "required": false + }, + { + "type": "ManagedBotUpdated", + "description": "A new bot was created to be managed by the bot or token of a bot was changed", + "html_description": "Optional. A new bot was created to be managed by the bot or token of a bot was changed", + "rst_description": "*Optional*. A new bot was created to be managed by the bot or token of a bot was changed\n", + "name": "managed_bot", + "required": false } ], "category": "types" @@ -560,6 +568,14 @@ "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 + }, + { + "type": "Boolean", + "description": "True, if other bots can be created to be controlled by the bot. Returned only in getMe.", + "html_description": "Optional. True, if other bots can be created to be controlled by the bot. Returned only in getMe.", + "rst_description": "*Optional*. :code:`True`, if other bots can be created to be controlled by the bot. Returned only in :class:`aiogram.methods.get_me.GetMe`.\n", + "name": "can_manage_bots", + "required": false } ], "category": "types" @@ -1119,6 +1135,14 @@ "name": "sender_business_bot", "required": false }, + { + "type": "String", + "description": "Tag or custom title of the sender of the message; for supergroups only", + "html_description": "Optional. Tag or custom title of the sender of the message; for supergroups only", + "rst_description": "*Optional*. Tag or custom title of the sender of the message; for supergroups only\n", + "name": "sender_tag", + "required": false + }, { "type": "Integer", "description": "Date the message was sent in Unix time. It is always a positive number, representing a valid date.", @@ -1207,6 +1231,14 @@ "name": "reply_to_checklist_task_id", "required": false }, + { + "type": "String", + "description": "Persistent identifier of the specific poll option that is being replied to", + "html_description": "Optional. Persistent identifier of the specific poll option that is being replied to", + "rst_description": "*Optional*. Persistent identifier of the specific poll option that is being replied to\n", + "name": "reply_to_poll_option_id", + "required": false + }, { "type": "User", "description": "Bot through which the message was sent", @@ -1249,9 +1281,9 @@ }, { "type": "String", - "description": "The unique identifier of a media message group this message belongs to", - "html_description": "Optional. The unique identifier of a media message group this message belongs to", - "rst_description": "*Optional*. The unique identifier of a media message group this message belongs to\n", + "description": "The unique identifier inside this chat of a media message group this message belongs to", + "html_description": "Optional. The unique identifier inside this chat of a media message group this message belongs to", + "rst_description": "*Optional*. The unique identifier inside this chat of a media message group this message belongs to\n", "name": "media_group_id", "required": false }, @@ -1807,6 +1839,14 @@ "name": "giveaway_completed", "required": false }, + { + "type": "ManagedBotCreated", + "description": "Service message: user created a bot that will be managed by the current bot", + "html_description": "Optional. Service message: user created a bot that will be managed by the current bot", + "rst_description": "*Optional*. Service message: user created a bot that will be managed by the current bot\n", + "name": "managed_bot_created", + "required": false + }, { "type": "PaidMessagePriceChanged", "description": "Service message: the price for paid messages has changed in the chat", @@ -1815,6 +1855,22 @@ "name": "paid_message_price_changed", "required": false }, + { + "type": "PollOptionAdded", + "description": "Service message: answer option was added to a poll", + "html_description": "Optional. Service message: answer option was added to a poll", + "rst_description": "*Optional*. Service message: answer option was added to a poll\n", + "name": "poll_option_added", + "required": false + }, + { + "type": "PollOptionDeleted", + "description": "Service message: answer option was deleted from a poll", + "html_description": "Optional. Service message: answer option was deleted from a poll", + "rst_description": "*Optional*. Service message: answer option was deleted from a poll\n", + "name": "poll_option_deleted", + "required": false + }, { "type": "SuggestedPostApproved", "description": "Service message: a suggested post was approved", @@ -1898,8 +1954,8 @@ { "type": "InlineKeyboardMarkup", "description": "Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", - "html_description": "Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", - "rst_description": "*Optional*. Inline keyboard attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n", + "html_description": "Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", + "rst_description": "*Optional*. `Inline keyboard `_ attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n", "name": "reply_markup", "required": false } @@ -1976,9 +2032,9 @@ "annotations": [ { "type": "String", - "description": "Type of the entity. Currently, can be 'mention' (@username), 'hashtag' (#hashtag or #hashtag@chatusername), 'cashtag' ($USD or $USD@chatusername), 'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email' (do-not-reply@telegram.org), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic' (italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users without usernames), 'custom_emoji' (for inline custom emoji stickers)", - "html_description": "Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag or #hashtag@chatusername), “cashtag” ($USD or $USD@chatusername), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers)", - "rst_description": "Type of the entity. Currently, can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag` or :code:`#hashtag@chatusername`), 'cashtag' (:code:`$USD` or :code:`$USD@chatusername`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users `without usernames `_), 'custom_emoji' (for inline custom emoji stickers)\n", + "description": "Type of the entity. Currently, can be 'mention' (@username), 'hashtag' (#hashtag or #hashtag@chatusername), 'cashtag' ($USD or $USD@chatusername), 'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email' (do-not-reply@telegram.org), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic' (italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users without usernames), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)", + "html_description": "Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag or #hashtag@chatusername), “cashtag” ($USD or $USD@chatusername), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers), or “date_time” (for formatted date and time)", + "rst_description": "Type of the entity. Currently, can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag` or :code:`#hashtag@chatusername`), 'cashtag' (:code:`$USD` or :code:`$USD@chatusername`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users `without usernames `_), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)\n", "name": "type", "required": true }, @@ -2029,6 +2085,22 @@ "rst_description": "*Optional*. For 'custom_emoji' only, unique identifier of the custom emoji. Use :class:`aiogram.methods.get_custom_emoji_stickers.GetCustomEmojiStickers` to get full information about the sticker\n", "name": "custom_emoji_id", "required": false + }, + { + "type": "Integer", + "description": "For 'date_time' only, the Unix time associated with the entity", + "html_description": "Optional. For “date_time” only, the Unix time associated with the entity", + "rst_description": "*Optional*. For 'date_time' only, the Unix time associated with the entity\n", + "name": "unix_time", + "required": false + }, + { + "type": "String", + "description": "For 'date_time' only, the string that defines the formatting of the date and time. See date-time entity formatting for more details.", + "html_description": "Optional. For “date_time” only, the string that defines the formatting of the date and time. See date-time entity formatting for more details.", + "rst_description": "*Optional*. For 'date_time' only, the string that defines the formatting of the date and time. See `date-time entity formatting `_ for more details.\n", + "name": "date_time_format", + "required": false } ], "category": "types" @@ -2050,9 +2122,9 @@ }, { "type": "Array of MessageEntity", - "description": "Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes.", - "html_description": "Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes.", - "rst_description": "*Optional*. Special entities that appear in the quote. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are kept in quotes.\n", + "description": "Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are kept in quotes.", + "html_description": "Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are kept in quotes.", + "rst_description": "*Optional*. Special entities that appear in the quote. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are kept in quotes.\n", "name": "entities", "required": false }, @@ -2318,9 +2390,9 @@ }, { "type": "String", - "description": "Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. The message will fail to send if the quote isn't found in the original message.", - "html_description": "Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. The message will fail to send if the quote isn't found in the original message.", - "rst_description": "*Optional*. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities. The message will fail to send if the quote isn't found in the original message.\n", + "description": "Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities. The message will fail to send if the quote isn't found in the original message.", + "html_description": "Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities. The message will fail to send if the quote isn't found in the original message.", + "rst_description": "*Optional*. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities. The message will fail to send if the quote isn't found in the original message.\n", "name": "quote", "required": false }, @@ -2355,6 +2427,14 @@ "rst_description": "*Optional*. Identifier of the specific checklist task to be replied to\n", "name": "checklist_task_id", "required": false + }, + { + "type": "String", + "description": "Persistent identifier of the specific poll option to be replied to", + "html_description": "Optional. Persistent identifier of the specific poll option to be replied to", + "rst_description": "*Optional*. Persistent identifier of the specific poll option to be replied to\n", + "name": "poll_option_id", + "required": false } ], "category": "types" @@ -3310,6 +3390,14 @@ "html_description": "

This object contains information about one answer option in a poll.

", "rst_description": "This object contains information about one answer option in a poll.", "annotations": [ + { + "type": "String", + "description": "Unique identifier of the option, persistent on option addition and deletion", + "html_description": "Unique identifier of the option, persistent on option addition and deletion", + "rst_description": "Unique identifier of the option, persistent on option addition and deletion\n", + "name": "persistent_id", + "required": true + }, { "type": "String", "description": "Option text, 1-100 characters", @@ -3328,11 +3416,35 @@ }, { "type": "Integer", - "description": "Number of users that voted for this option", - "html_description": "Number of users that voted for this option", - "rst_description": "Number of users that voted for this option\n", + "description": "Number of users who voted for this option; may be 0 if unknown", + "html_description": "Number of users who voted for this option; may be 0 if unknown", + "rst_description": "Number of users who voted for this option; may be 0 if unknown\n", "name": "voter_count", "required": true + }, + { + "type": "User", + "description": "User who added the option; omitted if the option wasn't added by a user after poll creation", + "html_description": "Optional. User who added the option; omitted if the option wasn't added by a user after poll creation", + "rst_description": "*Optional*. User who added the option; omitted if the option wasn't added by a user after poll creation\n", + "name": "added_by_user", + "required": false + }, + { + "type": "Chat", + "description": "Chat that added the option; omitted if the option wasn't added by a chat after poll creation", + "html_description": "Optional. Chat that added the option; omitted if the option wasn't added by a chat after poll creation", + "rst_description": "*Optional*. Chat that added the option; omitted if the option wasn't added by a chat after poll creation\n", + "name": "added_by_chat", + "required": false + }, + { + "type": "Integer", + "description": "Point in time (Unix timestamp) when the option was added; omitted if the option existed in the original poll", + "html_description": "Optional. Point in time (Unix timestamp) when the option was added; omitted if the option existed in the original poll", + "rst_description": "*Optional*. Point in time (Unix timestamp) when the option was added; omitted if the option existed in the original poll\n", + "name": "addition_date", + "required": false } ], "category": "types" @@ -3409,6 +3521,14 @@ "rst_description": "0-based identifiers of chosen answer options. May be empty if the vote was retracted.\n", "name": "option_ids", "required": true + }, + { + "type": "Array of String", + "description": "Persistent identifiers of the chosen answer options. May be empty if the vote was retracted.", + "html_description": "Persistent identifiers of the chosen answer options. May be empty if the vote was retracted.", + "rst_description": "Persistent identifiers of the chosen answer options. May be empty if the vote was retracted.\n", + "name": "option_persistent_ids", + "required": true } ], "category": "types" @@ -3493,11 +3613,19 @@ "required": true }, { - "type": "Integer", - "description": "0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.", - "html_description": "Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.", - "rst_description": "*Optional*. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.\n", - "name": "correct_option_id", + "type": "Boolean", + "description": "True, if the poll allows to change the chosen answer options", + "html_description": "True, if the poll allows to change the chosen answer options", + "rst_description": ":code:`True`, if the poll allows to change the chosen answer options\n", + "name": "allows_revoting", + "required": true + }, + { + "type": "Array of Integer", + "description": "Array of 0-based identifiers of the correct answer options. Available only for polls in quiz mode which are closed or were sent (not forwarded) by the bot or to the private chat with the bot.", + "html_description": "Optional. Array of 0-based identifiers of the correct answer options. Available only for polls in quiz mode which are closed or were sent (not forwarded) by the bot or to the private chat with the bot.", + "rst_description": "*Optional*. Array of 0-based identifiers of the correct answer options. Available only for polls in quiz mode which are closed or were sent (not forwarded) by the bot or to the private chat with the bot.\n", + "name": "correct_option_ids", "required": false }, { @@ -3531,6 +3659,22 @@ "rst_description": "*Optional*. Point in time (Unix timestamp) when the poll will be automatically closed\n", "name": "close_date", "required": false + }, + { + "type": "String", + "description": "Description of the poll; for polls inside the Message object only", + "html_description": "Optional. Description of the poll; for polls inside the Message object only", + "rst_description": "*Optional*. Description of the poll; for polls inside the :class:`aiogram.types.message.Message` object only\n", + "name": "description", + "required": false + }, + { + "type": "Array of MessageEntity", + "description": "Special entities like usernames, URLs, bot commands, etc. that appear in the description", + "html_description": "Optional. Special entities like usernames, URLs, bot commands, etc. that appear in the description", + "rst_description": "*Optional*. Special entities like usernames, URLs, bot commands, etc. that appear in the description\n", + "name": "description_entities", + "required": false } ], "category": "types" @@ -3676,9 +3820,9 @@ }, { "type": "Array of MessageEntity", - "description": "List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "html_description": "Optional. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "rst_description": "*Optional*. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are allowed.\n", + "description": "List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "html_description": "Optional. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "rst_description": "*Optional*. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are allowed.\n", "name": "text_entities", "required": false } @@ -3710,9 +3854,9 @@ }, { "type": "Array of MessageEntity", - "description": "List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "html_description": "Optional. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "rst_description": "*Optional*. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are allowed.\n", + "description": "List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "html_description": "Optional. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "rst_description": "*Optional*. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are allowed.\n", "name": "title_entities", "required": false }, @@ -4005,6 +4149,134 @@ ], "category": "types" }, + { + "anchor": "managedbotcreated", + "name": "ManagedBotCreated", + "description": "This object contains information about the bot that was created to be managed by the current bot.", + "html_description": "

This object contains information about the bot that was created to be managed by the current bot.

", + "rst_description": "This object contains information about the bot that was created to be managed by the current bot.", + "annotations": [ + { + "type": "User", + "description": "Information about the bot. The bot's token can be fetched using the method getManagedBotToken.", + "html_description": "Information about the bot. The bot's token can be fetched using the method getManagedBotToken.", + "rst_description": "Information about the bot. The bot's token can be fetched using the method :class:`aiogram.methods.get_managed_bot_token.GetManagedBotToken`.\n", + "name": "bot", + "required": true + } + ], + "category": "types" + }, + { + "anchor": "managedbotupdated", + "name": "ManagedBotUpdated", + "description": "This object contains information about the creation or token update of a bot that is managed by the current bot.", + "html_description": "

This object contains information about the creation or token update of a bot that is managed by the current bot.

", + "rst_description": "This object contains information about the creation or token update of a bot that is managed by the current bot.", + "annotations": [ + { + "type": "User", + "description": "User that created the bot", + "html_description": "User that created the bot", + "rst_description": "User that created the bot\n", + "name": "user", + "required": true + }, + { + "type": "User", + "description": "Information about the bot. Token of the bot can be fetched using the method getManagedBotToken.", + "html_description": "Information about the bot. Token of the bot can be fetched using the method getManagedBotToken.", + "rst_description": "Information about the bot. Token of the bot can be fetched using the method :class:`aiogram.methods.get_managed_bot_token.GetManagedBotToken`.\n", + "name": "bot", + "required": true + } + ], + "category": "types" + }, + { + "anchor": "polloptionadded", + "name": "PollOptionAdded", + "description": "Describes a service message about an option added to a poll.", + "html_description": "

Describes a service message about an option added to a poll.

", + "rst_description": "Describes a service message about an option added to a poll.", + "annotations": [ + { + "type": "MaybeInaccessibleMessage", + "description": "Message containing the poll to which the option was added, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "html_description": "Optional. Message containing the poll to which the option was added, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "rst_description": "*Optional*. Message containing the poll to which the option was added, if known. Note that the :class:`aiogram.types.message.Message` object in this field will not contain the *reply_to_message* field even if it itself is a reply.\n", + "name": "poll_message", + "required": false + }, + { + "type": "String", + "description": "Unique identifier of the added option", + "html_description": "Unique identifier of the added option", + "rst_description": "Unique identifier of the added option\n", + "name": "option_persistent_id", + "required": true + }, + { + "type": "String", + "description": "Option text", + "html_description": "Option text", + "rst_description": "Option text\n", + "name": "option_text", + "required": true + }, + { + "type": "Array of MessageEntity", + "description": "Special entities that appear in the option_text", + "html_description": "Optional. Special entities that appear in the option_text", + "rst_description": "*Optional*. Special entities that appear in the *option_text*\n", + "name": "option_text_entities", + "required": false + } + ], + "category": "types" + }, + { + "anchor": "polloptiondeleted", + "name": "PollOptionDeleted", + "description": "Describes a service message about an option deleted from a poll.", + "html_description": "

Describes a service message about an option deleted from a poll.

", + "rst_description": "Describes a service message about an option deleted from a poll.", + "annotations": [ + { + "type": "MaybeInaccessibleMessage", + "description": "Message containing the poll from which the option was deleted, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "html_description": "Optional. Message containing the poll from which the option was deleted, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "rst_description": "*Optional*. Message containing the poll from which the option was deleted, if known. Note that the :class:`aiogram.types.message.Message` object in this field will not contain the *reply_to_message* field even if it itself is a reply.\n", + "name": "poll_message", + "required": false + }, + { + "type": "String", + "description": "Unique identifier of the deleted option", + "html_description": "Unique identifier of the deleted option", + "rst_description": "Unique identifier of the deleted option\n", + "name": "option_persistent_id", + "required": true + }, + { + "type": "String", + "description": "Option text", + "html_description": "Option text", + "rst_description": "Option text\n", + "name": "option_text", + "required": true + }, + { + "type": "Array of MessageEntity", + "description": "Special entities that appear in the option_text", + "html_description": "Optional. Special entities that appear in the option_text", + "rst_description": "*Optional*. Special entities that appear in the *option_text*\n", + "name": "option_text_entities", + "required": false + } + ], + "category": "types" + }, { "anchor": "chatboostadded", "name": "ChatBoostAdded", @@ -5473,6 +5745,14 @@ "name": "request_chat", "required": false }, + { + "type": "KeyboardButtonRequestManagedBot", + "description": "If specified, pressing the button will ask the user to create and share a bot that will be managed by the current bot. Available for bots that enabled management of other bots in the @BotFather Mini App. Available in private chats only.", + "html_description": "Optional. If specified, pressing the button will ask the user to create and share a bot that will be managed by the current bot. Available for bots that enabled management of other bots in the @BotFather Mini App. Available in private chats only.", + "rst_description": "*Optional*. If specified, pressing the button will ask the user to create and share a bot that will be managed by the current bot. Available for bots that enabled management of other bots in the `@BotFather `_ Mini App. Available in private chats only.\n", + "name": "request_managed_bot", + "required": false + }, { "type": "Boolean", "description": "If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only.", @@ -5672,6 +5952,40 @@ ], "category": "types" }, + { + "anchor": "keyboardbuttonrequestmanagedbot", + "name": "KeyboardButtonRequestManagedBot", + "description": "This object defines the parameters for the creation of a managed bot. Information about the created bot will be shared with the bot using the update managed_bot and a Message with the field managed_bot_created.", + "html_description": "

This object defines the parameters for the creation of a managed bot. Information about the created bot will be shared with the bot using the update managed_bot and a Message with the field managed_bot_created.

", + "rst_description": "This object defines the parameters for the creation of a managed bot. Information about the created bot will be shared with the bot using the update *managed_bot* and a :class:`aiogram.types.message.Message` with the field *managed_bot_created*.", + "annotations": [ + { + "type": "Integer", + "description": "Signed 32-bit identifier of the request. Must be unique within the message", + "html_description": "Signed 32-bit identifier of the request. Must be unique within the message", + "rst_description": "Signed 32-bit identifier of the request. Must be unique within the message\n", + "name": "request_id", + "required": true + }, + { + "type": "String", + "description": "Suggested name for the bot", + "html_description": "Optional. Suggested name for the bot", + "rst_description": "*Optional*. Suggested name for the bot\n", + "name": "suggested_name", + "required": false + }, + { + "type": "String", + "description": "Suggested username for the bot", + "html_description": "Optional. Suggested username for the bot", + "rst_description": "*Optional*. Suggested username for the bot\n", + "name": "suggested_username", + "required": false + } + ], + "category": "types" + }, { "anchor": "keyboardbuttonpolltype", "name": "KeyboardButtonPollType", @@ -6332,6 +6646,14 @@ "rst_description": "*Optional*. :code:`True`, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only\n", "name": "can_manage_direct_messages", "required": false + }, + { + "type": "Boolean", + "description": "True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "html_description": "Optional. True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "rst_description": "*Optional*. :code:`True`, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.\n", + "name": "can_manage_tags", + "required": false } ], "category": "types" @@ -6620,6 +6942,14 @@ "name": "can_manage_direct_messages", "required": false }, + { + "type": "Boolean", + "description": "True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "html_description": "Optional. True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "rst_description": "*Optional*. :code:`True`, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.\n", + "name": "can_manage_tags", + "required": false + }, { "type": "String", "description": "Custom title for this user", @@ -6646,6 +6976,14 @@ "name": "status", "required": true }, + { + "type": "String", + "description": "Tag of the member", + "html_description": "Optional. Tag of the member", + "rst_description": "*Optional*. Tag of the member\n", + "name": "tag", + "required": false + }, { "type": "User", "description": "Information about the user", @@ -6680,6 +7018,14 @@ "name": "status", "required": true }, + { + "type": "String", + "description": "Tag of the member", + "html_description": "Optional. Tag of the member", + "rst_description": "*Optional*. Tag of the member\n", + "name": "tag", + "required": false + }, { "type": "User", "description": "Information about the user", @@ -6776,6 +7122,14 @@ "name": "can_add_web_page_previews", "required": true }, + { + "type": "Boolean", + "description": "True, if the user is allowed to edit their own tag", + "html_description": "True, if the user is allowed to edit their own tag", + "rst_description": ":code:`True`, if the user is allowed to edit their own tag\n", + "name": "can_edit_tag", + "required": true + }, { "type": "Boolean", "description": "True, if the user is allowed to change the chat title, photo and other settings", @@ -7024,6 +7378,14 @@ "name": "can_add_web_page_previews", "required": false }, + { + "type": "Boolean", + "description": "True, if the user is allowed to edit their own tag", + "html_description": "Optional. True, if the user is allowed to edit their own tag", + "rst_description": "*Optional*. :code:`True`, if the user is allowed to edit their own tag\n", + "name": "can_edit_tag", + "required": false + }, { "type": "Boolean", "description": "True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups", @@ -9633,6 +9995,68 @@ ], "category": "types" }, + { + "anchor": "sentwebappmessage", + "name": "SentWebAppMessage", + "description": "Describes an inline message sent by a Web App on behalf of a user.", + "html_description": "

Describes an inline message sent by a Web App on behalf of a user.

", + "rst_description": "Describes an inline message sent by a `Web App `_ on behalf of a user.", + "annotations": [ + { + "type": "String", + "description": "Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.", + "html_description": "Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.", + "rst_description": "*Optional*. Identifier of the sent inline message. Available only if there is an `inline keyboard `_ attached to the message.\n", + "name": "inline_message_id", + "required": false + } + ], + "category": "types" + }, + { + "anchor": "preparedinlinemessage", + "name": "PreparedInlineMessage", + "description": "Describes an inline message to be sent by a user of a Mini App.", + "html_description": "

Describes an inline message to be sent by a user of a Mini App.

", + "rst_description": "Describes an inline message to be sent by a user of a Mini App.", + "annotations": [ + { + "type": "String", + "description": "Unique identifier of the prepared message", + "html_description": "Unique identifier of the prepared message", + "rst_description": "Unique identifier of the prepared message\n", + "name": "id", + "required": true + }, + { + "type": "Integer", + "description": "Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used", + "html_description": "Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used", + "rst_description": "Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used\n", + "name": "expiration_date", + "required": true + } + ], + "category": "types" + }, + { + "anchor": "preparedkeyboardbutton", + "name": "PreparedKeyboardButton", + "description": "Describes a keyboard button to be used by a user of a Mini App.", + "html_description": "

Describes a keyboard button to be used by a user of a Mini App.

", + "rst_description": "Describes a keyboard button to be used by a user of a Mini App.", + "annotations": [ + { + "type": "String", + "description": "Unique identifier of the keyboard button", + "html_description": "Unique identifier of the keyboard button", + "rst_description": "Unique identifier of the keyboard button\n", + "name": "id", + "required": true + } + ], + "category": "types" + }, { "anchor": "responseparameters", "name": "ResponseParameters", @@ -12780,18 +13204,50 @@ { "type": "Boolean", "required": false, - "description": "True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False", - "html_description": "True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False", - "rst_description": ":code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False`\n", + "description": "Pass True, if the poll allows multiple answers, defaults to False", + "html_description": "Pass True, if the poll allows multiple answers, defaults to False", + "rst_description": "Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False`\n", "name": "allows_multiple_answers" }, { - "type": "Integer", + "type": "Boolean", "required": false, - "description": "0-based identifier of the correct answer option, required for polls in quiz mode", - "html_description": "0-based identifier of the correct answer option, required for polls in quiz mode", - "rst_description": "0-based identifier of the correct answer option, required for polls in quiz mode\n", - "name": "correct_option_id" + "description": "Pass True, if the poll allows to change chosen answer options, defaults to False for quizzes and to True for regular polls", + "html_description": "Pass True, if the poll allows to change chosen answer options, defaults to False for quizzes and to True for regular polls", + "rst_description": "Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls\n", + "name": "allows_revoting" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True, if the poll options must be shown in random order", + "html_description": "Pass True, if the poll options must be shown in random order", + "rst_description": "Pass :code:`True`, if the poll options must be shown in random order\n", + "name": "shuffle_options" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes", + "html_description": "Pass True, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes", + "rst_description": "Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes\n", + "name": "allow_adding_options" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True, if poll results must be shown only after the poll closes", + "html_description": "Pass True, if poll results must be shown only after the poll closes", + "rst_description": "Pass :code:`True`, if poll results must be shown only after the poll closes\n", + "name": "hide_results_until_closes" + }, + { + "type": "Array of Integer", + "required": false, + "description": "A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode", + "html_description": "A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode", + "rst_description": "A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode\n", + "name": "correct_option_ids" }, { "type": "String", @@ -12820,17 +13276,17 @@ { "type": "Integer", "required": false, - "description": "Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date.", - "html_description": "Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date.", - "rst_description": "Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*.\n", + "description": "Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with close_date.", + "html_description": "Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with close_date.", + "rst_description": "Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*.\n", "name": "open_period" }, { "type": "Integer", "required": false, - "description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with open_period.", - "html_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with open_period.", - "rst_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*.\n", + "description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with open_period.", + "html_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with open_period.", + "rst_description": "Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*.\n", "name": "close_date" }, { @@ -12841,6 +13297,30 @@ "rst_description": "Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview.\n", "name": "is_closed" }, + { + "type": "String", + "required": false, + "description": "Description of the poll to be sent, 0-1024 characters after entities parsing", + "html_description": "Description of the poll to be sent, 0-1024 characters after entities parsing", + "rst_description": "Description of the poll to be sent, 0-1024 characters after entities parsing\n", + "name": "description" + }, + { + "type": "String", + "required": false, + "description": "Mode for parsing entities in the poll description. See formatting options for more details.", + "html_description": "Mode for parsing entities in the poll description. See formatting options for more details.", + "rst_description": "Mode for parsing entities in the poll description. See `formatting options `_ for more details.\n", + "name": "description_parse_mode" + }, + { + "type": "Array of MessageEntity", + "required": false, + "description": "A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of description_parse_mode", + "html_description": "A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of description_parse_mode", + "rst_description": "A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode*\n", + "name": "description_entities" + }, { "type": "Boolean", "required": false, @@ -12959,8 +13439,8 @@ "type": "InlineKeyboardMarkup", "required": false, "description": "A JSON-serialized object for an inline keyboard", - "html_description": "A JSON-serialized object for an inline keyboard", - "rst_description": "A JSON-serialized object for an inline keyboard\n", + "html_description": "A JSON-serialized object for an inline keyboard", + "rst_description": "A JSON-serialized object for an `inline keyboard `_\n", "name": "reply_markup" } ], @@ -13075,9 +13555,9 @@ { "anchor": "sendmessagedraft", "name": "sendMessageDraft", - "description": "Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns True on success.", - "html_description": "

Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns True on success.

", - "rst_description": "Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns :code:`True` on success.", + "description": "Use this method to stream a partial message to a user while the message is being generated. Returns True on success.", + "html_description": "

Use this method to stream a partial message to a user while the message is being generated. Returns True on success.

", + "rst_description": "Use this method to stream a partial message to a user while the message is being generated. Returns :code:`True` on success.", "annotations": [ { "type": "Integer", @@ -13610,6 +14090,14 @@ "html_description": "Pass True if the administrator can manage direct messages within the channel and decline suggested posts; for channels only", "rst_description": "Pass :code:`True` if the administrator can manage direct messages within the channel and decline suggested posts; for channels only\n", "name": "can_manage_direct_messages" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True if the administrator can edit the tags of regular members; for groups and supergroups only", + "html_description": "Pass True if the administrator can edit the tags of regular members; for groups and supergroups only", + "rst_description": "Pass :code:`True` if the administrator can edit the tags of regular members; for groups and supergroups only\n", + "name": "can_manage_tags" } ], "category": "methods" @@ -13648,6 +14136,40 @@ ], "category": "methods" }, + { + "anchor": "setchatmembertag", + "name": "setChatMemberTag", + "description": "Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the can_manage_tags administrator right. Returns True on success.", + "html_description": "

Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the can_manage_tags administrator right. Returns True on success.

", + "rst_description": "Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the *can_manage_tags* administrator right. Returns :code:`True` on success.", + "annotations": [ + { + "type": "Integer or String", + "required": true, + "description": "Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)", + "html_description": "Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)", + "rst_description": "Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)\n", + "name": "chat_id" + }, + { + "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": "String", + "required": false, + "description": "New tag for the member; 0-16 characters, emoji are not allowed", + "html_description": "New tag for the member; 0-16 characters, emoji are not allowed", + "rst_description": "New tag for the member; 0-16 characters, emoji are not allowed\n", + "name": "tag" + } + ], + "category": "methods" + }, { "anchor": "banchatsenderchat", "name": "banChatSenderChat", @@ -14753,6 +15275,42 @@ ], "category": "methods" }, + { + "anchor": "getmanagedbottoken", + "name": "getManagedBotToken", + "description": "Use this method to get the token of a managed bot. Returns the token as String on success.", + "html_description": "

Use this method to get the token of a managed bot. Returns the token as String on success.

", + "rst_description": "Use this method to get the token of a managed bot. Returns the token as *String* on success.", + "annotations": [ + { + "type": "Integer", + "required": true, + "description": "User identifier of the managed bot whose token will be returned", + "html_description": "User identifier of the managed bot whose token will be returned", + "rst_description": "User identifier of the managed bot whose token will be returned\n", + "name": "user_id" + } + ], + "category": "methods" + }, + { + "anchor": "replacemanagedbottoken", + "name": "replaceManagedBotToken", + "description": "Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as String on success.", + "html_description": "

Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as String on success.

", + "rst_description": "Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as *String* on success.", + "annotations": [ + { + "type": "Integer", + "required": true, + "description": "User identifier of the managed bot whose token will be replaced", + "html_description": "User identifier of the managed bot whose token will be replaced", + "rst_description": "User identifier of the managed bot whose token will be replaced\n", + "name": "user_id" + } + ], + "category": "methods" + }, { "anchor": "setmycommands", "name": "setMyCommands", @@ -15145,17 +15703,17 @@ { "type": "String", "required": false, - "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_parse_mode" }, { "type": "Array of MessageEntity", "required": false, - "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_entities" } ], @@ -15203,17 +15761,17 @@ { "type": "String", "required": false, - "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_parse_mode" }, { "type": "Array of MessageEntity", "required": false, - "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.", - "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored.", - "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.\n", + "description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.", + "html_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.", + "rst_description": "A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.\n", "name": "text_entities" } ], @@ -16210,6 +16768,116 @@ } ], "category": "methods" + }, + { + "anchor": "answerwebappquery", + "name": "answerWebAppQuery", + "description": "Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a SentWebAppMessage object is returned.", + "html_description": "

Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a SentWebAppMessage object is returned.

", + "rst_description": "Use this method to set the result of an interaction with a `Web App `_ and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a :class:`aiogram.types.sent_web_app_message.SentWebAppMessage` object is returned.", + "annotations": [ + { + "type": "String", + "required": true, + "description": "Unique identifier for the query to be answered", + "html_description": "Unique identifier for the query to be answered", + "rst_description": "Unique identifier for the query to be answered\n", + "name": "web_app_query_id" + }, + { + "type": "InlineQueryResult", + "required": true, + "description": "A JSON-serialized object describing the message to be sent", + "html_description": "A JSON-serialized object describing the message to be sent", + "rst_description": "A JSON-serialized object describing the message to be sent\n", + "name": "result" + } + ], + "category": "methods" + }, + { + "anchor": "savepreparedinlinemessage", + "name": "savePreparedInlineMessage", + "description": "Stores a message that can be sent by a user of a Mini App. Returns a PreparedInlineMessage object.", + "html_description": "

Stores a message that can be sent by a user of a Mini App. Returns a PreparedInlineMessage object.

", + "rst_description": "Stores a message that can be sent by a user of a Mini App. Returns a :class:`aiogram.types.prepared_inline_message.PreparedInlineMessage` object.", + "annotations": [ + { + "type": "Integer", + "required": true, + "description": "Unique identifier of the target user that can use the prepared message", + "html_description": "Unique identifier of the target user that can use the prepared message", + "rst_description": "Unique identifier of the target user that can use the prepared message\n", + "name": "user_id" + }, + { + "type": "InlineQueryResult", + "required": true, + "description": "A JSON-serialized object describing the message to be sent", + "html_description": "A JSON-serialized object describing the message to be sent", + "rst_description": "A JSON-serialized object describing the message to be sent\n", + "name": "result" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True if the message can be sent to private chats with users", + "html_description": "Pass True if the message can be sent to private chats with users", + "rst_description": "Pass :code:`True` if the message can be sent to private chats with users\n", + "name": "allow_user_chats" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True if the message can be sent to private chats with bots", + "html_description": "Pass True if the message can be sent to private chats with bots", + "rst_description": "Pass :code:`True` if the message can be sent to private chats with bots\n", + "name": "allow_bot_chats" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True if the message can be sent to group and supergroup chats", + "html_description": "Pass True if the message can be sent to group and supergroup chats", + "rst_description": "Pass :code:`True` if the message can be sent to group and supergroup chats\n", + "name": "allow_group_chats" + }, + { + "type": "Boolean", + "required": false, + "description": "Pass True if the message can be sent to channel chats", + "html_description": "Pass True if the message can be sent to channel chats", + "rst_description": "Pass :code:`True` if the message can be sent to channel chats\n", + "name": "allow_channel_chats" + } + ], + "category": "methods" + }, + { + "anchor": "savepreparedkeyboardbutton", + "name": "savePreparedKeyboardButton", + "description": "Stores a keyboard button that can be used by a user within a Mini App. Returns a PreparedKeyboardButton object.", + "html_description": "

Stores a keyboard button that can be used by a user within a Mini App. Returns a PreparedKeyboardButton object.

", + "rst_description": "Stores a keyboard button that can be used by a user within a Mini App. Returns a :class:`aiogram.types.prepared_keyboard_button.PreparedKeyboardButton` object.", + "annotations": [ + { + "type": "Integer", + "required": true, + "description": "Unique identifier of the target user that can use the button", + "html_description": "Unique identifier of the target user that can use the button", + "rst_description": "Unique identifier of the target user that can use the button\n", + "name": "user_id" + }, + { + "type": "KeyboardButton", + "required": true, + "description": "A JSON-serialized object describing the button to be saved. The button must be of the type request_users, request_chat, or request_managed_bot", + "html_description": "A JSON-serialized object describing the button to be saved. The button must be of the type request_users, request_chat, or request_managed_bot", + "rst_description": "A JSON-serialized object describing the button to be saved. The button must be of the type *request_users*, *request_chat*, or *request_managed_bot*\n", + "name": "button" + } + ], + "category": "methods" } ], "description": "All methods in the Bot API are case-insensitive. We support GET and POST HTTP methods. Use either URL query string or application/json or application/x-www-form-urlencoded or multipart/form-data for passing parameters in Bot API requests.\nOn successful call, a JSON-object containing the result will be returned." @@ -16631,8 +17299,8 @@ "type": "InlineKeyboardMarkup", "required": false, "description": "A JSON-serialized object for the new inline keyboard for the message", - "html_description": "A JSON-serialized object for the new inline keyboard for the message", - "rst_description": "A JSON-serialized object for the new inline keyboard for the message\n", + "html_description": "A JSON-serialized object for the new inline keyboard for the message", + "rst_description": "A JSON-serialized object for the new `inline keyboard `_ for the message\n", "name": "reply_markup" } ], @@ -18727,8 +19395,8 @@ { "type": "InlineKeyboardMarkup", "description": "Inline keyboard attached to the message", - "html_description": "Optional. Inline keyboard attached to the message", - "rst_description": "*Optional*. Inline keyboard attached to the message\n", + "html_description": "Optional. Inline keyboard attached to the message", + "rst_description": "*Optional*. `Inline keyboard `_ attached to the message\n", "name": "reply_markup", "required": false }, @@ -20275,134 +20943,6 @@ } ], "category": "types" - }, - { - "anchor": "answerwebappquery", - "name": "answerWebAppQuery", - "description": "Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a SentWebAppMessage object is returned.", - "html_description": "

Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a SentWebAppMessage object is returned.

", - "rst_description": "Use this method to set the result of an interaction with a `Web App `_ and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a :class:`aiogram.types.sent_web_app_message.SentWebAppMessage` object is returned.", - "annotations": [ - { - "type": "String", - "required": true, - "description": "Unique identifier for the query to be answered", - "html_description": "Unique identifier for the query to be answered", - "rst_description": "Unique identifier for the query to be answered\n", - "name": "web_app_query_id" - }, - { - "type": "InlineQueryResult", - "required": true, - "description": "A JSON-serialized object describing the message to be sent", - "html_description": "A JSON-serialized object describing the message to be sent", - "rst_description": "A JSON-serialized object describing the message to be sent\n", - "name": "result" - } - ], - "category": "methods" - }, - { - "anchor": "sentwebappmessage", - "name": "SentWebAppMessage", - "description": "Describes an inline message sent by a Web App on behalf of a user.", - "html_description": "

Describes an inline message sent by a Web App on behalf of a user.

", - "rst_description": "Describes an inline message sent by a `Web App `_ on behalf of a user.", - "annotations": [ - { - "type": "String", - "description": "Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.", - "html_description": "Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.", - "rst_description": "*Optional*. Identifier of the sent inline message. Available only if there is an `inline keyboard `_ attached to the message.\n", - "name": "inline_message_id", - "required": false - } - ], - "category": "types" - }, - { - "anchor": "savepreparedinlinemessage", - "name": "savePreparedInlineMessage", - "description": "Stores a message that can be sent by a user of a Mini App. Returns a PreparedInlineMessage object.", - "html_description": "

Stores a message that can be sent by a user of a Mini App. Returns a PreparedInlineMessage object.

", - "rst_description": "Stores a message that can be sent by a user of a Mini App. Returns a :class:`aiogram.types.prepared_inline_message.PreparedInlineMessage` object.", - "annotations": [ - { - "type": "Integer", - "required": true, - "description": "Unique identifier of the target user that can use the prepared message", - "html_description": "Unique identifier of the target user that can use the prepared message", - "rst_description": "Unique identifier of the target user that can use the prepared message\n", - "name": "user_id" - }, - { - "type": "InlineQueryResult", - "required": true, - "description": "A JSON-serialized object describing the message to be sent", - "html_description": "A JSON-serialized object describing the message to be sent", - "rst_description": "A JSON-serialized object describing the message to be sent\n", - "name": "result" - }, - { - "type": "Boolean", - "required": false, - "description": "Pass True if the message can be sent to private chats with users", - "html_description": "Pass True if the message can be sent to private chats with users", - "rst_description": "Pass :code:`True` if the message can be sent to private chats with users\n", - "name": "allow_user_chats" - }, - { - "type": "Boolean", - "required": false, - "description": "Pass True if the message can be sent to private chats with bots", - "html_description": "Pass True if the message can be sent to private chats with bots", - "rst_description": "Pass :code:`True` if the message can be sent to private chats with bots\n", - "name": "allow_bot_chats" - }, - { - "type": "Boolean", - "required": false, - "description": "Pass True if the message can be sent to group and supergroup chats", - "html_description": "Pass True if the message can be sent to group and supergroup chats", - "rst_description": "Pass :code:`True` if the message can be sent to group and supergroup chats\n", - "name": "allow_group_chats" - }, - { - "type": "Boolean", - "required": false, - "description": "Pass True if the message can be sent to channel chats", - "html_description": "Pass True if the message can be sent to channel chats", - "rst_description": "Pass :code:`True` if the message can be sent to channel chats\n", - "name": "allow_channel_chats" - } - ], - "category": "methods" - }, - { - "anchor": "preparedinlinemessage", - "name": "PreparedInlineMessage", - "description": "Describes an inline message to be sent by a user of a Mini App.", - "html_description": "

Describes an inline message to be sent by a user of a Mini App.

", - "rst_description": "Describes an inline message to be sent by a user of a Mini App.", - "annotations": [ - { - "type": "String", - "description": "Unique identifier of the prepared message", - "html_description": "Unique identifier of the prepared message", - "rst_description": "Unique identifier of the prepared message\n", - "name": "id", - "required": true - }, - { - "type": "Integer", - "description": "Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used", - "html_description": "Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used", - "rst_description": "Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used\n", - "name": "expiration_date", - "required": true - } - ], - "category": "types" } ], "description": "The following methods and objects allow your bot to work in inline mode.\nPlease see our Introduction to Inline bots for more details.\nTo enable this option, send the /setinline command to @BotFather and provide the placeholder text that the user will see in the input field after typing your bot's name." @@ -22865,9 +23405,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\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-", + "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 »**", "annotations": [ { "type": "Integer", diff --git a/.butcher/types/Chat/aliases.yml b/.butcher/types/Chat/aliases.yml index 89b5843c..7a03c4a9 100644 --- a/.butcher/types/Chat/aliases.yml +++ b/.butcher/types/Chat/aliases.yml @@ -71,6 +71,10 @@ set_administrator_custom_title: method: setChatAdministratorCustomTitle fill: *self +set_member_tag: + method: setChatMemberTag + fill: *self + set_permissions: method: setChatPermissions fill: *self diff --git a/.butcher/types/ChatAdministratorRights/entity.json b/.butcher/types/ChatAdministratorRights/entity.json index 45ebc3b5..f271d1fd 100644 --- a/.butcher/types/ChatAdministratorRights/entity.json +++ b/.butcher/types/ChatAdministratorRights/entity.json @@ -138,6 +138,14 @@ "rst_description": "*Optional*. :code:`True`, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only\n", "name": "can_manage_direct_messages", "required": false + }, + { + "type": "Boolean", + "description": "True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "html_description": "Optional. True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "rst_description": "*Optional*. :code:`True`, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.\n", + "name": "can_manage_tags", + "required": false } ], "category": "types" diff --git a/.butcher/types/ChatMemberAdministrator/entity.json b/.butcher/types/ChatMemberAdministrator/entity.json index f1278554..7b55cc7a 100644 --- a/.butcher/types/ChatMemberAdministrator/entity.json +++ b/.butcher/types/ChatMemberAdministrator/entity.json @@ -163,6 +163,14 @@ "name": "can_manage_direct_messages", "required": false }, + { + "type": "Boolean", + "description": "True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "html_description": "Optional. True, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.", + "rst_description": "*Optional*. :code:`True`, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.\n", + "name": "can_manage_tags", + "required": false + }, { "type": "String", "description": "Custom title for this user", diff --git a/.butcher/types/ChatMemberMember/entity.json b/.butcher/types/ChatMemberMember/entity.json index c9988ee7..ed1a8304 100644 --- a/.butcher/types/ChatMemberMember/entity.json +++ b/.butcher/types/ChatMemberMember/entity.json @@ -19,6 +19,14 @@ "name": "status", "required": true }, + { + "type": "String", + "description": "Tag of the member", + "html_description": "Optional. Tag of the member", + "rst_description": "*Optional*. Tag of the member\n", + "name": "tag", + "required": false + }, { "type": "User", "description": "Information about the user", diff --git a/.butcher/types/ChatMemberRestricted/entity.json b/.butcher/types/ChatMemberRestricted/entity.json index 75ea1fc0..f0572284 100644 --- a/.butcher/types/ChatMemberRestricted/entity.json +++ b/.butcher/types/ChatMemberRestricted/entity.json @@ -19,6 +19,14 @@ "name": "status", "required": true }, + { + "type": "String", + "description": "Tag of the member", + "html_description": "Optional. Tag of the member", + "rst_description": "*Optional*. Tag of the member\n", + "name": "tag", + "required": false + }, { "type": "User", "description": "Information about the user", @@ -115,6 +123,14 @@ "name": "can_add_web_page_previews", "required": true }, + { + "type": "Boolean", + "description": "True, if the user is allowed to edit their own tag", + "html_description": "True, if the user is allowed to edit their own tag", + "rst_description": ":code:`True`, if the user is allowed to edit their own tag\n", + "name": "can_edit_tag", + "required": true + }, { "type": "Boolean", "description": "True, if the user is allowed to change the chat title, photo and other settings", diff --git a/.butcher/types/ChatPermissions/entity.json b/.butcher/types/ChatPermissions/entity.json index c488ef9f..d6ad3cc9 100644 --- a/.butcher/types/ChatPermissions/entity.json +++ b/.butcher/types/ChatPermissions/entity.json @@ -91,6 +91,14 @@ "name": "can_add_web_page_previews", "required": false }, + { + "type": "Boolean", + "description": "True, if the user is allowed to edit their own tag", + "html_description": "Optional. True, if the user is allowed to edit their own tag", + "rst_description": "*Optional*. :code:`True`, if the user is allowed to edit their own tag\n", + "name": "can_edit_tag", + "required": false + }, { "type": "Boolean", "description": "True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups", diff --git a/.butcher/types/GameHighScore/entity.json b/.butcher/types/GameHighScore/entity.json index 21a8a5e7..ce3f52d2 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\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-", + "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 »**", "annotations": [ { "type": "Integer", diff --git a/.butcher/types/InlineQueryResultDocument/entity.json b/.butcher/types/InlineQueryResultDocument/entity.json index ff703482..97bb05a6 100644 --- a/.butcher/types/InlineQueryResultDocument/entity.json +++ b/.butcher/types/InlineQueryResultDocument/entity.json @@ -86,8 +86,8 @@ { "type": "InlineKeyboardMarkup", "description": "Inline keyboard attached to the message", - "html_description": "Optional. Inline keyboard attached to the message", - "rst_description": "*Optional*. Inline keyboard attached to the message\n", + "html_description": "Optional. Inline keyboard attached to the message", + "rst_description": "*Optional*. `Inline keyboard `_ attached to the message\n", "name": "reply_markup", "required": false }, diff --git a/.butcher/types/InputChecklist/entity.json b/.butcher/types/InputChecklist/entity.json index a04bdf65..f2c45f06 100644 --- a/.butcher/types/InputChecklist/entity.json +++ b/.butcher/types/InputChecklist/entity.json @@ -29,9 +29,9 @@ }, { "type": "Array of MessageEntity", - "description": "List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "html_description": "Optional. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "rst_description": "*Optional*. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are allowed.\n", + "description": "List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "html_description": "Optional. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "rst_description": "*Optional*. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are allowed.\n", "name": "title_entities", "required": false }, diff --git a/.butcher/types/InputChecklistTask/entity.json b/.butcher/types/InputChecklistTask/entity.json index 7fd5b8cd..b12d4239 100644 --- a/.butcher/types/InputChecklistTask/entity.json +++ b/.butcher/types/InputChecklistTask/entity.json @@ -37,9 +37,9 @@ }, { "type": "Array of MessageEntity", - "description": "List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "html_description": "Optional. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are allowed.", - "rst_description": "*Optional*. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are allowed.\n", + "description": "List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "html_description": "Optional. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.", + "rst_description": "*Optional*. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are allowed.\n", "name": "text_entities", "required": false } diff --git a/.butcher/types/KeyboardButton/entity.json b/.butcher/types/KeyboardButton/entity.json index 16771a3b..fae93931 100644 --- a/.butcher/types/KeyboardButton/entity.json +++ b/.butcher/types/KeyboardButton/entity.json @@ -51,6 +51,14 @@ "name": "request_chat", "required": false }, + { + "type": "KeyboardButtonRequestManagedBot", + "description": "If specified, pressing the button will ask the user to create and share a bot that will be managed by the current bot. Available for bots that enabled management of other bots in the @BotFather Mini App. Available in private chats only.", + "html_description": "Optional. If specified, pressing the button will ask the user to create and share a bot that will be managed by the current bot. Available for bots that enabled management of other bots in the @BotFather Mini App. Available in private chats only.", + "rst_description": "*Optional*. If specified, pressing the button will ask the user to create and share a bot that will be managed by the current bot. Available for bots that enabled management of other bots in the `@BotFather `_ Mini App. Available in private chats only.\n", + "name": "request_managed_bot", + "required": false + }, { "type": "Boolean", "description": "If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only.", diff --git a/.butcher/types/KeyboardButtonRequestManagedBot/entity.json b/.butcher/types/KeyboardButtonRequestManagedBot/entity.json new file mode 100644 index 00000000..7db0dc16 --- /dev/null +++ b/.butcher/types/KeyboardButtonRequestManagedBot/entity.json @@ -0,0 +1,41 @@ +{ + "meta": {}, + "group": { + "title": "Available types", + "anchor": "available-types" + }, + "object": { + "anchor": "keyboardbuttonrequestmanagedbot", + "name": "KeyboardButtonRequestManagedBot", + "description": "This object defines the parameters for the creation of a managed bot. Information about the created bot will be shared with the bot using the update managed_bot and a Message with the field managed_bot_created.", + "html_description": "

This object defines the parameters for the creation of a managed bot. Information about the created bot will be shared with the bot using the update managed_bot and a Message with the field managed_bot_created.

", + "rst_description": "This object defines the parameters for the creation of a managed bot. Information about the created bot will be shared with the bot using the update *managed_bot* and a :class:`aiogram.types.message.Message` with the field *managed_bot_created*.", + "annotations": [ + { + "type": "Integer", + "description": "Signed 32-bit identifier of the request. Must be unique within the message", + "html_description": "Signed 32-bit identifier of the request. Must be unique within the message", + "rst_description": "Signed 32-bit identifier of the request. Must be unique within the message\n", + "name": "request_id", + "required": true + }, + { + "type": "String", + "description": "Suggested name for the bot", + "html_description": "Optional. Suggested name for the bot", + "rst_description": "*Optional*. Suggested name for the bot\n", + "name": "suggested_name", + "required": false + }, + { + "type": "String", + "description": "Suggested username for the bot", + "html_description": "Optional. Suggested username for the bot", + "rst_description": "*Optional*. Suggested username for the bot\n", + "name": "suggested_username", + "required": false + } + ], + "category": "types" + } +} diff --git a/.butcher/types/ManagedBotCreated/entity.json b/.butcher/types/ManagedBotCreated/entity.json new file mode 100644 index 00000000..89e8f85c --- /dev/null +++ b/.butcher/types/ManagedBotCreated/entity.json @@ -0,0 +1,25 @@ +{ + "meta": {}, + "group": { + "title": "Available types", + "anchor": "available-types" + }, + "object": { + "anchor": "managedbotcreated", + "name": "ManagedBotCreated", + "description": "This object contains information about the bot that was created to be managed by the current bot.", + "html_description": "

This object contains information about the bot that was created to be managed by the current bot.

", + "rst_description": "This object contains information about the bot that was created to be managed by the current bot.", + "annotations": [ + { + "type": "User", + "description": "Information about the bot. The bot's token can be fetched using the method getManagedBotToken.", + "html_description": "Information about the bot. The bot's token can be fetched using the method getManagedBotToken.", + "rst_description": "Information about the bot. The bot's token can be fetched using the method :class:`aiogram.methods.get_managed_bot_token.GetManagedBotToken`.\n", + "name": "bot", + "required": true + } + ], + "category": "types" + } +} diff --git a/.butcher/types/ManagedBotUpdated/entity.json b/.butcher/types/ManagedBotUpdated/entity.json new file mode 100644 index 00000000..323cb0b9 --- /dev/null +++ b/.butcher/types/ManagedBotUpdated/entity.json @@ -0,0 +1,33 @@ +{ + "meta": {}, + "group": { + "title": "Available types", + "anchor": "available-types" + }, + "object": { + "anchor": "managedbotupdated", + "name": "ManagedBotUpdated", + "description": "This object contains information about the creation or token update of a bot that is managed by the current bot.", + "html_description": "

This object contains information about the creation or token update of a bot that is managed by the current bot.

", + "rst_description": "This object contains information about the creation or token update of a bot that is managed by the current bot.", + "annotations": [ + { + "type": "User", + "description": "User that created the bot", + "html_description": "User that created the bot", + "rst_description": "User that created the bot\n", + "name": "user", + "required": true + }, + { + "type": "User", + "description": "Information about the bot. Token of the bot can be fetched using the method getManagedBotToken.", + "html_description": "Information about the bot. Token of the bot can be fetched using the method getManagedBotToken.", + "rst_description": "Information about the bot. Token of the bot can be fetched using the method :class:`aiogram.methods.get_managed_bot_token.GetManagedBotToken`.\n", + "name": "bot", + "required": true + } + ], + "category": "types" + } +} diff --git a/.butcher/types/Message/entity.json b/.butcher/types/Message/entity.json index 594442fc..1cf9a3e2 100644 --- a/.butcher/types/Message/entity.json +++ b/.butcher/types/Message/entity.json @@ -67,6 +67,14 @@ "name": "sender_business_bot", "required": false }, + { + "type": "String", + "description": "Tag or custom title of the sender of the message; for supergroups only", + "html_description": "Optional. Tag or custom title of the sender of the message; for supergroups only", + "rst_description": "*Optional*. Tag or custom title of the sender of the message; for supergroups only\n", + "name": "sender_tag", + "required": false + }, { "type": "Integer", "description": "Date the message was sent in Unix time. It is always a positive number, representing a valid date.", @@ -155,6 +163,14 @@ "name": "reply_to_checklist_task_id", "required": false }, + { + "type": "String", + "description": "Persistent identifier of the specific poll option that is being replied to", + "html_description": "Optional. Persistent identifier of the specific poll option that is being replied to", + "rst_description": "*Optional*. Persistent identifier of the specific poll option that is being replied to\n", + "name": "reply_to_poll_option_id", + "required": false + }, { "type": "User", "description": "Bot through which the message was sent", @@ -197,9 +213,9 @@ }, { "type": "String", - "description": "The unique identifier of a media message group this message belongs to", - "html_description": "Optional. The unique identifier of a media message group this message belongs to", - "rst_description": "*Optional*. The unique identifier of a media message group this message belongs to\n", + "description": "The unique identifier inside this chat of a media message group this message belongs to", + "html_description": "Optional. The unique identifier inside this chat of a media message group this message belongs to", + "rst_description": "*Optional*. The unique identifier inside this chat of a media message group this message belongs to\n", "name": "media_group_id", "required": false }, @@ -755,6 +771,14 @@ "name": "giveaway_completed", "required": false }, + { + "type": "ManagedBotCreated", + "description": "Service message: user created a bot that will be managed by the current bot", + "html_description": "Optional. Service message: user created a bot that will be managed by the current bot", + "rst_description": "*Optional*. Service message: user created a bot that will be managed by the current bot\n", + "name": "managed_bot_created", + "required": false + }, { "type": "PaidMessagePriceChanged", "description": "Service message: the price for paid messages has changed in the chat", @@ -763,6 +787,22 @@ "name": "paid_message_price_changed", "required": false }, + { + "type": "PollOptionAdded", + "description": "Service message: answer option was added to a poll", + "html_description": "Optional. Service message: answer option was added to a poll", + "rst_description": "*Optional*. Service message: answer option was added to a poll\n", + "name": "poll_option_added", + "required": false + }, + { + "type": "PollOptionDeleted", + "description": "Service message: answer option was deleted from a poll", + "html_description": "Optional. Service message: answer option was deleted from a poll", + "rst_description": "*Optional*. Service message: answer option was deleted from a poll\n", + "name": "poll_option_deleted", + "required": false + }, { "type": "SuggestedPostApproved", "description": "Service message: a suggested post was approved", @@ -846,8 +886,8 @@ { "type": "InlineKeyboardMarkup", "description": "Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", - "html_description": "Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", - "rst_description": "*Optional*. Inline keyboard attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n", + "html_description": "Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", + "rst_description": "*Optional*. `Inline keyboard `_ attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n", "name": "reply_markup", "required": false }, diff --git a/.butcher/types/MessageEntity/entity.json b/.butcher/types/MessageEntity/entity.json index 8d5a1d13..37dc6cf4 100644 --- a/.butcher/types/MessageEntity/entity.json +++ b/.butcher/types/MessageEntity/entity.json @@ -13,9 +13,9 @@ "annotations": [ { "type": "String", - "description": "Type of the entity. Currently, can be 'mention' (@username), 'hashtag' (#hashtag or #hashtag@chatusername), 'cashtag' ($USD or $USD@chatusername), 'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email' (do-not-reply@telegram.org), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic' (italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users without usernames), 'custom_emoji' (for inline custom emoji stickers)", - "html_description": "Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag or #hashtag@chatusername), “cashtag” ($USD or $USD@chatusername), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers)", - "rst_description": "Type of the entity. Currently, can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag` or :code:`#hashtag@chatusername`), 'cashtag' (:code:`$USD` or :code:`$USD@chatusername`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users `without usernames `_), 'custom_emoji' (for inline custom emoji stickers)\n", + "description": "Type of the entity. Currently, can be 'mention' (@username), 'hashtag' (#hashtag or #hashtag@chatusername), 'cashtag' ($USD or $USD@chatusername), 'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email' (do-not-reply@telegram.org), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic' (italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users without usernames), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)", + "html_description": "Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag or #hashtag@chatusername), “cashtag” ($USD or $USD@chatusername), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers), or “date_time” (for formatted date and time)", + "rst_description": "Type of the entity. Currently, can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag` or :code:`#hashtag@chatusername`), 'cashtag' (:code:`$USD` or :code:`$USD@chatusername`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users `without usernames `_), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)\n", "name": "type", "required": true }, @@ -66,6 +66,22 @@ "rst_description": "*Optional*. For 'custom_emoji' only, unique identifier of the custom emoji. Use :class:`aiogram.methods.get_custom_emoji_stickers.GetCustomEmojiStickers` to get full information about the sticker\n", "name": "custom_emoji_id", "required": false + }, + { + "type": "Integer", + "description": "For 'date_time' only, the Unix time associated with the entity", + "html_description": "Optional. For “date_time” only, the Unix time associated with the entity", + "rst_description": "*Optional*. For 'date_time' only, the Unix time associated with the entity\n", + "name": "unix_time", + "required": false + }, + { + "type": "String", + "description": "For 'date_time' only, the string that defines the formatting of the date and time. See date-time entity formatting for more details.", + "html_description": "Optional. For “date_time” only, the string that defines the formatting of the date and time. See date-time entity formatting for more details.", + "rst_description": "*Optional*. For 'date_time' only, the string that defines the formatting of the date and time. See `date-time entity formatting `_ for more details.\n", + "name": "date_time_format", + "required": false } ], "category": "types" diff --git a/.butcher/types/Poll/entity.json b/.butcher/types/Poll/entity.json index 00b93cc3..2c5ef7f1 100644 --- a/.butcher/types/Poll/entity.json +++ b/.butcher/types/Poll/entity.json @@ -84,11 +84,19 @@ "required": true }, { - "type": "Integer", - "description": "0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.", - "html_description": "Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.", - "rst_description": "*Optional*. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.\n", - "name": "correct_option_id", + "type": "Boolean", + "description": "True, if the poll allows to change the chosen answer options", + "html_description": "True, if the poll allows to change the chosen answer options", + "rst_description": ":code:`True`, if the poll allows to change the chosen answer options\n", + "name": "allows_revoting", + "required": true + }, + { + "type": "Array of Integer", + "description": "Array of 0-based identifiers of the correct answer options. Available only for polls in quiz mode which are closed or were sent (not forwarded) by the bot or to the private chat with the bot.", + "html_description": "Optional. Array of 0-based identifiers of the correct answer options. Available only for polls in quiz mode which are closed or were sent (not forwarded) by the bot or to the private chat with the bot.", + "rst_description": "*Optional*. Array of 0-based identifiers of the correct answer options. Available only for polls in quiz mode which are closed or were sent (not forwarded) by the bot or to the private chat with the bot.\n", + "name": "correct_option_ids", "required": false }, { @@ -122,6 +130,34 @@ "rst_description": "*Optional*. Point in time (Unix timestamp) when the poll will be automatically closed\n", "name": "close_date", "required": false + }, + { + "type": "String", + "description": "Description of the poll; for polls inside the Message object only", + "html_description": "Optional. Description of the poll; for polls inside the Message object only", + "rst_description": "*Optional*. Description of the poll; for polls inside the :class:`aiogram.types.message.Message` object only\n", + "name": "description", + "required": false + }, + { + "type": "Array of MessageEntity", + "description": "Special entities like usernames, URLs, bot commands, etc. that appear in the description", + "html_description": "Optional. Special entities like usernames, URLs, bot commands, etc. that appear in the description", + "rst_description": "*Optional*. Special entities like usernames, URLs, bot commands, etc. that appear in the description\n", + "name": "description_entities", + "required": false + }, + { + "type": "Integer", + "description": "0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.", + "html_description": "Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.", + "rst_description": "*Optional*. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.\n", + "name": "correct_option_id", + "required": false, + "deprecated": { + "version": "9.6", + "release_date": "2026-04-03" + } } ], "category": "types" diff --git a/.butcher/types/PollAnswer/entity.json b/.butcher/types/PollAnswer/entity.json index 3b14d6dc..b1e0007c 100644 --- a/.butcher/types/PollAnswer/entity.json +++ b/.butcher/types/PollAnswer/entity.json @@ -42,6 +42,14 @@ "rst_description": "0-based identifiers of chosen answer options. May be empty if the vote was retracted.\n", "name": "option_ids", "required": true + }, + { + "type": "Array of String", + "description": "Persistent identifiers of the chosen answer options. May be empty if the vote was retracted.", + "html_description": "Persistent identifiers of the chosen answer options. May be empty if the vote was retracted.", + "rst_description": "Persistent identifiers of the chosen answer options. May be empty if the vote was retracted.\n", + "name": "option_persistent_ids", + "required": true } ], "category": "types" diff --git a/.butcher/types/PollOption/entity.json b/.butcher/types/PollOption/entity.json index 5c5efa26..c592d13a 100644 --- a/.butcher/types/PollOption/entity.json +++ b/.butcher/types/PollOption/entity.json @@ -11,6 +11,14 @@ "html_description": "

This object contains information about one answer option in a poll.

", "rst_description": "This object contains information about one answer option in a poll.", "annotations": [ + { + "type": "String", + "description": "Unique identifier of the option, persistent on option addition and deletion", + "html_description": "Unique identifier of the option, persistent on option addition and deletion", + "rst_description": "Unique identifier of the option, persistent on option addition and deletion\n", + "name": "persistent_id", + "required": true + }, { "type": "String", "description": "Option text, 1-100 characters", @@ -29,11 +37,35 @@ }, { "type": "Integer", - "description": "Number of users that voted for this option", - "html_description": "Number of users that voted for this option", - "rst_description": "Number of users that voted for this option\n", + "description": "Number of users who voted for this option; may be 0 if unknown", + "html_description": "Number of users who voted for this option; may be 0 if unknown", + "rst_description": "Number of users who voted for this option; may be 0 if unknown\n", "name": "voter_count", "required": true + }, + { + "type": "User", + "description": "User who added the option; omitted if the option wasn't added by a user after poll creation", + "html_description": "Optional. User who added the option; omitted if the option wasn't added by a user after poll creation", + "rst_description": "*Optional*. User who added the option; omitted if the option wasn't added by a user after poll creation\n", + "name": "added_by_user", + "required": false + }, + { + "type": "Chat", + "description": "Chat that added the option; omitted if the option wasn't added by a chat after poll creation", + "html_description": "Optional. Chat that added the option; omitted if the option wasn't added by a chat after poll creation", + "rst_description": "*Optional*. Chat that added the option; omitted if the option wasn't added by a chat after poll creation\n", + "name": "added_by_chat", + "required": false + }, + { + "type": "Integer", + "description": "Point in time (Unix timestamp) when the option was added; omitted if the option existed in the original poll", + "html_description": "Optional. Point in time (Unix timestamp) when the option was added; omitted if the option existed in the original poll", + "rst_description": "*Optional*. Point in time (Unix timestamp) when the option was added; omitted if the option existed in the original poll\n", + "name": "addition_date", + "required": false } ], "category": "types" diff --git a/.butcher/types/PollOption/replace.yml b/.butcher/types/PollOption/replace.yml new file mode 100644 index 00000000..a2abf27d --- /dev/null +++ b/.butcher/types/PollOption/replace.yml @@ -0,0 +1,5 @@ +annotations: + addition_date: + parsed_type: + type: std + name: DateTime diff --git a/.butcher/types/PollOptionAdded/entity.json b/.butcher/types/PollOptionAdded/entity.json new file mode 100644 index 00000000..b9e6e987 --- /dev/null +++ b/.butcher/types/PollOptionAdded/entity.json @@ -0,0 +1,49 @@ +{ + "meta": {}, + "group": { + "title": "Available types", + "anchor": "available-types" + }, + "object": { + "anchor": "polloptionadded", + "name": "PollOptionAdded", + "description": "Describes a service message about an option added to a poll.", + "html_description": "

Describes a service message about an option added to a poll.

", + "rst_description": "Describes a service message about an option added to a poll.", + "annotations": [ + { + "type": "MaybeInaccessibleMessage", + "description": "Message containing the poll to which the option was added, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "html_description": "Optional. Message containing the poll to which the option was added, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "rst_description": "*Optional*. Message containing the poll to which the option was added, if known. Note that the :class:`aiogram.types.message.Message` object in this field will not contain the *reply_to_message* field even if it itself is a reply.\n", + "name": "poll_message", + "required": false + }, + { + "type": "String", + "description": "Unique identifier of the added option", + "html_description": "Unique identifier of the added option", + "rst_description": "Unique identifier of the added option\n", + "name": "option_persistent_id", + "required": true + }, + { + "type": "String", + "description": "Option text", + "html_description": "Option text", + "rst_description": "Option text\n", + "name": "option_text", + "required": true + }, + { + "type": "Array of MessageEntity", + "description": "Special entities that appear in the option_text", + "html_description": "Optional. Special entities that appear in the option_text", + "rst_description": "*Optional*. Special entities that appear in the *option_text*\n", + "name": "option_text_entities", + "required": false + } + ], + "category": "types" + } +} diff --git a/.butcher/types/PollOptionDeleted/entity.json b/.butcher/types/PollOptionDeleted/entity.json new file mode 100644 index 00000000..0a34b964 --- /dev/null +++ b/.butcher/types/PollOptionDeleted/entity.json @@ -0,0 +1,49 @@ +{ + "meta": {}, + "group": { + "title": "Available types", + "anchor": "available-types" + }, + "object": { + "anchor": "polloptiondeleted", + "name": "PollOptionDeleted", + "description": "Describes a service message about an option deleted from a poll.", + "html_description": "

Describes a service message about an option deleted from a poll.

", + "rst_description": "Describes a service message about an option deleted from a poll.", + "annotations": [ + { + "type": "MaybeInaccessibleMessage", + "description": "Message containing the poll from which the option was deleted, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "html_description": "Optional. Message containing the poll from which the option was deleted, if known. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.", + "rst_description": "*Optional*. Message containing the poll from which the option was deleted, if known. Note that the :class:`aiogram.types.message.Message` object in this field will not contain the *reply_to_message* field even if it itself is a reply.\n", + "name": "poll_message", + "required": false + }, + { + "type": "String", + "description": "Unique identifier of the deleted option", + "html_description": "Unique identifier of the deleted option", + "rst_description": "Unique identifier of the deleted option\n", + "name": "option_persistent_id", + "required": true + }, + { + "type": "String", + "description": "Option text", + "html_description": "Option text", + "rst_description": "Option text\n", + "name": "option_text", + "required": true + }, + { + "type": "Array of MessageEntity", + "description": "Special entities that appear in the option_text", + "html_description": "Optional. Special entities that appear in the option_text", + "rst_description": "*Optional*. Special entities that appear in the *option_text*\n", + "name": "option_text_entities", + "required": false + } + ], + "category": "types" + } +} diff --git a/.butcher/types/PreparedInlineMessage/entity.json b/.butcher/types/PreparedInlineMessage/entity.json index 39c0c493..ab30e165 100644 --- a/.butcher/types/PreparedInlineMessage/entity.json +++ b/.butcher/types/PreparedInlineMessage/entity.json @@ -1,8 +1,8 @@ { "meta": {}, "group": { - "title": "Inline mode", - "anchor": "inline-mode" + "title": "Available types", + "anchor": "available-types" }, "object": { "anchor": "preparedinlinemessage", diff --git a/.butcher/types/PreparedKeyboardButton/entity.json b/.butcher/types/PreparedKeyboardButton/entity.json new file mode 100644 index 00000000..dde483f0 --- /dev/null +++ b/.butcher/types/PreparedKeyboardButton/entity.json @@ -0,0 +1,25 @@ +{ + "meta": {}, + "group": { + "title": "Available types", + "anchor": "available-types" + }, + "object": { + "anchor": "preparedkeyboardbutton", + "name": "PreparedKeyboardButton", + "description": "Describes a keyboard button to be used by a user of a Mini App.", + "html_description": "

Describes a keyboard button to be used by a user of a Mini App.

", + "rst_description": "Describes a keyboard button to be used by a user of a Mini App.", + "annotations": [ + { + "type": "String", + "description": "Unique identifier of the keyboard button", + "html_description": "Unique identifier of the keyboard button", + "rst_description": "Unique identifier of the keyboard button\n", + "name": "id", + "required": true + } + ], + "category": "types" + } +} diff --git a/.butcher/types/ReplyParameters/entity.json b/.butcher/types/ReplyParameters/entity.json index b479d0bd..0d11d31a 100644 --- a/.butcher/types/ReplyParameters/entity.json +++ b/.butcher/types/ReplyParameters/entity.json @@ -37,9 +37,9 @@ }, { "type": "String", - "description": "Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. The message will fail to send if the quote isn't found in the original message.", - "html_description": "Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. The message will fail to send if the quote isn't found in the original message.", - "rst_description": "*Optional*. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities. The message will fail to send if the quote isn't found in the original message.\n", + "description": "Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities. The message will fail to send if the quote isn't found in the original message.", + "html_description": "Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities. The message will fail to send if the quote isn't found in the original message.", + "rst_description": "*Optional*. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities. The message will fail to send if the quote isn't found in the original message.\n", "name": "quote", "required": false }, @@ -74,6 +74,14 @@ "rst_description": "*Optional*. Identifier of the specific checklist task to be replied to\n", "name": "checklist_task_id", "required": false + }, + { + "type": "String", + "description": "Persistent identifier of the specific poll option to be replied to", + "html_description": "Optional. Persistent identifier of the specific poll option to be replied to", + "rst_description": "*Optional*. Persistent identifier of the specific poll option to be replied to\n", + "name": "poll_option_id", + "required": false } ], "category": "types" diff --git a/.butcher/types/SentWebAppMessage/entity.json b/.butcher/types/SentWebAppMessage/entity.json index a70568af..1960264d 100644 --- a/.butcher/types/SentWebAppMessage/entity.json +++ b/.butcher/types/SentWebAppMessage/entity.json @@ -1,8 +1,8 @@ { "meta": {}, "group": { - "title": "Inline mode", - "anchor": "inline-mode" + "title": "Available types", + "anchor": "available-types" }, "object": { "anchor": "sentwebappmessage", diff --git a/.butcher/types/TextQuote/entity.json b/.butcher/types/TextQuote/entity.json index 2f7c767c..26b587e9 100644 --- a/.butcher/types/TextQuote/entity.json +++ b/.butcher/types/TextQuote/entity.json @@ -21,9 +21,9 @@ }, { "type": "Array of MessageEntity", - "description": "Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes.", - "html_description": "Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes.", - "rst_description": "*Optional*. Special entities that appear in the quote. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are kept in quotes.\n", + "description": "Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are kept in quotes.", + "html_description": "Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are kept in quotes.", + "rst_description": "*Optional*. Special entities that appear in the quote. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are kept in quotes.\n", "name": "entities", "required": false }, diff --git a/.butcher/types/Update/entity.json b/.butcher/types/Update/entity.json index 256b073b..47b1c70c 100644 --- a/.butcher/types/Update/entity.json +++ b/.butcher/types/Update/entity.json @@ -202,6 +202,14 @@ "rst_description": "*Optional*. A boost was removed from a chat. The bot must be an administrator in the chat to receive these updates.\n", "name": "removed_chat_boost", "required": false + }, + { + "type": "ManagedBotUpdated", + "description": "A new bot was created to be managed by the bot or token of a bot was changed", + "html_description": "Optional. A new bot was created to be managed by the bot or token of a bot was changed", + "rst_description": "*Optional*. A new bot was created to be managed by the bot or token of a bot was changed\n", + "name": "managed_bot", + "required": false } ], "category": "types" diff --git a/.butcher/types/User/entity.json b/.butcher/types/User/entity.json index cead909a..ce116110 100644 --- a/.butcher/types/User/entity.json +++ b/.butcher/types/User/entity.json @@ -130,6 +130,14 @@ "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 + }, + { + "type": "Boolean", + "description": "True, if other bots can be created to be controlled by the bot. Returned only in getMe.", + "html_description": "Optional. True, if other bots can be created to be controlled by the bot. Returned only in getMe.", + "rst_description": "*Optional*. :code:`True`, if other bots can be created to be controlled by the bot. Returned only in :class:`aiogram.methods.get_me.GetMe`.\n", + "name": "can_manage_bots", + "required": false } ], "category": "types" diff --git a/.claude/commands/aiogram-api-changelog.md b/.claude/commands/aiogram-api-changelog.md new file mode 100644 index 00000000..909187a1 --- /dev/null +++ b/.claude/commands/aiogram-api-changelog.md @@ -0,0 +1,50 @@ +Generate a Bot API changelog fragment for aiogram. + +Arguments: $ARGUMENTS (format: ` `) + +## Steps + +1. Parse `$ARGUMENTS`: the first token is the issue/PR number (e.g. `1792`), the rest is the changelog URL (e.g. `https://core.telegram.org/bots/api-changelog#april-3-2026`). + +2. Fetch the changelog URL using WebFetch. Extract **all** changes for the Bot API version anchored by that URL: new methods, new types, new/changed fields, new parameters, renamed fields, and any other notable changes. + +3. Discover which aiogram modules were generated for the new symbols: + - New methods → search `aiogram/methods/.py` via Glob + - New types → search `aiogram/types/.py` via Glob + - Confirm every referenced module exists before writing it into the RST. + +4. Create `CHANGES/.misc.rst` (overwrite if it exists) with the following RST structure, following the style used for previous Bot API updates in `CHANGES.rst`: + +```rst +Updated to `Bot API X.Y <{url}>`_ + +**{Section heading — e.g. "Feature Area"}** + +*New Methods:* + +- Added :class:`aiogram.methods..` method - + +*New Types:* + +- Added :class:`aiogram.types..` type - + +*New Fields:* + +- Added :code:`` field to :class:`aiogram.types..` - + +*New Parameters for* :class:`aiogram.methods..`: + +- Added :code:`` - +``` + +Rules for writing the RST: +- Use `:class:`` for types and methods, `:meth:`` for shortcuts, `:code:`` for field/parameter names. +- Module paths must be the full dotted path (e.g. `aiogram.types.poll_option.PollOption`), always verified against real files. +- If a symbol has a shortcut method on a type, mention it with `:meth:`. +- Group related changes under bold section headings (e.g. **Polls**, **Managed Bots**). +- Within each section use italic sub-headings (*New Methods:*, *New Types:*, *New Fields:*, *New Parameters for ...:*) — omit a sub-heading if there is nothing to list under it. +- Describe each item with a brief, user-facing sentence after the dash. +- Do not add a trailing newline after the last bullet. +- Do not include an issue/PR back-reference link (towncrier adds that automatically). + +5. Print the path of the created file and confirm it is done. diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 00000000..2e510aff --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1,2 @@ +/cache +/project.local.yml diff --git a/.serena/memories/code_style.md b/.serena/memories/code_style.md new file mode 100644 index 00000000..75ec81b9 --- /dev/null +++ b/.serena/memories/code_style.md @@ -0,0 +1,42 @@ +# Code Style & Conventions + +## General +- `from __future__ import annotations` at the top of every Python file +- Full type hints on all function signatures and class fields +- Max line length: **99** characters (ruff enforced) +- snake_case for Python names; camelCase used only in `__api_method__` strings + +## Pydantic models +- All Telegram types extend `TelegramObject(BotContextController, BaseModel)` from `aiogram/types/base.py` +- `TelegramObject` is frozen; use `MutableTelegramObject` when mutation is needed +- Fields default to `None` for optional API parameters; use `Field(None, json_schema_extra={"deprecated": True})` for deprecated fields +- Use `Default("key")` sentinel (from `aiogram.client.default`) for user-configurable defaults like `parse_mode`, `protect_content` +- `TYPE_CHECKING` guards for circular imports — keep runtime imports lean + +## API Methods +- Each method is a class inheriting `TelegramMethod[ReturnType]` from `aiogram/methods/base.py` +- Required class attrs: `__returning__` (return type), `__api_method__` (camelCase string) +- Fields with `None` default = optional param +- Method docstring format: short description + `Source: https://core.telegram.org/bots/api#methodname` +- File name: snake_case of method name (e.g., `SendMessage` → `send_message.py`) + +## Imports order (ruff/isort enforced) +1. stdlib +2. third-party +3. `aiogram` (first-party) +4. relative imports + +## Ruff rules enforced +- A (annotations), B (bugbear), C4 (comprehensions), DTZ (datetimez), E, F, I (isort), PERF, PL (pylint), Q (quotes), RET, SIM, T10, T20, UP (pyupgrade) +- Several PLR rules disabled (Telegram API naturally has many params/methods) +- `F401` disabled (re-exports in `__init__.py` intentional) + +## Code generation convention +- **Never hand-edit generated files** (`.butcher/**/entity.json`, auto-generated `aiogram/types/*.py`, `aiogram/methods/*.py`, `aiogram/enums/*.py`) +- Add features via `.butcher` YAML config/aliases + templates, then regenerate +- After codegen: always run lint + mypy + +## Naming patterns +- Enums: PascalCase class, UPPER_SNAKE members (e.g., `ContentType.TEXT`) +- Test files: `test_.py` +- Test classes: `Test` with `async def test_(self, bot: MockedBot)` diff --git a/.serena/memories/codebase_structure.md b/.serena/memories/codebase_structure.md new file mode 100644 index 00000000..8c8d87fc --- /dev/null +++ b/.serena/memories/codebase_structure.md @@ -0,0 +1,84 @@ +# Codebase Structure + +## Top-level layout +``` +aiogram/ # Main package +.butcher/ # Code generation inputs (DO NOT edit entity.json files) +tests/ # Test suite +docs/ # Sphinx documentation (RST) +examples/ # Example bot scripts +scripts/ # Version bump scripts +CHANGES/ # Towncrier changelog fragments (CHANGES/..rst) +``` + +## aiogram/ package +``` +aiogram/ +├── __init__.py # Public API re-exports +├── __meta__.py # Version (hatch reads this) +├── exceptions.py # Framework exceptions +├── loggers.py # Named loggers +├── client/ +│ ├── bot.py # Bot class — all API methods as async shortcuts +│ ├── session/ # HTTP session backends (aiohttp, base) +│ ├── default.py # Default() sentinel for configurable defaults +│ └── context_controller.py # Bot context injection into models +├── types/ +│ ├── base.py # TelegramObject (Pydantic BaseModel), MutableTelegramObject +│ ├── *.py # One file per Telegram type +│ └── __init__.py # Exports all types +├── methods/ +│ ├── base.py # TelegramMethod[T] base class +│ ├── *.py # One file per API method (e.g., send_message.py → SendMessage) +│ └── __init__.py +├── enums/ +│ ├── content_type.py # ContentType enum +│ ├── update_type.py # UpdateType enum +│ └── *.py +├── dispatcher/ +│ ├── dispatcher.py # Dispatcher (main update processor) +│ ├── router.py # Router (Blueprint-style routing) +│ ├── middlewares/ # Middleware system +│ ├── event/ # Event observer, typed decorators +│ └── flags/ # Handler flags +├── filters/ # Built-in filters (Command, StateFilter, etc.) +├── fsm/ +│ ├── context.py # FSMContext +│ ├── state.py # State, StatesGroup +│ └── storage/ # Memory, Redis, MongoDB storage backends +├── handlers/ # Base handler types +├── utils/ +│ ├── keyboard.py # Keyboard builder utilities +│ ├── text_decorations.py # HTML/Markdown formatting +│ └── i18n/ # Internationalization support +└── webhook/ # Webhook integrations (aiohttp, SimpleRequestHandler, etc.) +``` + +## .butcher/ (Code generation) +``` +.butcher/ +├── schema/schema.json # Full Bot API schema (source of truth) +├── types/ # Per-type entity.json + optional alias YAML overrides +├── methods/ # Per-method entity.json + optional alias YAML overrides +├── enums/ # Per-enum entity.json + optional YAML overrides +└── templates/ # Jinja2 templates for generated Python files +``` + +**Rule**: Edit `.yml` alias files or templates in `.butcher/`, never `.entity.json` files directly. Regenerate after changes. + +## tests/ layout +``` +tests/ +├── conftest.py # Shared fixtures (bot, dispatcher, etc.) +├── mocked_bot.py # MockedBot for testing without real HTTP +├── test_api/ +│ ├── test_types/ # Tests for Telegram types +│ ├── test_methods/ # Tests for API method classes +│ └── test_client/ # HTTP client tests +├── test_dispatcher/ # Dispatcher/Router/Middleware tests +├── test_filters/ # Filter tests +├── test_fsm/ # FSM and storage tests +├── test_handler/ # Handler tests +├── test_utils/ # Utility tests +└── test_webhook/ # Webhook integration tests +``` diff --git a/.serena/memories/codegen_workflow.md b/.serena/memories/codegen_workflow.md new file mode 100644 index 00000000..d3b1984e --- /dev/null +++ b/.serena/memories/codegen_workflow.md @@ -0,0 +1,44 @@ +# Bot API Codegen Workflow + +## How code generation works +aiogram uses `butcher` (via `aiogram-cli`) to auto-generate Python files from the Telegram Bot API schema. + +### Source of truth +- `.butcher/schema/schema.json` — full parsed Bot API schema +- `.butcher/types//entity.json` — parsed entity metadata (DO NOT edit) +- `.butcher/methods//entity.json` — parsed method metadata (DO NOT edit) +- `.butcher/enums//entity.json` — parsed enum metadata (DO NOT edit) +- `.butcher/templates/` — Jinja2 templates that produce Python code +- YAML alias/override files in `.butcher/types/`, `.butcher/methods/` — edit these for customizations + +### Generated files (DO NOT hand-edit) +- `aiogram/types/*.py` (except `base.py`, `custom.py`, `_union.py` — framework internals) +- `aiogram/methods/*.py` (except `base.py`) +- `aiogram/enums/*.py` +- `aiogram/client/bot.py` (the `Bot` class shortcuts) +- `aiogram/types/__init__.py`, `aiogram/methods/__init__.py` + +### Regeneration commands +```bash +uv run --extra cli butcher parse # re-parse from API schema +uv run --extra cli butcher refresh # refresh entity JSON from parsed schema +uv run --extra cli butcher apply all # apply templates → generate Python files +``` + +### Adding a new type/method/shortcut +1. Update the `.butcher` YAML alias/config file for the entity +2. Run regeneration (parse → refresh → apply) +3. Run lint + mypy + tests +4. Commit both the `.butcher` config changes AND the generated Python files + +### API version bumps (maintainers) +```bash +make update-api args=patch # or minor/major +``` +This runs butcher parse/refresh/apply + version bump scripts that update: +- `aiogram/__meta__.py` +- `README.rst` +- `docs/index.rst` + +## Key constraint +The maintainers enforce: **never add types/methods/shortcuts by hand-editing generated files**. All changes must go through `.butcher` config so future regenerations preserve them. diff --git a/.serena/memories/dispatcher/adding_new_event_types.md b/.serena/memories/dispatcher/adding_new_event_types.md new file mode 100644 index 00000000..1196bf13 --- /dev/null +++ b/.serena/memories/dispatcher/adding_new_event_types.md @@ -0,0 +1,43 @@ +# Adding a New Update/Event Type to aiogram Dispatcher + +When Telegram Bot API adds a new update type (e.g. `managed_bot`, `purchased_paid_media`), the following files must be touched. Types, enums, and butcher configs are generated — the dispatcher integration is manual. + +## Checklist (in order) + +### Generated / butcher layer (run `butcher parse && butcher refresh && butcher apply all`) +- `.butcher/types//entity.json` — type definition +- `.butcher/types/Update/entity.json` — add field to Update entity +- `aiogram/types/.py` — generated type class +- `aiogram/types/__init__.py` — export +- `aiogram/enums/update_type.py` — `NEW_TYPE = "new_type"` enum member +- `aiogram/types/update.py` — `new_type: NewType | None = None` field + TYPE_CHECKING import + `__init__` signature + +### Manual dispatcher integration (NOT generated) + +1. **`aiogram/types/update.py`** — `event_type` property (lines ~161-215): add `if self.new_type: return "new_type"` before the `raise UpdateTypeLookupError` line + +2. **`aiogram/dispatcher/router.py`** — two places in `Router.__init__`: + - Add `self.new_type = TelegramEventObserver(router=self, event_name="new_type")` after the last observer attribute (before `self.errors`) + - Add `"new_type": self.new_type,` to the `self.observers` dict (before `"error"`) + +3. **`aiogram/dispatcher/middlewares/user_context.py`** — `resolve_event_context()` method: add `if event.new_type: return EventContext(user=..., chat=...)` before `return EventContext()`. Use `user` field for user-scoped events, `chat` for chat-scoped. No `business_connection_id` unless the event has one. + +### Tests (manual) + +4. **`tests/test_dispatcher/test_dispatcher.py`** — add `pytest.param("new_type", Update(update_id=42, new_type=NewType(...)), has_chat, has_user)` to `test_listen_update` parametrize list. Import `NewType` in the imports block. + +5. **`tests/test_dispatcher/test_router.py`** — add `assert router.observers["new_type"] == router.new_type` to `test_observers_config` + +### Docs (generated or manual stub) +- `docs/api/types/.rst` — RST stub +- `docs/api/types/index.rst` — add to index + +## Key invariants +- The snake_case name must be identical across: `UpdateType` enum value, `Update` field name, `event_type` return string, Router attribute name, observers dict key, and `TelegramEventObserver(event_name=...)`. +- `Update.event_type` uses `@lru_cache()` — never mutate Update fields after construction. +- The routing machinery (`propagate_event`, middleware chains, sub-router propagation) requires **zero changes** — it operates on observer names looked up dynamically. + +## Example: `managed_bot` (API 9.6) +- Type: `ManagedBotUpdated` with fields `user: User` (creator) and `bot_user: User` (the managed bot) +- user_context: `EventContext(user=event.managed_bot.user)` — user only, no chat +- `has_chat=False, has_user=True` in test parametrization diff --git a/.serena/memories/project_overview.md b/.serena/memories/project_overview.md new file mode 100644 index 00000000..f11d32cc --- /dev/null +++ b/.serena/memories/project_overview.md @@ -0,0 +1,43 @@ +# Project Overview: aiogram + +## Purpose +**aiogram** is a modern, fully asynchronous Python framework for the Telegram Bot API (currently supports Bot API 9.5+). It provides a high-level interface for building Telegram bots using asyncio. + +## Tech Stack +- **Python**: 3.10–3.14 (also supports PyPy) +- **Async runtime**: asyncio + aiohttp (HTTP client) +- **Data validation**: Pydantic v2 +- **Package manager**: `uv` +- **Linter/formatter**: `ruff` (check + format) +- **Type checker**: `mypy` +- **Testing**: `pytest` with `pytest-asyncio`, `aresponses` +- **Docs**: Sphinx (reStructuredText), `sphinx-autobuild` +- **Changelog**: `towncrier` +- **Code generation**: `butcher` (aiogram-cli) — generates types, methods, enums from Bot API schema + +## Key Links +- Docs (English): https://docs.aiogram.dev/en/dev-3.x/ +- GitHub: https://github.com/aiogram/aiogram/ +- Bot API schema: `.butcher/schema/schema.json` + +## Architecture Summary +The framework is layered: +1. **`aiogram/client/`** — `Bot` class (all API methods as shortcuts), session management, context controller +2. **`aiogram/types/`** — Pydantic models for all Telegram types (`TelegramObject` base) +3. **`aiogram/methods/`** — `TelegramMethod[ReturnType]` subclasses, one per Bot API method +4. **`aiogram/dispatcher/`** — Dispatcher, Router (blueprints), middleware, event observers +5. **`aiogram/filters/`** — Filter classes for routing +6. **`aiogram/fsm/`** — Finite State Machine (states, storage backends) +7. **`aiogram/enums/`** — Enumerations (ContentType, UpdateType, etc.) +8. **`aiogram/utils/`** — Text decoration, keyboards, i18n, etc. +9. **`aiogram/webhook/`** — Webhook server integrations (aiohttp, FastAPI, etc.) +10. **`.butcher/`** — Code generation inputs: YAML/JSON configs for types/methods/enums + Jinja2 templates + +## Optional extras +- `fast` — uvloop + aiodns +- `redis` — Redis FSM storage +- `mongo` — MongoDB FSM storage +- `proxy` — SOCKS proxy support +- `i18n` — Babel-based i18n +- `cli` — `butcher` codegen CLI +- `signature` — cryptographic signature verification diff --git a/.serena/memories/suggested_commands.md b/.serena/memories/suggested_commands.md new file mode 100644 index 00000000..b17b204d --- /dev/null +++ b/.serena/memories/suggested_commands.md @@ -0,0 +1,66 @@ +# Suggested Commands + +## Setup +```bash +uv sync --all-extras --group dev --group test +uv run pre-commit install +``` + +## Lint & Format (quick loop — use before every commit) +```bash +uv run ruff check --show-fixes --preview aiogram examples +uv run ruff format --check --diff aiogram tests scripts examples +uv run mypy aiogram +``` + +## Auto-fix formatting +```bash +uv run ruff format aiogram tests scripts examples +uv run ruff check --fix aiogram tests scripts examples +``` + +## Run tests +```bash +uv run pytest tests # basic +uv run pytest tests --redis redis://localhost:6379/0 # with Redis +uv run pytest tests --mongo mongodb://mongo:mongo@localhost:27017 # with MongoDB +``` + +## Build docs +```bash +# Live-reload dev server +uv run --extra docs sphinx-autobuild --watch aiogram/ --watch CHANGES.rst --watch README.rst docs/ docs/_build/ +# One-shot build +uv run --extra docs bash -c 'cd docs && make html' +``` + +## Code generation (Bot API codegen) +```bash +# After editing .butcher/*.yml or templates: +uv run --extra cli butcher parse +uv run --extra cli butcher refresh +uv run --extra cli butcher apply all +``` + +## API version bump (maintainers only) +```bash +make update-api args=patch # runs butcher parse/refresh/apply + version bump +``` + +## Changelog +```bash +# Preview draft +uv run --extra docs towncrier build --draft +# Build final +uv run --extra docs towncrier build --yes +``` + +## Clean build artifacts +```bash +make clean +``` + +## Build package +```bash +uv build +``` diff --git a/.serena/memories/task_completion.md b/.serena/memories/task_completion.md new file mode 100644 index 00000000..e25dd178 --- /dev/null +++ b/.serena/memories/task_completion.md @@ -0,0 +1,43 @@ +# Task Completion Checklist + +Run these before marking any task done or requesting review. + +## Quick loop (every PR) +```bash +uv run ruff check --show-fixes --preview aiogram examples +uv run ruff format --check --diff aiogram tests scripts examples +uv run mypy aiogram +uv run pytest tests +``` + +## Codegen tasks (when touching .butcher/ or generated API files) +```bash +uv run --extra cli butcher parse +uv run --extra cli butcher refresh +uv run --extra cli butcher apply all +# Then re-run quick loop +``` + +## Integration tests (only if Redis/Mongo storage touched) +```bash +uv run pytest --redis redis://localhost:6379/0 tests +uv run pytest --mongo mongodb://mongo:mongo@localhost:27017 tests +``` + +## Docs (only if docs/ or public API changed) +```bash +uv run --extra docs bash -c 'cd docs && make html' +``` + +## Changelog fragment (required unless PR has `skip news` label) +- Create `CHANGES/..rst` +- Valid categories: `feature`, `bugfix`, `doc`, `removal`, `misc` +- Content: user-visible behavior description (not internal/process details) +- Do NOT edit `CHANGES.rst` directly + +## PR quality checklist +1. Tests added/updated for all behavior changes +2. Quick loop passes (ruff + mypy + pytest) +3. Changelog fragment added (or justified `skip news`) +4. If codegen-related: both `.butcher` source config AND generated files updated +5. PR body has clear reproduction/validation steps diff --git a/.serena/memories/testing_patterns.md b/.serena/memories/testing_patterns.md new file mode 100644 index 00000000..7914d508 --- /dev/null +++ b/.serena/memories/testing_patterns.md @@ -0,0 +1,45 @@ +# Testing Patterns + +## Framework +- pytest with async support +- No `pytest-asyncio` explicit marks needed (configured globally in pyproject.toml) +- `MockedBot` (tests/mocked_bot.py) — use for all bot method tests, no real HTTP + +## MockedBot pattern +```python +from tests.mocked_bot import MockedBot +from aiogram.methods import SendMessage +from aiogram.types import Message, Chat +import datetime + +class TestSendMessage: + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for( + SendMessage, + ok=True, + result=Message( + message_id=42, + date=datetime.datetime.now(), + text="test", + chat=Chat(id=42, type="private"), + ), + ) + response: Message = await bot.send_message(chat_id=42, text="test") + bot.get_request() + assert response == prepare_result.result +``` + +## Test structure +- Class per type/method: `class TestSendMessage:` +- One test per scenario: `async def test_(self, ...)` +- `bot` fixture comes from `tests/conftest.py` + +## Integration tests +- Redis: `uv run pytest --redis redis://localhost:6379/0 tests` +- MongoDB: `uv run pytest --mongo mongodb://mongo:mongo@localhost:27017 tests` +- Only run these when Redis/Mongo storage code is affected + +## What NOT to do +- Do not mock the database/storage in FSM tests — use real backends or memory storage +- Do not introduce new test dependencies for small tests +- Keep test style consistent with existing suite diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 00000000..b751e51f --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,152 @@ +# the name by which the project can be referenced within Serena +project_name: "aiogram3" + + +# list of languages for which language servers are started; choose from: +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# java julia kotlin lua markdown +# matlab nix pascal perl php +# php_phpactor powershell python python_jedi r +# rego ruby ruby_solargraph rust scala +# swift terraform toml typescript typescript_vts +# vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- python + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: "utf-8" + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: + +# whether to use project's .gitignore files to ignore files +ignore_all_files_in_gitignore: true + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + +# list of tool names to exclude. +# This extends the existing exclusions (e.g. from the global configuration) +# +# Below is the complete list of tools for convenience. +# To make sure you have the latest list of tools, and to view their descriptions, +# execute `uv run scripts/print_tool_overview.py`. +# +# * `activate_project`: Activates a project by name. +# * `check_onboarding_performed`: Checks whether project onboarding was already performed. +# * `create_text_file`: Creates/overwrites a file in the project directory. +# * `delete_lines`: Deletes a range of lines within a file. +# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `execute_shell_command`: Executes a shell command. +# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. +# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). +# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. +# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. +# * `initial_instructions`: Gets the initial instructions for the current project. +# Should only be used in settings where the system prompt cannot be set, +# e.g. in clients you have no control over, like Claude Desktop. +# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. +# * `insert_at_line`: Inserts content at a given line in a file. +# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. +# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). +# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). +# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). +# * `read_file`: Reads a file within the project directory. +# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. +# * `remove_project`: Removes a project from the Serena configuration. +# * `replace_lines`: Replaces a range of lines within a file with new content. +# * `replace_symbol_body`: Replaces the full definition of a symbol. +# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `search_for_pattern`: Performs a search for a pattern in the project. +# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. +# * `switch_modes`: Activates modes by providing a list of their names +# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. +# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. +# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. +# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. +excluded_tools: [] + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +fixed_tools: [] + +# list of mode names to that are always to be included in the set of active modes +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this setting overrides the global configuration. +# Set this to [] to disable base modes for this project. +# Set this to a list of mode names to always include the respective modes for this project. +base_modes: + +# list of mode names that are to be activated by default. +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# This setting can, in turn, be overridden by CLI parameters (--mode). +default_modes: + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +# time budget (seconds) per tool call for the retrieval of additional symbol information +# such as docstrings or parameter information. +# This overrides the corresponding setting in the global configuration; see the documentation there. +# If null or missing, use the setting from the global configuration. +symbol_info_budget: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..2caf7949 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,131 @@ +# AGENTS.md + +This file defines how coding agents should contribute to `aiogram` on `dev-3.x`. + +## Scope and defaults + +- Base branch: `dev-3.x` +- Python: `>=3.10` +- Main tooling: `uv`, `ruff`, `mypy`, `pytest`, `towncrier`, `butcher` +- Keep diffs focused; avoid unrelated refactors/reformatting. + +## Setup + +```bash +uv sync --all-extras --group dev --group test +uv run pre-commit install +``` + +Note: `uv run pre-commit install` writes hooks to the shared repository `.git/hooks` +(common for all worktrees), not only for the current worktree. + +## Codebase Navigation + +Use **Serena MCP** for all codebase navigation tasks. Serena provides semantic, symbol-aware tools that are more efficient than raw file reads: + +- `get_symbols_overview` — list classes/methods in a file without reading the full body +- `find_symbol` — locate a specific class, method, or field by name path +- `find_referencing_symbols` — find all usages of a symbol across the codebase +- `search_for_pattern` — regex search when symbol names are unknown + +Prefer Serena's symbol tools over `Read`/`Grep` for source code exploration. Only fall back to file-based tools when Serena is unavailable or for non-code files (JSON configs, Markdown, etc.). + +## Mandatory local checks before PR + +Code style/lint in this repository is enforced via Ruff (`ruff check` + `ruff format`). + +Quick loop (recommended for most PR iterations): + +```bash +uv run ruff check --show-fixes --preview aiogram examples +uv run ruff format --check --diff aiogram tests scripts examples +uv run mypy aiogram +uv run pytest tests +``` + +Full loop (run before final review request): + +```bash +# Run quick loop first, then: +uv run pytest --redis redis://:/ tests # when Redis storage paths are affected +uv run pytest --mongo mongodb://:@: tests # when Mongo storage paths are affected +uv run --extra docs bash -c 'cd docs && make html' # when docs or generated API docs are affected +``` + +If changes touch Redis/Mongo storage behavior, run integration variants too: + +```bash +uv run pytest --redis redis://:/ tests +uv run pytest --mongo mongodb://:@: tests +``` + +Run these only if you have accessible Redis/Mongo instances in your environment. + + +## Changelog rules (CI-gated) + +- Add `CHANGES/..rst` unless PR has `skip news` label. +- Valid categories: `feature`, `bugfix`, `doc`, `removal`, `misc`. +- Changelog text must describe user-visible behavior changes, not process/org details. +- Do not edit `CHANGES.rst` directly for regular PRs. + +## Bot API/codegen workflow (critical) + +`aiogram` API layers are generated. For Bot API related work: + +- Prefer editing generator inputs (`.butcher/**/*.yml`, aliases, templates) instead of hand-editing generated code. +- Do not manually edit `.butcher/**/entity.json` (parser/codegen will overwrite it). +- For new shortcuts, add alias/config in `.butcher` and regenerate. +- Regeneration flow: + +```bash +uv run --extra cli butcher parse +uv run --extra cli butcher refresh +uv run --extra cli butcher apply all +``` + +For maintainers preparing an API/version bump only: + +```bash +make update-api args=patch +``` + +`make update-api args=...` also runs version bump scripts and updates version-related files +(`aiogram/__meta__.py`, `README.rst`, `docs/index.rst`). + +After regeneration, run lint/type/tests again. + +## Maintainer review signals (recent PRs) + +These patterns repeatedly appeared in maintainer feedback and should be treated as hard constraints: + +- Keep generation path consistent: shortcuts/features should be added through `.butcher` config + generation, not ad-hoc manual edits. +- Keep test style consistent with existing suite; avoid introducing new dependencies for small tests. +- Preserve framework contracts (e.g., dispatcher/workflow data passed to startup/shutdown callbacks). +- When fixing generated API metadata/docs, update the source mapping in `.butcher` so future regenerations keep the fix. + +## Documentation work + +For docs changes: + +```bash +uv run --extra docs sphinx-autobuild --watch aiogram/ --watch CHANGES.rst --watch README.rst docs/ docs/_build/ +``` + +`sphinx-autobuild` is long-running by design. + +Or quick build: + +```bash +uv run --extra docs bash -c 'cd docs && make html' +``` + +## PR quality checklist + +Before requesting review: + +1. Tests added/updated for behavior changes. +2. Local lint/type/tests pass. +3. Changelog fragment added (or `skip news` is justified). +4. If codegen-related: generated files and source config are both updated coherently. +5. PR body includes clear reproduction/validation steps. diff --git a/CHANGES.rst b/CHANGES.rst index be4cb56e..4e67682c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,6 +16,121 @@ Changelog .. towncrier release notes start +3.27.0 (2026-04-04) +==================== + +Features +-------- + +- Added `__eq__` and `__hash__` methods to the `Default` class. + `#1707 `_ + + +Bugfixes +-------- + +- ``CommandStart(deep_link=False)`` now correctly rejects messages that contain deep-link arguments. Previously ``deep_link=False`` (the default) did not distinguish between ``/start`` and ``/start ``. The default is changed to ``None`` (accept both) to preserve backward compatibility. + `#1713 `_ +- Fixed ``HtmlDecoration.custom_emoji()`` to use the correct ``emoji-id`` attribute name instead of ``emoji_id`` in the ```` tag, matching the Telegram Bot API specification. + `#1782 `_ +- Remove redundant list() around sorted() and fix router type name in validation error message + `#1788 `_ + + +Misc +---- + +- Updated to `Bot API 9.6 `_ + + **Managed Bots** + + *New Methods:* + + - Added :class:`aiogram.methods.get_managed_bot_token.GetManagedBotToken` method - retrieves the token of a managed bot + - Added :class:`aiogram.methods.replace_managed_bot_token.ReplaceManagedBotToken` method - generates a new token for a managed bot, invalidating the previous one + - Added :class:`aiogram.methods.save_prepared_keyboard_button.SavePreparedKeyboardButton` method - saves a keyboard button to be used in Mini Apps via :code:`requestChat` + + *New Types:* + + - Added :class:`aiogram.types.keyboard_button_request_managed_bot.KeyboardButtonRequestManagedBot` type - defines criteria for selecting a managed bot via a keyboard button + - Added :class:`aiogram.types.managed_bot_created.ManagedBotCreated` type - describes a service message about a managed bot being created + - Added :class:`aiogram.types.managed_bot_updated.ManagedBotUpdated` type - describes updates to a managed bot + - Added :class:`aiogram.types.prepared_keyboard_button.PreparedKeyboardButton` type - represents a prepared keyboard button for use in Mini Apps + + *New Fields:* + + - Added :code:`can_manage_bots` field to :class:`aiogram.types.user.User` - indicates whether the bot can manage other bots + - Added :code:`request_managed_bot` field to :class:`aiogram.types.keyboard_button.KeyboardButton` - requests the user to select a managed bot + - Added :code:`managed_bot_created` field to :class:`aiogram.types.message.Message` - service message about a managed bot being created (type: :class:`aiogram.types.managed_bot_created.ManagedBotCreated`) + - Added :code:`managed_bot` field to :class:`aiogram.types.update.Update` - contains updates received by a managed bot + + **Polls** + + *New Types:* + + - Added :class:`aiogram.types.poll_option_added.PollOptionAdded` type - describes a service message about a new option added to a poll + - Added :class:`aiogram.types.poll_option_deleted.PollOptionDeleted` type - describes a service message about a poll option being deleted + + *New Fields:* + + - Replaced :code:`correct_option_id` with :code:`correct_option_ids` in :class:`aiogram.types.poll.Poll` - supports multiple correct answers for quiz polls + - Added :code:`allows_revoting` field to :class:`aiogram.types.poll.Poll` - indicates whether users are allowed to change their vote + - Added :code:`description` and :code:`description_entities` fields to :class:`aiogram.types.poll.Poll` - optional poll description with formatting entities + - Added :code:`persistent_id` field to :class:`aiogram.types.poll_option.PollOption` - stable identifier for a poll option + - Added :code:`added_by_user` and :code:`added_by_chat` fields to :class:`aiogram.types.poll_option.PollOption` - identifies who added the option + - Added :code:`addition_date` field to :class:`aiogram.types.poll_option.PollOption` - date when the option was added + - Added :code:`option_persistent_ids` field to :class:`aiogram.types.poll_answer.PollAnswer` - persistent IDs of the chosen options + - Added :code:`poll_option_id` field to :class:`aiogram.types.reply_parameters.ReplyParameters` - allows replying to a specific poll option + - Added :code:`reply_to_poll_option_id` field to :class:`aiogram.types.message.Message` - the persistent ID of the poll option the message replies to + + *New Parameters for* :class:`aiogram.methods.send_poll.SendPoll`: + + - Replaced :code:`correct_option_id` with :code:`correct_option_ids` - supports multiple correct answers for quiz polls + - Added :code:`allows_revoting` - allows users to change their vote after submission + - Added :code:`shuffle_options` - randomizes the order of poll options for each user + - Added :code:`allow_adding_options` - allows users to add their own poll options + - Added :code:`hide_results_until_closes` - hides vote results until the poll is closed + - Added :code:`description`, :code:`description_parse_mode`, :code:`description_entities` - optional poll description with parse mode and formatting + `#1792 `_ + + +3.26.0 (2026-03-03) +==================== + +Bugfixes +-------- + +- Fixed scene transitions to preserve middleware-injected data when moving between scenes via ``SceneWizard.goto``. + `#1687 `_ +- Added ``icon_custom_emoji_id`` and ``style`` parameters to ``InlineKeyboardBuilder.button`` and ``ReplyKeyboardBuilder.button`` signatures. + `#1768 `_ +- Fixed Pydantic protected namespace warning for `model_custom_emoji_id` by adding `protected_namespaces=()` to `model_config`. + `#1772 `_ + + +Misc +---- + +- Documented webhook security constraints for proxy deployments, including trust requirements for :code:`X-Forwarded-For` and recommended defense-in-depth checks. + `#47 `_ +- Updated to `Bot API 9.5 `_ + + **New Methods:** + + - Added :class:`aiogram.methods.send_message_draft.SendMessageDraft` method - allowed for all bots to stream partial messages while they are being generated + - Added :class:`aiogram.methods.set_chat_member_tag.SetChatMemberTag` method - allows bots to set a custom tag for a chat member; available via :meth:`aiogram.types.chat.Chat.set_member_tag` shortcut + + **New Fields:** + + - Added :code:`date_time` type to :class:`aiogram.types.message_entity.MessageEntity` with :code:`unix_time` and :code:`date_time_format` fields - allows bots to display a formatted date and time to the user + - Added :code:`tag` field to :class:`aiogram.types.chat_member_member.ChatMemberMember` and :class:`aiogram.types.chat_member_restricted.ChatMemberRestricted` - the custom tag set for the chat member + - Added :code:`can_edit_tag` field to :class:`aiogram.types.chat_member_restricted.ChatMemberRestricted` and :class:`aiogram.types.chat_permissions.ChatPermissions` - indicates whether the user is allowed to edit their own tag + - Added :code:`can_manage_tags` field to :class:`aiogram.types.chat_member_administrator.ChatMemberAdministrator` and :class:`aiogram.types.chat_administrator_rights.ChatAdministratorRights` - indicates whether the administrator can manage tags of other chat members + - Added :code:`can_manage_tags` parameter to :class:`aiogram.methods.promote_chat_member.PromoteChatMember` method + - Added :code:`sender_tag` field to :class:`aiogram.types.message.Message` - the tag of the message sender in the chat + `#1780 `_ + + 3.25.0 (2026-02-10) ==================== diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..a97d7a44 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# CLAUDE.md + +Use @AGENTS.md as the source of truth for contribution workflow, checks, and Bot API codegen rules in this repository. diff --git a/README.rst b/README.rst index c3b660d3..94c8da9f 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.4 `_ and gets fast updates to the latest versions of the Bot API +- Supports `Telegram Bot API 9.6 `_ 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 0c33f1a9..f60aa330 100644 --- a/aiogram/__meta__.py +++ b/aiogram/__meta__.py @@ -1,2 +1,2 @@ -__version__ = "3.25.0" -__api_version__ = "9.4" +__version__ = "3.27.0" +__api_version__ = "9.6" diff --git a/aiogram/client/bot.py b/aiogram/client/bot.py index d10429f6..e85c1367 100644 --- a/aiogram/client/bot.py +++ b/aiogram/client/bot.py @@ -80,6 +80,7 @@ from ..methods import ( GetFile, GetForumTopicIconStickers, GetGameHighScores, + GetManagedBotToken, GetMe, GetMyCommands, GetMyDefaultAdministratorRights, @@ -110,11 +111,13 @@ from ..methods import ( RemoveUserVerification, ReopenForumTopic, ReopenGeneralForumTopic, + ReplaceManagedBotToken, ReplaceStickerInSet, RepostStory, RestrictChatMember, RevokeChatInviteLink, SavePreparedInlineMessage, + SavePreparedKeyboardButton, SendAnimation, SendAudio, SendChatAction, @@ -144,6 +147,7 @@ from ..methods import ( SetBusinessAccountUsername, SetChatAdministratorCustomTitle, SetChatDescription, + SetChatMemberTag, SetChatMenuButton, SetChatPermissions, SetChatPhoto, @@ -215,6 +219,7 @@ from ..types import ( InputProfilePhotoUnion, InputSticker, InputStoryContentUnion, + KeyboardButton, LabeledPrice, LinkPreviewOptions, MaskPosition, @@ -227,6 +232,7 @@ from ..types import ( PassportElementErrorUnion, Poll, PreparedInlineMessage, + PreparedKeyboardButton, ReactionTypeUnion, ReplyMarkupUnion, ReplyParameters, @@ -2021,6 +2027,7 @@ class Bot: can_pin_messages: bool | None = None, can_manage_topics: bool | None = None, can_manage_direct_messages: bool | None = None, + can_manage_tags: bool | None = None, request_timeout: int | None = None, ) -> bool: """ @@ -2046,6 +2053,7 @@ class Bot: :param can_pin_messages: Pass :code:`True` if the administrator can pin messages; for supergroups only :param can_manage_topics: Pass :code:`True` if the user is allowed to create, rename, close, and reopen forum topics; for supergroups only :param can_manage_direct_messages: Pass :code:`True` if the administrator can manage direct messages within the channel and decline suggested posts; for channels only + :param can_manage_tags: Pass :code:`True` if the administrator can edit the tags of regular members; for groups and supergroups only :param request_timeout: Request timeout :return: Returns :code:`True` on success. """ @@ -2069,6 +2077,7 @@ class Bot: can_pin_messages=can_pin_messages, can_manage_topics=can_manage_topics, can_manage_direct_messages=can_manage_direct_messages, + can_manage_tags=can_manage_tags, ) return await self(call, request_timeout=request_timeout) @@ -3004,13 +3013,20 @@ class Bot: is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, @@ -3018,6 +3034,7 @@ class Bot: reply_parameters: ReplyParameters | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, reply_to_message_id: int | None = None, request_timeout: int | None = None, ) -> Message: @@ -3035,14 +3052,21 @@ class Bot: :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance @@ -3050,6 +3074,7 @@ class Bot: :param reply_parameters: Description of the message to reply to :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :param reply_to_message_id: If the message is a reply, ID of the original message :param request_timeout: Request timeout :return: On success, the sent :class:`aiogram.types.message.Message` is returned. @@ -3066,13 +3091,20 @@ class Bot: is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, @@ -3080,6 +3112,7 @@ class Bot: reply_parameters=reply_parameters, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, reply_to_message_id=reply_to_message_id, ) return await self(call, request_timeout=request_timeout) @@ -4900,8 +4933,8 @@ class Bot: :param chat_id: Required if *user_id* is not specified. Unique identifier for the chat or username of the channel (in the format :code:`@channelusername`) that will receive the gift. :param pay_for_upgrade: Pass :code:`True` to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver :param text: Text that will be shown along with the gift; 0-128 characters - :param text_parse_mode: Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored. - :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored. + :param text_parse_mode: Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored. + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored. :param request_timeout: Request timeout :return: Returns :code:`True` on success. """ @@ -5228,8 +5261,8 @@ class Bot: :param month_count: Number of months the Telegram Premium subscription will be active for the user; must be one of 3, 6, or 12 :param star_count: Number of Telegram Stars to pay for the Telegram Premium subscription; must be 1000 for 3 months, 1500 for 6 months, and 2500 for 12 months :param text: Text that will be shown along with the service message about the subscription; 0-128 characters - :param text_parse_mode: Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored. - :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored. + :param text_parse_mode: Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored. + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored. :param request_timeout: Request timeout :return: Returns :code:`True` on success. """ @@ -5560,7 +5593,7 @@ class Bot: :param chat_id: Unique identifier for the target chat :param message_id: Unique identifier for the target message :param checklist: A JSON-serialized object for the new checklist - :param reply_markup: A JSON-serialized object for the new inline keyboard for the message + :param reply_markup: A JSON-serialized object for the new `inline keyboard `_ for the message :param request_timeout: Request timeout :return: On success, the edited :class:`aiogram.types.message.Message` is returned. """ @@ -5614,7 +5647,7 @@ class Bot: :param protect_content: Protects the contents of the sent message from forwarding and saving :param message_effect_id: Unique identifier of the message effect to be added to the message :param reply_parameters: A JSON-serialized object for description of the message to reply to - :param reply_markup: A JSON-serialized object for an inline keyboard + :param reply_markup: A JSON-serialized object for an `inline keyboard `_ :param request_timeout: Request timeout :return: On success, the sent :class:`aiogram.types.message.Message` is returned. """ @@ -5823,7 +5856,7 @@ class Bot: request_timeout: int | None = None, ) -> bool: """ - Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns :code:`True` on success. + Use this method to stream a partial message to a user while the message is being generated. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#sendmessagedraft @@ -5908,3 +5941,92 @@ class Bot: photo=photo, ) return await self(call, request_timeout=request_timeout) + + async def set_chat_member_tag( + self, + chat_id: ChatIdUnion, + user_id: int, + tag: str | None = None, + request_timeout: int | None = None, + ) -> bool: + """ + Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the *can_manage_tags* administrator right. Returns :code:`True` on success. + + Source: https://core.telegram.org/bots/api#setchatmembertag + + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`) + :param user_id: Unique identifier of the target user + :param tag: New tag for the member; 0-16 characters, emoji are not allowed + :param request_timeout: Request timeout + :return: Returns :code:`True` on success. + """ + + call = SetChatMemberTag( + chat_id=chat_id, + user_id=user_id, + tag=tag, + ) + return await self(call, request_timeout=request_timeout) + + async def get_managed_bot_token( + self, + user_id: int, + request_timeout: int | None = None, + ) -> str: + """ + Use this method to get the token of a managed bot. Returns the token as *String* on success. + + Source: https://core.telegram.org/bots/api#getmanagedbottoken + + :param user_id: User identifier of the managed bot whose token will be returned + :param request_timeout: Request timeout + :return: Returns the token as *String* on success. + """ + + call = GetManagedBotToken( + user_id=user_id, + ) + return await self(call, request_timeout=request_timeout) + + async def replace_managed_bot_token( + self, + user_id: int, + request_timeout: int | None = None, + ) -> str: + """ + Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as *String* on success. + + Source: https://core.telegram.org/bots/api#replacemanagedbottoken + + :param user_id: User identifier of the managed bot whose token will be replaced + :param request_timeout: Request timeout + :return: Returns the new token as *String* on success. + """ + + call = ReplaceManagedBotToken( + user_id=user_id, + ) + return await self(call, request_timeout=request_timeout) + + async def save_prepared_keyboard_button( + self, + user_id: int, + button: KeyboardButton, + request_timeout: int | None = None, + ) -> PreparedKeyboardButton: + """ + Stores a keyboard button that can be used by a user within a Mini App. Returns a :class:`aiogram.types.prepared_keyboard_button.PreparedKeyboardButton` object. + + Source: https://core.telegram.org/bots/api#savepreparedkeyboardbutton + + :param user_id: Unique identifier of the target user that can use the button + :param button: A JSON-serialized object describing the button to be saved. The button must be of the type *request_users*, *request_chat*, or *request_managed_bot* + :param request_timeout: Request timeout + :return: Returns a :class:`aiogram.types.prepared_keyboard_button.PreparedKeyboardButton` object. + """ + + call = SavePreparedKeyboardButton( + user_id=user_id, + button=button, + ) + return await self(call, request_timeout=request_timeout) diff --git a/aiogram/client/default.py b/aiogram/client/default.py index ee422982..00c8645e 100644 --- a/aiogram/client/default.py +++ b/aiogram/client/default.py @@ -28,6 +28,14 @@ class Default: def __repr__(self) -> str: return f"<{self}>" + def __eq__(self, other: object) -> bool: + if not isinstance(other, Default): + return NotImplemented + return self._name == other._name + + def __hash__(self) -> int: + return hash(self._name) + @dataclass(**dataclass_kwargs(slots=True, kw_only=True)) class DefaultBotProperties: diff --git a/aiogram/dispatcher/middlewares/user_context.py b/aiogram/dispatcher/middlewares/user_context.py index 844ddd96..40b407e9 100644 --- a/aiogram/dispatcher/middlewares/user_context.py +++ b/aiogram/dispatcher/middlewares/user_context.py @@ -183,4 +183,6 @@ class UserContextMiddleware(BaseMiddleware): return EventContext( user=event.purchased_paid_media.from_user, ) + if event.managed_bot: + return EventContext(user=event.managed_bot.user) return EventContext() diff --git a/aiogram/dispatcher/router.py b/aiogram/dispatcher/router.py index 90a2362a..dde35c3c 100644 --- a/aiogram/dispatcher/router.py +++ b/aiogram/dispatcher/router.py @@ -85,6 +85,7 @@ class Router: router=self, event_name="purchased_paid_media", ) + self.managed_bot = TelegramEventObserver(router=self, event_name="managed_bot") self.errors = self.error = TelegramEventObserver(router=self, event_name="error") @@ -115,6 +116,7 @@ class Router: "edited_business_message": self.edited_business_message, "business_message": self.business_message, "purchased_paid_media": self.purchased_paid_media, + "managed_bot": self.managed_bot, "error": self.errors, } @@ -131,7 +133,7 @@ class Router: Is useful for getting updates only for registered event types. :param skip_events: skip specified event names - :return: set of registered names + :return: sorted list of registered names """ handlers_in_use: set[str] = set() if skip_events is None: @@ -143,7 +145,7 @@ class Router: if observer.handlers and update_name not in skip_events: handlers_in_use.add(update_name) - return list(sorted(handlers_in_use)) # NOQA: C413 + return sorted(handlers_in_use) async def propagate_event(self, update_type: str, event: TelegramObject, **kwargs: Any) -> Any: kwargs.update(event_router=self) @@ -262,7 +264,7 @@ class Router: :return: """ if not isinstance(router, Router): - msg = f"router should be instance of Router not {type(router).__class__.__name__}" + msg = f"router should be instance of Router not {type(router).__name__!r}" raise ValueError(msg) router.parent_router = self return router diff --git a/aiogram/enums/content_type.py b/aiogram/enums/content_type.py index b2a555d4..0c099661 100644 --- a/aiogram/enums/content_type.py +++ b/aiogram/enums/content_type.py @@ -67,7 +67,10 @@ class ContentType(str, Enum): GIVEAWAY = "giveaway" GIVEAWAY_WINNERS = "giveaway_winners" GIVEAWAY_COMPLETED = "giveaway_completed" + MANAGED_BOT_CREATED = "managed_bot_created" PAID_MESSAGE_PRICE_CHANGED = "paid_message_price_changed" + POLL_OPTION_ADDED = "poll_option_added" + POLL_OPTION_DELETED = "poll_option_deleted" SUGGESTED_POST_APPROVED = "suggested_post_approved" SUGGESTED_POST_APPROVAL_FAILED = "suggested_post_approval_failed" SUGGESTED_POST_DECLINED = "suggested_post_declined" diff --git a/aiogram/enums/message_entity_type.py b/aiogram/enums/message_entity_type.py index b67dc039..e1dba489 100644 --- a/aiogram/enums/message_entity_type.py +++ b/aiogram/enums/message_entity_type.py @@ -27,3 +27,4 @@ class MessageEntityType(str, Enum): TEXT_LINK = "text_link" TEXT_MENTION = "text_mention" CUSTOM_EMOJI = "custom_emoji" + DATE_TIME = "date_time" diff --git a/aiogram/enums/update_type.py b/aiogram/enums/update_type.py index 2629264d..0534a664 100644 --- a/aiogram/enums/update_type.py +++ b/aiogram/enums/update_type.py @@ -31,3 +31,4 @@ class UpdateType(str, Enum): CHAT_JOIN_REQUEST = "chat_join_request" CHAT_BOOST = "chat_boost" REMOVED_CHAT_BOOST = "removed_chat_boost" + MANAGED_BOT = "managed_bot" diff --git a/aiogram/filters/command.py b/aiogram/filters/command.py index 498cd8c7..ecb03c48 100644 --- a/aiogram/filters/command.py +++ b/aiogram/filters/command.py @@ -240,7 +240,7 @@ class CommandObject: class CommandStart(Command): def __init__( self, - deep_link: bool = False, + deep_link: bool | None = None, deep_link_encoded: bool = False, ignore_case: bool = False, ignore_mention: bool = False, @@ -282,7 +282,12 @@ class CommandStart(Command): return command # noqa: RET504 def validate_deeplink(self, command: CommandObject) -> CommandObject: - if not self.deep_link: + if self.deep_link is None: + return command + if self.deep_link is False: + if command.args: + msg = "Deep-link was not expected" + raise CommandException(msg) return command if not command.args: msg = "Deep-link was missing" diff --git a/aiogram/fsm/scene.py b/aiogram/fsm/scene.py index da0a52d2..4c7fa72c 100644 --- a/aiogram/fsm/scene.py +++ b/aiogram/fsm/scene.py @@ -259,6 +259,7 @@ class SceneHandlerWrapper: ) raise SceneException(msg) from None event_update: Update = kwargs["event_update"] + scenes.data = {**scenes.data, **kwargs} scene = self.scene( wizard=SceneWizard( scene_config=self.scene.__scene_config__, @@ -712,6 +713,9 @@ class ScenesManager: :param kwargs: Additional keyword arguments to pass to the scene's wizard.enter() method. :return: None """ + if kwargs: + self.data = {**self.data, **kwargs} + if _check_active: active_scene = await self._get_active_scene() if active_scene is not None: diff --git a/aiogram/methods/__init__.py b/aiogram/methods/__init__.py index 786e53e5..5a0a66f0 100644 --- a/aiogram/methods/__init__.py +++ b/aiogram/methods/__init__.py @@ -62,6 +62,7 @@ from .get_custom_emoji_stickers import GetCustomEmojiStickers from .get_file import GetFile from .get_forum_topic_icon_stickers import GetForumTopicIconStickers from .get_game_high_scores import GetGameHighScores +from .get_managed_bot_token import GetManagedBotToken from .get_me import GetMe from .get_my_commands import GetMyCommands from .get_my_default_administrator_rights import GetMyDefaultAdministratorRights @@ -92,11 +93,13 @@ 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 +from .replace_managed_bot_token import ReplaceManagedBotToken from .replace_sticker_in_set import ReplaceStickerInSet from .repost_story import RepostStory from .restrict_chat_member import RestrictChatMember from .revoke_chat_invite_link import RevokeChatInviteLink from .save_prepared_inline_message import SavePreparedInlineMessage +from .save_prepared_keyboard_button import SavePreparedKeyboardButton from .send_animation import SendAnimation from .send_audio import SendAudio from .send_chat_action import SendChatAction @@ -126,6 +129,7 @@ from .set_business_account_profile_photo import SetBusinessAccountProfilePhoto from .set_business_account_username import SetBusinessAccountUsername from .set_chat_administrator_custom_title import SetChatAdministratorCustomTitle from .set_chat_description import SetChatDescription +from .set_chat_member_tag import SetChatMemberTag from .set_chat_menu_button import SetChatMenuButton from .set_chat_permissions import SetChatPermissions from .set_chat_photo import SetChatPhoto @@ -229,6 +233,7 @@ __all__ = ( "GetFile", "GetForumTopicIconStickers", "GetGameHighScores", + "GetManagedBotToken", "GetMe", "GetMyCommands", "GetMyDefaultAdministratorRights", @@ -259,6 +264,7 @@ __all__ = ( "RemoveUserVerification", "ReopenForumTopic", "ReopenGeneralForumTopic", + "ReplaceManagedBotToken", "ReplaceStickerInSet", "RepostStory", "Request", @@ -266,6 +272,7 @@ __all__ = ( "RestrictChatMember", "RevokeChatInviteLink", "SavePreparedInlineMessage", + "SavePreparedKeyboardButton", "SendAnimation", "SendAudio", "SendChatAction", @@ -295,6 +302,7 @@ __all__ = ( "SetBusinessAccountUsername", "SetChatAdministratorCustomTitle", "SetChatDescription", + "SetChatMemberTag", "SetChatMenuButton", "SetChatPermissions", "SetChatPhoto", diff --git a/aiogram/methods/edit_message_checklist.py b/aiogram/methods/edit_message_checklist.py index 6f7bbf7d..83d3815f 100644 --- a/aiogram/methods/edit_message_checklist.py +++ b/aiogram/methods/edit_message_checklist.py @@ -25,7 +25,7 @@ class EditMessageChecklist(TelegramMethod[Message]): checklist: InputChecklist """A JSON-serialized object for the new checklist""" reply_markup: InlineKeyboardMarkup | None = None - """A JSON-serialized object for the new inline keyboard for the message""" + """A JSON-serialized object for the new `inline keyboard `_ for the message""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! diff --git a/aiogram/methods/get_managed_bot_token.py b/aiogram/methods/get_managed_bot_token.py new file mode 100644 index 00000000..d4d28453 --- /dev/null +++ b/aiogram/methods/get_managed_bot_token.py @@ -0,0 +1,28 @@ +from typing import TYPE_CHECKING, Any + +from .base import TelegramMethod + + +class GetManagedBotToken(TelegramMethod[str]): + """ + Use this method to get the token of a managed bot. Returns the token as *String* on success. + + Source: https://core.telegram.org/bots/api#getmanagedbottoken + """ + + __returning__ = str + __api_method__ = "getManagedBotToken" + + user_id: int + """User identifier of the managed bot whose token will be returned""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__(__pydantic__self__, *, user_id: int, **__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, **__pydantic_kwargs) diff --git a/aiogram/methods/gift_premium_subscription.py b/aiogram/methods/gift_premium_subscription.py index 2feaa5f5..58332c6b 100644 --- a/aiogram/methods/gift_premium_subscription.py +++ b/aiogram/methods/gift_premium_subscription.py @@ -25,9 +25,9 @@ class GiftPremiumSubscription(TelegramMethod[bool]): text: str | None = None """Text that will be shown along with the service message about the subscription; 0-128 characters""" text_parse_mode: str | None = None - """Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.""" + """Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.""" text_entities: list[MessageEntity] | None = None - """A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.""" + """A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! diff --git a/aiogram/methods/promote_chat_member.py b/aiogram/methods/promote_chat_member.py index e26f821e..6f8d4dbf 100644 --- a/aiogram/methods/promote_chat_member.py +++ b/aiogram/methods/promote_chat_member.py @@ -52,6 +52,8 @@ class PromoteChatMember(TelegramMethod[bool]): """Pass :code:`True` if the user is allowed to create, rename, close, and reopen forum topics; for supergroups only""" can_manage_direct_messages: bool | None = None """Pass :code:`True` if the administrator can manage direct messages within the channel and decline suggested posts; for channels only""" + can_manage_tags: bool | None = None + """Pass :code:`True` if the administrator can edit the tags of regular members; for groups and supergroups only""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -78,6 +80,7 @@ class PromoteChatMember(TelegramMethod[bool]): can_pin_messages: bool | None = None, can_manage_topics: bool | None = None, can_manage_direct_messages: bool | None = None, + can_manage_tags: bool | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -103,5 +106,6 @@ class PromoteChatMember(TelegramMethod[bool]): can_pin_messages=can_pin_messages, can_manage_topics=can_manage_topics, can_manage_direct_messages=can_manage_direct_messages, + can_manage_tags=can_manage_tags, **__pydantic_kwargs, ) diff --git a/aiogram/methods/replace_managed_bot_token.py b/aiogram/methods/replace_managed_bot_token.py new file mode 100644 index 00000000..aa224736 --- /dev/null +++ b/aiogram/methods/replace_managed_bot_token.py @@ -0,0 +1,28 @@ +from typing import TYPE_CHECKING, Any + +from .base import TelegramMethod + + +class ReplaceManagedBotToken(TelegramMethod[str]): + """ + Use this method to revoke the current token of a managed bot and generate a new one. Returns the new token as *String* on success. + + Source: https://core.telegram.org/bots/api#replacemanagedbottoken + """ + + __returning__ = str + __api_method__ = "replaceManagedBotToken" + + user_id: int + """User identifier of the managed bot whose token will be replaced""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__(__pydantic__self__, *, user_id: int, **__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, **__pydantic_kwargs) diff --git a/aiogram/methods/save_prepared_keyboard_button.py b/aiogram/methods/save_prepared_keyboard_button.py new file mode 100644 index 00000000..03546fbb --- /dev/null +++ b/aiogram/methods/save_prepared_keyboard_button.py @@ -0,0 +1,33 @@ +from typing import TYPE_CHECKING, Any + +from ..types import KeyboardButton, PreparedKeyboardButton +from .base import TelegramMethod + + +class SavePreparedKeyboardButton(TelegramMethod[PreparedKeyboardButton]): + """ + Stores a keyboard button that can be used by a user within a Mini App. Returns a :class:`aiogram.types.prepared_keyboard_button.PreparedKeyboardButton` object. + + Source: https://core.telegram.org/bots/api#savepreparedkeyboardbutton + """ + + __returning__ = PreparedKeyboardButton + __api_method__ = "savePreparedKeyboardButton" + + user_id: int + """Unique identifier of the target user that can use the button""" + button: KeyboardButton + """A JSON-serialized object describing the button to be saved. The button must be of the type *request_users*, *request_chat*, or *request_managed_bot*""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__( + __pydantic__self__, *, user_id: int, button: KeyboardButton, **__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, button=button, **__pydantic_kwargs) diff --git a/aiogram/methods/send_checklist.py b/aiogram/methods/send_checklist.py index 852c8110..7a00317b 100644 --- a/aiogram/methods/send_checklist.py +++ b/aiogram/methods/send_checklist.py @@ -31,7 +31,7 @@ class SendChecklist(TelegramMethod[Message]): reply_parameters: ReplyParameters | None = None """A JSON-serialized object for description of the message to reply to""" reply_markup: InlineKeyboardMarkup | None = None - """A JSON-serialized object for an inline keyboard""" + """A JSON-serialized object for an `inline keyboard `_""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! diff --git a/aiogram/methods/send_gift.py b/aiogram/methods/send_gift.py index 17107a3d..df272fa9 100644 --- a/aiogram/methods/send_gift.py +++ b/aiogram/methods/send_gift.py @@ -28,9 +28,9 @@ class SendGift(TelegramMethod[bool]): text: str | None = None """Text that will be shown along with the gift; 0-128 characters""" text_parse_mode: str | None = None - """Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.""" + """Mode for parsing entities in the text. See `formatting options `_ for more details. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.""" text_entities: list[MessageEntity] | None = None - """A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', and 'custom_emoji' are ignored.""" + """A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of *text_parse_mode*. Entities other than 'bold', 'italic', 'underline', 'strikethrough', 'spoiler', 'custom_emoji', and 'date_time' are ignored.""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! diff --git a/aiogram/methods/send_message_draft.py b/aiogram/methods/send_message_draft.py index 6124f73e..b93c286c 100644 --- a/aiogram/methods/send_message_draft.py +++ b/aiogram/methods/send_message_draft.py @@ -8,7 +8,7 @@ from .base import TelegramMethod class SendMessageDraft(TelegramMethod[bool]): """ - Use this method to stream a partial message to a user while the message is being generated; supported only for bots with forum topic mode enabled. Returns :code:`True` on success. + Use this method to stream a partial message to a user while the message is being generated. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#sendmessagedraft """ diff --git a/aiogram/methods/send_poll.py b/aiogram/methods/send_poll.py index 01979ff1..b089b181 100644 --- a/aiogram/methods/send_poll.py +++ b/aiogram/methods/send_poll.py @@ -46,9 +46,17 @@ class SendPoll(TelegramMethod[Message]): type: str | None = None """Poll type, 'quiz' or 'regular', defaults to 'regular'""" allows_multiple_answers: bool | None = None - """:code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False`""" - correct_option_id: int | None = None - """0-based identifier of the correct answer option, required for polls in quiz mode""" + """Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False`""" + allows_revoting: bool | None = None + """Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls""" + shuffle_options: bool | None = None + """Pass :code:`True`, if the poll options must be shown in random order""" + allow_adding_options: bool | None = None + """Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes""" + hide_results_until_closes: bool | None = None + """Pass :code:`True`, if poll results must be shown only after the poll closes""" + correct_option_ids: list[int] | None = None + """A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode""" explanation: str | None = None """Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing""" explanation_parse_mode: str | Default | None = Default("parse_mode") @@ -56,11 +64,17 @@ class SendPoll(TelegramMethod[Message]): explanation_entities: list[MessageEntity] | None = None """A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode*""" open_period: int | None = None - """Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*.""" + """Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*.""" close_date: DateTimeUnion | None = None - """Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*.""" + """Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*.""" is_closed: bool | None = None """Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview.""" + description: str | None = None + """Description of the poll to be sent, 0-1024 characters after entities parsing""" + description_parse_mode: str | Default | None = Default("parse_mode") + """Mode for parsing entities in the poll description. See `formatting options `_ for more details.""" + description_entities: list[MessageEntity] | None = None + """A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode*""" disable_notification: bool | None = None """Sends the message `silently `_. Users will receive a notification with no sound.""" protect_content: bool | Default | None = Default("protect_content") @@ -78,6 +92,11 @@ class SendPoll(TelegramMethod[Message]): .. deprecated:: API:7.0 https://core.telegram.org/bots/api-changelog#december-29-2023""" + correct_option_id: int | None = Field(None, json_schema_extra={"deprecated": True}) + """0-based identifier of the correct answer option, required for polls in quiz mode + +.. deprecated:: API:9.6 + https://core.telegram.org/bots/api-changelog#april-3-2026""" reply_to_message_id: int | None = Field(None, json_schema_extra={"deprecated": True}) """If the message is a reply, ID of the original message @@ -101,13 +120,20 @@ class SendPoll(TelegramMethod[Message]): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, @@ -115,6 +141,7 @@ class SendPoll(TelegramMethod[Message]): reply_parameters: ReplyParameters | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, reply_to_message_id: int | None = None, **__pydantic_kwargs: Any, ) -> None: @@ -133,13 +160,20 @@ class SendPoll(TelegramMethod[Message]): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, @@ -147,6 +181,7 @@ class SendPoll(TelegramMethod[Message]): reply_parameters=reply_parameters, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, reply_to_message_id=reply_to_message_id, **__pydantic_kwargs, ) diff --git a/aiogram/methods/set_chat_member_tag.py b/aiogram/methods/set_chat_member_tag.py new file mode 100644 index 00000000..de8a2d09 --- /dev/null +++ b/aiogram/methods/set_chat_member_tag.py @@ -0,0 +1,40 @@ +from typing import TYPE_CHECKING, Any + +from ..types import ChatIdUnion +from .base import TelegramMethod + + +class SetChatMemberTag(TelegramMethod[bool]): + """ + Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the *can_manage_tags* administrator right. Returns :code:`True` on success. + + Source: https://core.telegram.org/bots/api#setchatmembertag + """ + + __returning__ = bool + __api_method__ = "setChatMemberTag" + + chat_id: ChatIdUnion + """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" + user_id: int + """Unique identifier of the target user""" + tag: str | None = None + """New tag for the member; 0-16 characters, emoji are not allowed""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__( + __pydantic__self__, + *, + chat_id: ChatIdUnion, + user_id: int, + tag: str | 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__(chat_id=chat_id, user_id=user_id, tag=tag, **__pydantic_kwargs) diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 90e59515..bfe0365a 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -495,6 +495,7 @@ __all__ = ( "KeyboardButton", "KeyboardButtonPollType", "KeyboardButtonRequestChat", + "KeyboardButtonRequestManagedBot", "KeyboardButtonRequestUser", "KeyboardButtonRequestUsers", "LabeledPrice", @@ -502,6 +503,8 @@ __all__ = ( "Location", "LocationAddress", "LoginUrl", + "ManagedBotCreated", + "ManagedBotUpdated", "MaskPosition", "MaybeInaccessibleMessage", "MaybeInaccessibleMessageUnion", @@ -554,8 +557,11 @@ __all__ = ( "Poll", "PollAnswer", "PollOption", + "PollOptionAdded", + "PollOptionDeleted", "PreCheckoutQuery", "PreparedInlineMessage", + "PreparedKeyboardButton", "ProximityAlertTriggered", "ReactionCount", "ReactionType", @@ -652,6 +658,12 @@ __all__ = ( from ..client.default import Default as _Default from .chat_owner_changed import ChatOwnerChanged from .chat_owner_left import ChatOwnerLeft +from .keyboard_button_request_managed_bot import KeyboardButtonRequestManagedBot +from .managed_bot_created import ManagedBotCreated +from .managed_bot_updated import ManagedBotUpdated +from .poll_option_added import PollOptionAdded +from .poll_option_deleted import PollOptionDeleted +from .prepared_keyboard_button import PreparedKeyboardButton from .user_profile_audios import UserProfileAudios from .video_quality import VideoQuality diff --git a/aiogram/types/base.py b/aiogram/types/base.py index 7e47b564..4ba749d8 100644 --- a/aiogram/types/base.py +++ b/aiogram/types/base.py @@ -16,6 +16,7 @@ class TelegramObject(BotContextController, BaseModel): populate_by_name=True, arbitrary_types_allowed=True, defer_build=True, + protected_namespaces=(), ) @model_validator(mode="before") diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index bea95afb..cde24548 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -28,6 +28,7 @@ if TYPE_CHECKING: SendChatAction, SetChatAdministratorCustomTitle, SetChatDescription, + SetChatMemberTag, SetChatPermissions, SetChatPhoto, SetChatStickerSet, @@ -967,6 +968,38 @@ class Chat(TelegramObject): **kwargs, ).as_(self._bot) + def set_member_tag( + self, + user_id: int, + tag: str | None = None, + **kwargs: Any, + ) -> SetChatMemberTag: + """ + Shortcut for method :class:`aiogram.methods.set_chat_member_tag.SetChatMemberTag` + will automatically fill method attributes: + + - :code:`chat_id` + + Use this method to set a tag for a regular member in a group or a supergroup. The bot must be an administrator in the chat for this to work and must have the *can_manage_tags* administrator right. Returns :code:`True` on success. + + Source: https://core.telegram.org/bots/api#setchatmembertag + + :param user_id: Unique identifier of the target user + :param tag: New tag for the member; 0-16 characters, emoji are not allowed + :return: instance of method :class:`aiogram.methods.set_chat_member_tag.SetChatMemberTag` + """ + # DO NOT EDIT MANUALLY!!! + # This method was auto-generated via `butcher` + + from aiogram.methods import SetChatMemberTag + + return SetChatMemberTag( + chat_id=self.id, + user_id=user_id, + tag=tag, + **kwargs, + ).as_(self._bot) + def set_permissions( self, permissions: ChatPermissions, @@ -1018,6 +1051,7 @@ class Chat(TelegramObject): can_pin_messages: bool | None = None, can_manage_topics: bool | None = None, can_manage_direct_messages: bool | None = None, + can_manage_tags: bool | None = None, **kwargs: Any, ) -> PromoteChatMember: """ @@ -1047,6 +1081,7 @@ class Chat(TelegramObject): :param can_pin_messages: Pass :code:`True` if the administrator can pin messages; for supergroups only :param can_manage_topics: Pass :code:`True` if the user is allowed to create, rename, close, and reopen forum topics; for supergroups only :param can_manage_direct_messages: Pass :code:`True` if the administrator can manage direct messages within the channel and decline suggested posts; for channels only + :param can_manage_tags: Pass :code:`True` if the administrator can edit the tags of regular members; for groups and supergroups only :return: instance of method :class:`aiogram.methods.promote_chat_member.PromoteChatMember` """ # DO NOT EDIT MANUALLY!!! @@ -1073,6 +1108,7 @@ class Chat(TelegramObject): can_pin_messages=can_pin_messages, can_manage_topics=can_manage_topics, can_manage_direct_messages=can_manage_direct_messages, + can_manage_tags=can_manage_tags, **kwargs, ).as_(self._bot) diff --git a/aiogram/types/chat_administrator_rights.py b/aiogram/types/chat_administrator_rights.py index cac9ae01..4c0811b9 100644 --- a/aiogram/types/chat_administrator_rights.py +++ b/aiogram/types/chat_administrator_rights.py @@ -47,6 +47,8 @@ class ChatAdministratorRights(TelegramObject): """*Optional*. :code:`True`, if the user is allowed to create, rename, close, and reopen forum topics; for supergroups only""" can_manage_direct_messages: bool | None = None """*Optional*. :code:`True`, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only""" + can_manage_tags: bool | None = None + """*Optional*. :code:`True`, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -71,6 +73,7 @@ class ChatAdministratorRights(TelegramObject): can_pin_messages: bool | None = None, can_manage_topics: bool | None = None, can_manage_direct_messages: bool | None = None, + can_manage_tags: bool | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -94,5 +97,6 @@ class ChatAdministratorRights(TelegramObject): can_pin_messages=can_pin_messages, can_manage_topics=can_manage_topics, can_manage_direct_messages=can_manage_direct_messages, + can_manage_tags=can_manage_tags, **__pydantic_kwargs, ) diff --git a/aiogram/types/chat_join_request.py b/aiogram/types/chat_join_request.py index 899e567b..46f14d32 100644 --- a/aiogram/types/chat_join_request.py +++ b/aiogram/types/chat_join_request.py @@ -1783,13 +1783,20 @@ class ChatJoinRequest(TelegramObject): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, @@ -1797,6 +1804,7 @@ class ChatJoinRequest(TelegramObject): reply_parameters: ReplyParameters | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, reply_to_message_id: int | None = None, **kwargs: Any, ) -> SendPoll: @@ -1818,14 +1826,21 @@ class ChatJoinRequest(TelegramObject): :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance @@ -1833,6 +1848,7 @@ class ChatJoinRequest(TelegramObject): :param reply_parameters: Description of the message to reply to :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :param reply_to_message_id: If the message is a reply, ID of the original message :return: instance of method :class:`aiogram.methods.send_poll.SendPoll` """ @@ -1852,13 +1868,20 @@ class ChatJoinRequest(TelegramObject): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, @@ -1866,6 +1889,7 @@ class ChatJoinRequest(TelegramObject): reply_parameters=reply_parameters, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, reply_to_message_id=reply_to_message_id, **kwargs, ).as_(self._bot) @@ -1881,13 +1905,20 @@ class ChatJoinRequest(TelegramObject): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, @@ -1895,6 +1926,7 @@ class ChatJoinRequest(TelegramObject): reply_parameters: ReplyParameters | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, reply_to_message_id: int | None = None, **kwargs: Any, ) -> SendPoll: @@ -1916,14 +1948,21 @@ class ChatJoinRequest(TelegramObject): :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance @@ -1931,6 +1970,7 @@ class ChatJoinRequest(TelegramObject): :param reply_parameters: Description of the message to reply to :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :param reply_to_message_id: If the message is a reply, ID of the original message :return: instance of method :class:`aiogram.methods.send_poll.SendPoll` """ @@ -1950,13 +1990,20 @@ class ChatJoinRequest(TelegramObject): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, @@ -1964,6 +2011,7 @@ class ChatJoinRequest(TelegramObject): reply_parameters=reply_parameters, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, reply_to_message_id=reply_to_message_id, **kwargs, ).as_(self._bot) diff --git a/aiogram/types/chat_member_administrator.py b/aiogram/types/chat_member_administrator.py index b24dece3..1d8915bc 100644 --- a/aiogram/types/chat_member_administrator.py +++ b/aiogram/types/chat_member_administrator.py @@ -54,6 +54,8 @@ class ChatMemberAdministrator(ChatMember): """*Optional*. :code:`True`, if the user is allowed to create, rename, close, and reopen forum topics; for supergroups only""" can_manage_direct_messages: bool | None = None """*Optional*. :code:`True`, if the administrator can manage direct messages of the channel and decline suggested posts; for channels only""" + can_manage_tags: bool | None = None + """*Optional*. :code:`True`, if the administrator can edit the tags of regular members; for groups and supergroups only. If omitted defaults to the value of can_pin_messages.""" custom_title: str | None = None """*Optional*. Custom title for this user""" @@ -83,6 +85,7 @@ class ChatMemberAdministrator(ChatMember): can_pin_messages: bool | None = None, can_manage_topics: bool | None = None, can_manage_direct_messages: bool | None = None, + can_manage_tags: bool | None = None, custom_title: str | None = None, **__pydantic_kwargs: Any, ) -> None: @@ -110,6 +113,7 @@ class ChatMemberAdministrator(ChatMember): can_pin_messages=can_pin_messages, can_manage_topics=can_manage_topics, can_manage_direct_messages=can_manage_direct_messages, + can_manage_tags=can_manage_tags, custom_title=custom_title, **__pydantic_kwargs, ) diff --git a/aiogram/types/chat_member_member.py b/aiogram/types/chat_member_member.py index 5b3ce5e0..0cd89e76 100644 --- a/aiogram/types/chat_member_member.py +++ b/aiogram/types/chat_member_member.py @@ -21,6 +21,8 @@ class ChatMemberMember(ChatMember): """The member's status in the chat, always 'member'""" user: User """Information about the user""" + tag: str | None = None + """*Optional*. Tag of the member""" until_date: DateTime | None = None """*Optional*. Date when the user's subscription will expire; Unix time""" @@ -33,6 +35,7 @@ class ChatMemberMember(ChatMember): *, status: Literal[ChatMemberStatus.MEMBER] = ChatMemberStatus.MEMBER, user: User, + tag: str | None = None, until_date: DateTime | None = None, **__pydantic_kwargs: Any, ) -> None: @@ -40,4 +43,6 @@ class ChatMemberMember(ChatMember): # This method was auto-generated via `butcher` # Is needed only for type checking and IDE support without any additional plugins - super().__init__(status=status, user=user, until_date=until_date, **__pydantic_kwargs) + super().__init__( + status=status, user=user, tag=tag, until_date=until_date, **__pydantic_kwargs + ) diff --git a/aiogram/types/chat_member_restricted.py b/aiogram/types/chat_member_restricted.py index 1466350f..0fc162ff 100644 --- a/aiogram/types/chat_member_restricted.py +++ b/aiogram/types/chat_member_restricted.py @@ -43,6 +43,8 @@ class ChatMemberRestricted(ChatMember): """:code:`True`, if the user is allowed to send animations, games, stickers and use inline bots""" can_add_web_page_previews: bool """:code:`True`, if the user is allowed to add web page previews to their messages""" + can_edit_tag: bool + """:code:`True`, if the user is allowed to edit their own tag""" can_change_info: bool """:code:`True`, if the user is allowed to change the chat title, photo and other settings""" can_invite_users: bool @@ -53,6 +55,8 @@ class ChatMemberRestricted(ChatMember): """:code:`True`, if the user is allowed to create forum topics""" until_date: DateTime """Date when restrictions will be lifted for this user; Unix time. If 0, then the user is restricted forever""" + tag: str | None = None + """*Optional*. Tag of the member""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -74,11 +78,13 @@ class ChatMemberRestricted(ChatMember): can_send_polls: bool, can_send_other_messages: bool, can_add_web_page_previews: bool, + can_edit_tag: bool, can_change_info: bool, can_invite_users: bool, can_pin_messages: bool, can_manage_topics: bool, until_date: DateTime, + tag: str | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -99,10 +105,12 @@ class ChatMemberRestricted(ChatMember): can_send_polls=can_send_polls, can_send_other_messages=can_send_other_messages, can_add_web_page_previews=can_add_web_page_previews, + can_edit_tag=can_edit_tag, can_change_info=can_change_info, can_invite_users=can_invite_users, can_pin_messages=can_pin_messages, can_manage_topics=can_manage_topics, until_date=until_date, + tag=tag, **__pydantic_kwargs, ) diff --git a/aiogram/types/chat_member_updated.py b/aiogram/types/chat_member_updated.py index b693f491..34cd5c09 100644 --- a/aiogram/types/chat_member_updated.py +++ b/aiogram/types/chat_member_updated.py @@ -924,13 +924,20 @@ class ChatMemberUpdated(TelegramObject): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, @@ -938,6 +945,7 @@ class ChatMemberUpdated(TelegramObject): reply_parameters: ReplyParameters | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, reply_to_message_id: int | None = None, **kwargs: Any, ) -> SendPoll: @@ -959,14 +967,21 @@ class ChatMemberUpdated(TelegramObject): :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance @@ -974,6 +989,7 @@ class ChatMemberUpdated(TelegramObject): :param reply_parameters: Description of the message to reply to :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :param reply_to_message_id: If the message is a reply, ID of the original message :return: instance of method :class:`aiogram.methods.send_poll.SendPoll` """ @@ -993,13 +1009,20 @@ class ChatMemberUpdated(TelegramObject): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, @@ -1007,6 +1030,7 @@ class ChatMemberUpdated(TelegramObject): reply_parameters=reply_parameters, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, reply_to_message_id=reply_to_message_id, **kwargs, ).as_(self._bot) diff --git a/aiogram/types/chat_permissions.py b/aiogram/types/chat_permissions.py index dfb0fb77..473a38bb 100644 --- a/aiogram/types/chat_permissions.py +++ b/aiogram/types/chat_permissions.py @@ -32,6 +32,8 @@ class ChatPermissions(MutableTelegramObject): """*Optional*. :code:`True`, if the user is allowed to send animations, games, stickers and use inline bots""" can_add_web_page_previews: bool | None = None """*Optional*. :code:`True`, if the user is allowed to add web page previews to their messages""" + can_edit_tag: bool | None = None + """*Optional*. :code:`True`, if the user is allowed to edit their own tag""" can_change_info: bool | None = None """*Optional*. :code:`True`, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups""" can_invite_users: bool | None = None @@ -58,6 +60,7 @@ class ChatPermissions(MutableTelegramObject): can_send_polls: bool | None = None, can_send_other_messages: bool | None = None, can_add_web_page_previews: bool | None = None, + can_edit_tag: bool | None = None, can_change_info: bool | None = None, can_invite_users: bool | None = None, can_pin_messages: bool | None = None, @@ -79,6 +82,7 @@ class ChatPermissions(MutableTelegramObject): can_send_polls=can_send_polls, can_send_other_messages=can_send_other_messages, can_add_web_page_previews=can_add_web_page_previews, + can_edit_tag=can_edit_tag, can_change_info=can_change_info, can_invite_users=can_invite_users, can_pin_messages=can_pin_messages, diff --git a/aiogram/types/game_high_score.py b/aiogram/types/game_high_score.py index e8fdcf40..5364be6e 100644 --- a/aiogram/types/game_high_score.py +++ b/aiogram/types/game_high_score.py @@ -15,8 +15,6 @@ 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 8b8a9f1f..32262625 100644 --- a/aiogram/types/inaccessible_message.py +++ b/aiogram/types/inaccessible_message.py @@ -1746,13 +1746,20 @@ class InaccessibleMessage(MaybeInaccessibleMessage): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, @@ -1760,6 +1767,7 @@ class InaccessibleMessage(MaybeInaccessibleMessage): reply_parameters: ReplyParameters | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, reply_to_message_id: int | None = None, **kwargs: Any, ) -> SendPoll: @@ -1781,14 +1789,21 @@ class InaccessibleMessage(MaybeInaccessibleMessage): :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance @@ -1796,6 +1811,7 @@ class InaccessibleMessage(MaybeInaccessibleMessage): :param reply_parameters: Description of the message to reply to :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :param reply_to_message_id: If the message is a reply, ID of the original message :return: instance of method :class:`aiogram.methods.send_poll.SendPoll` """ @@ -1819,13 +1835,20 @@ class InaccessibleMessage(MaybeInaccessibleMessage): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, @@ -1833,6 +1856,7 @@ class InaccessibleMessage(MaybeInaccessibleMessage): reply_parameters=reply_parameters, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, reply_to_message_id=reply_to_message_id, **kwargs, ).as_(self._bot) @@ -1848,19 +1872,27 @@ class InaccessibleMessage(MaybeInaccessibleMessage): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, message_effect_id: str | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, **kwargs: Any, ) -> SendPoll: """ @@ -1882,20 +1914,28 @@ class InaccessibleMessage(MaybeInaccessibleMessage): :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :return: instance of method :class:`aiogram.methods.send_poll.SendPoll` """ # DO NOT EDIT MANUALLY!!! @@ -1919,19 +1959,27 @@ class InaccessibleMessage(MaybeInaccessibleMessage): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, message_effect_id=message_effect_id, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, **kwargs, ).as_(self._bot) diff --git a/aiogram/types/inline_query_result_document.py b/aiogram/types/inline_query_result_document.py index c40d31b8..1665c8b3 100644 --- a/aiogram/types/inline_query_result_document.py +++ b/aiogram/types/inline_query_result_document.py @@ -38,7 +38,7 @@ class InlineQueryResultDocument(InlineQueryResult): description: str | None = None """*Optional*. Short description of the result""" reply_markup: InlineKeyboardMarkup | None = None - """*Optional*. Inline keyboard attached to the message""" + """*Optional*. `Inline keyboard `_ attached to the message""" input_message_content: InputMessageContentUnion | None = None """*Optional*. Content of the message to be sent instead of the file""" thumbnail_url: str | None = None diff --git a/aiogram/types/input_checklist.py b/aiogram/types/input_checklist.py index f5cd5135..ac0250d3 100644 --- a/aiogram/types/input_checklist.py +++ b/aiogram/types/input_checklist.py @@ -23,7 +23,7 @@ class InputChecklist(TelegramObject): parse_mode: str | None = None """*Optional*. Mode for parsing entities in the title. See `formatting options `_ for more details.""" title_entities: list[MessageEntity] | None = None - """*Optional*. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are allowed.""" + """*Optional*. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are allowed.""" others_can_add_tasks: bool | None = None """*Optional*. Pass :code:`True` if other users can add tasks to the checklist""" others_can_mark_tasks_as_done: bool | None = None diff --git a/aiogram/types/input_checklist_task.py b/aiogram/types/input_checklist_task.py index 9cfbf131..27d6f0eb 100644 --- a/aiogram/types/input_checklist_task.py +++ b/aiogram/types/input_checklist_task.py @@ -22,7 +22,7 @@ class InputChecklistTask(TelegramObject): parse_mode: str | None = None """*Optional*. Mode for parsing entities in the text. See `formatting options `_ for more details.""" text_entities: list[MessageEntity] | None = None - """*Optional*. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are allowed.""" + """*Optional*. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are allowed.""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! diff --git a/aiogram/types/keyboard_button.py b/aiogram/types/keyboard_button.py index f0c4af22..ca29d5bb 100644 --- a/aiogram/types/keyboard_button.py +++ b/aiogram/types/keyboard_button.py @@ -9,6 +9,7 @@ from .base import MutableTelegramObject if TYPE_CHECKING: from .keyboard_button_poll_type import KeyboardButtonPollType from .keyboard_button_request_chat import KeyboardButtonRequestChat + from .keyboard_button_request_managed_bot import KeyboardButtonRequestManagedBot from .keyboard_button_request_user import KeyboardButtonRequestUser from .keyboard_button_request_users import KeyboardButtonRequestUsers from .web_app_info import WebAppInfo @@ -31,6 +32,8 @@ class KeyboardButton(MutableTelegramObject): """*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 """*Optional*. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a 'chat_shared' service message. Available in private chats only.""" + request_managed_bot: KeyboardButtonRequestManagedBot | None = None + """*Optional*. If specified, pressing the button will ask the user to create and share a bot that will be managed by the current bot. Available for bots that enabled management of other bots in the `@BotFather `_ Mini App. Available in private chats only.""" request_contact: bool | None = None """*Optional*. If :code:`True`, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only.""" request_location: bool | None = None @@ -59,6 +62,7 @@ class KeyboardButton(MutableTelegramObject): style: str | None = None, request_users: KeyboardButtonRequestUsers | None = None, request_chat: KeyboardButtonRequestChat | None = None, + request_managed_bot: KeyboardButtonRequestManagedBot | None = None, request_contact: bool | None = None, request_location: bool | None = None, request_poll: KeyboardButtonPollType | None = None, @@ -76,6 +80,7 @@ class KeyboardButton(MutableTelegramObject): style=style, request_users=request_users, request_chat=request_chat, + request_managed_bot=request_managed_bot, request_contact=request_contact, request_location=request_location, request_poll=request_poll, diff --git a/aiogram/types/keyboard_button_request_managed_bot.py b/aiogram/types/keyboard_button_request_managed_bot.py new file mode 100644 index 00000000..2f81e952 --- /dev/null +++ b/aiogram/types/keyboard_button_request_managed_bot.py @@ -0,0 +1,41 @@ +from typing import TYPE_CHECKING, Any + +from .base import TelegramObject + + +class KeyboardButtonRequestManagedBot(TelegramObject): + """ + This object defines the parameters for the creation of a managed bot. Information about the created bot will be shared with the bot using the update *managed_bot* and a :class:`aiogram.types.message.Message` with the field *managed_bot_created*. + + Source: https://core.telegram.org/bots/api#keyboardbuttonrequestmanagedbot + """ + + request_id: int + """Signed 32-bit identifier of the request. Must be unique within the message""" + suggested_name: str | None = None + """*Optional*. Suggested name for the bot""" + suggested_username: str | None = None + """*Optional*. Suggested username for the bot""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__( + __pydantic__self__, + *, + request_id: int, + suggested_name: str | None = None, + suggested_username: str | 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__( + request_id=request_id, + suggested_name=suggested_name, + suggested_username=suggested_username, + **__pydantic_kwargs, + ) diff --git a/aiogram/types/managed_bot_created.py b/aiogram/types/managed_bot_created.py new file mode 100644 index 00000000..d8fd2b19 --- /dev/null +++ b/aiogram/types/managed_bot_created.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from pydantic import Field + +from .base import TelegramObject + +if TYPE_CHECKING: + from .user import User + + +class ManagedBotCreated(TelegramObject): + """ + This object contains information about the bot that was created to be managed by the current bot. + + Source: https://core.telegram.org/bots/api#managedbotcreated + """ + + bot_user: User = Field(..., alias="bot") + """Information about the bot. The bot's token can be fetched using the method :class:`aiogram.methods.get_managed_bot_token.GetManagedBotToken`.""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__(__pydantic__self__, *, bot_user: 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__(bot_user=bot_user, **__pydantic_kwargs) diff --git a/aiogram/types/managed_bot_updated.py b/aiogram/types/managed_bot_updated.py new file mode 100644 index 00000000..fd9e8f8e --- /dev/null +++ b/aiogram/types/managed_bot_updated.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from pydantic import Field + +from .base import TelegramObject + +if TYPE_CHECKING: + from .user import User + + +class ManagedBotUpdated(TelegramObject): + """ + This object contains information about the creation or token update of a bot that is managed by the current bot. + + Source: https://core.telegram.org/bots/api#managedbotupdated + """ + + user: User + """User that created the bot""" + bot_user: User = Field(..., alias="bot") + """Information about the bot. Token of the bot can be fetched using the method :class:`aiogram.methods.get_managed_bot_token.GetManagedBotToken`.""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__( + __pydantic__self__, *, user: User, bot_user: 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__(user=user, bot_user=bot_user, **__pydantic_kwargs) diff --git a/aiogram/types/message.py b/aiogram/types/message.py index b35fe0fb..d2bd7754 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -90,6 +90,7 @@ if TYPE_CHECKING: from .labeled_price import LabeledPrice from .link_preview_options import LinkPreviewOptions from .location import Location + from .managed_bot_created import ManagedBotCreated from .maybe_inaccessible_message_union import MaybeInaccessibleMessageUnion from .media_union import MediaUnion from .message_auto_delete_timer_changed import MessageAutoDeleteTimerChanged @@ -100,6 +101,8 @@ if TYPE_CHECKING: from .passport_data import PassportData from .photo_size import PhotoSize from .poll import Poll + from .poll_option_added import PollOptionAdded + from .poll_option_deleted import PollOptionDeleted from .proximity_alert_triggered import ProximityAlertTriggered from .reaction_type_union import ReactionTypeUnion from .refunded_payment import RefundedPayment @@ -157,6 +160,8 @@ class Message(MaybeInaccessibleMessage): """*Optional*. If the sender of the message boosted the chat, the number of boosts added by the user""" sender_business_bot: User | None = None """*Optional*. The bot that actually sent the message on behalf of the business account. Available only for outgoing messages sent on behalf of the connected business account.""" + sender_tag: str | None = None + """*Optional*. Tag or custom title of the sender of the message; for supergroups only""" business_connection_id: str | None = None """*Optional*. Unique identifier of the business connection from which the message was received. If non-empty, the message belongs to a chat of the corresponding business account that is independent from any potential bot chat which might share the same identifier.""" forward_origin: MessageOriginUnion | None = None @@ -175,6 +180,8 @@ class Message(MaybeInaccessibleMessage): """*Optional*. For replies to a story, the original story""" reply_to_checklist_task_id: int | None = None """*Optional*. Identifier of the specific checklist task that is being replied to""" + reply_to_poll_option_id: str | None = None + """*Optional*. Persistent identifier of the specific poll option that is being replied to""" via_bot: User | None = None """*Optional*. Bot through which the message was sent""" edit_date: int | None = None @@ -186,7 +193,7 @@ class Message(MaybeInaccessibleMessage): is_paid_post: bool | None = None """*Optional*. :code:`True`, if the message is a paid post. Note that such posts must not be deleted for 24 hours to receive the payment and can't be edited.""" media_group_id: str | None = None - """*Optional*. The unique identifier of a media message group this message belongs to""" + """*Optional*. The unique identifier inside this chat of a media message group this message belongs to""" author_signature: str | None = None """*Optional*. Signature of the post author for messages in channels, or the custom title of an anonymous group administrator""" paid_star_count: int | None = None @@ -325,8 +332,14 @@ class Message(MaybeInaccessibleMessage): """*Optional*. A giveaway with public winners was completed""" giveaway_completed: GiveawayCompleted | None = None """*Optional*. Service message: a giveaway without public winners was completed""" + managed_bot_created: ManagedBotCreated | None = None + """*Optional*. Service message: user created a bot that will be managed by the current bot""" paid_message_price_changed: PaidMessagePriceChanged | None = None """*Optional*. Service message: the price for paid messages has changed in the chat""" + poll_option_added: PollOptionAdded | None = None + """*Optional*. Service message: answer option was added to a poll""" + poll_option_deleted: PollOptionDeleted | None = None + """*Optional*. Service message: answer option was deleted from a poll""" suggested_post_approved: SuggestedPostApproved | None = None """*Optional*. Service message: a suggested post was approved""" suggested_post_approval_failed: SuggestedPostApprovalFailed | None = None @@ -348,7 +361,7 @@ class Message(MaybeInaccessibleMessage): web_app_data: WebAppData | None = None """*Optional*. Service message: data sent by a Web App""" reply_markup: InlineKeyboardMarkup | None = None - """*Optional*. Inline keyboard attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.""" + """*Optional*. `Inline keyboard `_ attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.""" forward_date: DateTime | None = Field(None, json_schema_extra={"deprecated": True}) """*Optional*. For forwarded messages, date the original message was sent in Unix time @@ -401,6 +414,7 @@ class Message(MaybeInaccessibleMessage): sender_chat: Chat | None = None, sender_boost_count: int | None = None, sender_business_bot: User | None = None, + sender_tag: str | None = None, business_connection_id: str | None = None, forward_origin: MessageOriginUnion | None = None, is_topic_message: bool | None = None, @@ -410,6 +424,7 @@ class Message(MaybeInaccessibleMessage): quote: TextQuote | None = None, reply_to_story: Story | None = None, reply_to_checklist_task_id: int | None = None, + reply_to_poll_option_id: str | None = None, via_bot: User | None = None, edit_date: int | None = None, has_protected_content: bool | None = None, @@ -485,7 +500,10 @@ class Message(MaybeInaccessibleMessage): giveaway: Giveaway | None = None, giveaway_winners: GiveawayWinners | None = None, giveaway_completed: GiveawayCompleted | None = None, + managed_bot_created: ManagedBotCreated | None = None, paid_message_price_changed: PaidMessagePriceChanged | None = None, + poll_option_added: PollOptionAdded | None = None, + poll_option_deleted: PollOptionDeleted | None = None, suggested_post_approved: SuggestedPostApproved | None = None, suggested_post_approval_failed: SuggestedPostApprovalFailed | None = None, suggested_post_declined: SuggestedPostDeclined | None = None, @@ -520,6 +538,7 @@ class Message(MaybeInaccessibleMessage): sender_chat=sender_chat, sender_boost_count=sender_boost_count, sender_business_bot=sender_business_bot, + sender_tag=sender_tag, business_connection_id=business_connection_id, forward_origin=forward_origin, is_topic_message=is_topic_message, @@ -529,6 +548,7 @@ class Message(MaybeInaccessibleMessage): quote=quote, reply_to_story=reply_to_story, reply_to_checklist_task_id=reply_to_checklist_task_id, + reply_to_poll_option_id=reply_to_poll_option_id, via_bot=via_bot, edit_date=edit_date, has_protected_content=has_protected_content, @@ -604,7 +624,10 @@ class Message(MaybeInaccessibleMessage): giveaway=giveaway, giveaway_winners=giveaway_winners, giveaway_completed=giveaway_completed, + managed_bot_created=managed_bot_created, paid_message_price_changed=paid_message_price_changed, + poll_option_added=poll_option_added, + poll_option_deleted=poll_option_deleted, suggested_post_approved=suggested_post_approved, suggested_post_approval_failed=suggested_post_approval_failed, suggested_post_declined=suggested_post_declined, @@ -770,6 +793,12 @@ class Message(MaybeInaccessibleMessage): return ContentType.SUGGESTED_POST_PAID if self.suggested_post_refunded: return ContentType.SUGGESTED_POST_REFUNDED + if self.managed_bot_created: + return ContentType.MANAGED_BOT_CREATED + if self.poll_option_added: + return ContentType.POLL_OPTION_ADDED + if self.poll_option_deleted: + return ContentType.POLL_OPTION_DELETED return ContentType.UNKNOWN def _unparse_entities(self, text_decoration: TextDecoration) -> str: @@ -2440,19 +2469,27 @@ class Message(MaybeInaccessibleMessage): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, message_effect_id: str | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, **kwargs: Any, ) -> SendPoll: """ @@ -2474,20 +2511,28 @@ class Message(MaybeInaccessibleMessage): :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance :param message_effect_id: Unique identifier of the message effect to be added to the message; for private chats only :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :return: instance of method :class:`aiogram.methods.send_poll.SendPoll` """ # DO NOT EDIT MANUALLY!!! @@ -2511,19 +2556,27 @@ class Message(MaybeInaccessibleMessage): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, message_effect_id=message_effect_id, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, **kwargs, ).as_(self._bot) @@ -2536,13 +2589,20 @@ class Message(MaybeInaccessibleMessage): is_anonymous: bool | None = None, type: str | None = None, allows_multiple_answers: bool | None = None, - correct_option_id: int | None = None, + allows_revoting: bool | None = None, + shuffle_options: bool | None = None, + allow_adding_options: bool | None = None, + hide_results_until_closes: bool | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_parse_mode: str | Default | None = Default("parse_mode"), explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTimeUnion | None = None, is_closed: bool | None = None, + description: str | None = None, + description_parse_mode: str | Default | None = Default("parse_mode"), + description_entities: list[MessageEntity] | None = None, disable_notification: bool | None = None, protect_content: bool | Default | None = Default("protect_content"), allow_paid_broadcast: bool | None = None, @@ -2550,6 +2610,7 @@ class Message(MaybeInaccessibleMessage): reply_parameters: ReplyParameters | None = None, reply_markup: ReplyMarkupUnion | None = None, allow_sending_without_reply: bool | None = None, + correct_option_id: int | None = None, reply_to_message_id: int | None = None, **kwargs: Any, ) -> SendPoll: @@ -2571,14 +2632,21 @@ class Message(MaybeInaccessibleMessage): :param question_entities: A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of *question_parse_mode* :param is_anonymous: :code:`True`, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: :code:`True`, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` - :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param allows_multiple_answers: Pass :code:`True`, if the poll allows multiple answers, defaults to :code:`False` + :param allows_revoting: Pass :code:`True`, if the poll allows to change chosen answer options, defaults to :code:`False` for quizzes and to :code:`True` for regular polls + :param shuffle_options: Pass :code:`True`, if the poll options must be shown in random order + :param allow_adding_options: Pass :code:`True`, if answer options can be added to the poll after creation; not supported for anonymous polls and quizzes + :param hide_results_until_closes: Pass :code:`True`, if poll results must be shown only after the poll closes + :param correct_option_ids: A JSON-serialized list of monotonically increasing 0-based identifiers of the correct answer options, required for polls in quiz mode :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. :param explanation_entities: A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of *explanation_parse_mode* - :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param open_period: Amount of time in seconds the poll will be active after creation, 5-2628000. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 2628000 seconds in the future. Can't be used together with *open_period*. :param is_closed: Pass :code:`True` if the poll needs to be immediately closed. This can be useful for poll preview. + :param description: Description of the poll to be sent, 0-1024 characters after entities parsing + :param description_parse_mode: Mode for parsing entities in the poll description. See `formatting options `_ for more details. + :param description_entities: A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of *description_parse_mode* :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param protect_content: Protects the contents of the sent message from forwarding and saving :param allow_paid_broadcast: Pass :code:`True` to allow up to 1000 messages per second, ignoring `broadcasting limits `_ for a fee of 0.1 Telegram Stars per message. The relevant Stars will be withdrawn from the bot's balance @@ -2586,6 +2654,7 @@ class Message(MaybeInaccessibleMessage): :param reply_parameters: Description of the message to reply to :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove a reply keyboard or to force a reply from the user :param allow_sending_without_reply: Pass :code:`True` if the message should be sent even if the specified replied-to message is not found + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode :param reply_to_message_id: If the message is a reply, ID of the original message :return: instance of method :class:`aiogram.methods.send_poll.SendPoll` """ @@ -2609,13 +2678,20 @@ class Message(MaybeInaccessibleMessage): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, - correct_option_id=correct_option_id, + allows_revoting=allows_revoting, + shuffle_options=shuffle_options, + allow_adding_options=allow_adding_options, + hide_results_until_closes=hide_results_until_closes, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_parse_mode=explanation_parse_mode, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, + description=description, + description_parse_mode=description_parse_mode, + description_entities=description_entities, disable_notification=disable_notification, protect_content=protect_content, allow_paid_broadcast=allow_paid_broadcast, @@ -2623,6 +2699,7 @@ class Message(MaybeInaccessibleMessage): reply_parameters=reply_parameters, reply_markup=reply_markup, allow_sending_without_reply=allow_sending_without_reply, + correct_option_id=correct_option_id, reply_to_message_id=reply_to_message_id, **kwargs, ).as_(self._bot) diff --git a/aiogram/types/message_entity.py b/aiogram/types/message_entity.py index 608b8435..68ae3025 100644 --- a/aiogram/types/message_entity.py +++ b/aiogram/types/message_entity.py @@ -17,7 +17,7 @@ class MessageEntity(MutableTelegramObject): """ type: str - """Type of the entity. Currently, can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag` or :code:`#hashtag@chatusername`), 'cashtag' (:code:`$USD` or :code:`$USD@chatusername`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users `without usernames `_), 'custom_emoji' (for inline custom emoji stickers)""" + """Type of the entity. Currently, can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag` or :code:`#hashtag@chatusername`), 'cashtag' (:code:`$USD` or :code:`$USD@chatusername`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'spoiler' (spoiler message), 'blockquote' (block quotation), 'expandable_blockquote' (collapsed-by-default block quotation), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users `without usernames `_), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)""" offset: int """Offset in `UTF-16 code units `_ to the start of the entity""" length: int @@ -30,6 +30,10 @@ class MessageEntity(MutableTelegramObject): """*Optional*. For 'pre' only, the programming language of the entity text""" custom_emoji_id: str | None = None """*Optional*. For 'custom_emoji' only, unique identifier of the custom emoji. Use :class:`aiogram.methods.get_custom_emoji_stickers.GetCustomEmojiStickers` to get full information about the sticker""" + unix_time: int | None = None + """*Optional*. For 'date_time' only, the Unix time associated with the entity""" + date_time_format: str | None = None + """*Optional*. For 'date_time' only, the string that defines the formatting of the date and time. See `date-time entity formatting `_ for more details.""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -45,6 +49,8 @@ class MessageEntity(MutableTelegramObject): user: User | None = None, language: str | None = None, custom_emoji_id: str | None = None, + unix_time: int | None = None, + date_time_format: str | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -59,6 +65,8 @@ class MessageEntity(MutableTelegramObject): user=user, language=language, custom_emoji_id=custom_emoji_id, + unix_time=unix_time, + date_time_format=date_time_format, **__pydantic_kwargs, ) diff --git a/aiogram/types/poll.py b/aiogram/types/poll.py index d9eff35a..88714145 100644 --- a/aiogram/types/poll.py +++ b/aiogram/types/poll.py @@ -2,6 +2,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any +from pydantic import Field + from .base import TelegramObject from .custom import DateTime @@ -33,10 +35,12 @@ class Poll(TelegramObject): """Poll type, currently can be 'regular' or 'quiz'""" allows_multiple_answers: bool """:code:`True`, if the poll allows multiple answers""" + allows_revoting: bool + """:code:`True`, if the poll allows to change the chosen answer options""" question_entities: list[MessageEntity] | None = None """*Optional*. Special entities that appear in the *question*. Currently, only custom emoji entities are allowed in poll questions""" - correct_option_id: int | None = None - """*Optional*. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.""" + correct_option_ids: list[int] | None = None + """*Optional*. Array of 0-based identifiers of the correct answer options. Available only for polls in quiz mode which are closed or were sent (not forwarded) by the bot or to the private chat with the bot.""" explanation: str | None = None """*Optional*. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters""" explanation_entities: list[MessageEntity] | None = None @@ -45,6 +49,15 @@ class Poll(TelegramObject): """*Optional*. Amount of time in seconds the poll will be active after creation""" close_date: DateTime | None = None """*Optional*. Point in time (Unix timestamp) when the poll will be automatically closed""" + description: str | None = None + """*Optional*. Description of the poll; for polls inside the :class:`aiogram.types.message.Message` object only""" + description_entities: list[MessageEntity] | None = None + """*Optional*. Special entities like usernames, URLs, bot commands, etc. that appear in the description""" + correct_option_id: int | None = Field(None, json_schema_extra={"deprecated": True}) + """*Optional*. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot. + +.. deprecated:: API:9.6 + https://core.telegram.org/bots/api-changelog#april-3-2026""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -61,12 +74,16 @@ class Poll(TelegramObject): is_anonymous: bool, type: str, allows_multiple_answers: bool, + allows_revoting: bool, question_entities: list[MessageEntity] | None = None, - correct_option_id: int | None = None, + correct_option_ids: list[int] | None = None, explanation: str | None = None, explanation_entities: list[MessageEntity] | None = None, open_period: int | None = None, close_date: DateTime | None = None, + description: str | None = None, + description_entities: list[MessageEntity] | None = None, + correct_option_id: int | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -82,11 +99,15 @@ class Poll(TelegramObject): is_anonymous=is_anonymous, type=type, allows_multiple_answers=allows_multiple_answers, + allows_revoting=allows_revoting, question_entities=question_entities, - correct_option_id=correct_option_id, + correct_option_ids=correct_option_ids, explanation=explanation, explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, + description=description, + description_entities=description_entities, + correct_option_id=correct_option_id, **__pydantic_kwargs, ) diff --git a/aiogram/types/poll_answer.py b/aiogram/types/poll_answer.py index 030b2379..2a991316 100644 --- a/aiogram/types/poll_answer.py +++ b/aiogram/types/poll_answer.py @@ -20,6 +20,8 @@ class PollAnswer(TelegramObject): """Unique poll identifier""" option_ids: list[int] """0-based identifiers of chosen answer options. May be empty if the vote was retracted.""" + option_persistent_ids: list[str] + """Persistent identifiers of the chosen answer options. May be empty if the vote was retracted.""" voter_chat: Chat | None = None """*Optional*. The chat that changed the answer to the poll, if the voter is anonymous""" user: User | None = None @@ -34,6 +36,7 @@ class PollAnswer(TelegramObject): *, poll_id: str, option_ids: list[int], + option_persistent_ids: list[str], voter_chat: Chat | None = None, user: User | None = None, **__pydantic_kwargs: Any, @@ -45,6 +48,7 @@ class PollAnswer(TelegramObject): super().__init__( poll_id=poll_id, option_ids=option_ids, + option_persistent_ids=option_persistent_ids, voter_chat=voter_chat, user=user, **__pydantic_kwargs, diff --git a/aiogram/types/poll_option.py b/aiogram/types/poll_option.py index 5a91ab3d..24c23b5f 100644 --- a/aiogram/types/poll_option.py +++ b/aiogram/types/poll_option.py @@ -3,9 +3,12 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any from .base import TelegramObject +from .custom import DateTime if TYPE_CHECKING: + from .chat import Chat from .message_entity import MessageEntity + from .user import User class PollOption(TelegramObject): @@ -15,12 +18,20 @@ class PollOption(TelegramObject): Source: https://core.telegram.org/bots/api#polloption """ + persistent_id: str + """Unique identifier of the option, persistent on option addition and deletion""" text: str """Option text, 1-100 characters""" voter_count: int - """Number of users that voted for this option""" + """Number of users who voted for this option; may be 0 if unknown""" text_entities: list[MessageEntity] | None = None """*Optional*. Special entities that appear in the option *text*. Currently, only custom emoji entities are allowed in poll option texts""" + added_by_user: User | None = None + """*Optional*. User who added the option; omitted if the option wasn't added by a user after poll creation""" + added_by_chat: Chat | None = None + """*Optional*. Chat that added the option; omitted if the option wasn't added by a chat after poll creation""" + addition_date: DateTime | None = None + """*Optional*. Point in time (Unix timestamp) when the option was added; omitted if the option existed in the original poll""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -29,9 +40,13 @@ class PollOption(TelegramObject): def __init__( __pydantic__self__, *, + persistent_id: str, text: str, voter_count: int, text_entities: list[MessageEntity] | None = None, + added_by_user: User | None = None, + added_by_chat: Chat | None = None, + addition_date: DateTime | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -39,8 +54,12 @@ class PollOption(TelegramObject): # Is needed only for type checking and IDE support without any additional plugins super().__init__( + persistent_id=persistent_id, text=text, voter_count=voter_count, text_entities=text_entities, + added_by_user=added_by_user, + added_by_chat=added_by_chat, + addition_date=addition_date, **__pydantic_kwargs, ) diff --git a/aiogram/types/poll_option_added.py b/aiogram/types/poll_option_added.py new file mode 100644 index 00000000..ff3a2006 --- /dev/null +++ b/aiogram/types/poll_option_added.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from .base import TelegramObject + +if TYPE_CHECKING: + from .maybe_inaccessible_message_union import MaybeInaccessibleMessageUnion + from .message_entity import MessageEntity + + +class PollOptionAdded(TelegramObject): + """ + Describes a service message about an option added to a poll. + + Source: https://core.telegram.org/bots/api#polloptionadded + """ + + option_persistent_id: str + """Unique identifier of the added option""" + option_text: str + """Option text""" + poll_message: MaybeInaccessibleMessageUnion | None = None + """*Optional*. Message containing the poll to which the option was added, if known. Note that the :class:`aiogram.types.message.Message` object in this field will not contain the *reply_to_message* field even if it itself is a reply.""" + option_text_entities: list[MessageEntity] | None = None + """*Optional*. Special entities that appear in the *option_text*""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__( + __pydantic__self__, + *, + option_persistent_id: str, + option_text: str, + poll_message: MaybeInaccessibleMessageUnion | None = None, + option_text_entities: list[MessageEntity] | 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__( + option_persistent_id=option_persistent_id, + option_text=option_text, + poll_message=poll_message, + option_text_entities=option_text_entities, + **__pydantic_kwargs, + ) diff --git a/aiogram/types/poll_option_deleted.py b/aiogram/types/poll_option_deleted.py new file mode 100644 index 00000000..a2d7d01e --- /dev/null +++ b/aiogram/types/poll_option_deleted.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from .base import TelegramObject + +if TYPE_CHECKING: + from .maybe_inaccessible_message_union import MaybeInaccessibleMessageUnion + from .message_entity import MessageEntity + + +class PollOptionDeleted(TelegramObject): + """ + Describes a service message about an option deleted from a poll. + + Source: https://core.telegram.org/bots/api#polloptiondeleted + """ + + option_persistent_id: str + """Unique identifier of the deleted option""" + option_text: str + """Option text""" + poll_message: MaybeInaccessibleMessageUnion | None = None + """*Optional*. Message containing the poll from which the option was deleted, if known. Note that the :class:`aiogram.types.message.Message` object in this field will not contain the *reply_to_message* field even if it itself is a reply.""" + option_text_entities: list[MessageEntity] | None = None + """*Optional*. Special entities that appear in the *option_text*""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__( + __pydantic__self__, + *, + option_persistent_id: str, + option_text: str, + poll_message: MaybeInaccessibleMessageUnion | None = None, + option_text_entities: list[MessageEntity] | 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__( + option_persistent_id=option_persistent_id, + option_text=option_text, + poll_message=poll_message, + option_text_entities=option_text_entities, + **__pydantic_kwargs, + ) diff --git a/aiogram/types/prepared_keyboard_button.py b/aiogram/types/prepared_keyboard_button.py new file mode 100644 index 00000000..b3322311 --- /dev/null +++ b/aiogram/types/prepared_keyboard_button.py @@ -0,0 +1,25 @@ +from typing import TYPE_CHECKING, Any + +from .base import TelegramObject + + +class PreparedKeyboardButton(TelegramObject): + """ + Describes a keyboard button to be used by a user of a Mini App. + + Source: https://core.telegram.org/bots/api#preparedkeyboardbutton + """ + + id: str + """Unique identifier of the keyboard button""" + + if TYPE_CHECKING: + # DO NOT EDIT MANUALLY!!! + # This section was auto-generated via `butcher` + + def __init__(__pydantic__self__, *, id: str, **__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__(id=id, **__pydantic_kwargs) diff --git a/aiogram/types/reply_parameters.py b/aiogram/types/reply_parameters.py index 3ad9131d..9e265b1d 100644 --- a/aiogram/types/reply_parameters.py +++ b/aiogram/types/reply_parameters.py @@ -24,7 +24,7 @@ class ReplyParameters(TelegramObject): allow_sending_without_reply: bool | Default | None = Default("allow_sending_without_reply") """*Optional*. Pass :code:`True` if the message should be sent even if the specified message to be replied to is not found. Always :code:`False` for replies in another chat or forum topic. Always :code:`True` for messages sent on behalf of a business account.""" quote: str | None = None - """*Optional*. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities. The message will fail to send if the quote isn't found in the original message.""" + """*Optional*. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities. The message will fail to send if the quote isn't found in the original message.""" quote_parse_mode: str | Default | None = Default("parse_mode") """*Optional*. Mode for parsing entities in the quote. See `formatting options `_ for more details.""" quote_entities: list[MessageEntity] | None = None @@ -33,6 +33,8 @@ class ReplyParameters(TelegramObject): """*Optional*. Position of the quote in the original message in UTF-16 code units""" checklist_task_id: int | None = None """*Optional*. Identifier of the specific checklist task to be replied to""" + poll_option_id: str | None = None + """*Optional*. Persistent identifier of the specific poll option to be replied to""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -51,6 +53,7 @@ class ReplyParameters(TelegramObject): quote_entities: list[MessageEntity] | None = None, quote_position: int | None = None, checklist_task_id: int | None = None, + poll_option_id: str | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -66,5 +69,6 @@ class ReplyParameters(TelegramObject): quote_entities=quote_entities, quote_position=quote_position, checklist_task_id=checklist_task_id, + poll_option_id=poll_option_id, **__pydantic_kwargs, ) diff --git a/aiogram/types/text_quote.py b/aiogram/types/text_quote.py index b0f610e3..2b534763 100644 --- a/aiogram/types/text_quote.py +++ b/aiogram/types/text_quote.py @@ -20,7 +20,7 @@ class TextQuote(TelegramObject): position: int """Approximate quote position in the original message in UTF-16 code units as specified by the sender""" entities: list[MessageEntity] | None = None - """*Optional*. Special entities that appear in the quote. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, and *custom_emoji* entities are kept in quotes.""" + """*Optional*. Special entities that appear in the quote. Currently, only *bold*, *italic*, *underline*, *strikethrough*, *spoiler*, *custom_emoji*, and *date_time* entities are kept in quotes.""" is_manual: bool | None = None """*Optional*. :code:`True`, if the quote was chosen manually by the message sender. Otherwise, the quote was added automatically by the server.""" diff --git a/aiogram/types/update.py b/aiogram/types/update.py index aab31339..cf13b22a 100644 --- a/aiogram/types/update.py +++ b/aiogram/types/update.py @@ -15,6 +15,7 @@ if TYPE_CHECKING: from .chat_member_updated import ChatMemberUpdated from .chosen_inline_result import ChosenInlineResult from .inline_query import InlineQuery + from .managed_bot_updated import ManagedBotUpdated from .message import Message from .message_reaction_count_updated import MessageReactionCountUpdated from .message_reaction_updated import MessageReactionUpdated @@ -82,6 +83,8 @@ class Update(TelegramObject): """*Optional*. A chat boost was added or changed. The bot must be an administrator in the chat to receive these updates.""" removed_chat_boost: ChatBoostRemoved | None = None """*Optional*. A boost was removed from a chat. The bot must be an administrator in the chat to receive these updates.""" + managed_bot: ManagedBotUpdated | None = None + """*Optional*. A new bot was created to be managed by the bot or token of a bot was changed""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -114,6 +117,7 @@ class Update(TelegramObject): chat_join_request: ChatJoinRequest | None = None, chat_boost: ChatBoostUpdated | None = None, removed_chat_boost: ChatBoostRemoved | None = None, + managed_bot: ManagedBotUpdated | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -145,6 +149,7 @@ class Update(TelegramObject): chat_join_request=chat_join_request, chat_boost=chat_boost, removed_chat_boost=removed_chat_boost, + managed_bot=managed_bot, **__pydantic_kwargs, ) @@ -206,6 +211,8 @@ class Update(TelegramObject): return "business_message" if self.purchased_paid_media: return "purchased_paid_media" + if self.managed_bot: + return "managed_bot" raise UpdateTypeLookupError("Update does not contain any known event type.") diff --git a/aiogram/types/user.py b/aiogram/types/user.py index 7f03504c..3722a56f 100644 --- a/aiogram/types/user.py +++ b/aiogram/types/user.py @@ -47,6 +47,8 @@ class User(TelegramObject): """*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`.""" + can_manage_bots: bool | None = None + """*Optional*. :code:`True`, if other bots can be created to be controlled by the bot. Returned only in :class:`aiogram.methods.get_me.GetMe`.""" if TYPE_CHECKING: # DO NOT EDIT MANUALLY!!! @@ -70,6 +72,7 @@ class User(TelegramObject): has_main_web_app: bool | None = None, has_topics_enabled: bool | None = None, allows_users_to_create_topics: bool | None = None, + can_manage_bots: bool | None = None, **__pydantic_kwargs: Any, ) -> None: # DO NOT EDIT MANUALLY!!! @@ -92,6 +95,7 @@ class User(TelegramObject): has_main_web_app=has_main_web_app, has_topics_enabled=has_topics_enabled, allows_users_to_create_topics=allows_users_to_create_topics, + can_manage_bots=can_manage_bots, **__pydantic_kwargs, ) diff --git a/aiogram/utils/formatting.py b/aiogram/utils/formatting.py index cbfd9758..4dfccd64 100644 --- a/aiogram/utils/formatting.py +++ b/aiogram/utils/formatting.py @@ -2,6 +2,7 @@ from __future__ import annotations import textwrap from collections.abc import Generator, Iterable, Iterator +from datetime import datetime from typing import Any, ClassVar from typing_extensions import Self @@ -534,6 +535,26 @@ class ExpandableBlockQuote(Text): type = MessageEntityType.EXPANDABLE_BLOCKQUOTE +class DateTime(Text): + type = MessageEntityType.DATE_TIME + + def __init__( + self, + *body: NodeType, + unix_time: int | datetime, + date_time_format: str | None = None, + **params: Any, + ) -> None: + if isinstance(unix_time, datetime): + unix_time = int(unix_time.timestamp()) + super().__init__( + *body, + unix_time=unix_time, + date_time_format=date_time_format, + **params, + ) + + NODE_TYPES: dict[str | None, type[Text]] = { Text.type: Text, HashTag.type: HashTag, @@ -554,6 +575,7 @@ NODE_TYPES: dict[str | None, type[Text]] = { CustomEmoji.type: CustomEmoji, BlockQuote.type: BlockQuote, ExpandableBlockQuote.type: ExpandableBlockQuote, + DateTime.type: DateTime, } diff --git a/aiogram/utils/keyboard.py b/aiogram/utils/keyboard.py index 8824edfd..2dba452c 100644 --- a/aiogram/utils/keyboard.py +++ b/aiogram/utils/keyboard.py @@ -303,6 +303,8 @@ class InlineKeyboardBuilder(KeyboardBuilder[InlineKeyboardButton]): self, *, text: str, + icon_custom_emoji_id: str | None = None, + style: str | None = None, url: str | None = None, callback_data: str | CallbackData | None = None, web_app: WebAppInfo | None = None, @@ -319,6 +321,8 @@ class InlineKeyboardBuilder(KeyboardBuilder[InlineKeyboardButton]): InlineKeyboardBuilder, self._button( text=text, + icon_custom_emoji_id=icon_custom_emoji_id, + style=style, url=url, callback_data=callback_data, web_app=web_app, @@ -375,6 +379,8 @@ class ReplyKeyboardBuilder(KeyboardBuilder[KeyboardButton]): 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, @@ -387,6 +393,8 @@ class ReplyKeyboardBuilder(KeyboardBuilder[KeyboardButton]): ReplyKeyboardBuilder, self._button( 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/utils/text_decorations.py b/aiogram/utils/text_decorations.py index 00762c0a..c835c27b 100644 --- a/aiogram/utils/text_decorations.py +++ b/aiogram/utils/text_decorations.py @@ -3,9 +3,11 @@ from __future__ import annotations import html import re from abc import ABC, abstractmethod +from datetime import date, datetime, time from typing import TYPE_CHECKING, cast from aiogram.enums import MessageEntityType +from aiogram.utils.link import create_tg_link if TYPE_CHECKING: from collections.abc import Generator @@ -78,6 +80,12 @@ class TextDecoration(ABC): return self.link(value=text, link=cast(str, entity.url)) if entity.type == MessageEntityType.CUSTOM_EMOJI: return self.custom_emoji(value=text, custom_emoji_id=cast(str, entity.custom_emoji_id)) + if entity.type == MessageEntityType.DATE_TIME: + return self.date_time( + value=text, + unix_time=cast(int, entity.unix_time), + date_time_format=entity.date_time_format, + ) # This case is not possible because of `if` above, but if any new entity is added to # API it will be here too @@ -180,54 +188,105 @@ class TextDecoration(ABC): def expandable_blockquote(self, value: str) -> str: pass + @abstractmethod + def date_time( + self, + value: str, + unix_time: int | datetime, + date_time_format: str | None = None, + ) -> str: + pass + class HtmlDecoration(TextDecoration): BOLD_TAG = "b" ITALIC_TAG = "i" UNDERLINE_TAG = "u" STRIKETHROUGH_TAG = "s" + CODE_TAG = "code" + PRE_TAG = "pre" + LINK_TAG = "a" SPOILER_TAG = "tg-spoiler" EMOJI_TAG = "tg-emoji" + DATE_TIME_TAG = "tg-time" BLOCKQUOTE_TAG = "blockquote" + def _tag( + self, + tag: str, + content: str, + *, + attrs: dict[str, str] | None = None, + flags: list[str] | None = None, + ) -> str: + prepared_attrs: list[str] = [] + if attrs: + prepared_attrs.extend(f'{k}="{v}"' for k, v in attrs.items()) + if flags: + prepared_attrs.extend(f"{flag}" for flag in flags) + + attrs_str = " ".join(prepared_attrs) + if attrs_str: + attrs_str = " " + attrs_str + + return f"<{tag}{attrs_str}>{content}" + def link(self, value: str, link: str) -> str: - return f'{value}' + return self._tag(self.LINK_TAG, value, attrs={"href": link}) def bold(self, value: str) -> str: - return f"<{self.BOLD_TAG}>{value}" + return self._tag(self.BOLD_TAG, value) def italic(self, value: str) -> str: - return f"<{self.ITALIC_TAG}>{value}" + return self._tag(self.ITALIC_TAG, value) def code(self, value: str) -> str: - return f"{value}" + return self._tag(self.CODE_TAG, value) def pre(self, value: str) -> str: - return f"
{value}
" + return self._tag(self.PRE_TAG, value) def pre_language(self, value: str, language: str) -> str: - return f'
{value}
' + return self._tag( + self.PRE_TAG, + self._tag(self.CODE_TAG, value, attrs={"language": f"language-{language}"}), + ) def underline(self, value: str) -> str: - return f"<{self.UNDERLINE_TAG}>{value}" + return self._tag(self.UNDERLINE_TAG, value) def strikethrough(self, value: str) -> str: - return f"<{self.STRIKETHROUGH_TAG}>{value}" + return self._tag(self.STRIKETHROUGH_TAG, value) def spoiler(self, value: str) -> str: - return f"<{self.SPOILER_TAG}>{value}" + return self._tag(self.SPOILER_TAG, value) def quote(self, value: str) -> str: return html.escape(value, quote=False) def custom_emoji(self, value: str, custom_emoji_id: str) -> str: - return f'<{self.EMOJI_TAG} emoji-id="{custom_emoji_id}">{value}' + return self._tag(self.EMOJI_TAG, value, attrs={"emoji-id": custom_emoji_id}) def blockquote(self, value: str) -> str: - return f"<{self.BLOCKQUOTE_TAG}>{value}" + return self._tag(self.BLOCKQUOTE_TAG, value) def expandable_blockquote(self, value: str) -> str: - return f"<{self.BLOCKQUOTE_TAG} expandable>{value}" + return self._tag(self.BLOCKQUOTE_TAG, value, flags=["expandable"]) + + def date_time( + self, + value: str, + unix_time: int | datetime, + date_time_format: str | None = None, + ) -> str: + if isinstance(unix_time, datetime): + unix_time = int(unix_time.timestamp()) + + args = {"unix": str(unix_time)} + if date_time_format: + args["format"] = date_time_format + + return self._tag(self.DATE_TIME_TAG, value, attrs=args) class MarkdownDecoration(TextDecoration): @@ -264,7 +323,8 @@ class MarkdownDecoration(TextDecoration): return re.sub(pattern=self.MARKDOWN_QUOTE_PATTERN, repl=r"\\\1", string=value) def custom_emoji(self, value: str, custom_emoji_id: str) -> str: - return f"!{self.link(value=value, link=f'tg://emoji?id={custom_emoji_id}')}" + link = create_tg_link("emoji", emoji_id=custom_emoji_id) + return f"!{self.link(value=value, link=link)}" def blockquote(self, value: str) -> str: return "\n".join(f">{line}" for line in value.splitlines()) @@ -272,6 +332,22 @@ class MarkdownDecoration(TextDecoration): def expandable_blockquote(self, value: str) -> str: return "\n".join(f">{line}" for line in value.splitlines()) + "||" + def date_time( + self, + value: str, + unix_time: int | datetime, + date_time_format: str | None = None, + ) -> str: + if isinstance(unix_time, datetime): + unix_time = int(unix_time.timestamp()) + + link_params = {"unix": str(unix_time)} + if date_time_format: + link_params["format"] = date_time_format + link = create_tg_link("time", **link_params) + + return f"!{self.link(value, link=link)}" + html_decoration = HtmlDecoration() markdown_decoration = MarkdownDecoration() diff --git a/docs/api/methods/get_managed_bot_token.rst b/docs/api/methods/get_managed_bot_token.rst new file mode 100644 index 00000000..5ad46aaf --- /dev/null +++ b/docs/api/methods/get_managed_bot_token.rst @@ -0,0 +1,38 @@ +################## +getManagedBotToken +################## + +Returns: :obj:`str` + +.. automodule:: aiogram.methods.get_managed_bot_token + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: str = await bot.get_managed_bot_token(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_managed_bot_token import GetManagedBotToken` +- alias: :code:`from aiogram.methods import GetManagedBotToken` + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: str = await bot(GetManagedBotToken(...)) diff --git a/docs/api/methods/get_user_profile_audios.rst b/docs/api/methods/get_user_profile_audios.rst index f9567228..553ab296 100644 --- a/docs/api/methods/get_user_profile_audios.rst +++ b/docs/api/methods/get_user_profile_audios.rst @@ -36,3 +36,11 @@ With specific bot .. code-block:: python result: UserProfileAudios = await bot(GetUserProfileAudios(...)) + + + + +As shortcut from received object +-------------------------------- + +- :meth:`aiogram.types.user.User.get_profile_audios` diff --git a/docs/api/methods/index.rst b/docs/api/methods/index.rst index bbd6303a..187f8bb6 100644 --- a/docs/api/methods/index.rst +++ b/docs/api/methods/index.rst @@ -35,6 +35,7 @@ Available methods :maxdepth: 1 answer_callback_query + answer_web_app_query approve_chat_join_request ban_chat_member ban_chat_sender_chat @@ -74,6 +75,7 @@ Available methods get_chat_menu_button get_file get_forum_topic_icon_stickers + get_managed_bot_token get_me get_my_commands get_my_default_administrator_rights @@ -98,9 +100,12 @@ Available methods remove_user_verification reopen_forum_topic reopen_general_forum_topic + replace_managed_bot_token repost_story restrict_chat_member revoke_chat_invite_link + save_prepared_inline_message + save_prepared_keyboard_button send_animation send_audio send_chat_action @@ -127,6 +132,7 @@ Available methods set_business_account_username set_chat_administrator_custom_title set_chat_description + set_chat_member_tag set_chat_menu_button set_chat_permissions set_chat_photo @@ -172,16 +178,6 @@ Updating messages stop_message_live_location stop_poll -Inline mode -=========== - -.. toctree:: - :maxdepth: 1 - - answer_inline_query - answer_web_app_query - save_prepared_inline_message - Games ===== @@ -225,3 +221,11 @@ Telegram Passport :maxdepth: 1 set_passport_data_errors + +Inline mode +=========== + +.. toctree:: + :maxdepth: 1 + + answer_inline_query diff --git a/docs/api/methods/replace_managed_bot_token.rst b/docs/api/methods/replace_managed_bot_token.rst new file mode 100644 index 00000000..97b2bc1b --- /dev/null +++ b/docs/api/methods/replace_managed_bot_token.rst @@ -0,0 +1,45 @@ +###################### +replaceManagedBotToken +###################### + +Returns: :obj:`str` + +.. automodule:: aiogram.methods.replace_managed_bot_token + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: str = await bot.replace_managed_bot_token(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.replace_managed_bot_token import ReplaceManagedBotToken` +- alias: :code:`from aiogram.methods import ReplaceManagedBotToken` + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: str = await bot(ReplaceManagedBotToken(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return ReplaceManagedBotToken(...) diff --git a/docs/api/methods/save_prepared_keyboard_button.rst b/docs/api/methods/save_prepared_keyboard_button.rst new file mode 100644 index 00000000..3a8ff2fb --- /dev/null +++ b/docs/api/methods/save_prepared_keyboard_button.rst @@ -0,0 +1,45 @@ +########################## +savePreparedKeyboardButton +########################## + +Returns: :obj:`PreparedKeyboardButton` + +.. automodule:: aiogram.methods.save_prepared_keyboard_button + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: PreparedKeyboardButton = await bot.save_prepared_keyboard_button(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.save_prepared_keyboard_button import SavePreparedKeyboardButton` +- alias: :code:`from aiogram.methods import SavePreparedKeyboardButton` + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: PreparedKeyboardButton = await bot(SavePreparedKeyboardButton(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SavePreparedKeyboardButton(...) diff --git a/docs/api/methods/set_chat_member_tag.rst b/docs/api/methods/set_chat_member_tag.rst new file mode 100644 index 00000000..1890810d --- /dev/null +++ b/docs/api/methods/set_chat_member_tag.rst @@ -0,0 +1,51 @@ +################ +setChatMemberTag +################ + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_chat_member_tag + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_chat_member_tag(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_chat_member_tag import SetChatMemberTag` +- alias: :code:`from aiogram.methods import SetChatMemberTag` + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetChatMemberTag(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetChatMemberTag(...) + + +As shortcut from received object +-------------------------------- + +- :meth:`aiogram.types.chat.Chat.set_member_tag` diff --git a/docs/api/types/index.rst b/docs/api/types/index.rst index 8cfa8a45..24fe694c 100644 --- a/docs/api/types/index.rst +++ b/docs/api/types/index.rst @@ -125,12 +125,15 @@ Available types keyboard_button keyboard_button_poll_type keyboard_button_request_chat + keyboard_button_request_managed_bot keyboard_button_request_user keyboard_button_request_users link_preview_options location location_address login_url + managed_bot_created + managed_bot_updated maybe_inaccessible_message menu_button menu_button_commands @@ -161,6 +164,10 @@ Available types poll poll_answer poll_option + poll_option_added + poll_option_deleted + prepared_inline_message + prepared_keyboard_button proximity_alert_triggered reaction_count reaction_type @@ -171,6 +178,7 @@ Available types reply_keyboard_remove reply_parameters response_parameters + sent_web_app_message shared_user star_amount story @@ -255,8 +263,6 @@ Inline mode input_message_content input_text_message_content input_venue_message_content - prepared_inline_message - sent_web_app_message Payments ======== diff --git a/docs/api/types/keyboard_button_request_managed_bot.rst b/docs/api/types/keyboard_button_request_managed_bot.rst new file mode 100644 index 00000000..baddbea6 --- /dev/null +++ b/docs/api/types/keyboard_button_request_managed_bot.rst @@ -0,0 +1,10 @@ +############################### +KeyboardButtonRequestManagedBot +############################### + + +.. automodule:: aiogram.types.keyboard_button_request_managed_bot + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields diff --git a/docs/api/types/managed_bot_created.rst b/docs/api/types/managed_bot_created.rst new file mode 100644 index 00000000..8912801e --- /dev/null +++ b/docs/api/types/managed_bot_created.rst @@ -0,0 +1,10 @@ +################# +ManagedBotCreated +################# + + +.. automodule:: aiogram.types.managed_bot_created + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields diff --git a/docs/api/types/managed_bot_updated.rst b/docs/api/types/managed_bot_updated.rst new file mode 100644 index 00000000..8794dbc4 --- /dev/null +++ b/docs/api/types/managed_bot_updated.rst @@ -0,0 +1,10 @@ +################# +ManagedBotUpdated +################# + + +.. automodule:: aiogram.types.managed_bot_updated + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields diff --git a/docs/api/types/poll_option_added.rst b/docs/api/types/poll_option_added.rst new file mode 100644 index 00000000..688def81 --- /dev/null +++ b/docs/api/types/poll_option_added.rst @@ -0,0 +1,10 @@ +############### +PollOptionAdded +############### + + +.. automodule:: aiogram.types.poll_option_added + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields diff --git a/docs/api/types/poll_option_deleted.rst b/docs/api/types/poll_option_deleted.rst new file mode 100644 index 00000000..b4655292 --- /dev/null +++ b/docs/api/types/poll_option_deleted.rst @@ -0,0 +1,10 @@ +################# +PollOptionDeleted +################# + + +.. automodule:: aiogram.types.poll_option_deleted + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields diff --git a/docs/api/types/prepared_keyboard_button.rst b/docs/api/types/prepared_keyboard_button.rst new file mode 100644 index 00000000..2972ad6b --- /dev/null +++ b/docs/api/types/prepared_keyboard_button.rst @@ -0,0 +1,10 @@ +###################### +PreparedKeyboardButton +###################### + + +.. automodule:: aiogram.types.prepared_keyboard_button + :members: + :member-order: bysource + :undoc-members: True + :exclude-members: model_config,model_fields diff --git a/docs/dispatcher/webhook.rst b/docs/dispatcher/webhook.rst index 73bcddbc..26c58f36 100644 --- a/docs/dispatcher/webhook.rst +++ b/docs/dispatcher/webhook.rst @@ -66,6 +66,19 @@ It can be acy using firewall rules or nginx configuration or middleware on appli So, aiogram has an implementation of the IP filtering middleware for aiohttp. +`aiogram` IP filtering middleware reads the left-most IP address from `X-Forwarded-For`. + +.. warning:: + + `X-Forwarded-For` is trustworthy only if all webhook traffic goes through a trusted reverse proxy that rewrites this header. + If your application is directly reachable from the Internet, this header can be forged. + +For production deployments, use defense in depth: + +- Always set and verify :code:`X-Telegram-Bot-Api-Secret-Token` +- Restrict network access to the webhook endpoint (firewall, security groups, ACL) +- Ensure the backend app is not publicly reachable and accepts requests only from the trusted proxy + .. autofunction:: aiogram.webhook.aiohttp_server.ip_filter_middleware .. autoclass:: aiogram.webhook.security.IPFilter diff --git a/docs/locale/uk_UA/LC_MESSAGES/dispatcher/webhook.po b/docs/locale/uk_UA/LC_MESSAGES/dispatcher/webhook.po index 880fef46..ee10e8ca 100644 --- a/docs/locale/uk_UA/LC_MESSAGES/dispatcher/webhook.po +++ b/docs/locale/uk_UA/LC_MESSAGES/dispatcher/webhook.po @@ -203,45 +203,95 @@ msgstr "" #: ../../dispatcher/webhook.rst:51 msgid "Security" -msgstr "" +msgstr "Безпека" #: ../../dispatcher/webhook.rst:53 msgid "" "Telegram supports two methods to verify incoming requests that they are " "from Telegram:" -msgstr "" +msgstr "Telegram підтримує два методи перевірки вхідних запитів, що вони надходять від Telegram:" #: ../../dispatcher/webhook.rst:56 msgid "Using a secret token" -msgstr "" +msgstr "Використання секретного токена" #: ../../dispatcher/webhook.rst:58 msgid "" "When you set webhook, you can specify a secret token and then use it to " "verify incoming requests." msgstr "" +"Коли ви налаштовуєте webhook, ви можете вказати секретний токен і потім " +"використовувати його для перевірки вхідних запитів." #: ../../dispatcher/webhook.rst:61 msgid "Using IP filtering" -msgstr "" +msgstr "Використання фільтрації за IP" #: ../../dispatcher/webhook.rst:63 msgid "" "You can specify a list of IP addresses from which you expect incoming " "requests, and then use it to verify incoming requests." msgstr "" +"Ви можете вказати список IP-адрес, з яких очікуєте вхідні запити, і " +"використовувати його для перевірки запитів." #: ../../dispatcher/webhook.rst:65 msgid "" "It can be acy using firewall rules or nginx configuration or middleware " "on application level." msgstr "" +"Це можна зробити за допомогою правил firewall, конфігурації nginx або " +"middleware на рівні застосунку." #: ../../dispatcher/webhook.rst:67 msgid "" "So, aiogram has an implementation of the IP filtering middleware for " "aiohttp." msgstr "" +"Тому в aiogram є реалізація middleware для фільтрації за IP для aiohttp." + +#: ../../dispatcher/webhook.rst:69 +msgid "" +"`aiogram` IP filtering middleware reads the left-most IP address from " +"`X-Forwarded-For`." +msgstr "" +"IP-фільтр middleware в `aiogram` читає крайню ліву IP-адресу з " +"`X-Forwarded-For`." + +#: ../../dispatcher/webhook.rst:73 +msgid "" +"`X-Forwarded-For` is trustworthy only if all webhook traffic goes through a" +" trusted reverse proxy that rewrites this header. If your application is " +"directly reachable from the Internet, this header can be forged." +msgstr "" +"`X-Forwarded-For` можна вважати надійним лише тоді, коли весь webhook-" +"трафік проходить через довірений reverse proxy, який перезаписує цей " +"заголовок. Якщо ваш застосунок напряму доступний з Інтернету, цей " +"заголовок можна підробити." + +#: ../../dispatcher/webhook.rst:76 +msgid "For production deployments, use defense in depth:" +msgstr "Для production-деплойментів використовуйте багаторівневий захист:" + +#: ../../dispatcher/webhook.rst:78 +msgid "Always set and verify :code:`X-Telegram-Bot-Api-Secret-Token`" +msgstr "Завжди встановлюйте та перевіряйте :code:`X-Telegram-Bot-Api-Secret-Token`" + +#: ../../dispatcher/webhook.rst:79 +msgid "" +"Restrict network access to the webhook endpoint (firewall, security " +"groups, ACL)" +msgstr "" +"Обмежуйте мережевий доступ до webhook endpoint (firewall, security groups, " +"ACL)" + +#: ../../dispatcher/webhook.rst:80 +msgid "" +"Ensure the backend app is not publicly reachable and accepts requests only " +"from the trusted proxy" +msgstr "" +"Переконайтеся, що backend-застосунок не доступний публічно та приймає " +"запити лише від довіреного proxy" #: ../../dispatcher/webhook.rst:75 msgid "Examples" diff --git a/tests/test_api/test_client/test_default.py b/tests/test_api/test_client/test_default.py index 2445c435..7e354c89 100644 --- a/tests/test_api/test_client/test_default.py +++ b/tests/test_api/test_client/test_default.py @@ -24,6 +24,15 @@ class TestDefault: default = Default("test") assert repr(default) == "" + def test_eq_same_name(self): + assert Default("test") == Default("test") + + def test_eq_different_name(self): + assert Default("foo") != Default("bar") + + def test_hash(self): + assert hash(Default("test")) == hash(Default("test")) + class TestDefaultBotProperties: def test_post_init_empty(self): diff --git a/tests/test_api/test_methods/test_get_managed_bot_token.py b/tests/test_api/test_methods/test_get_managed_bot_token.py new file mode 100644 index 00000000..db1fe00f --- /dev/null +++ b/tests/test_api/test_methods/test_get_managed_bot_token.py @@ -0,0 +1,11 @@ +from aiogram.methods import GetManagedBotToken +from tests.mocked_bot import MockedBot + + +class TestGetManagedBotToken: + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(GetManagedBotToken, ok=True, result="42:NEW_TOKEN") + + response: str = await bot.get_managed_bot_token(user_id=42) + 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 ee3b7f4e..1f19f3da 100644 --- a/tests/test_api/test_methods/test_promote_chat_member.py +++ b/tests/test_api/test_methods/test_promote_chat_member.py @@ -6,6 +6,11 @@ class TestPromoteChatMember: async def test_bot_method(self, bot: MockedBot): prepare_result = bot.add_result_for(PromoteChatMember, ok=True, result=True) - response: bool = await bot.promote_chat_member(chat_id=-42, user_id=42) - bot.get_request() + response: bool = await bot.promote_chat_member( + chat_id=-42, + user_id=42, + can_manage_tags=True, + ) + request = bot.get_request() + assert request.can_manage_tags is True assert response == prepare_result.result diff --git a/tests/test_api/test_methods/test_replace_managed_bot_token.py b/tests/test_api/test_methods/test_replace_managed_bot_token.py new file mode 100644 index 00000000..5f71afe2 --- /dev/null +++ b/tests/test_api/test_methods/test_replace_managed_bot_token.py @@ -0,0 +1,13 @@ +from aiogram.methods import ReplaceManagedBotToken +from tests.mocked_bot import MockedBot + + +class TestReplaceManagedBotToken: + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for( + ReplaceManagedBotToken, ok=True, result="42:REPLACED_TOKEN" + ) + + response: str = await bot.replace_managed_bot_token(user_id=42) + bot.get_request() + assert response == prepare_result.result diff --git a/tests/test_api/test_methods/test_save_prepared_keyboard_button.py b/tests/test_api/test_methods/test_save_prepared_keyboard_button.py new file mode 100644 index 00000000..04ca1e69 --- /dev/null +++ b/tests/test_api/test_methods/test_save_prepared_keyboard_button.py @@ -0,0 +1,22 @@ +from aiogram.methods import SavePreparedKeyboardButton +from aiogram.types import KeyboardButton, KeyboardButtonRequestManagedBot, PreparedKeyboardButton +from tests.mocked_bot import MockedBot + + +class TestSavePreparedKeyboardButton: + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for( + SavePreparedKeyboardButton, + ok=True, + result=PreparedKeyboardButton(id="test-id"), + ) + + response: PreparedKeyboardButton = await bot.save_prepared_keyboard_button( + user_id=42, + button=KeyboardButton( + text="Create bot", + request_managed_bot=KeyboardButtonRequestManagedBot(request_id=1), + ), + ) + 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 27288b51..6e8ce7e9 100644 --- a/tests/test_api/test_methods/test_send_poll.py +++ b/tests/test_api/test_methods/test_send_poll.py @@ -17,13 +17,14 @@ class TestSendPoll: id="QA", question="Q", options=[ - PollOption(text="A", voter_count=0), - PollOption(text="B", voter_count=0), + PollOption(persistent_id="1", text="A", voter_count=0), + PollOption(persistent_id="2", text="B", voter_count=0), ], is_closed=False, is_anonymous=False, type="quiz", allows_multiple_answers=False, + allows_revoting=False, total_voter_count=0, correct_option_id=0, ), diff --git a/tests/test_api/test_methods/test_set_chat_member_tag.py b/tests/test_api/test_methods/test_set_chat_member_tag.py new file mode 100644 index 00000000..edc581cd --- /dev/null +++ b/tests/test_api/test_methods/test_set_chat_member_tag.py @@ -0,0 +1,14 @@ +from aiogram.methods import SetChatMemberTag +from tests.mocked_bot import MockedBot + + +class TestSetChatMemberTag: + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(SetChatMemberTag, ok=True, result=True) + + response: bool = await bot.set_chat_member_tag(chat_id=-42, user_id=42, tag="test") + request = bot.get_request() + assert request.chat_id == -42 + assert request.user_id == 42 + assert request.tag == "test" + 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 64d0406c..93613218 100644 --- a/tests/test_api/test_methods/test_stop_poll.py +++ b/tests/test_api/test_methods/test_stop_poll.py @@ -11,11 +11,15 @@ class TestStopPoll: result=Poll( id="QA", question="Q", - options=[PollOption(text="A", voter_count=0), PollOption(text="B", voter_count=0)], + options=[ + PollOption(persistent_id="1", text="A", voter_count=0), + PollOption(persistent_id="2", text="B", voter_count=0), + ], is_closed=False, is_anonymous=False, type="quiz", allows_multiple_answers=False, + allows_revoting=False, total_voter_count=0, correct_option_id=0, ), diff --git a/tests/test_api/test_types/test_chat.py b/tests/test_api/test_types/test_chat.py index 360b2ee1..b3e63854 100644 --- a/tests/test_api/test_types/test_chat.py +++ b/tests/test_api/test_types/test_chat.py @@ -115,6 +115,14 @@ class TestChat: method = chat.set_administrator_custom_title(user_id=1, custom_title="test") assert method.chat_id == chat.id + def test_set_member_tag(self): + chat = Chat(id=-42, type="supergroup") + + method = chat.set_member_tag(user_id=42, tag="test") + assert method.chat_id == chat.id + assert method.user_id == 42 + assert method.tag == "test" + def test_set_permissions(self): chat = Chat(id=-42, type="supergroup") diff --git a/tests/test_api/test_types/test_chat_member_tag_permissions.py b/tests/test_api/test_types/test_chat_member_tag_permissions.py new file mode 100644 index 00000000..30aa5481 --- /dev/null +++ b/tests/test_api/test_types/test_chat_member_tag_permissions.py @@ -0,0 +1,84 @@ +from datetime import datetime + +from aiogram.types import ( + ChatAdministratorRights, + ChatMemberAdministrator, + ChatMemberMember, + ChatMemberRestricted, + ChatPermissions, + User, +) + + +class TestChatMemberTagPermissions: + def test_chat_administrator_rights_can_manage_tags(self): + rights = ChatAdministratorRights( + is_anonymous=False, + can_manage_chat=True, + can_delete_messages=True, + can_manage_video_chats=True, + can_restrict_members=True, + can_promote_members=True, + can_change_info=True, + can_invite_users=True, + can_post_stories=True, + can_edit_stories=True, + can_delete_stories=True, + can_manage_tags=True, + ) + assert rights.can_manage_tags is True + + def test_chat_member_administrator_can_manage_tags(self): + admin = ChatMemberAdministrator( + user=User(id=42, is_bot=False, first_name="User"), + can_be_edited=True, + is_anonymous=False, + can_manage_chat=True, + can_delete_messages=True, + can_manage_video_chats=True, + can_restrict_members=True, + can_promote_members=True, + can_change_info=True, + can_invite_users=True, + can_post_stories=True, + can_edit_stories=True, + can_delete_stories=True, + can_manage_tags=True, + ) + assert admin.can_manage_tags is True + + def test_chat_permissions_can_edit_tag(self): + permissions = ChatPermissions(can_edit_tag=True) + assert permissions.can_edit_tag is True + + def test_chat_member_member_tag(self): + member = ChatMemberMember( + user=User(id=42, is_bot=False, first_name="User"), + tag="premium", + ) + assert member.tag == "premium" + + def test_chat_member_restricted_can_edit_tag_and_tag(self): + restricted = ChatMemberRestricted( + user=User(id=42, is_bot=False, first_name="User"), + is_member=True, + can_send_messages=True, + can_send_audios=True, + can_send_documents=True, + can_send_photos=True, + can_send_videos=True, + can_send_video_notes=True, + can_send_voice_notes=True, + can_send_polls=True, + can_send_other_messages=True, + can_add_web_page_previews=True, + can_edit_tag=True, + can_change_info=True, + can_invite_users=True, + can_pin_messages=True, + can_manage_topics=True, + until_date=datetime.now(), + tag="premium", + ) + assert restricted.can_edit_tag is True + assert restricted.tag == "premium" diff --git a/tests/test_api/test_types/test_keyboard_button_request_managed_bot.py b/tests/test_api/test_types/test_keyboard_button_request_managed_bot.py new file mode 100644 index 00000000..053c2c5f --- /dev/null +++ b/tests/test_api/test_types/test_keyboard_button_request_managed_bot.py @@ -0,0 +1,19 @@ +from aiogram.types import KeyboardButtonRequestManagedBot + + +class TestKeyboardButtonRequestManagedBot: + def test_required_fields(self): + obj = KeyboardButtonRequestManagedBot(request_id=1) + assert obj.request_id == 1 + assert obj.suggested_name is None + assert obj.suggested_username is None + + def test_optional_fields(self): + obj = KeyboardButtonRequestManagedBot( + request_id=2, + suggested_name="My Bot", + suggested_username="my_bot", + ) + assert obj.request_id == 2 + assert obj.suggested_name == "My Bot" + assert obj.suggested_username == "my_bot" diff --git a/tests/test_api/test_types/test_managed_bot_created.py b/tests/test_api/test_types/test_managed_bot_created.py new file mode 100644 index 00000000..b2380d56 --- /dev/null +++ b/tests/test_api/test_types/test_managed_bot_created.py @@ -0,0 +1,9 @@ +from aiogram.types import ManagedBotCreated, User + + +class TestManagedBotCreated: + def test_fields(self): + bot_user = User(id=123, is_bot=True, first_name="TestBot") + obj = ManagedBotCreated(bot=bot_user) + assert obj.bot_user == bot_user + assert obj.bot_user.id == 123 diff --git a/tests/test_api/test_types/test_managed_bot_updated.py b/tests/test_api/test_types/test_managed_bot_updated.py new file mode 100644 index 00000000..59dec0ae --- /dev/null +++ b/tests/test_api/test_types/test_managed_bot_updated.py @@ -0,0 +1,10 @@ +from aiogram.types import ManagedBotUpdated, User + + +class TestManagedBotUpdated: + def test_fields(self): + user = User(id=42, is_bot=False, first_name="Creator") + bot_user = User(id=123, is_bot=True, first_name="TestBot") + obj = ManagedBotUpdated(user=user, bot=bot_user) + assert obj.user == user + assert obj.bot_user == bot_user diff --git a/tests/test_api/test_types/test_message.py b/tests/test_api/test_types/test_message.py index 51d1790d..a13d810a 100644 --- a/tests/test_api/test_types/test_message.py +++ b/tests/test_api/test_types/test_message.py @@ -76,6 +76,7 @@ from aiogram.types import ( InputMediaPhoto, Invoice, Location, + ManagedBotCreated, MessageAutoDeleteTimerChanged, MessageEntity, PaidMediaInfo, @@ -85,6 +86,8 @@ from aiogram.types import ( PhotoSize, Poll, PollOption, + PollOptionAdded, + PollOptionDeleted, ProximityAlertTriggered, ReactionTypeCustomEmoji, RefundedPayment, @@ -426,13 +429,14 @@ TEST_MESSAGE_POLL = Message( id="QA", question="Q", options=[ - PollOption(text="A", voter_count=0), - PollOption(text="B", voter_count=0), + PollOption(persistent_id="1", text="A", voter_count=0), + PollOption(persistent_id="2", text="B", voter_count=0), ], is_closed=False, is_anonymous=False, type="quiz", allows_multiple_answers=False, + allows_revoting=False, total_voter_count=0, correct_option_id=1, ), @@ -858,6 +862,35 @@ TEST_MESSAGE_SUGGESTED_POST_REFUNDED = Message( from_user=User(id=42, is_bot=False, first_name="Test"), suggested_post_refunded=SuggestedPostRefunded(reason="post_deleted"), ) +TEST_MESSAGE_MANAGED_BOT_CREATED = Message( + message_id=42, + date=datetime.datetime.now(), + chat=Chat(id=42, type="private"), + from_user=User(id=42, is_bot=False, first_name="Test"), + managed_bot_created=ManagedBotCreated( + bot_user=User(id=100, is_bot=True, first_name="ManagedBot"), + ), +) +TEST_MESSAGE_POLL_OPTION_ADDED = Message( + message_id=42, + date=datetime.datetime.now(), + chat=Chat(id=42, type="private"), + from_user=User(id=42, is_bot=False, first_name="Test"), + poll_option_added=PollOptionAdded( + option_persistent_id="1", + option_text="New option", + ), +) +TEST_MESSAGE_POLL_OPTION_DELETED = Message( + message_id=42, + date=datetime.datetime.now(), + chat=Chat(id=42, type="private"), + from_user=User(id=42, is_bot=False, first_name="Test"), + poll_option_deleted=PollOptionDeleted( + option_persistent_id="1", + option_text="Deleted option", + ), +) MESSAGES_AND_CONTENT_TYPES = [ [TEST_MESSAGE_TEXT, ContentType.TEXT], @@ -937,6 +970,9 @@ MESSAGES_AND_CONTENT_TYPES = [ [TEST_MESSAGE_SUGGESTED_POST_DECLINED, ContentType.SUGGESTED_POST_DECLINED], [TEST_MESSAGE_SUGGESTED_POST_PAID, ContentType.SUGGESTED_POST_PAID], [TEST_MESSAGE_SUGGESTED_POST_REFUNDED, ContentType.SUGGESTED_POST_REFUNDED], + [TEST_MESSAGE_MANAGED_BOT_CREATED, ContentType.MANAGED_BOT_CREATED], + [TEST_MESSAGE_POLL_OPTION_ADDED, ContentType.POLL_OPTION_ADDED], + [TEST_MESSAGE_POLL_OPTION_DELETED, ContentType.POLL_OPTION_DELETED], [TEST_MESSAGE_UNKNOWN, ContentType.UNKNOWN], ] @@ -1013,6 +1049,9 @@ MESSAGES_AND_COPY_METHODS = [ [TEST_MESSAGE_SUGGESTED_POST_DECLINED, None], [TEST_MESSAGE_SUGGESTED_POST_PAID, None], [TEST_MESSAGE_SUGGESTED_POST_REFUNDED, None], + [TEST_MESSAGE_MANAGED_BOT_CREATED, None], + [TEST_MESSAGE_POLL_OPTION_ADDED, None], + [TEST_MESSAGE_POLL_OPTION_DELETED, None], [TEST_MESSAGE_UNKNOWN, None], ] diff --git a/tests/test_api/test_types/test_poll_option_added.py b/tests/test_api/test_types/test_poll_option_added.py new file mode 100644 index 00000000..4a790b6e --- /dev/null +++ b/tests/test_api/test_types/test_poll_option_added.py @@ -0,0 +1,18 @@ +from aiogram.types import PollOptionAdded + + +class TestPollOptionAdded: + def test_required_fields(self): + obj = PollOptionAdded(option_persistent_id="opt1", option_text="Option A") + assert obj.option_persistent_id == "opt1" + assert obj.option_text == "Option A" + assert obj.poll_message is None + assert obj.option_text_entities is None + + def test_optional_fields(self): + obj = PollOptionAdded( + option_persistent_id="opt2", + option_text="Option B", + option_text_entities=[], + ) + assert obj.option_text_entities == [] diff --git a/tests/test_api/test_types/test_poll_option_deleted.py b/tests/test_api/test_types/test_poll_option_deleted.py new file mode 100644 index 00000000..853cedf3 --- /dev/null +++ b/tests/test_api/test_types/test_poll_option_deleted.py @@ -0,0 +1,18 @@ +from aiogram.types import PollOptionDeleted + + +class TestPollOptionDeleted: + def test_required_fields(self): + obj = PollOptionDeleted(option_persistent_id="opt1", option_text="Option A") + assert obj.option_persistent_id == "opt1" + assert obj.option_text == "Option A" + assert obj.poll_message is None + assert obj.option_text_entities is None + + def test_optional_fields(self): + obj = PollOptionDeleted( + option_persistent_id="opt2", + option_text="Option B", + option_text_entities=[], + ) + assert obj.option_text_entities == [] diff --git a/tests/test_api/test_types/test_prepared_keyboard_button.py b/tests/test_api/test_types/test_prepared_keyboard_button.py new file mode 100644 index 00000000..1a50d7a8 --- /dev/null +++ b/tests/test_api/test_types/test_prepared_keyboard_button.py @@ -0,0 +1,7 @@ +from aiogram.types import PreparedKeyboardButton + + +class TestPreparedKeyboardButton: + def test_fields(self): + obj = PreparedKeyboardButton(id="abc123") + assert obj.id == "abc123" diff --git a/tests/test_dispatcher/test_dispatcher.py b/tests/test_dispatcher/test_dispatcher.py index ca7d4092..014f44d3 100644 --- a/tests/test_dispatcher/test_dispatcher.py +++ b/tests/test_dispatcher/test_dispatcher.py @@ -30,6 +30,7 @@ from aiogram.types import ( ChatMemberUpdated, ChosenInlineResult, InlineQuery, + ManagedBotUpdated, Message, MessageReactionCountUpdated, MessageReactionUpdated, @@ -380,13 +381,14 @@ class TestDispatcher: id="poll id", question="Q?", options=[ - PollOption(text="A1", voter_count=2), - PollOption(text="A2", voter_count=3), + PollOption(persistent_id="1", text="A1", voter_count=2), + PollOption(persistent_id="2", text="A2", voter_count=3), ], is_closed=False, is_anonymous=False, type="quiz", allows_multiple_answers=False, + allows_revoting=False, total_voter_count=0, correct_option_id=1, ), @@ -402,6 +404,7 @@ class TestDispatcher: poll_id="poll id", user=User(id=42, is_bot=False, first_name="Test"), option_ids=[42], + option_persistent_ids=["1"], ), ), False, @@ -600,6 +603,18 @@ class TestDispatcher: False, True, ), + pytest.param( + "managed_bot", + Update( + update_id=42, + managed_bot=ManagedBotUpdated( + user=User(id=42, is_bot=False, first_name="Test"), + bot_user=User(id=100, is_bot=True, first_name="ManagedBot"), + ), + ), + False, + True, + ), ], ) async def test_listen_update( @@ -655,13 +670,14 @@ class TestDispatcher: id="poll id", question="Q?", options=[ - PollOption(text="A1", voter_count=2), - PollOption(text="A2", voter_count=3), + PollOption(persistent_id="1", text="A1", voter_count=2), + PollOption(persistent_id="2", text="A2", voter_count=3), ], is_closed=False, is_anonymous=False, type="quiz", allows_multiple_answers=False, + allows_revoting=False, total_voter_count=0, correct_option_id=0, ), diff --git a/tests/test_dispatcher/test_router.py b/tests/test_dispatcher/test_router.py index 1ac78480..2c9f7820 100644 --- a/tests/test_dispatcher/test_router.py +++ b/tests/test_dispatcher/test_router.py @@ -50,7 +50,9 @@ class TestRouter: def test_include_router_by_string_bad_type(self): router = Router() - with pytest.raises(ValueError, match=r"router should be instance of Router"): + with pytest.raises( + ValueError, match=r"router should be instance of Router not 'TestRouter'" + ): router.include_router(self) def test_set_parent_router_bad_type(self): @@ -71,6 +73,7 @@ class TestRouter: assert router.observers["shipping_query"] == router.shipping_query assert router.observers["pre_checkout_query"] == router.pre_checkout_query assert router.observers["poll"] == router.poll + assert router.observers["managed_bot"] == router.managed_bot async def test_emit_startup(self): router1 = Router() diff --git a/tests/test_filters/test_chat_member_updated.py b/tests/test_filters/test_chat_member_updated.py index c88b705e..4582f052 100644 --- a/tests/test_filters/test_chat_member_updated.py +++ b/tests/test_filters/test_chat_member_updated.py @@ -314,6 +314,7 @@ class TestChatMemberUpdatedStatusFilter: "can_send_polls": True, "can_send_other_messages": True, "can_add_web_page_previews": True, + "can_edit_tag": True, "can_post_stories": True, "can_edit_stories": True, "can_delete_stories": True, diff --git a/tests/test_filters/test_command.py b/tests/test_filters/test_command.py index ec79098a..2c01e151 100644 --- a/tests/test_filters/test_command.py +++ b/tests/test_filters/test_command.py @@ -85,6 +85,9 @@ class TestCommandFilter: False, ], ["/start test", CommandStart(), True], + ["/start", CommandStart(), True], + ["/start", CommandStart(deep_link=False), True], + ["/start test", CommandStart(deep_link=False), False], ["/start", CommandStart(deep_link=True), False], ["/start test", CommandStart(deep_link=True), True], ["/start test", CommandStart(deep_link=True, deep_link_encoded=True), False], @@ -175,7 +178,7 @@ class TestCommandStart: cmd = CommandStart() assert ( str(cmd) - == "CommandStart(ignore_case=False, ignore_mention=False, deep_link=False, deep_link_encoded=False)" + == "CommandStart(ignore_case=False, ignore_mention=False, deep_link_encoded=False)" ) diff --git a/tests/test_fsm/test_scene.py b/tests/test_fsm/test_scene.py index 3a9944b0..ba601325 100644 --- a/tests/test_fsm/test_scene.py +++ b/tests/test_fsm/test_scene.py @@ -253,6 +253,7 @@ class TestSceneHandlerWrapper: state_mock = AsyncMock(spec=FSMContext) scenes_mock = AsyncMock(spec=ScenesManager) + scenes_mock.data = {} event_update_mock = Update( update_id=42, message=Message( @@ -282,6 +283,7 @@ class TestSceneHandlerWrapper: state_mock = AsyncMock(spec=FSMContext) scenes_mock = AsyncMock(spec=ScenesManager) + scenes_mock.data = {} event_update_mock = Update( update_id=42, message=Message( diff --git a/tests/test_handler/test_poll.py b/tests/test_handler/test_poll.py index b070874d..9288b265 100644 --- a/tests/test_handler/test_poll.py +++ b/tests/test_handler/test_poll.py @@ -9,11 +9,12 @@ class TestShippingQueryHandler: event = Poll( id="query", question="Q?", - options=[PollOption(text="A1", voter_count=1)], + options=[PollOption(persistent_id="1", text="A1", voter_count=1)], is_closed=True, is_anonymous=False, type="quiz", allows_multiple_answers=False, + allows_revoting=False, total_voter_count=0, correct_option_id=0, ) diff --git a/tests/test_issues/test_1687_scene_goto_loses_middleware_data.py b/tests/test_issues/test_1687_scene_goto_loses_middleware_data.py new file mode 100644 index 00000000..d0433298 --- /dev/null +++ b/tests/test_issues/test_1687_scene_goto_loses_middleware_data.py @@ -0,0 +1,106 @@ +from collections.abc import Awaitable, Callable +from datetime import datetime +from typing import Any + +from aiogram import BaseMiddleware, Dispatcher +from aiogram.enums import ChatType +from aiogram.filters import CommandStart +from aiogram.fsm.scene import After, Scene, SceneRegistry, on +from aiogram.types import Chat, Message, TelegramObject, Update, User +from tests.mocked_bot import MockedBot + + +class TestContextMiddleware(BaseMiddleware): + async def __call__( + self, + handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]], + event: TelegramObject, + data: dict[str, Any], + ) -> Any: + data["test_context"] = "context from middleware" + return await handler(event, data) + + +class TargetScene(Scene, state="target"): + entered_with_context: str | None = None + + @on.message.enter() + async def on_enter(self, message: Message, test_context: str) -> None: + type(self).entered_with_context = test_context + + +class StartScene(Scene, state="start"): + @on.message.enter() + async def on_start(self, message: Message) -> None: + await self.wizard.goto(TargetScene) + + +class StartSceneWithAfter(Scene, state="start_with_after"): + @on.message(after=After.goto(TargetScene)) + async def goto_target_with_after(self, message: Message) -> None: + pass + + +async def test_scene_goto_preserves_message_middleware_data(bot: MockedBot) -> None: + dp = Dispatcher() + registry = SceneRegistry(dp) + registry.add(StartScene, TargetScene) + dp.message.register(StartScene.as_handler(), CommandStart()) + dp.message.middleware(TestContextMiddleware()) + + TargetScene.entered_with_context = None + + update = Update( + update_id=1, + message=Message( + message_id=1, + date=datetime.now(), + chat=Chat(id=42, type=ChatType.PRIVATE), + from_user=User(id=42, is_bot=False, first_name="Test"), + text="/start", + ), + ) + + await dp.feed_update(bot, update) + + assert TargetScene.entered_with_context == "context from middleware" + + +async def test_scene_after_goto_preserves_message_middleware_data(bot: MockedBot) -> None: + dp = Dispatcher() + registry = SceneRegistry(dp) + registry.add(StartSceneWithAfter, TargetScene) + dp.message.register(StartSceneWithAfter.as_handler(), CommandStart()) + dp.message.middleware(TestContextMiddleware()) + + TargetScene.entered_with_context = None + + await dp.feed_update( + bot, + Update( + update_id=1, + message=Message( + message_id=1, + date=datetime.now(), + chat=Chat(id=42, type=ChatType.PRIVATE), + from_user=User(id=42, is_bot=False, first_name="Test"), + text="/start", + ), + ), + ) + + await dp.feed_update( + bot, + Update( + update_id=2, + message=Message( + message_id=2, + date=datetime.now(), + chat=Chat(id=42, type=ChatType.PRIVATE), + from_user=User(id=42, is_bot=False, first_name="Test"), + text="go", + ), + ), + ) + + assert TargetScene.entered_with_context == "context from middleware" diff --git a/tests/test_utils/test_chat_member.py b/tests/test_utils/test_chat_member.py index 8a42600c..34f32d1c 100644 --- a/tests/test_utils/test_chat_member.py +++ b/tests/test_utils/test_chat_member.py @@ -70,6 +70,7 @@ CHAT_MEMBER_RESTRICTED = ChatMemberRestricted( can_send_polls=False, can_send_other_messages=False, can_add_web_page_previews=False, + can_edit_tag=False, can_change_info=False, can_invite_users=False, can_pin_messages=False, diff --git a/tests/test_utils/test_formatting.py b/tests/test_utils/test_formatting.py index ffaef31e..25a81ba2 100644 --- a/tests/test_utils/test_formatting.py +++ b/tests/test_utils/test_formatting.py @@ -1,3 +1,5 @@ +from datetime import datetime, timezone + import pytest from aiogram.enums import MessageEntityType @@ -9,6 +11,7 @@ from aiogram.utils.formatting import ( CashTag, Code, CustomEmoji, + DateTime, Email, ExpandableBlockQuote, HashTag, @@ -93,7 +96,7 @@ class TestNode: ], [ Pre("test", language="python"), - '
test
', + '
test
', ], [ TextLink("test", url="https://example.com"), @@ -115,6 +118,14 @@ class TestNode: ExpandableBlockQuote("test"), "
test
", ], + [ + DateTime("test", unix_time=42, date_time_format="yMd"), + 'test', + ], + [ + DateTime("test", unix_time=42), + 'test', + ], ], ) def test_render_plain_only(self, node: Text, result: str): @@ -358,6 +369,38 @@ class TestUtils: assert isinstance(node, Bold) assert node._body == ("test",) + def test_apply_entity_date_time(self): + node = _apply_entity( + MessageEntity( + type=MessageEntityType.DATE_TIME, + offset=0, + length=4, + unix_time=42, + date_time_format="yMd", + ), + "test", + ) + assert isinstance(node, DateTime) + assert node._body == ("test",) + assert node._params["unix_time"] == 42 + assert node._params["date_time_format"] == "yMd" + + def test_date_time_with_datetime_object(self): + dt = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + node = DateTime("test", unix_time=dt) + assert isinstance(node, DateTime) + assert node._params["unix_time"] == 1704067200 + + def test_date_time_with_datetime_and_format(self): + dt = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + node = DateTime("test", unix_time=dt, date_time_format="yMd") + assert node._params["unix_time"] == 1704067200 + assert node._params["date_time_format"] == "yMd" + + def test_date_time_as_markdown(self): + node = DateTime("test", unix_time=42, date_time_format="yMd") + assert node.as_markdown() == "![test](tg://time?unix=42&format=yMd)" + def test_as_line(self): node = as_line("test", "test", "test") assert isinstance(node, Text) diff --git a/tests/test_utils/test_keyboard.py b/tests/test_utils/test_keyboard.py index e80fed8e..b374e7ac 100644 --- a/tests/test_utils/test_keyboard.py +++ b/tests/test_utils/test_keyboard.py @@ -214,11 +214,63 @@ class TestKeyboardBuilder: "builder_type,kwargs,expected", [ [ReplyKeyboardBuilder, {"text": "test"}, KeyboardButton(text="test")], + [ + ReplyKeyboardBuilder, + {"text": "test", "icon_custom_emoji_id": "emoji-id"}, + KeyboardButton(text="test", icon_custom_emoji_id="emoji-id"), + ], + [ + ReplyKeyboardBuilder, + {"text": "test", "style": "success"}, + KeyboardButton(text="test", style="success"), + ], + [ + ReplyKeyboardBuilder, + {"text": "test", "icon_custom_emoji_id": "emoji-id", "style": "success"}, + KeyboardButton( + text="test", + icon_custom_emoji_id="emoji-id", + style="success", + ), + ], [ InlineKeyboardBuilder, {"text": "test", "callback_data": "callback"}, InlineKeyboardButton(text="test", callback_data="callback"), ], + [ + InlineKeyboardBuilder, + { + "text": "test", + "icon_custom_emoji_id": "emoji-id", + "callback_data": "callback", + }, + InlineKeyboardButton( + text="test", + icon_custom_emoji_id="emoji-id", + callback_data="callback", + ), + ], + [ + InlineKeyboardBuilder, + {"text": "test", "style": "primary", "callback_data": "callback"}, + InlineKeyboardButton(text="test", style="primary", callback_data="callback"), + ], + [ + InlineKeyboardBuilder, + { + "text": "test", + "icon_custom_emoji_id": "emoji-id", + "style": "primary", + "callback_data": "callback", + }, + InlineKeyboardButton( + text="test", + icon_custom_emoji_id="emoji-id", + style="primary", + callback_data="callback", + ), + ], [ InlineKeyboardBuilder, {"text": "test", "callback_data": MyCallback(value="test")}, @@ -242,6 +294,45 @@ class TestKeyboardBuilder: def test_as_markup(self, builder, expected): assert isinstance(builder.as_markup(), expected) + @pytest.mark.parametrize( + "builder,button_kwargs,icon_custom_emoji_id,style", + [ + [ + ReplyKeyboardBuilder(), + {"text": "test", "icon_custom_emoji_id": "emoji-id", "style": "success"}, + "emoji-id", + "success", + ], + [ + InlineKeyboardBuilder(), + { + "text": "test", + "icon_custom_emoji_id": "emoji-id", + "style": "primary", + "callback_data": "callback", + }, + "emoji-id", + "primary", + ], + ], + ) + def test_as_markup_preserves_icon_and_style( + self, + builder, + button_kwargs, + icon_custom_emoji_id, + style, + ): + builder.button(**button_kwargs) + markup = builder.as_markup() + if isinstance(markup, ReplyKeyboardMarkup): + button = markup.keyboard[0][0] + else: + button = markup.inline_keyboard[0][0] + + assert button.icon_custom_emoji_id == icon_custom_emoji_id + assert button.style == style + @pytest.mark.parametrize( "markup,builder_type", [ diff --git a/tests/test_utils/test_text_decorations.py b/tests/test_utils/test_text_decorations.py index b4ccb5e8..4ebd5f3a 100644 --- a/tests/test_utils/test_text_decorations.py +++ b/tests/test_utils/test_text_decorations.py @@ -1,3 +1,5 @@ +from datetime import datetime, timezone + import pytest from aiogram.types import MessageEntity, User @@ -25,7 +27,7 @@ class TestTextDecoration: [ html_decoration, MessageEntity(type="pre", offset=0, length=5, language="python"), - '
test
', + '
test
', ], [html_decoration, MessageEntity(type="underline", offset=0, length=5), "test"], [ @@ -74,6 +76,17 @@ class TestTextDecoration: MessageEntity(type="expandable_blockquote", offset=0, length=5), "
test
", ], + [ + html_decoration, + MessageEntity( + type="date_time", + offset=0, + length=5, + unix_time=42, + date_time_format="yMd", + ), + 'test', + ], [markdown_decoration, MessageEntity(type="bold", offset=0, length=5), "*test*"], [markdown_decoration, MessageEntity(type="italic", offset=0, length=5), "_\rtest_\r"], [markdown_decoration, MessageEntity(type="code", offset=0, length=5), "`test`"], @@ -102,7 +115,7 @@ class TestTextDecoration: [ markdown_decoration, MessageEntity(type="custom_emoji", offset=0, length=5, custom_emoji_id="42"), - "![test](tg://emoji?id=42)", + "![test](tg://emoji?emoji_id=42)", ], [ markdown_decoration, @@ -124,6 +137,27 @@ class TestTextDecoration: MessageEntity(type="expandable_blockquote", offset=0, length=5), ">test||", ], + [ + markdown_decoration, + MessageEntity( + type="date_time", + offset=0, + length=5, + unix_time=42, + date_time_format="yMd", + ), + "![test](tg://time?unix=42&format=yMd)", + ], + [ + html_decoration, + MessageEntity(type="date_time", offset=0, length=5, unix_time=42), + 'test', + ], + [ + markdown_decoration, + MessageEntity(type="date_time", offset=0, length=5, unix_time=42), + "![test](tg://time?unix=42)", + ], ], ) def test_apply_single_entity( @@ -131,6 +165,38 @@ class TestTextDecoration: ): assert decorator.apply_entity(entity, "test") == result + @pytest.mark.parametrize( + "decorator,date_time_format,expected", + [ + ( + html_decoration, + None, + 'test', + ), + ( + html_decoration, + "yMd", + 'test', + ), + ( + markdown_decoration, + None, + "![test](tg://time?unix=1704067200)", + ), + ( + markdown_decoration, + "yMd", + "![test](tg://time?unix=1704067200&format=yMd)", + ), + ], + ) + def test_date_time_with_datetime_object( + self, decorator: TextDecoration, date_time_format: str | None, expected: str + ): + dt = datetime(2024, 1, 1, 0, 0, 0, tzinfo=timezone.utc) + result = decorator.date_time("test", unix_time=dt, date_time_format=date_time_format) + assert result == expected + def test_unknown_apply_entity(self): assert ( html_decoration.apply_entity( @@ -296,6 +362,22 @@ class TestTextDecoration: ], "test@example.com", ], + [ + html_decoration, + "test", + [MessageEntity(type="date_time", offset=0, length=4, unix_time=42)], + 'test', + ], + [ + html_decoration, + "test", + [ + MessageEntity( + type="date_time", offset=0, length=4, unix_time=42, date_time_format="yMd" + ) + ], + 'test', + ], ], ) def test_unparse(