Added support for Telegram Bot API 9.5 (#1780)

* Update API methods and types for Telegram Bot API 9.5

* Draft: follow-up for Bot API 9.5 (#1780) (#1781)

* Add set_chat_member_tag shortcut coverage

* Add set_member_tag shortcut tests and align decoration expectations

* Fix follow-up test coverage for sender_tag and can_edit_tag

* Add changelog fragment for PR 1781

* Align changelog with base PR #1780

* Expand 1780 changelog to cover base and follow-up scope

* Treat sender_tag as metadata, not message content type

---------

Co-authored-by: Latand <latand@users.noreply.github.com>
Co-authored-by: Codex Agent <codex@openclaw.local>

* Add tests for date_time formatting with Unix time and datetime objects

* Update changelog with Telegram Bot API 9.5 changes

---------

Co-authored-by: Kostiantyn Kriuchkov <36363097+Latand@users.noreply.github.com>
Co-authored-by: Latand <latand@users.noreply.github.com>
Co-authored-by: Codex Agent <codex@openclaw.local>
This commit is contained in:
Alex Root Junior 2026-03-03 01:19:11 +02:00 committed by GitHub
parent 73710acb4c
commit f68c24d620
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 872 additions and 79 deletions

View file

@ -1 +1 @@
9.4 9.5

View file

@ -39,6 +39,7 @@ extract:
- reply_to_story - reply_to_story
- business_connection_id - business_connection_id
- sender_business_bot - sender_business_bot
- sender_tag
- is_from_offline - is_from_offline
- has_media_spoiler - has_media_spoiler
- effect_id - effect_id

View file

@ -47,8 +47,8 @@
"type": "InlineKeyboardMarkup", "type": "InlineKeyboardMarkup",
"required": false, "required": false,
"description": "A JSON-serialized object for the new inline keyboard for the message", "description": "A JSON-serialized object for the new inline keyboard for the message",
"html_description": "<td>A JSON-serialized object for the new inline keyboard for the message</td>", "html_description": "<td>A JSON-serialized object for the new <a href=\"/bots/features#inline-keyboards\">inline keyboard</a> for the message</td>",
"rst_description": "A JSON-serialized object for the new inline keyboard for the message\n", "rst_description": "A JSON-serialized object for the new `inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_ for the message\n",
"name": "reply_markup" "name": "reply_markup"
} }
], ],

View file

@ -154,6 +154,14 @@
"html_description": "<td>Pass <em>True</em> if the administrator can manage direct messages within the channel and decline suggested posts; for channels only</td>", "html_description": "<td>Pass <em>True</em> if the administrator can manage direct messages within the channel and decline suggested posts; for channels only</td>",
"rst_description": "Pass :code:`True` if the administrator can manage direct messages within the channel and decline suggested posts; for channels only\n", "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" "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": "<td>Pass <em>True</em> if the administrator can edit the tags of regular members; for groups and supergroups only</td>",
"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" "category": "methods"

View file

@ -71,8 +71,8 @@
"type": "InlineKeyboardMarkup", "type": "InlineKeyboardMarkup",
"required": false, "required": false,
"description": "A JSON-serialized object for an inline keyboard", "description": "A JSON-serialized object for an inline keyboard",
"html_description": "<td>A JSON-serialized object for an inline keyboard</td>", "html_description": "<td>A JSON-serialized object for an <a href=\"/bots/features#inline-keyboards\">inline keyboard</a></td>",
"rst_description": "A JSON-serialized object for an inline keyboard\n", "rst_description": "A JSON-serialized object for an `inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_\n",
"name": "reply_markup" "name": "reply_markup"
} }
], ],

View file

@ -7,9 +7,9 @@
"object": { "object": {
"anchor": "sendmessagedraft", "anchor": "sendmessagedraft",
"name": "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.", "description": "Use this method to stream a partial message to a user while the message is being generated. Returns True on success.",
"html_description": "<p>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 <em>True</em> on success.</p>", "html_description": "<p>Use this method to stream a partial message to a user while the message is being generated. Returns <em>True</em> on success.</p>",
"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.", "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": [ "annotations": [
{ {
"type": "Integer", "type": "Integer",

View file

@ -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": "<p>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 <em>can_manage_tags</em> administrator right. Returns <em>True</em> on success.</p>",
"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": "<td>Unique identifier for the target chat or username of the target supergroup (in the format <code>@supergroupusername</code>)</td>",
"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": "<td>Unique identifier of the target user</td>",
"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": "<td>New tag for the member; 0-16 characters, emoji are not allowed</td>",
"rst_description": "New tag for the member; 0-16 characters, emoji are not allowed\n",
"name": "tag"
}
],
"category": "methods"
}
}

View file

@ -1,7 +1,7 @@
{ {
"api": { "api": {
"version": "9.4", "version": "9.5",
"release_date": "2026-02-09" "release_date": "2026-03-01"
}, },
"items": [ "items": [
{ {
@ -1119,6 +1119,14 @@
"name": "sender_business_bot", "name": "sender_business_bot",
"required": false "required": false
}, },
{
"type": "String",
"description": "Tag or custom title of the sender of the message; for supergroups only",
"html_description": "<td><em>Optional</em>. Tag or custom title of the sender of the message; for supergroups only</td>",
"rst_description": "*Optional*. Tag or custom title of the sender of the message; for supergroups only\n",
"name": "sender_tag",
"required": false
},
{ {
"type": "Integer", "type": "Integer",
"description": "Date the message was sent in Unix time. It is always a positive number, representing a valid date.", "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", "type": "String",
"description": "The unique identifier of a media message group this message belongs to", "description": "The unique identifier inside this chat of a media message group this message belongs to",
"html_description": "<td><em>Optional</em>. The unique identifier of a media message group this message belongs to</td>", "html_description": "<td><em>Optional</em>. The unique identifier inside this chat of a media message group this message belongs to</td>",
"rst_description": "*Optional*. The unique identifier of a media message group this message belongs to\n", "rst_description": "*Optional*. The unique identifier inside this chat of a media message group this message belongs to\n",
"name": "media_group_id", "name": "media_group_id",
"required": false "required": false
}, },
@ -1898,8 +1906,8 @@
{ {
"type": "InlineKeyboardMarkup", "type": "InlineKeyboardMarkup",
"description": "Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", "description": "Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.",
"html_description": "<td><em>Optional</em>. Inline keyboard attached to the message. <code>login_url</code> buttons are represented as ordinary <code>url</code> buttons.</td>", "html_description": "<td><em>Optional</em>. <a href=\"/bots/features#inline-keyboards\">Inline keyboard</a> attached to the message. <code>login_url</code> buttons are represented as ordinary <code>url</code> buttons.</td>",
"rst_description": "*Optional*. Inline keyboard attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n", "rst_description": "*Optional*. `Inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_ attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n",
"name": "reply_markup", "name": "reply_markup",
"required": false "required": false
} }
@ -1976,9 +1984,9 @@
"annotations": [ "annotations": [
{ {
"type": "String", "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)", "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": "<td>Type of the entity. Currently, can be &#8220;mention&#8221; (<code>@username</code>), &#8220;hashtag&#8221; (<code>#hashtag</code> or <code>#hashtag@chatusername</code>), &#8220;cashtag&#8221; (<code>$USD</code> or <code>$USD@chatusername</code>), &#8220;bot_command&#8221; (<code>/start@jobs_bot</code>), &#8220;url&#8221; (<code>https://telegram.org</code>), &#8220;email&#8221; (<code>do-not-reply@telegram.org</code>), &#8220;phone_number&#8221; (<code>+1-212-555-0123</code>), &#8220;bold&#8221; (<strong>bold text</strong>), &#8220;italic&#8221; (<em>italic text</em>), &#8220;underline&#8221; (underlined text), &#8220;strikethrough&#8221; (strikethrough text), &#8220;spoiler&#8221; (spoiler message), &#8220;blockquote&#8221; (block quotation), &#8220;expandable_blockquote&#8221; (collapsed-by-default block quotation), &#8220;code&#8221; (monowidth string), &#8220;pre&#8221; (monowidth block), &#8220;text_link&#8221; (for clickable text URLs), &#8220;text_mention&#8221; (for users <a href=\"https://telegram.org/blog/edit#new-mentions\">without usernames</a>), &#8220;custom_emoji&#8221; (for inline custom emoji stickers)</td>", "html_description": "<td>Type of the entity. Currently, can be &#8220;mention&#8221; (<code>@username</code>), &#8220;hashtag&#8221; (<code>#hashtag</code> or <code>#hashtag@chatusername</code>), &#8220;cashtag&#8221; (<code>$USD</code> or <code>$USD@chatusername</code>), &#8220;bot_command&#8221; (<code>/start@jobs_bot</code>), &#8220;url&#8221; (<code>https://telegram.org</code>), &#8220;email&#8221; (<code>do-not-reply@telegram.org</code>), &#8220;phone_number&#8221; (<code>+1-212-555-0123</code>), &#8220;bold&#8221; (<strong>bold text</strong>), &#8220;italic&#8221; (<em>italic text</em>), &#8220;underline&#8221; (underlined text), &#8220;strikethrough&#8221; (strikethrough text), &#8220;spoiler&#8221; (spoiler message), &#8220;blockquote&#8221; (block quotation), &#8220;expandable_blockquote&#8221; (collapsed-by-default block quotation), &#8220;code&#8221; (monowidth string), &#8220;pre&#8221; (monowidth block), &#8220;text_link&#8221; (for clickable text URLs), &#8220;text_mention&#8221; (for users <a href=\"https://telegram.org/blog/edit#new-mentions\">without usernames</a>), &#8220;custom_emoji&#8221; (for inline custom emoji stickers), or &#8220;date_time&#8221; (for formatted date and time)</td>",
"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 <https://telegram.org/blog/edit#new-mentions>`_), 'custom_emoji' (for inline custom emoji stickers)\n", "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 <https://telegram.org/blog/edit#new-mentions>`_), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)\n",
"name": "type", "name": "type",
"required": true "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", "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", "name": "custom_emoji_id",
"required": false "required": false
},
{
"type": "Integer",
"description": "For 'date_time' only, the Unix time associated with the entity",
"html_description": "<td><em>Optional</em>. For &#8220;date_time&#8221; only, the Unix time associated with the entity</td>",
"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": "<td><em>Optional</em>. For &#8220;date_time&#8221; only, the string that defines the formatting of the date and time. See <a href=\"#date-time-entity-formatting\">date-time entity formatting</a> for more details.</td>",
"rst_description": "*Optional*. For 'date_time' only, the string that defines the formatting of the date and time. See `date-time entity formatting <https://core.telegram.org/bots/api#date-time-entity-formatting>`_ for more details.\n",
"name": "date_time_format",
"required": false
} }
], ],
"category": "types" "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", "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", "name": "can_manage_direct_messages",
"required": false "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": "<td><em>Optional</em>. <em>True</em>, 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.</td>",
"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" "category": "types"
@ -6620,6 +6652,14 @@
"name": "can_manage_direct_messages", "name": "can_manage_direct_messages",
"required": false "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": "<td><em>Optional</em>. <em>True</em>, 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.</td>",
"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", "type": "String",
"description": "Custom title for this user", "description": "Custom title for this user",
@ -6646,6 +6686,14 @@
"name": "status", "name": "status",
"required": true "required": true
}, },
{
"type": "String",
"description": "Tag of the member",
"html_description": "<td><em>Optional</em>. Tag of the member</td>",
"rst_description": "*Optional*. Tag of the member\n",
"name": "tag",
"required": false
},
{ {
"type": "User", "type": "User",
"description": "Information about the user", "description": "Information about the user",
@ -6680,6 +6728,14 @@
"name": "status", "name": "status",
"required": true "required": true
}, },
{
"type": "String",
"description": "Tag of the member",
"html_description": "<td><em>Optional</em>. Tag of the member</td>",
"rst_description": "*Optional*. Tag of the member\n",
"name": "tag",
"required": false
},
{ {
"type": "User", "type": "User",
"description": "Information about the user", "description": "Information about the user",
@ -6776,6 +6832,14 @@
"name": "can_add_web_page_previews", "name": "can_add_web_page_previews",
"required": true "required": true
}, },
{
"type": "Boolean",
"description": "True, if the user is allowed to edit their own tag",
"html_description": "<td><em>True</em>, if the user is allowed to edit their own tag</td>",
"rst_description": ":code:`True`, if the user is allowed to edit their own tag\n",
"name": "can_edit_tag",
"required": true
},
{ {
"type": "Boolean", "type": "Boolean",
"description": "True, if the user is allowed to change the chat title, photo and other settings", "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", "name": "can_add_web_page_previews",
"required": false "required": false
}, },
{
"type": "Boolean",
"description": "True, if the user is allowed to edit their own tag",
"html_description": "<td><em>Optional</em>. <em>True</em>, if the user is allowed to edit their own tag</td>",
"rst_description": "*Optional*. :code:`True`, if the user is allowed to edit their own tag\n",
"name": "can_edit_tag",
"required": false
},
{ {
"type": "Boolean", "type": "Boolean",
"description": "True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups", "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", "type": "InlineKeyboardMarkup",
"required": false, "required": false,
"description": "A JSON-serialized object for an inline keyboard", "description": "A JSON-serialized object for an inline keyboard",
"html_description": "<td>A JSON-serialized object for an inline keyboard</td>", "html_description": "<td>A JSON-serialized object for an <a href=\"/bots/features#inline-keyboards\">inline keyboard</a></td>",
"rst_description": "A JSON-serialized object for an inline keyboard\n", "rst_description": "A JSON-serialized object for an `inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_\n",
"name": "reply_markup" "name": "reply_markup"
} }
], ],
@ -13075,9 +13147,9 @@
{ {
"anchor": "sendmessagedraft", "anchor": "sendmessagedraft",
"name": "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.", "description": "Use this method to stream a partial message to a user while the message is being generated. Returns True on success.",
"html_description": "<p>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 <em>True</em> on success.</p>", "html_description": "<p>Use this method to stream a partial message to a user while the message is being generated. Returns <em>True</em> on success.</p>",
"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.", "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": [ "annotations": [
{ {
"type": "Integer", "type": "Integer",
@ -13610,6 +13682,14 @@
"html_description": "<td>Pass <em>True</em> if the administrator can manage direct messages within the channel and decline suggested posts; for channels only</td>", "html_description": "<td>Pass <em>True</em> if the administrator can manage direct messages within the channel and decline suggested posts; for channels only</td>",
"rst_description": "Pass :code:`True` if the administrator can manage direct messages within the channel and decline suggested posts; for channels only\n", "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" "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": "<td>Pass <em>True</em> if the administrator can edit the tags of regular members; for groups and supergroups only</td>",
"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" "category": "methods"
@ -13648,6 +13728,40 @@
], ],
"category": "methods" "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": "<p>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 <em>can_manage_tags</em> administrator right. Returns <em>True</em> on success.</p>",
"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": "<td>Unique identifier for the target chat or username of the target supergroup (in the format <code>@supergroupusername</code>)</td>",
"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": "<td>Unique identifier of the target user</td>",
"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": "<td>New tag for the member; 0-16 characters, emoji are not allowed</td>",
"rst_description": "New tag for the member; 0-16 characters, emoji are not allowed\n",
"name": "tag"
}
],
"category": "methods"
},
{ {
"anchor": "banchatsenderchat", "anchor": "banchatsenderchat",
"name": "banChatSenderChat", "name": "banChatSenderChat",
@ -16631,8 +16745,8 @@
"type": "InlineKeyboardMarkup", "type": "InlineKeyboardMarkup",
"required": false, "required": false,
"description": "A JSON-serialized object for the new inline keyboard for the message", "description": "A JSON-serialized object for the new inline keyboard for the message",
"html_description": "<td>A JSON-serialized object for the new inline keyboard for the message</td>", "html_description": "<td>A JSON-serialized object for the new <a href=\"/bots/features#inline-keyboards\">inline keyboard</a> for the message</td>",
"rst_description": "A JSON-serialized object for the new inline keyboard for the message\n", "rst_description": "A JSON-serialized object for the new `inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_ for the message\n",
"name": "reply_markup" "name": "reply_markup"
} }
], ],
@ -18727,8 +18841,8 @@
{ {
"type": "InlineKeyboardMarkup", "type": "InlineKeyboardMarkup",
"description": "Inline keyboard attached to the message", "description": "Inline keyboard attached to the message",
"html_description": "<td><em>Optional</em>. Inline keyboard attached to the message</td>", "html_description": "<td><em>Optional</em>. <a href=\"/bots/features#inline-keyboards\">Inline keyboard</a> attached to the message</td>",
"rst_description": "*Optional*. Inline keyboard attached to the message\n", "rst_description": "*Optional*. `Inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_ attached to the message\n",
"name": "reply_markup", "name": "reply_markup",
"required": false "required": false
}, },
@ -22865,9 +22979,9 @@
{ {
"anchor": "gamehighscore", "anchor": "gamehighscore",
"name": "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-", "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": "<p>This object represents one row of the high scores table for a game.</p><p>And that's about all we've got for now.<br/>\nIf you've got any questions, please check out our <a href=\"/bots/faq\"><strong>Bot FAQ &#187;</strong></a><br/>\n-</p>", "html_description": "<p>This object represents one row of the high scores table for a game.</p><p>And that's about all we've got for now.<br/>\nIf you've got any questions, please check out our <a href=\"/bots/faq\"><strong>Bot FAQ &#187;</strong></a></p>",
"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 <https://core.telegram.org/bots/faq>`_ **Bot FAQ »**\n\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 <https://core.telegram.org/bots/faq>`_ **Bot FAQ »**",
"annotations": [ "annotations": [
{ {
"type": "Integer", "type": "Integer",

View file

@ -71,6 +71,10 @@ set_administrator_custom_title:
method: setChatAdministratorCustomTitle method: setChatAdministratorCustomTitle
fill: *self fill: *self
set_member_tag:
method: setChatMemberTag
fill: *self
set_permissions: set_permissions:
method: setChatPermissions method: setChatPermissions
fill: *self fill: *self

View file

@ -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", "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", "name": "can_manage_direct_messages",
"required": false "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": "<td><em>Optional</em>. <em>True</em>, 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.</td>",
"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" "category": "types"

View file

@ -163,6 +163,14 @@
"name": "can_manage_direct_messages", "name": "can_manage_direct_messages",
"required": false "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": "<td><em>Optional</em>. <em>True</em>, 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.</td>",
"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", "type": "String",
"description": "Custom title for this user", "description": "Custom title for this user",

View file

@ -19,6 +19,14 @@
"name": "status", "name": "status",
"required": true "required": true
}, },
{
"type": "String",
"description": "Tag of the member",
"html_description": "<td><em>Optional</em>. Tag of the member</td>",
"rst_description": "*Optional*. Tag of the member\n",
"name": "tag",
"required": false
},
{ {
"type": "User", "type": "User",
"description": "Information about the user", "description": "Information about the user",

View file

@ -19,6 +19,14 @@
"name": "status", "name": "status",
"required": true "required": true
}, },
{
"type": "String",
"description": "Tag of the member",
"html_description": "<td><em>Optional</em>. Tag of the member</td>",
"rst_description": "*Optional*. Tag of the member\n",
"name": "tag",
"required": false
},
{ {
"type": "User", "type": "User",
"description": "Information about the user", "description": "Information about the user",
@ -115,6 +123,14 @@
"name": "can_add_web_page_previews", "name": "can_add_web_page_previews",
"required": true "required": true
}, },
{
"type": "Boolean",
"description": "True, if the user is allowed to edit their own tag",
"html_description": "<td><em>True</em>, if the user is allowed to edit their own tag</td>",
"rst_description": ":code:`True`, if the user is allowed to edit their own tag\n",
"name": "can_edit_tag",
"required": true
},
{ {
"type": "Boolean", "type": "Boolean",
"description": "True, if the user is allowed to change the chat title, photo and other settings", "description": "True, if the user is allowed to change the chat title, photo and other settings",

View file

@ -91,6 +91,14 @@
"name": "can_add_web_page_previews", "name": "can_add_web_page_previews",
"required": false "required": false
}, },
{
"type": "Boolean",
"description": "True, if the user is allowed to edit their own tag",
"html_description": "<td><em>Optional</em>. <em>True</em>, if the user is allowed to edit their own tag</td>",
"rst_description": "*Optional*. :code:`True`, if the user is allowed to edit their own tag\n",
"name": "can_edit_tag",
"required": false
},
{ {
"type": "Boolean", "type": "Boolean",
"description": "True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups", "description": "True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups",

View file

@ -7,9 +7,9 @@
"object": { "object": {
"anchor": "gamehighscore", "anchor": "gamehighscore",
"name": "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-", "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": "<p>This object represents one row of the high scores table for a game.</p><p>And that's about all we've got for now.<br/>\nIf you've got any questions, please check out our <a href=\"/bots/faq\"><strong>Bot FAQ &#187;</strong></a><br/>\n-</p>", "html_description": "<p>This object represents one row of the high scores table for a game.</p><p>And that's about all we've got for now.<br/>\nIf you've got any questions, please check out our <a href=\"/bots/faq\"><strong>Bot FAQ &#187;</strong></a></p>",
"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 <https://core.telegram.org/bots/faq>`_ **Bot FAQ »**\n\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 <https://core.telegram.org/bots/faq>`_ **Bot FAQ »**",
"annotations": [ "annotations": [
{ {
"type": "Integer", "type": "Integer",

View file

@ -86,8 +86,8 @@
{ {
"type": "InlineKeyboardMarkup", "type": "InlineKeyboardMarkup",
"description": "Inline keyboard attached to the message", "description": "Inline keyboard attached to the message",
"html_description": "<td><em>Optional</em>. Inline keyboard attached to the message</td>", "html_description": "<td><em>Optional</em>. <a href=\"/bots/features#inline-keyboards\">Inline keyboard</a> attached to the message</td>",
"rst_description": "*Optional*. Inline keyboard attached to the message\n", "rst_description": "*Optional*. `Inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_ attached to the message\n",
"name": "reply_markup", "name": "reply_markup",
"required": false "required": false
}, },

View file

@ -67,6 +67,14 @@
"name": "sender_business_bot", "name": "sender_business_bot",
"required": false "required": false
}, },
{
"type": "String",
"description": "Tag or custom title of the sender of the message; for supergroups only",
"html_description": "<td><em>Optional</em>. Tag or custom title of the sender of the message; for supergroups only</td>",
"rst_description": "*Optional*. Tag or custom title of the sender of the message; for supergroups only\n",
"name": "sender_tag",
"required": false
},
{ {
"type": "Integer", "type": "Integer",
"description": "Date the message was sent in Unix time. It is always a positive number, representing a valid date.", "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", "type": "String",
"description": "The unique identifier of a media message group this message belongs to", "description": "The unique identifier inside this chat of a media message group this message belongs to",
"html_description": "<td><em>Optional</em>. The unique identifier of a media message group this message belongs to</td>", "html_description": "<td><em>Optional</em>. The unique identifier inside this chat of a media message group this message belongs to</td>",
"rst_description": "*Optional*. The unique identifier of a media message group this message belongs to\n", "rst_description": "*Optional*. The unique identifier inside this chat of a media message group this message belongs to\n",
"name": "media_group_id", "name": "media_group_id",
"required": false "required": false
}, },
@ -846,8 +854,8 @@
{ {
"type": "InlineKeyboardMarkup", "type": "InlineKeyboardMarkup",
"description": "Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.", "description": "Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.",
"html_description": "<td><em>Optional</em>. Inline keyboard attached to the message. <code>login_url</code> buttons are represented as ordinary <code>url</code> buttons.</td>", "html_description": "<td><em>Optional</em>. <a href=\"/bots/features#inline-keyboards\">Inline keyboard</a> attached to the message. <code>login_url</code> buttons are represented as ordinary <code>url</code> buttons.</td>",
"rst_description": "*Optional*. Inline keyboard attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n", "rst_description": "*Optional*. `Inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_ attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.\n",
"name": "reply_markup", "name": "reply_markup",
"required": false "required": false
}, },

View file

@ -13,9 +13,9 @@
"annotations": [ "annotations": [
{ {
"type": "String", "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)", "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": "<td>Type of the entity. Currently, can be &#8220;mention&#8221; (<code>@username</code>), &#8220;hashtag&#8221; (<code>#hashtag</code> or <code>#hashtag@chatusername</code>), &#8220;cashtag&#8221; (<code>$USD</code> or <code>$USD@chatusername</code>), &#8220;bot_command&#8221; (<code>/start@jobs_bot</code>), &#8220;url&#8221; (<code>https://telegram.org</code>), &#8220;email&#8221; (<code>do-not-reply@telegram.org</code>), &#8220;phone_number&#8221; (<code>+1-212-555-0123</code>), &#8220;bold&#8221; (<strong>bold text</strong>), &#8220;italic&#8221; (<em>italic text</em>), &#8220;underline&#8221; (underlined text), &#8220;strikethrough&#8221; (strikethrough text), &#8220;spoiler&#8221; (spoiler message), &#8220;blockquote&#8221; (block quotation), &#8220;expandable_blockquote&#8221; (collapsed-by-default block quotation), &#8220;code&#8221; (monowidth string), &#8220;pre&#8221; (monowidth block), &#8220;text_link&#8221; (for clickable text URLs), &#8220;text_mention&#8221; (for users <a href=\"https://telegram.org/blog/edit#new-mentions\">without usernames</a>), &#8220;custom_emoji&#8221; (for inline custom emoji stickers)</td>", "html_description": "<td>Type of the entity. Currently, can be &#8220;mention&#8221; (<code>@username</code>), &#8220;hashtag&#8221; (<code>#hashtag</code> or <code>#hashtag@chatusername</code>), &#8220;cashtag&#8221; (<code>$USD</code> or <code>$USD@chatusername</code>), &#8220;bot_command&#8221; (<code>/start@jobs_bot</code>), &#8220;url&#8221; (<code>https://telegram.org</code>), &#8220;email&#8221; (<code>do-not-reply@telegram.org</code>), &#8220;phone_number&#8221; (<code>+1-212-555-0123</code>), &#8220;bold&#8221; (<strong>bold text</strong>), &#8220;italic&#8221; (<em>italic text</em>), &#8220;underline&#8221; (underlined text), &#8220;strikethrough&#8221; (strikethrough text), &#8220;spoiler&#8221; (spoiler message), &#8220;blockquote&#8221; (block quotation), &#8220;expandable_blockquote&#8221; (collapsed-by-default block quotation), &#8220;code&#8221; (monowidth string), &#8220;pre&#8221; (monowidth block), &#8220;text_link&#8221; (for clickable text URLs), &#8220;text_mention&#8221; (for users <a href=\"https://telegram.org/blog/edit#new-mentions\">without usernames</a>), &#8220;custom_emoji&#8221; (for inline custom emoji stickers), or &#8220;date_time&#8221; (for formatted date and time)</td>",
"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 <https://telegram.org/blog/edit#new-mentions>`_), 'custom_emoji' (for inline custom emoji stickers)\n", "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 <https://telegram.org/blog/edit#new-mentions>`_), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)\n",
"name": "type", "name": "type",
"required": true "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", "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", "name": "custom_emoji_id",
"required": false "required": false
},
{
"type": "Integer",
"description": "For 'date_time' only, the Unix time associated with the entity",
"html_description": "<td><em>Optional</em>. For &#8220;date_time&#8221; only, the Unix time associated with the entity</td>",
"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": "<td><em>Optional</em>. For &#8220;date_time&#8221; only, the string that defines the formatting of the date and time. See <a href=\"#date-time-entity-formatting\">date-time entity formatting</a> for more details.</td>",
"rst_description": "*Optional*. For 'date_time' only, the string that defines the formatting of the date and time. See `date-time entity formatting <https://core.telegram.org/bots/api#date-time-entity-formatting>`_ for more details.\n",
"name": "date_time_format",
"required": false
} }
], ],
"category": "types" "category": "types"

15
CHANGES/1780.misc.rst Normal file
View file

@ -0,0 +1,15 @@
Updated to `Bot API 9.5 <https://core.telegram.org/bots/api-changelog#march-1-2026>`_
**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

View file

@ -52,7 +52,7 @@ Features
- Asynchronous (`asyncio docs <https://docs.python.org/3/library/asyncio.html>`_, :pep:`492`) - Asynchronous (`asyncio docs <https://docs.python.org/3/library/asyncio.html>`_, :pep:`492`)
- Has type hints (:pep:`484`) and can be used with `mypy <http://mypy-lang.org/>`_ - Has type hints (:pep:`484`) and can be used with `mypy <http://mypy-lang.org/>`_
- Supports `PyPy <https://www.pypy.org/>`_ - Supports `PyPy <https://www.pypy.org/>`_
- Supports `Telegram Bot API 9.4 <https://core.telegram.org/bots/api>`_ and gets fast updates to the latest versions of the Bot API - Supports `Telegram Bot API 9.5 <https://core.telegram.org/bots/api>`_ and gets fast updates to the latest versions of the Bot API
- Telegram Bot API integration code was `autogenerated <https://github.com/aiogram/tg-codegen>`_ and can be easily re-generated when API gets updated - Telegram Bot API integration code was `autogenerated <https://github.com/aiogram/tg-codegen>`_ and can be easily re-generated when API gets updated
- Updates router (Blueprints) - Updates router (Blueprints)
- Has Finite State Machine - Has Finite State Machine

View file

@ -1,2 +1,2 @@
__version__ = "3.25.0" __version__ = "3.26.0"
__api_version__ = "9.4" __api_version__ = "9.5"

View file

@ -144,6 +144,7 @@ from ..methods import (
SetBusinessAccountUsername, SetBusinessAccountUsername,
SetChatAdministratorCustomTitle, SetChatAdministratorCustomTitle,
SetChatDescription, SetChatDescription,
SetChatMemberTag,
SetChatMenuButton, SetChatMenuButton,
SetChatPermissions, SetChatPermissions,
SetChatPhoto, SetChatPhoto,
@ -2021,6 +2022,7 @@ class Bot:
can_pin_messages: bool | None = None, can_pin_messages: bool | None = None,
can_manage_topics: bool | None = None, can_manage_topics: bool | None = None,
can_manage_direct_messages: bool | None = None, can_manage_direct_messages: bool | None = None,
can_manage_tags: bool | None = None,
request_timeout: int | None = None, request_timeout: int | None = None,
) -> bool: ) -> 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_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_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_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 :param request_timeout: Request timeout
:return: Returns :code:`True` on success. :return: Returns :code:`True` on success.
""" """
@ -2069,6 +2072,7 @@ class Bot:
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,
can_manage_topics=can_manage_topics, can_manage_topics=can_manage_topics,
can_manage_direct_messages=can_manage_direct_messages, can_manage_direct_messages=can_manage_direct_messages,
can_manage_tags=can_manage_tags,
) )
return await self(call, request_timeout=request_timeout) return await self(call, request_timeout=request_timeout)
@ -5560,7 +5564,7 @@ class Bot:
:param chat_id: Unique identifier for the target chat :param chat_id: Unique identifier for the target chat
:param message_id: Unique identifier for the target message :param message_id: Unique identifier for the target message
:param checklist: A JSON-serialized object for the new checklist :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 <https://core.telegram.org/bots/features#inline-keyboards>`_ for the message
:param request_timeout: Request timeout :param request_timeout: Request timeout
:return: On success, the edited :class:`aiogram.types.message.Message` is returned. :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 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 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_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 <https://core.telegram.org/bots/features#inline-keyboards>`_
:param request_timeout: Request timeout :param request_timeout: Request timeout
:return: On success, the sent :class:`aiogram.types.message.Message` is returned. :return: On success, the sent :class:`aiogram.types.message.Message` is returned.
""" """
@ -5823,7 +5827,7 @@ class Bot:
request_timeout: int | None = None, request_timeout: int | None = None,
) -> bool: ) -> 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 Source: https://core.telegram.org/bots/api#sendmessagedraft
@ -5908,3 +5912,29 @@ class Bot:
photo=photo, photo=photo,
) )
return await self(call, request_timeout=request_timeout) 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)

View file

@ -27,3 +27,4 @@ class MessageEntityType(str, Enum):
TEXT_LINK = "text_link" TEXT_LINK = "text_link"
TEXT_MENTION = "text_mention" TEXT_MENTION = "text_mention"
CUSTOM_EMOJI = "custom_emoji" CUSTOM_EMOJI = "custom_emoji"
DATE_TIME = "date_time"

View file

@ -126,6 +126,7 @@ from .set_business_account_profile_photo import SetBusinessAccountProfilePhoto
from .set_business_account_username import SetBusinessAccountUsername from .set_business_account_username import SetBusinessAccountUsername
from .set_chat_administrator_custom_title import SetChatAdministratorCustomTitle from .set_chat_administrator_custom_title import SetChatAdministratorCustomTitle
from .set_chat_description import SetChatDescription from .set_chat_description import SetChatDescription
from .set_chat_member_tag import SetChatMemberTag
from .set_chat_menu_button import SetChatMenuButton from .set_chat_menu_button import SetChatMenuButton
from .set_chat_permissions import SetChatPermissions from .set_chat_permissions import SetChatPermissions
from .set_chat_photo import SetChatPhoto from .set_chat_photo import SetChatPhoto
@ -295,6 +296,7 @@ __all__ = (
"SetBusinessAccountUsername", "SetBusinessAccountUsername",
"SetChatAdministratorCustomTitle", "SetChatAdministratorCustomTitle",
"SetChatDescription", "SetChatDescription",
"SetChatMemberTag",
"SetChatMenuButton", "SetChatMenuButton",
"SetChatPermissions", "SetChatPermissions",
"SetChatPhoto", "SetChatPhoto",

View file

@ -25,7 +25,7 @@ class EditMessageChecklist(TelegramMethod[Message]):
checklist: InputChecklist checklist: InputChecklist
"""A JSON-serialized object for the new checklist""" """A JSON-serialized object for the new checklist"""
reply_markup: InlineKeyboardMarkup | None = None 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 <https://core.telegram.org/bots/features#inline-keyboards>`_ for the message"""
if TYPE_CHECKING: if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!

View file

@ -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""" """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 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""" """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: if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -78,6 +80,7 @@ class PromoteChatMember(TelegramMethod[bool]):
can_pin_messages: bool | None = None, can_pin_messages: bool | None = None,
can_manage_topics: bool | None = None, can_manage_topics: bool | None = None,
can_manage_direct_messages: bool | None = None, can_manage_direct_messages: bool | None = None,
can_manage_tags: bool | None = None,
**__pydantic_kwargs: Any, **__pydantic_kwargs: Any,
) -> None: ) -> None:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -103,5 +106,6 @@ class PromoteChatMember(TelegramMethod[bool]):
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,
can_manage_topics=can_manage_topics, can_manage_topics=can_manage_topics,
can_manage_direct_messages=can_manage_direct_messages, can_manage_direct_messages=can_manage_direct_messages,
can_manage_tags=can_manage_tags,
**__pydantic_kwargs, **__pydantic_kwargs,
) )

View file

@ -31,7 +31,7 @@ class SendChecklist(TelegramMethod[Message]):
reply_parameters: ReplyParameters | None = None reply_parameters: ReplyParameters | None = None
"""A JSON-serialized object for description of the message to reply to""" """A JSON-serialized object for description of the message to reply to"""
reply_markup: InlineKeyboardMarkup | None = None reply_markup: InlineKeyboardMarkup | None = None
"""A JSON-serialized object for an inline keyboard""" """A JSON-serialized object for an `inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_"""
if TYPE_CHECKING: if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!

View file

@ -8,7 +8,7 @@ from .base import TelegramMethod
class SendMessageDraft(TelegramMethod[bool]): 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 Source: https://core.telegram.org/bots/api#sendmessagedraft
""" """

View file

@ -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)

View file

@ -28,6 +28,7 @@ if TYPE_CHECKING:
SendChatAction, SendChatAction,
SetChatAdministratorCustomTitle, SetChatAdministratorCustomTitle,
SetChatDescription, SetChatDescription,
SetChatMemberTag,
SetChatPermissions, SetChatPermissions,
SetChatPhoto, SetChatPhoto,
SetChatStickerSet, SetChatStickerSet,
@ -967,6 +968,38 @@ class Chat(TelegramObject):
**kwargs, **kwargs,
).as_(self._bot) ).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( def set_permissions(
self, self,
permissions: ChatPermissions, permissions: ChatPermissions,
@ -1018,6 +1051,7 @@ class Chat(TelegramObject):
can_pin_messages: bool | None = None, can_pin_messages: bool | None = None,
can_manage_topics: bool | None = None, can_manage_topics: bool | None = None,
can_manage_direct_messages: bool | None = None, can_manage_direct_messages: bool | None = None,
can_manage_tags: bool | None = None,
**kwargs: Any, **kwargs: Any,
) -> PromoteChatMember: ) -> 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_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_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_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` :return: instance of method :class:`aiogram.methods.promote_chat_member.PromoteChatMember`
""" """
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -1073,6 +1108,7 @@ class Chat(TelegramObject):
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,
can_manage_topics=can_manage_topics, can_manage_topics=can_manage_topics,
can_manage_direct_messages=can_manage_direct_messages, can_manage_direct_messages=can_manage_direct_messages,
can_manage_tags=can_manage_tags,
**kwargs, **kwargs,
).as_(self._bot) ).as_(self._bot)

View file

@ -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""" """*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 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""" """*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: if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -71,6 +73,7 @@ class ChatAdministratorRights(TelegramObject):
can_pin_messages: bool | None = None, can_pin_messages: bool | None = None,
can_manage_topics: bool | None = None, can_manage_topics: bool | None = None,
can_manage_direct_messages: bool | None = None, can_manage_direct_messages: bool | None = None,
can_manage_tags: bool | None = None,
**__pydantic_kwargs: Any, **__pydantic_kwargs: Any,
) -> None: ) -> None:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -94,5 +97,6 @@ class ChatAdministratorRights(TelegramObject):
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,
can_manage_topics=can_manage_topics, can_manage_topics=can_manage_topics,
can_manage_direct_messages=can_manage_direct_messages, can_manage_direct_messages=can_manage_direct_messages,
can_manage_tags=can_manage_tags,
**__pydantic_kwargs, **__pydantic_kwargs,
) )

View file

@ -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""" """*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 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""" """*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 custom_title: str | None = None
"""*Optional*. Custom title for this user""" """*Optional*. Custom title for this user"""
@ -83,6 +85,7 @@ class ChatMemberAdministrator(ChatMember):
can_pin_messages: bool | None = None, can_pin_messages: bool | None = None,
can_manage_topics: bool | None = None, can_manage_topics: bool | None = None,
can_manage_direct_messages: bool | None = None, can_manage_direct_messages: bool | None = None,
can_manage_tags: bool | None = None,
custom_title: str | None = None, custom_title: str | None = None,
**__pydantic_kwargs: Any, **__pydantic_kwargs: Any,
) -> None: ) -> None:
@ -110,6 +113,7 @@ class ChatMemberAdministrator(ChatMember):
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,
can_manage_topics=can_manage_topics, can_manage_topics=can_manage_topics,
can_manage_direct_messages=can_manage_direct_messages, can_manage_direct_messages=can_manage_direct_messages,
can_manage_tags=can_manage_tags,
custom_title=custom_title, custom_title=custom_title,
**__pydantic_kwargs, **__pydantic_kwargs,
) )

View file

@ -21,6 +21,8 @@ class ChatMemberMember(ChatMember):
"""The member's status in the chat, always 'member'""" """The member's status in the chat, always 'member'"""
user: User user: User
"""Information about the user""" """Information about the user"""
tag: str | None = None
"""*Optional*. Tag of the member"""
until_date: DateTime | None = None until_date: DateTime | None = None
"""*Optional*. Date when the user's subscription will expire; Unix time""" """*Optional*. Date when the user's subscription will expire; Unix time"""
@ -33,6 +35,7 @@ class ChatMemberMember(ChatMember):
*, *,
status: Literal[ChatMemberStatus.MEMBER] = ChatMemberStatus.MEMBER, status: Literal[ChatMemberStatus.MEMBER] = ChatMemberStatus.MEMBER,
user: User, user: User,
tag: str | None = None,
until_date: DateTime | None = None, until_date: DateTime | None = None,
**__pydantic_kwargs: Any, **__pydantic_kwargs: Any,
) -> None: ) -> None:
@ -40,4 +43,6 @@ class ChatMemberMember(ChatMember):
# This method was auto-generated via `butcher` # This method was auto-generated via `butcher`
# Is needed only for type checking and IDE support without any additional plugins # 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
)

View file

@ -43,6 +43,8 @@ class ChatMemberRestricted(ChatMember):
""":code:`True`, if the user is allowed to send animations, games, stickers and use inline bots""" """:code:`True`, if the user is allowed to send animations, games, stickers and use inline bots"""
can_add_web_page_previews: bool can_add_web_page_previews: bool
""":code:`True`, if the user is allowed to add web page previews to their messages""" """: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 can_change_info: bool
""":code:`True`, if the user is allowed to change the chat title, photo and other settings""" """:code:`True`, if the user is allowed to change the chat title, photo and other settings"""
can_invite_users: bool can_invite_users: bool
@ -53,6 +55,8 @@ class ChatMemberRestricted(ChatMember):
""":code:`True`, if the user is allowed to create forum topics""" """:code:`True`, if the user is allowed to create forum topics"""
until_date: DateTime until_date: DateTime
"""Date when restrictions will be lifted for this user; Unix time. If 0, then the user is restricted forever""" """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: if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -74,11 +78,13 @@ class ChatMemberRestricted(ChatMember):
can_send_polls: bool, can_send_polls: bool,
can_send_other_messages: bool, can_send_other_messages: bool,
can_add_web_page_previews: bool, can_add_web_page_previews: bool,
can_edit_tag: bool,
can_change_info: bool, can_change_info: bool,
can_invite_users: bool, can_invite_users: bool,
can_pin_messages: bool, can_pin_messages: bool,
can_manage_topics: bool, can_manage_topics: bool,
until_date: DateTime, until_date: DateTime,
tag: str | None = None,
**__pydantic_kwargs: Any, **__pydantic_kwargs: Any,
) -> None: ) -> None:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -99,10 +105,12 @@ class ChatMemberRestricted(ChatMember):
can_send_polls=can_send_polls, can_send_polls=can_send_polls,
can_send_other_messages=can_send_other_messages, can_send_other_messages=can_send_other_messages,
can_add_web_page_previews=can_add_web_page_previews, can_add_web_page_previews=can_add_web_page_previews,
can_edit_tag=can_edit_tag,
can_change_info=can_change_info, can_change_info=can_change_info,
can_invite_users=can_invite_users, can_invite_users=can_invite_users,
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,
can_manage_topics=can_manage_topics, can_manage_topics=can_manage_topics,
until_date=until_date, until_date=until_date,
tag=tag,
**__pydantic_kwargs, **__pydantic_kwargs,
) )

View file

@ -32,6 +32,8 @@ class ChatPermissions(MutableTelegramObject):
"""*Optional*. :code:`True`, if the user is allowed to send animations, games, stickers and use inline bots""" """*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 can_add_web_page_previews: bool | None = None
"""*Optional*. :code:`True`, if the user is allowed to add web page previews to their messages""" """*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 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""" """*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 can_invite_users: bool | None = None
@ -58,6 +60,7 @@ class ChatPermissions(MutableTelegramObject):
can_send_polls: bool | None = None, can_send_polls: bool | None = None,
can_send_other_messages: bool | None = None, can_send_other_messages: bool | None = None,
can_add_web_page_previews: bool | None = None, can_add_web_page_previews: bool | None = None,
can_edit_tag: bool | None = None,
can_change_info: bool | None = None, can_change_info: bool | None = None,
can_invite_users: bool | None = None, can_invite_users: bool | None = None,
can_pin_messages: bool | None = None, can_pin_messages: bool | None = None,
@ -79,6 +82,7 @@ class ChatPermissions(MutableTelegramObject):
can_send_polls=can_send_polls, can_send_polls=can_send_polls,
can_send_other_messages=can_send_other_messages, can_send_other_messages=can_send_other_messages,
can_add_web_page_previews=can_add_web_page_previews, can_add_web_page_previews=can_add_web_page_previews,
can_edit_tag=can_edit_tag,
can_change_info=can_change_info, can_change_info=can_change_info,
can_invite_users=can_invite_users, can_invite_users=can_invite_users,
can_pin_messages=can_pin_messages, can_pin_messages=can_pin_messages,

View file

@ -15,8 +15,6 @@ class GameHighScore(TelegramObject):
If you've got any questions, please check out our `https://core.telegram.org/bots/faq <https://core.telegram.org/bots/faq>`_ **Bot FAQ »** If you've got any questions, please check out our `https://core.telegram.org/bots/faq <https://core.telegram.org/bots/faq>`_ **Bot FAQ »**
-
Source: https://core.telegram.org/bots/api#gamehighscore Source: https://core.telegram.org/bots/api#gamehighscore
""" """

View file

@ -38,7 +38,7 @@ class InlineQueryResultDocument(InlineQueryResult):
description: str | None = None description: str | None = None
"""*Optional*. Short description of the result""" """*Optional*. Short description of the result"""
reply_markup: InlineKeyboardMarkup | None = None reply_markup: InlineKeyboardMarkup | None = None
"""*Optional*. Inline keyboard attached to the message""" """*Optional*. `Inline keyboard <https://core.telegram.org/bots/features#inline-keyboards>`_ attached to the message"""
input_message_content: InputMessageContentUnion | None = None input_message_content: InputMessageContentUnion | None = None
"""*Optional*. Content of the message to be sent instead of the file""" """*Optional*. Content of the message to be sent instead of the file"""
thumbnail_url: str | None = None thumbnail_url: str | None = None

View file

@ -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""" """*Optional*. If the sender of the message boosted the chat, the number of boosts added by the user"""
sender_business_bot: User | None = None 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.""" """*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 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.""" """*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 forward_origin: MessageOriginUnion | None = None
@ -186,7 +188,7 @@ class Message(MaybeInaccessibleMessage):
is_paid_post: bool | None = None 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.""" """*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 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 author_signature: str | None = None
"""*Optional*. Signature of the post author for messages in channels, or the custom title of an anonymous group administrator""" """*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 paid_star_count: int | None = None
@ -348,7 +350,7 @@ class Message(MaybeInaccessibleMessage):
web_app_data: WebAppData | None = None web_app_data: WebAppData | None = None
"""*Optional*. Service message: data sent by a Web App""" """*Optional*. Service message: data sent by a Web App"""
reply_markup: InlineKeyboardMarkup | None = None 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 <https://core.telegram.org/bots/features#inline-keyboards>`_ 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}) forward_date: DateTime | None = Field(None, json_schema_extra={"deprecated": True})
"""*Optional*. For forwarded messages, date the original message was sent in Unix time """*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_chat: Chat | None = None,
sender_boost_count: int | None = None, sender_boost_count: int | None = None,
sender_business_bot: User | None = None, sender_business_bot: User | None = None,
sender_tag: str | None = None,
business_connection_id: str | None = None, business_connection_id: str | None = None,
forward_origin: MessageOriginUnion | None = None, forward_origin: MessageOriginUnion | None = None,
is_topic_message: bool | None = None, is_topic_message: bool | None = None,
@ -520,6 +523,7 @@ class Message(MaybeInaccessibleMessage):
sender_chat=sender_chat, sender_chat=sender_chat,
sender_boost_count=sender_boost_count, sender_boost_count=sender_boost_count,
sender_business_bot=sender_business_bot, sender_business_bot=sender_business_bot,
sender_tag=sender_tag,
business_connection_id=business_connection_id, business_connection_id=business_connection_id,
forward_origin=forward_origin, forward_origin=forward_origin,
is_topic_message=is_topic_message, is_topic_message=is_topic_message,

View file

@ -17,7 +17,7 @@ class MessageEntity(MutableTelegramObject):
""" """
type: str 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 <https://telegram.org/blog/edit#new-mentions>`_), '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 <https://telegram.org/blog/edit#new-mentions>`_), 'custom_emoji' (for inline custom emoji stickers), or 'date_time' (for formatted date and time)"""
offset: int offset: int
"""Offset in `UTF-16 code units <https://core.telegram.org/api/entities#entity-length>`_ to the start of the entity""" """Offset in `UTF-16 code units <https://core.telegram.org/api/entities#entity-length>`_ to the start of the entity"""
length: int length: int
@ -30,6 +30,10 @@ class MessageEntity(MutableTelegramObject):
"""*Optional*. For 'pre' only, the programming language of the entity text""" """*Optional*. For 'pre' only, the programming language of the entity text"""
custom_emoji_id: str | None = None 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""" """*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 <https://core.telegram.org/bots/api#date-time-entity-formatting>`_ for more details."""
if TYPE_CHECKING: if TYPE_CHECKING:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -45,6 +49,8 @@ class MessageEntity(MutableTelegramObject):
user: User | None = None, user: User | None = None,
language: str | None = None, language: str | None = None,
custom_emoji_id: str | None = None, custom_emoji_id: str | None = None,
unix_time: int | None = None,
date_time_format: str | None = None,
**__pydantic_kwargs: Any, **__pydantic_kwargs: Any,
) -> None: ) -> None:
# DO NOT EDIT MANUALLY!!! # DO NOT EDIT MANUALLY!!!
@ -59,6 +65,8 @@ class MessageEntity(MutableTelegramObject):
user=user, user=user,
language=language, language=language,
custom_emoji_id=custom_emoji_id, custom_emoji_id=custom_emoji_id,
unix_time=unix_time,
date_time_format=date_time_format,
**__pydantic_kwargs, **__pydantic_kwargs,
) )

View file

@ -2,6 +2,7 @@ from __future__ import annotations
import textwrap import textwrap
from collections.abc import Generator, Iterable, Iterator from collections.abc import Generator, Iterable, Iterator
from datetime import datetime
from typing import Any, ClassVar from typing import Any, ClassVar
from typing_extensions import Self from typing_extensions import Self
@ -534,6 +535,26 @@ class ExpandableBlockQuote(Text):
type = MessageEntityType.EXPANDABLE_BLOCKQUOTE 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]] = { NODE_TYPES: dict[str | None, type[Text]] = {
Text.type: Text, Text.type: Text,
HashTag.type: HashTag, HashTag.type: HashTag,
@ -554,6 +575,7 @@ NODE_TYPES: dict[str | None, type[Text]] = {
CustomEmoji.type: CustomEmoji, CustomEmoji.type: CustomEmoji,
BlockQuote.type: BlockQuote, BlockQuote.type: BlockQuote,
ExpandableBlockQuote.type: ExpandableBlockQuote, ExpandableBlockQuote.type: ExpandableBlockQuote,
DateTime.type: DateTime,
} }

View file

@ -3,9 +3,11 @@ from __future__ import annotations
import html import html
import re import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from datetime import date, datetime, time
from typing import TYPE_CHECKING, cast from typing import TYPE_CHECKING, cast
from aiogram.enums import MessageEntityType from aiogram.enums import MessageEntityType
from aiogram.utils.link import create_tg_link
if TYPE_CHECKING: if TYPE_CHECKING:
from collections.abc import Generator from collections.abc import Generator
@ -78,6 +80,12 @@ class TextDecoration(ABC):
return self.link(value=text, link=cast(str, entity.url)) return self.link(value=text, link=cast(str, entity.url))
if entity.type == MessageEntityType.CUSTOM_EMOJI: if entity.type == MessageEntityType.CUSTOM_EMOJI:
return self.custom_emoji(value=text, custom_emoji_id=cast(str, entity.custom_emoji_id)) 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 # This case is not possible because of `if` above, but if any new entity is added to
# API it will be here too # API it will be here too
@ -180,54 +188,105 @@ class TextDecoration(ABC):
def expandable_blockquote(self, value: str) -> str: def expandable_blockquote(self, value: str) -> str:
pass pass
@abstractmethod
def date_time(
self,
value: str,
unix_time: int | datetime,
date_time_format: str | None = None,
) -> str:
pass
class HtmlDecoration(TextDecoration): class HtmlDecoration(TextDecoration):
BOLD_TAG = "b" BOLD_TAG = "b"
ITALIC_TAG = "i" ITALIC_TAG = "i"
UNDERLINE_TAG = "u" UNDERLINE_TAG = "u"
STRIKETHROUGH_TAG = "s" STRIKETHROUGH_TAG = "s"
CODE_TAG = "code"
PRE_TAG = "pre"
LINK_TAG = "a"
SPOILER_TAG = "tg-spoiler" SPOILER_TAG = "tg-spoiler"
EMOJI_TAG = "tg-emoji" EMOJI_TAG = "tg-emoji"
DATE_TIME_TAG = "tg-time"
BLOCKQUOTE_TAG = "blockquote" 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: def link(self, value: str, link: str) -> str:
return f'<a href="{link}">{value}</a>' return self._tag(self.LINK_TAG, value, attrs={"href": link})
def bold(self, value: str) -> str: 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: 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: def code(self, value: str) -> str:
return f"<code>{value}</code>" return self._tag(self.CODE_TAG, value)
def pre(self, value: str) -> str: def pre(self, value: str) -> str:
return f"<pre>{value}</pre>" return self._tag(self.PRE_TAG, value)
def pre_language(self, value: str, language: str) -> str: def pre_language(self, value: str, language: str) -> str:
return f'<pre><code class="language-{language}">{value}</code></pre>' return self._tag(
self.PRE_TAG,
self._tag(self.CODE_TAG, value, attrs={"language": f"language-{language}"}),
)
def underline(self, value: str) -> str: 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: 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: 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: def quote(self, value: str) -> str:
return html.escape(value, quote=False) return html.escape(value, quote=False)
def custom_emoji(self, value: str, custom_emoji_id: str) -> str: 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: 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: 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): class MarkdownDecoration(TextDecoration):
@ -264,7 +323,8 @@ class MarkdownDecoration(TextDecoration):
return re.sub(pattern=self.MARKDOWN_QUOTE_PATTERN, repl=r"\\\1", string=value) return re.sub(pattern=self.MARKDOWN_QUOTE_PATTERN, repl=r"\\\1", string=value)
def custom_emoji(self, value: str, custom_emoji_id: str) -> str: 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: def blockquote(self, value: str) -> str:
return "\n".join(f">{line}" for line in value.splitlines()) return "\n".join(f">{line}" for line in value.splitlines())
@ -272,6 +332,22 @@ class MarkdownDecoration(TextDecoration):
def expandable_blockquote(self, value: str) -> str: def expandable_blockquote(self, value: str) -> str:
return "\n".join(f">{line}" for line in value.splitlines()) + "||" 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() html_decoration = HtmlDecoration()
markdown_decoration = MarkdownDecoration() markdown_decoration = MarkdownDecoration()

View file

@ -36,3 +36,11 @@ With specific bot
.. code-block:: python .. code-block:: python
result: UserProfileAudios = await bot(GetUserProfileAudios(...)) result: UserProfileAudios = await bot(GetUserProfileAudios(...))
As shortcut from received object
--------------------------------
- :meth:`aiogram.types.user.User.get_profile_audios`

View file

@ -127,6 +127,7 @@ Available methods
set_business_account_username set_business_account_username
set_chat_administrator_custom_title set_chat_administrator_custom_title
set_chat_description set_chat_description
set_chat_member_tag
set_chat_menu_button set_chat_menu_button
set_chat_permissions set_chat_permissions
set_chat_photo set_chat_photo

View file

@ -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(...)

View file

@ -6,6 +6,11 @@ class TestPromoteChatMember:
async def test_bot_method(self, bot: MockedBot): async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(PromoteChatMember, ok=True, result=True) prepare_result = bot.add_result_for(PromoteChatMember, ok=True, result=True)
response: bool = await bot.promote_chat_member(chat_id=-42, user_id=42) response: bool = await bot.promote_chat_member(
bot.get_request() 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 assert response == prepare_result.result

View file

@ -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

View file

@ -115,6 +115,14 @@ class TestChat:
method = chat.set_administrator_custom_title(user_id=1, custom_title="test") method = chat.set_administrator_custom_title(user_id=1, custom_title="test")
assert method.chat_id == chat.id 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): def test_set_permissions(self):
chat = Chat(id=-42, type="supergroup") chat = Chat(id=-42, type="supergroup")

View file

@ -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"

View file

@ -314,6 +314,7 @@ class TestChatMemberUpdatedStatusFilter:
"can_send_polls": True, "can_send_polls": True,
"can_send_other_messages": True, "can_send_other_messages": True,
"can_add_web_page_previews": True, "can_add_web_page_previews": True,
"can_edit_tag": True,
"can_post_stories": True, "can_post_stories": True,
"can_edit_stories": True, "can_edit_stories": True,
"can_delete_stories": True, "can_delete_stories": True,

View file

@ -70,6 +70,7 @@ CHAT_MEMBER_RESTRICTED = ChatMemberRestricted(
can_send_polls=False, can_send_polls=False,
can_send_other_messages=False, can_send_other_messages=False,
can_add_web_page_previews=False, can_add_web_page_previews=False,
can_edit_tag=False,
can_change_info=False, can_change_info=False,
can_invite_users=False, can_invite_users=False,
can_pin_messages=False, can_pin_messages=False,

View file

@ -1,3 +1,5 @@
from datetime import datetime, timezone
import pytest import pytest
from aiogram.enums import MessageEntityType from aiogram.enums import MessageEntityType
@ -9,6 +11,7 @@ from aiogram.utils.formatting import (
CashTag, CashTag,
Code, Code,
CustomEmoji, CustomEmoji,
DateTime,
Email, Email,
ExpandableBlockQuote, ExpandableBlockQuote,
HashTag, HashTag,
@ -93,7 +96,7 @@ class TestNode:
], ],
[ [
Pre("test", language="python"), Pre("test", language="python"),
'<pre><code class="language-python">test</code></pre>', '<pre><code language="language-python">test</code></pre>',
], ],
[ [
TextLink("test", url="https://example.com"), TextLink("test", url="https://example.com"),
@ -105,7 +108,7 @@ class TestNode:
], ],
[ [
CustomEmoji("test", custom_emoji_id="42"), CustomEmoji("test", custom_emoji_id="42"),
'<tg-emoji emoji-id="42">test</tg-emoji>', '<tg-emoji emoji_id="42">test</tg-emoji>',
], ],
[ [
BlockQuote("test"), BlockQuote("test"),
@ -115,6 +118,14 @@ class TestNode:
ExpandableBlockQuote("test"), ExpandableBlockQuote("test"),
"<blockquote expandable>test</blockquote>", "<blockquote expandable>test</blockquote>",
], ],
[
DateTime("test", unix_time=42, date_time_format="yMd"),
'<tg-time unix="42" format="yMd">test</tg-time>',
],
[
DateTime("test", unix_time=42),
'<tg-time unix="42">test</tg-time>',
],
], ],
) )
def test_render_plain_only(self, node: Text, result: str): def test_render_plain_only(self, node: Text, result: str):
@ -358,6 +369,38 @@ class TestUtils:
assert isinstance(node, Bold) assert isinstance(node, Bold)
assert node._body == ("test",) 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): def test_as_line(self):
node = as_line("test", "test", "test") node = as_line("test", "test", "test")
assert isinstance(node, Text) assert isinstance(node, Text)

View file

@ -1,3 +1,5 @@
from datetime import datetime, timezone
import pytest import pytest
from aiogram.types import MessageEntity, User from aiogram.types import MessageEntity, User
@ -25,7 +27,7 @@ class TestTextDecoration:
[ [
html_decoration, html_decoration,
MessageEntity(type="pre", offset=0, length=5, language="python"), MessageEntity(type="pre", offset=0, length=5, language="python"),
'<pre><code class="language-python">test</code></pre>', '<pre><code language="language-python">test</code></pre>',
], ],
[html_decoration, MessageEntity(type="underline", offset=0, length=5), "<u>test</u>"], [html_decoration, MessageEntity(type="underline", offset=0, length=5), "<u>test</u>"],
[ [
@ -57,7 +59,7 @@ class TestTextDecoration:
[ [
html_decoration, html_decoration,
MessageEntity(type="custom_emoji", offset=0, length=5, custom_emoji_id="42"), MessageEntity(type="custom_emoji", offset=0, length=5, custom_emoji_id="42"),
'<tg-emoji emoji-id="42">test</tg-emoji>', '<tg-emoji emoji_id="42">test</tg-emoji>',
], ],
[ [
html_decoration, html_decoration,
@ -74,6 +76,17 @@ class TestTextDecoration:
MessageEntity(type="expandable_blockquote", offset=0, length=5), MessageEntity(type="expandable_blockquote", offset=0, length=5),
"<blockquote expandable>test</blockquote>", "<blockquote expandable>test</blockquote>",
], ],
[
html_decoration,
MessageEntity(
type="date_time",
offset=0,
length=5,
unix_time=42,
date_time_format="yMd",
),
'<tg-time unix="42" format="yMd">test</tg-time>',
],
[markdown_decoration, MessageEntity(type="bold", offset=0, length=5), "*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="italic", offset=0, length=5), "_\rtest_\r"],
[markdown_decoration, MessageEntity(type="code", offset=0, length=5), "`test`"], [markdown_decoration, MessageEntity(type="code", offset=0, length=5), "`test`"],
@ -102,7 +115,7 @@ class TestTextDecoration:
[ [
markdown_decoration, markdown_decoration,
MessageEntity(type="custom_emoji", offset=0, length=5, custom_emoji_id="42"), 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, markdown_decoration,
@ -124,6 +137,27 @@ class TestTextDecoration:
MessageEntity(type="expandable_blockquote", offset=0, length=5), MessageEntity(type="expandable_blockquote", offset=0, length=5),
">test||", ">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),
'<tg-time unix="42">test</tg-time>',
],
[
markdown_decoration,
MessageEntity(type="date_time", offset=0, length=5, unix_time=42),
"![test](tg://time?unix=42)",
],
], ],
) )
def test_apply_single_entity( def test_apply_single_entity(
@ -131,6 +165,38 @@ class TestTextDecoration:
): ):
assert decorator.apply_entity(entity, "test") == result assert decorator.apply_entity(entity, "test") == result
@pytest.mark.parametrize(
"decorator,date_time_format,expected",
[
(
html_decoration,
None,
'<tg-time unix="1704067200">test</tg-time>',
),
(
html_decoration,
"yMd",
'<tg-time unix="1704067200" format="yMd">test</tg-time>',
),
(
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): def test_unknown_apply_entity(self):
assert ( assert (
html_decoration.apply_entity( html_decoration.apply_entity(
@ -296,6 +362,22 @@ class TestTextDecoration:
], ],
"<b>test@example.com</b>", "<b>test@example.com</b>",
], ],
[
html_decoration,
"test",
[MessageEntity(type="date_time", offset=0, length=4, unix_time=42)],
'<tg-time unix="42">test</tg-time>',
],
[
html_decoration,
"test",
[
MessageEntity(
type="date_time", offset=0, length=4, unix_time=42, date_time_format="yMd"
)
],
'<tg-time unix="42" format="yMd">test</tg-time>',
],
], ],
) )
def test_unparse( def test_unparse(