`_\n",
"name": "reply_markup"
}
],
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/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..faa2aee0 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.5",
+ "release_date": "2026-03-01"
},
"items": [
{
@@ -1119,6 +1119,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.",
@@ -1249,9 +1257,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
},
@@ -1898,8 +1906,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 +1984,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 +2037,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"
@@ -6332,6 +6356,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 +6652,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 +6686,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 +6728,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 +6832,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 +7088,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",
@@ -12959,8 +13031,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 +13147,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 +13682,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 +13728,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",
@@ -16631,8 +16745,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 +18841,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
},
@@ -22865,9 +22979,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/Message/entity.json b/.butcher/types/Message/entity.json
index 594442fc..7225d275 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.",
@@ -197,9 +205,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
},
@@ -846,8 +854,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/CHANGES/1780.misc.rst b/CHANGES/1780.misc.rst
new file mode 100644
index 00000000..6037a9d7
--- /dev/null
+++ b/CHANGES/1780.misc.rst
@@ -0,0 +1,15 @@
+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
diff --git a/README.rst b/README.rst
index c3b660d3..a04ff797 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.5 `_ 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..00647de5 100644
--- a/aiogram/__meta__.py
+++ b/aiogram/__meta__.py
@@ -1,2 +1,2 @@
-__version__ = "3.25.0"
-__api_version__ = "9.4"
+__version__ = "3.26.0"
+__api_version__ = "9.5"
diff --git a/aiogram/client/bot.py b/aiogram/client/bot.py
index d10429f6..195c06dd 100644
--- a/aiogram/client/bot.py
+++ b/aiogram/client/bot.py
@@ -144,6 +144,7 @@ from ..methods import (
SetBusinessAccountUsername,
SetChatAdministratorCustomTitle,
SetChatDescription,
+ SetChatMemberTag,
SetChatMenuButton,
SetChatPermissions,
SetChatPhoto,
@@ -2021,6 +2022,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 +2048,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 +2072,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)
@@ -5560,7 +5564,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 +5618,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 +5827,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 +5912,29 @@ 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)
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/methods/__init__.py b/aiogram/methods/__init__.py
index 786e53e5..b4c93d6c 100644
--- a/aiogram/methods/__init__.py
+++ b/aiogram/methods/__init__.py
@@ -126,6 +126,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
@@ -295,6 +296,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/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/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_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/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/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_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_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/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/message.py b/aiogram/types/message.py
index b35fe0fb..1b7f9677 100644
--- a/aiogram/types/message.py
+++ b/aiogram/types/message.py
@@ -157,6 +157,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
@@ -186,7 +188,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
@@ -348,7 +350,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 +403,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,
@@ -520,6 +523,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,
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/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/text_decorations.py b/aiogram/utils/text_decorations.py
index 00762c0a..b8edead2 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}{tag}>"
+
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}{self.BOLD_TAG}>"
+ return self._tag(self.BOLD_TAG, value)
def italic(self, value: str) -> str:
- return f"<{self.ITALIC_TAG}>{value}{self.ITALIC_TAG}>"
+ 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}{self.UNDERLINE_TAG}>"
+ return self._tag(self.UNDERLINE_TAG, value)
def strikethrough(self, value: str) -> str:
- return f"<{self.STRIKETHROUGH_TAG}>{value}{self.STRIKETHROUGH_TAG}>"
+ return self._tag(self.STRIKETHROUGH_TAG, value)
def spoiler(self, value: str) -> str:
- return f"<{self.SPOILER_TAG}>{value}{self.SPOILER_TAG}>"
+ 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}{self.EMOJI_TAG}>'
+ 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}{self.BLOCKQUOTE_TAG}>"
+ return self._tag(self.BLOCKQUOTE_TAG, value)
def expandable_blockquote(self, value: str) -> str:
- return f"<{self.BLOCKQUOTE_TAG} expandable>{value}{self.BLOCKQUOTE_TAG}>"
+ 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_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..f83a4f10 100644
--- a/docs/api/methods/index.rst
+++ b/docs/api/methods/index.rst
@@ -127,6 +127,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
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..79090f6f
--- /dev/null
+++ b/docs/api/methods/set_chat_member_tag.rst
@@ -0,0 +1,45 @@
+################
+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(...)
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_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_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_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_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..8efc1598 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"),
@@ -105,7 +108,7 @@ class TestNode:
],
[
CustomEmoji("test", custom_emoji_id="42"),
- 'test',
+ 'test',
],
[
BlockQuote("test"),
@@ -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() == ""
+
def test_as_line(self):
node = as_line("test", "test", "test")
assert isinstance(node, Text)
diff --git a/tests/test_utils/test_text_decorations.py b/tests/test_utils/test_text_decorations.py
index b4ccb5e8..6325edb8 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"],
[
@@ -57,7 +59,7 @@ class TestTextDecoration:
[
html_decoration,
MessageEntity(type="custom_emoji", offset=0, length=5, custom_emoji_id="42"),
- 'test',
+ 'test',
],
[
html_decoration,
@@ -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"),
- "",
+ "",
],
[
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",
+ ),
+ "",
+ ],
+ [
+ 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),
+ "",
+ ],
],
)
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,
+ "",
+ ),
+ (
+ markdown_decoration,
+ "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(