mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Merge branch 'dev-3.x' into formatting
This commit is contained in:
commit
28e4c15d75
64 changed files with 1146 additions and 740 deletions
|
|
@ -38,9 +38,9 @@
|
|||
{
|
||||
"type": "Boolean",
|
||||
"required": false,
|
||||
"description": "Pass True if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query",
|
||||
"html_description": "<td>Pass <em>True</em> if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query</td>",
|
||||
"rst_description": "Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query\n",
|
||||
"description": "Pass True if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.",
|
||||
"html_description": "<td>Pass <em>True</em> if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.</td>",
|
||||
"rst_description": "Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.\n",
|
||||
"name": "is_personal"
|
||||
},
|
||||
{
|
||||
|
|
@ -52,12 +52,12 @@
|
|||
"name": "next_offset"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"type": "InlineQueryResultsButton",
|
||||
"required": false,
|
||||
"description": "If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter",
|
||||
"html_description": "<td>If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter <em>switch_pm_parameter</em></td>",
|
||||
"rst_description": "If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*\n",
|
||||
"name": "switch_pm_text"
|
||||
"description": "A JSON-serialized object describing a button to be shown above inline query results",
|
||||
"html_description": "<td>A JSON-serialized object describing a button to be shown above inline query results</td>",
|
||||
"rst_description": "A JSON-serialized object describing a button to be shown above inline query results\n",
|
||||
"name": "button"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
|
|
@ -65,7 +65,23 @@
|
|||
"description": "Deep-linking parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.\n\nExample: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.",
|
||||
"html_description": "<td><a href=\"/bots/features#deep-linking\">Deep-linking</a> parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only <code>A-Z</code>, <code>a-z</code>, <code>0-9</code>, <code>_</code> and <code>-</code> are allowed.<br/>\n<br/>\n<em>Example:</em> An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a <a href=\"#inlinekeyboardmarkup\"><em>switch_inline</em></a> button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.</td>",
|
||||
"rst_description": "`Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.\n\n\n\n*Example:* An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a `https://core.telegram.org/bots/api#inlinekeyboardmarkup <https://core.telegram.org/bots/api#inlinekeyboardmarkup>`_ *switch_inline* button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.\n",
|
||||
"name": "switch_pm_parameter"
|
||||
"name": "switch_pm_parameter",
|
||||
"deprecated": {
|
||||
"version": "6.7",
|
||||
"release_date": "2023-04-21"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter",
|
||||
"html_description": "<td>If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter <em>switch_pm_parameter</em></td>",
|
||||
"rst_description": "If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*\n",
|
||||
"name": "switch_pm_text",
|
||||
"deprecated": {
|
||||
"version": "6.7",
|
||||
"release_date": "2023-04-21"
|
||||
}
|
||||
}
|
||||
],
|
||||
"category": "methods"
|
||||
|
|
|
|||
25
.butcher/methods/getMyName/entity.json
Normal file
25
.butcher/methods/getMyName/entity.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"meta": {},
|
||||
"group": {
|
||||
"title": "Available methods",
|
||||
"anchor": "available-methods"
|
||||
},
|
||||
"object": {
|
||||
"anchor": "getmyname",
|
||||
"name": "getMyName",
|
||||
"description": "Use this method to get the current bot name for the given user language. Returns BotName on success.",
|
||||
"html_description": "<p>Use this method to get the current bot name for the given user language. Returns <a href=\"#botname\">BotName</a> on success.</p>",
|
||||
"rst_description": "Use this method to get the current bot name for the given user language. Returns :class:`aiogram.types.bot_name.BotName` on success.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "A two-letter ISO 639-1 language code or an empty string",
|
||||
"html_description": "<td>A two-letter ISO 639-1 language code or an empty string</td>",
|
||||
"rst_description": "A two-letter ISO 639-1 language code or an empty string\n",
|
||||
"name": "language_code"
|
||||
}
|
||||
],
|
||||
"category": "methods"
|
||||
}
|
||||
}
|
||||
33
.butcher/methods/setMyName/entity.json
Normal file
33
.butcher/methods/setMyName/entity.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"meta": {},
|
||||
"group": {
|
||||
"title": "Available methods",
|
||||
"anchor": "available-methods"
|
||||
},
|
||||
"object": {
|
||||
"anchor": "setmyname",
|
||||
"name": "setMyName",
|
||||
"description": "Use this method to change the bot's name. Returns True on success.",
|
||||
"html_description": "<p>Use this method to change the bot's name. Returns <em>True</em> on success.</p>",
|
||||
"rst_description": "Use this method to change the bot's name. Returns :code:`True` on success.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.",
|
||||
"html_description": "<td>New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.</td>",
|
||||
"rst_description": "New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.\n",
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name.",
|
||||
"html_description": "<td>A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name.</td>",
|
||||
"rst_description": "A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name.\n",
|
||||
"name": "language_code"
|
||||
}
|
||||
],
|
||||
"category": "methods"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"api": {
|
||||
"version": "6.6",
|
||||
"release_date": "2023-03-09"
|
||||
"version": "6.7",
|
||||
"release_date": "2023-04-21"
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
|
|
@ -2421,10 +2421,19 @@
|
|||
{
|
||||
"anchor": "writeaccessallowed",
|
||||
"name": "WriteAccessAllowed",
|
||||
"description": "This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.",
|
||||
"html_description": "<p>This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.</p>",
|
||||
"rst_description": "This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.",
|
||||
"annotations": [],
|
||||
"description": "This object represents a service message about a user allowing a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link.",
|
||||
"html_description": "<p>This object represents a service message about a user allowing a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link.</p>",
|
||||
"rst_description": "This object represents a service message about a user allowing a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "Name of the Web App which was launched from a link",
|
||||
"html_description": "<td><em>Optional</em>. Name of the Web App which was launched from a link</td>",
|
||||
"rst_description": "*Optional*. Name of the Web App which was launched from a link\n",
|
||||
"name": "web_app_name",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
},
|
||||
{
|
||||
|
|
@ -2933,6 +2942,14 @@
|
|||
"name": "switch_inline_query_current_chat",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "SwitchInlineQueryChosenChat",
|
||||
"description": "If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field",
|
||||
"html_description": "<td><em>Optional</em>. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field</td>",
|
||||
"rst_description": "*Optional*. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field\n",
|
||||
"name": "switch_inline_query_chosen_chat",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "CallbackGame",
|
||||
"description": "Description of the game that will be launched when the user presses the button.\n\nNOTE: This type of button must always be the first button in the first row.",
|
||||
|
|
@ -2994,6 +3011,56 @@
|
|||
],
|
||||
"category": "types"
|
||||
},
|
||||
{
|
||||
"anchor": "switchinlinequerychosenchat",
|
||||
"name": "SwitchInlineQueryChosenChat",
|
||||
"description": "This object represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.",
|
||||
"html_description": "<p>This object represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.</p>",
|
||||
"rst_description": "This object represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted",
|
||||
"html_description": "<td><em>Optional</em>. The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted</td>",
|
||||
"rst_description": "*Optional*. The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted\n",
|
||||
"name": "query",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if private chats with users can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if private chats with users can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if private chats with users can be chosen\n",
|
||||
"name": "allow_user_chats",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if private chats with bots can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if private chats with bots can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if private chats with bots can be chosen\n",
|
||||
"name": "allow_bot_chats",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if group and supergroup chats can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if group and supergroup chats can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if group and supergroup chats can be chosen\n",
|
||||
"name": "allow_group_chats",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if channel chats can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if channel chats can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if channel chats can be chosen\n",
|
||||
"name": "allow_channel_chats",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
},
|
||||
{
|
||||
"anchor": "callbackquery",
|
||||
"name": "CallbackQuery",
|
||||
|
|
@ -3807,6 +3874,14 @@
|
|||
"rst_description": "*Optional*. Chat invite link, which was used by the user to join the chat; for joining by invite link events only.\n",
|
||||
"name": "invite_link",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if the user joined the chat via a chat folder invite link",
|
||||
"html_description": "<td><em>Optional</em>. True, if the user joined the chat via a chat folder invite link</td>",
|
||||
"rst_description": "*Optional*. True, if the user joined the chat via a chat folder invite link\n",
|
||||
"name": "via_chat_folder_invite_link",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
|
|
@ -4252,6 +4327,24 @@
|
|||
],
|
||||
"category": "types"
|
||||
},
|
||||
{
|
||||
"anchor": "botname",
|
||||
"name": "BotName",
|
||||
"description": "This object represents the bot's name.",
|
||||
"html_description": "<p>This object represents the bot's name.</p>",
|
||||
"rst_description": "This object represents the bot's name.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "The bot's name",
|
||||
"html_description": "<td>The bot's name</td>",
|
||||
"rst_description": "The bot's name\n",
|
||||
"name": "name",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
},
|
||||
{
|
||||
"anchor": "botdescription",
|
||||
"name": "BotDescription",
|
||||
|
|
@ -7988,6 +8081,50 @@
|
|||
],
|
||||
"category": "methods"
|
||||
},
|
||||
{
|
||||
"anchor": "setmyname",
|
||||
"name": "setMyName",
|
||||
"description": "Use this method to change the bot's name. Returns True on success.",
|
||||
"html_description": "<p>Use this method to change the bot's name. Returns <em>True</em> on success.</p>",
|
||||
"rst_description": "Use this method to change the bot's name. Returns :code:`True` on success.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.",
|
||||
"html_description": "<td>New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.</td>",
|
||||
"rst_description": "New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.\n",
|
||||
"name": "name"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name.",
|
||||
"html_description": "<td>A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name.</td>",
|
||||
"rst_description": "A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name.\n",
|
||||
"name": "language_code"
|
||||
}
|
||||
],
|
||||
"category": "methods"
|
||||
},
|
||||
{
|
||||
"anchor": "getmyname",
|
||||
"name": "getMyName",
|
||||
"description": "Use this method to get the current bot name for the given user language. Returns BotName on success.",
|
||||
"html_description": "<p>Use this method to get the current bot name for the given user language. Returns <a href=\"#botname\">BotName</a> on success.</p>",
|
||||
"rst_description": "Use this method to get the current bot name for the given user language. Returns :class:`aiogram.types.bot_name.BotName` on success.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "A two-letter ISO 639-1 language code or an empty string",
|
||||
"html_description": "<td>A two-letter ISO 639-1 language code or an empty string</td>",
|
||||
"rst_description": "A two-letter ISO 639-1 language code or an empty string\n",
|
||||
"name": "language_code"
|
||||
}
|
||||
],
|
||||
"category": "methods"
|
||||
},
|
||||
{
|
||||
"anchor": "setmydescription",
|
||||
"name": "setMyDescription",
|
||||
|
|
@ -9451,9 +9588,9 @@
|
|||
{
|
||||
"type": "Boolean",
|
||||
"required": false,
|
||||
"description": "Pass True if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query",
|
||||
"html_description": "<td>Pass <em>True</em> if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query</td>",
|
||||
"rst_description": "Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query\n",
|
||||
"description": "Pass True if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.",
|
||||
"html_description": "<td>Pass <em>True</em> if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.</td>",
|
||||
"rst_description": "Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.\n",
|
||||
"name": "is_personal"
|
||||
},
|
||||
{
|
||||
|
|
@ -9465,24 +9602,50 @@
|
|||
"name": "next_offset"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"type": "InlineQueryResultsButton",
|
||||
"required": false,
|
||||
"description": "If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter switch_pm_parameter",
|
||||
"html_description": "<td>If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter <em>switch_pm_parameter</em></td>",
|
||||
"rst_description": "If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*\n",
|
||||
"name": "switch_pm_text"
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"required": false,
|
||||
"description": "Deep-linking parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.\n\nExample: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.",
|
||||
"html_description": "<td><a href=\"/bots/features#deep-linking\">Deep-linking</a> parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only <code>A-Z</code>, <code>a-z</code>, <code>0-9</code>, <code>_</code> and <code>-</code> are allowed.<br/>\n<br/>\n<em>Example:</em> An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a <a href=\"#inlinekeyboardmarkup\"><em>switch_inline</em></a> button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.</td>",
|
||||
"rst_description": "`Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.\n\n\n\n*Example:* An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a `https://core.telegram.org/bots/api#inlinekeyboardmarkup <https://core.telegram.org/bots/api#inlinekeyboardmarkup>`_ *switch_inline* button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.\n",
|
||||
"name": "switch_pm_parameter"
|
||||
"description": "A JSON-serialized object describing a button to be shown above inline query results",
|
||||
"html_description": "<td>A JSON-serialized object describing a button to be shown above inline query results</td>",
|
||||
"rst_description": "A JSON-serialized object describing a button to be shown above inline query results\n",
|
||||
"name": "button"
|
||||
}
|
||||
],
|
||||
"category": "methods"
|
||||
},
|
||||
{
|
||||
"anchor": "inlinequeryresultsbutton",
|
||||
"name": "InlineQueryResultsButton",
|
||||
"description": "This object represents a button to be shown above inline query results. You must use exactly one of the optional fields.",
|
||||
"html_description": "<p>This object represents a button to be shown above inline query results. You <strong>must</strong> use exactly one of the optional fields.</p>",
|
||||
"rst_description": "This object represents a button to be shown above inline query results. You **must** use exactly one of the optional fields.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "Label text on the button",
|
||||
"html_description": "<td>Label text on the button</td>",
|
||||
"rst_description": "Label text on the button\n",
|
||||
"name": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "WebAppInfo",
|
||||
"description": "Description of the Web App that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method web_app_switch_inline_query inside the Web App.",
|
||||
"html_description": "<td><em>Optional</em>. Description of the <a href=\"/bots/webapps\">Web App</a> that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method <em>web_app_switch_inline_query</em> inside the Web App.</td>",
|
||||
"rst_description": "*Optional*. Description of the `Web App <https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method *web_app_switch_inline_query* inside the Web App.\n",
|
||||
"name": "web_app",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"description": "Deep-linking parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.\n\nExample: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.",
|
||||
"html_description": "<td><em>Optional</em>. <a href=\"/bots/features#deep-linking\">Deep-linking</a> parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only <code>A-Z</code>, <code>a-z</code>, <code>0-9</code>, <code>_</code> and <code>-</code> are allowed.<br/>\n<br/>\n<em>Example:</em> An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a <a href=\"#inlinekeyboardmarkup\"><em>switch_inline</em></a> button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.</td>",
|
||||
"rst_description": "*Optional*. `Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.\n\n\n\n*Example:* An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a `https://core.telegram.org/bots/api#inlinekeyboardmarkup <https://core.telegram.org/bots/api#inlinekeyboardmarkup>`_ *switch_inline* button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.\n",
|
||||
"name": "start_parameter",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
},
|
||||
{
|
||||
"anchor": "inlinequeryresult",
|
||||
"name": "InlineQueryResult",
|
||||
|
|
|
|||
25
.butcher/types/BotName/entity.json
Normal file
25
.butcher/types/BotName/entity.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"meta": {},
|
||||
"group": {
|
||||
"title": "Available types",
|
||||
"anchor": "available-types"
|
||||
},
|
||||
"object": {
|
||||
"anchor": "botname",
|
||||
"name": "BotName",
|
||||
"description": "This object represents the bot's name.",
|
||||
"html_description": "<p>This object represents the bot's name.</p>",
|
||||
"rst_description": "This object represents the bot's name.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "The bot's name",
|
||||
"html_description": "<td>The bot's name</td>",
|
||||
"rst_description": "The bot's name\n",
|
||||
"name": "name",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,14 @@
|
|||
"rst_description": "*Optional*. Chat invite link, which was used by the user to join the chat; for joining by invite link events only.\n",
|
||||
"name": "invite_link",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if the user joined the chat via a chat folder invite link",
|
||||
"html_description": "<td><em>Optional</em>. True, if the user joined the chat via a chat folder invite link</td>",
|
||||
"rst_description": "*Optional*. True, if the user joined the chat via a chat folder invite link\n",
|
||||
"name": "via_chat_folder_invite_link",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
|
|
|
|||
|
|
@ -67,6 +67,14 @@
|
|||
"name": "switch_inline_query_current_chat",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "SwitchInlineQueryChosenChat",
|
||||
"description": "If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field",
|
||||
"html_description": "<td><em>Optional</em>. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field</td>",
|
||||
"rst_description": "*Optional*. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field\n",
|
||||
"name": "switch_inline_query_chosen_chat",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "CallbackGame",
|
||||
"description": "Description of the game that will be launched when the user presses the button.\n\nNOTE: This type of button must always be the first button in the first row.",
|
||||
|
|
|
|||
41
.butcher/types/InlineQueryResultsButton/entity.json
Normal file
41
.butcher/types/InlineQueryResultsButton/entity.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"meta": {},
|
||||
"group": {
|
||||
"title": "Inline mode",
|
||||
"anchor": "inline-mode"
|
||||
},
|
||||
"object": {
|
||||
"anchor": "inlinequeryresultsbutton",
|
||||
"name": "InlineQueryResultsButton",
|
||||
"description": "This object represents a button to be shown above inline query results. You must use exactly one of the optional fields.",
|
||||
"html_description": "<p>This object represents a button to be shown above inline query results. You <strong>must</strong> use exactly one of the optional fields.</p>",
|
||||
"rst_description": "This object represents a button to be shown above inline query results. You **must** use exactly one of the optional fields.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "Label text on the button",
|
||||
"html_description": "<td>Label text on the button</td>",
|
||||
"rst_description": "Label text on the button\n",
|
||||
"name": "text",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "WebAppInfo",
|
||||
"description": "Description of the Web App that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method web_app_switch_inline_query inside the Web App.",
|
||||
"html_description": "<td><em>Optional</em>. Description of the <a href=\"/bots/webapps\">Web App</a> that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method <em>web_app_switch_inline_query</em> inside the Web App.</td>",
|
||||
"rst_description": "*Optional*. Description of the `Web App <https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method *web_app_switch_inline_query* inside the Web App.\n",
|
||||
"name": "web_app",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "String",
|
||||
"description": "Deep-linking parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.\n\nExample: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.",
|
||||
"html_description": "<td><em>Optional</em>. <a href=\"/bots/features#deep-linking\">Deep-linking</a> parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only <code>A-Z</code>, <code>a-z</code>, <code>0-9</code>, <code>_</code> and <code>-</code> are allowed.<br/>\n<br/>\n<em>Example:</em> An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a <a href=\"#inlinekeyboardmarkup\"><em>switch_inline</em></a> button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.</td>",
|
||||
"rst_description": "*Optional*. `Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.\n\n\n\n*Example:* An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube account to adapt search results accordingly. To do this, it displays a 'Connect your YouTube account' button above the results, or even before showing any. The user presses the button, switches to a private chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an OAuth link. Once done, the bot can offer a `https://core.telegram.org/bots/api#inlinekeyboardmarkup <https://core.telegram.org/bots/api#inlinekeyboardmarkup>`_ *switch_inline* button so that the user can easily return to the chat where they wanted to use the bot's inline capabilities.\n",
|
||||
"name": "start_parameter",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
}
|
||||
}
|
||||
57
.butcher/types/SwitchInlineQueryChosenChat/entity.json
Normal file
57
.butcher/types/SwitchInlineQueryChosenChat/entity.json
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
"meta": {},
|
||||
"group": {
|
||||
"title": "Available types",
|
||||
"anchor": "available-types"
|
||||
},
|
||||
"object": {
|
||||
"anchor": "switchinlinequerychosenchat",
|
||||
"name": "SwitchInlineQueryChosenChat",
|
||||
"description": "This object represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.",
|
||||
"html_description": "<p>This object represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.</p>",
|
||||
"rst_description": "This object represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted",
|
||||
"html_description": "<td><em>Optional</em>. The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted</td>",
|
||||
"rst_description": "*Optional*. The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted\n",
|
||||
"name": "query",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if private chats with users can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if private chats with users can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if private chats with users can be chosen\n",
|
||||
"name": "allow_user_chats",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if private chats with bots can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if private chats with bots can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if private chats with bots can be chosen\n",
|
||||
"name": "allow_bot_chats",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if group and supergroup chats can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if group and supergroup chats can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if group and supergroup chats can be chosen\n",
|
||||
"name": "allow_group_chats",
|
||||
"required": false
|
||||
},
|
||||
{
|
||||
"type": "Boolean",
|
||||
"description": "True, if channel chats can be chosen",
|
||||
"html_description": "<td><em>Optional</em>. True, if channel chats can be chosen</td>",
|
||||
"rst_description": "*Optional*. True, if channel chats can be chosen\n",
|
||||
"name": "allow_channel_chats",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
}
|
||||
}
|
||||
|
|
@ -7,10 +7,19 @@
|
|||
"object": {
|
||||
"anchor": "writeaccessallowed",
|
||||
"name": "WriteAccessAllowed",
|
||||
"description": "This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.",
|
||||
"html_description": "<p>This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.</p>",
|
||||
"rst_description": "This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.",
|
||||
"annotations": [],
|
||||
"description": "This object represents a service message about a user allowing a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link.",
|
||||
"html_description": "<p>This object represents a service message about a user allowing a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link.</p>",
|
||||
"rst_description": "This object represents a service message about a user allowing a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link.",
|
||||
"annotations": [
|
||||
{
|
||||
"type": "String",
|
||||
"description": "Name of the Web App which was launched from a link",
|
||||
"html_description": "<td><em>Optional</em>. Name of the Web App which was launched from a link</td>",
|
||||
"rst_description": "*Optional*. Name of the Web App which was launched from a link\n",
|
||||
"name": "web_app_name",
|
||||
"required": false
|
||||
}
|
||||
],
|
||||
"category": "types"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
CHANGES/1160.bugfix
Normal file
1
CHANGES/1160.bugfix
Normal file
|
|
@ -0,0 +1 @@
|
|||
Added missing FORUM_TOPIC_EDITED value to content_type property
|
||||
17
CHANGES/1161.feature.rst
Normal file
17
CHANGES/1161.feature.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
Added support for FSM in Forum topics.
|
||||
|
||||
The strategy can be changed in dispatcher:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from aiogram.fsm.strategy import FSMStrategy
|
||||
...
|
||||
dispatcher = Dispatcher(
|
||||
fsm_strategy=FSMStrategy.USER_IN_THREAD,
|
||||
storage=..., # Any persistent storage
|
||||
)
|
||||
|
||||
.. note::
|
||||
|
||||
If you have implemented you own storages you should extend record key generation
|
||||
with new one attribute - `thread_id`
|
||||
1
CHANGES/1162.bugfix.rst
Normal file
1
CHANGES/1162.bugfix.rst
Normal file
|
|
@ -0,0 +1 @@
|
|||
Fixed compatibility with Python 3.8-3.9
|
||||
4
CHANGES/1163.feature.rst
Normal file
4
CHANGES/1163.feature.rst
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
Improved CallbackData serialization.
|
||||
|
||||
- Minimized UUID (hex without dashes)
|
||||
- Replaced bool values with int (true=1, false=0)
|
||||
6
CHANGES/1168.misc.rst
Normal file
6
CHANGES/1168.misc.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
Added full support of `Bot API 6.7 <https://core.telegram.org/bots/api-changelog#april-21-2023>`_
|
||||
|
||||
.. warning::
|
||||
|
||||
Note that arguments *switch_pm_parameter* and *switch_pm_text* was deprecated
|
||||
and should be changed to *button* argument as described in API docs.
|
||||
3
CHANGES/1170.removal.rst
Normal file
3
CHANGES/1170.removal.rst
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Removed text filter in due to is planned to remove this filter few versions ago.
|
||||
|
||||
Use :code:`F.text` instead
|
||||
|
|
@ -71,6 +71,7 @@ from ..methods import (
|
|||
GetMyCommands,
|
||||
GetMyDefaultAdministratorRights,
|
||||
GetMyDescription,
|
||||
GetMyName,
|
||||
GetMyShortDescription,
|
||||
GetStickerSet,
|
||||
GetUpdates,
|
||||
|
|
@ -115,6 +116,7 @@ from ..methods import (
|
|||
SetMyCommands,
|
||||
SetMyDefaultAdministratorRights,
|
||||
SetMyDescription,
|
||||
SetMyName,
|
||||
SetMyShortDescription,
|
||||
SetPassportDataErrors,
|
||||
SetStickerEmojiList,
|
||||
|
|
@ -140,6 +142,7 @@ from ..types import (
|
|||
BotCommand,
|
||||
BotCommandScope,
|
||||
BotDescription,
|
||||
BotName,
|
||||
BotShortDescription,
|
||||
Chat,
|
||||
ChatAdministratorRights,
|
||||
|
|
@ -158,6 +161,7 @@ from ..types import (
|
|||
GameHighScore,
|
||||
InlineKeyboardMarkup,
|
||||
InlineQueryResult,
|
||||
InlineQueryResultsButton,
|
||||
InputFile,
|
||||
InputMedia,
|
||||
InputMediaAudio,
|
||||
|
|
@ -481,8 +485,9 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
cache_time: Optional[int] = None,
|
||||
is_personal: Optional[bool] = None,
|
||||
next_offset: Optional[str] = None,
|
||||
switch_pm_text: Optional[str] = None,
|
||||
button: Optional[InlineQueryResultsButton] = None,
|
||||
switch_pm_parameter: Optional[str] = None,
|
||||
switch_pm_text: Optional[str] = None,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
|
|
@ -495,10 +500,11 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
:param inline_query_id: Unique identifier for the answered query
|
||||
:param results: A JSON-serialized array of results for the inline query
|
||||
:param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300.
|
||||
:param is_personal: Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query
|
||||
:param is_personal: Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.
|
||||
:param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don't support pagination. Offset length can't exceed 64 bytes.
|
||||
:param switch_pm_text: If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*
|
||||
:param button: A JSON-serialized object describing a button to be shown above inline query results
|
||||
:param switch_pm_parameter: `Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.
|
||||
:param switch_pm_text: If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*
|
||||
:param request_timeout: Request timeout
|
||||
:return: On success, :code:`True` is returned.
|
||||
"""
|
||||
|
|
@ -509,8 +515,9 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
cache_time=cache_time,
|
||||
is_personal=is_personal,
|
||||
next_offset=next_offset,
|
||||
switch_pm_text=switch_pm_text,
|
||||
button=button,
|
||||
switch_pm_parameter=switch_pm_parameter,
|
||||
switch_pm_text=switch_pm_text,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
|
|
@ -3913,3 +3920,46 @@ class Bot(ContextInstanceMixin["Bot"]):
|
|||
title=title,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
async def get_my_name(
|
||||
self,
|
||||
language_code: Optional[str] = None,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> BotName:
|
||||
"""
|
||||
Use this method to get the current bot name for the given user language. Returns :class:`aiogram.types.bot_name.BotName` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getmyname
|
||||
|
||||
:param language_code: A two-letter ISO 639-1 language code or an empty string
|
||||
:param request_timeout: Request timeout
|
||||
:return: Returns :class:`aiogram.types.bot_name.BotName` on success.
|
||||
"""
|
||||
|
||||
call = GetMyName(
|
||||
language_code=language_code,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
||||
async def set_my_name(
|
||||
self,
|
||||
name: Optional[str] = None,
|
||||
language_code: Optional[str] = None,
|
||||
request_timeout: Optional[int] = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Use this method to change the bot's name. Returns :code:`True` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setmyname
|
||||
|
||||
:param name: New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.
|
||||
:param language_code: A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name.
|
||||
:param request_timeout: Request timeout
|
||||
:return: Returns :code:`True` on success.
|
||||
"""
|
||||
|
||||
call = SetMyName(
|
||||
name=name,
|
||||
language_code=language_code,
|
||||
)
|
||||
return await self(call, request_timeout=request_timeout)
|
||||
|
|
|
|||
|
|
@ -92,8 +92,8 @@ class Dispatcher(Router):
|
|||
|
||||
self.workflow_data: Dict[str, Any] = kwargs
|
||||
self._running_lock = Lock()
|
||||
self._stop_signal = Event()
|
||||
self._stopped_signal = Event()
|
||||
self._stop_signal: Optional[Event] = None
|
||||
self._stopped_signal: Optional[Event] = None
|
||||
|
||||
def __getitem__(self, item: str) -> Any:
|
||||
return self.workflow_data[item]
|
||||
|
|
@ -430,6 +430,8 @@ class Dispatcher(Router):
|
|||
"""
|
||||
if not self._running_lock.locked():
|
||||
raise RuntimeError("Polling is not started")
|
||||
if not self._stop_signal or not self._stopped_signal:
|
||||
return
|
||||
self._stop_signal.set()
|
||||
await self._stopped_signal.wait()
|
||||
|
||||
|
|
@ -438,6 +440,8 @@ class Dispatcher(Router):
|
|||
return
|
||||
|
||||
loggers.dispatcher.warning("Received %s signal", sig.name)
|
||||
if not self._stop_signal:
|
||||
return
|
||||
self._stop_signal.set()
|
||||
|
||||
async def start_polling(
|
||||
|
|
@ -473,6 +477,11 @@ class Dispatcher(Router):
|
|||
)
|
||||
|
||||
async with self._running_lock: # Prevent to run this method twice at a once
|
||||
if self._stop_signal is None:
|
||||
self._stop_signal = Event()
|
||||
if self._stopped_signal is None:
|
||||
self._stopped_signal = Event()
|
||||
|
||||
self._stop_signal.clear()
|
||||
self._stopped_signal.clear()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ from typing import Any, Awaitable, Callable, Dict, Iterator, Optional, Tuple
|
|||
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
||||
from aiogram.types import Chat, TelegramObject, Update, User
|
||||
|
||||
EVENT_FROM_USER_KEY = "event_from_user"
|
||||
EVENT_CHAT_KEY = "event_chat"
|
||||
EVENT_THREAD_ID_KEY = "event_thread_id"
|
||||
|
||||
|
||||
class UserContextMiddleware(BaseMiddleware):
|
||||
async def __call__(
|
||||
|
|
@ -14,61 +18,64 @@ class UserContextMiddleware(BaseMiddleware):
|
|||
) -> Any:
|
||||
if not isinstance(event, Update):
|
||||
raise RuntimeError("UserContextMiddleware got an unexpected event type!")
|
||||
chat, user = self.resolve_event_context(event=event)
|
||||
with self.context(chat=chat, user=user):
|
||||
if user is not None:
|
||||
data["event_from_user"] = user
|
||||
if chat is not None:
|
||||
data["event_chat"] = chat
|
||||
return await handler(event, data)
|
||||
|
||||
@contextmanager
|
||||
def context(self, chat: Optional[Chat] = None, user: Optional[User] = None) -> Iterator[None]:
|
||||
chat_token = None
|
||||
user_token = None
|
||||
if chat:
|
||||
chat_token = chat.set_current(chat)
|
||||
if user:
|
||||
user_token = user.set_current(user)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if chat and chat_token:
|
||||
chat.reset_current(chat_token)
|
||||
if user and user_token:
|
||||
user.reset_current(user_token)
|
||||
chat, user, thread_id = self.resolve_event_context(event=event)
|
||||
if user is not None:
|
||||
data[EVENT_FROM_USER_KEY] = user
|
||||
if chat is not None:
|
||||
data[EVENT_CHAT_KEY] = chat
|
||||
if thread_id is not None:
|
||||
data[EVENT_THREAD_ID_KEY] = thread_id
|
||||
return await handler(event, data)
|
||||
|
||||
@classmethod
|
||||
def resolve_event_context(cls, event: Update) -> Tuple[Optional[Chat], Optional[User]]:
|
||||
def resolve_event_context(
|
||||
cls, event: Update
|
||||
) -> Tuple[Optional[Chat], Optional[User], Optional[int]]:
|
||||
"""
|
||||
Resolve chat and user instance from Update object
|
||||
"""
|
||||
if event.message:
|
||||
return event.message.chat, event.message.from_user
|
||||
return (
|
||||
event.message.chat,
|
||||
event.message.from_user,
|
||||
event.message.message_thread_id if event.message.is_topic_message else None,
|
||||
)
|
||||
if event.edited_message:
|
||||
return event.edited_message.chat, event.edited_message.from_user
|
||||
return (
|
||||
event.edited_message.chat,
|
||||
event.edited_message.from_user,
|
||||
event.edited_message.message_thread_id
|
||||
if event.edited_message.is_topic_message
|
||||
else None,
|
||||
)
|
||||
if event.channel_post:
|
||||
return event.channel_post.chat, None
|
||||
return event.channel_post.chat, None, None
|
||||
if event.edited_channel_post:
|
||||
return event.edited_channel_post.chat, None
|
||||
return event.edited_channel_post.chat, None, None
|
||||
if event.inline_query:
|
||||
return None, event.inline_query.from_user
|
||||
return None, event.inline_query.from_user, None
|
||||
if event.chosen_inline_result:
|
||||
return None, event.chosen_inline_result.from_user
|
||||
return None, event.chosen_inline_result.from_user, None
|
||||
if event.callback_query:
|
||||
if event.callback_query.message:
|
||||
return event.callback_query.message.chat, event.callback_query.from_user
|
||||
return None, event.callback_query.from_user
|
||||
return (
|
||||
event.callback_query.message.chat,
|
||||
event.callback_query.from_user,
|
||||
event.callback_query.message.message_thread_id
|
||||
if event.callback_query.message.is_topic_message
|
||||
else None,
|
||||
)
|
||||
return None, event.callback_query.from_user, None
|
||||
if event.shipping_query:
|
||||
return None, event.shipping_query.from_user
|
||||
return None, event.shipping_query.from_user, None
|
||||
if event.pre_checkout_query:
|
||||
return None, event.pre_checkout_query.from_user
|
||||
return None, event.pre_checkout_query.from_user, None
|
||||
if event.poll_answer:
|
||||
return None, event.poll_answer.user
|
||||
return None, event.poll_answer.user, None
|
||||
if event.my_chat_member:
|
||||
return event.my_chat_member.chat, event.my_chat_member.from_user
|
||||
return event.my_chat_member.chat, event.my_chat_member.from_user, None
|
||||
if event.chat_member:
|
||||
return event.chat_member.chat, event.chat_member.from_user
|
||||
return event.chat_member.chat, event.chat_member.from_user, None
|
||||
if event.chat_join_request:
|
||||
return event.chat_join_request.chat, event.chat_join_request.from_user
|
||||
return None, None
|
||||
return event.chat_join_request.chat, event.chat_join_request.from_user, None
|
||||
return None, None, None
|
||||
|
|
|
|||
|
|
@ -19,14 +19,12 @@ from .exception import ExceptionMessageFilter, ExceptionTypeFilter
|
|||
from .logic import and_f, invert_f, or_f
|
||||
from .magic_data import MagicData
|
||||
from .state import StateFilter
|
||||
from .text import Text
|
||||
|
||||
BaseFilter = Filter
|
||||
|
||||
__all__ = (
|
||||
"Filter",
|
||||
"BaseFilter",
|
||||
"Text",
|
||||
"Command",
|
||||
"CommandObject",
|
||||
"CommandStart",
|
||||
|
|
|
|||
|
|
@ -67,7 +67,11 @@ class CallbackData(BaseModel):
|
|||
return ""
|
||||
if isinstance(value, Enum):
|
||||
return str(value.value)
|
||||
if isinstance(value, (int, str, float, Decimal, Fraction, UUID)):
|
||||
if isinstance(value, UUID):
|
||||
return value.hex
|
||||
if isinstance(value, bool):
|
||||
return str(int(value))
|
||||
if isinstance(value, (int, str, float, Decimal, Fraction)):
|
||||
return str(value)
|
||||
raise ValueError(
|
||||
f"Attribute {key}={value!r} of type {type(value).__name__!r}"
|
||||
|
|
|
|||
|
|
@ -1,136 +0,0 @@
|
|||
from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union
|
||||
|
||||
from aiogram.filters.base import Filter
|
||||
from aiogram.types import CallbackQuery, InlineQuery, Message, Poll
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from aiogram.utils.i18n.lazy_proxy import LazyProxy # NOQA
|
||||
|
||||
TextType = Union[str, "LazyProxy"]
|
||||
|
||||
|
||||
class Text(Filter):
|
||||
"""
|
||||
Is useful for filtering text :class:`aiogram.types.message.Message`,
|
||||
any :class:`aiogram.types.callback_query.CallbackQuery` with `data`,
|
||||
:class:`aiogram.types.inline_query.InlineQuery` or :class:`aiogram.types.poll.Poll` question.
|
||||
|
||||
.. warning::
|
||||
|
||||
Only one of `text`, `contains`, `startswith` or `endswith` argument can be used at once.
|
||||
Any of that arguments can be string, list, set or tuple of strings.
|
||||
|
||||
.. deprecated:: 3.0
|
||||
|
||||
use :ref:`magic-filter <magic-filters>`. For example do :pycode:`F.text == "text"` instead
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"text",
|
||||
"contains",
|
||||
"startswith",
|
||||
"endswith",
|
||||
"ignore_case",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
text: Optional[Union[Sequence[TextType], TextType]] = None,
|
||||
*,
|
||||
contains: Optional[Union[Sequence[TextType], TextType]] = None,
|
||||
startswith: Optional[Union[Sequence[TextType], TextType]] = None,
|
||||
endswith: Optional[Union[Sequence[TextType], TextType]] = None,
|
||||
ignore_case: bool = False,
|
||||
):
|
||||
"""
|
||||
|
||||
:param text: Text equals value or one of values
|
||||
:param contains: Text contains value or one of values
|
||||
:param startswith: Text starts with value or one of values
|
||||
:param endswith: Text ends with value or one of values
|
||||
:param ignore_case: Ignore case when checks
|
||||
"""
|
||||
self._validate_constraints(
|
||||
text=text,
|
||||
contains=contains,
|
||||
startswith=startswith,
|
||||
endswith=endswith,
|
||||
)
|
||||
self.text = self._prepare_argument(text)
|
||||
self.contains = self._prepare_argument(contains)
|
||||
self.startswith = self._prepare_argument(startswith)
|
||||
self.endswith = self._prepare_argument(endswith)
|
||||
self.ignore_case = ignore_case
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self._signature_to_string(
|
||||
text=self.text,
|
||||
contains=self.contains,
|
||||
startswith=self.startswith,
|
||||
endswith=self.endswith,
|
||||
ignore_case=self.ignore_case,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _prepare_argument(
|
||||
cls, value: Optional[Union[Sequence[TextType], TextType]]
|
||||
) -> Optional[Sequence[TextType]]:
|
||||
from aiogram.utils.i18n.lazy_proxy import LazyProxy
|
||||
|
||||
if isinstance(value, (str, LazyProxy)):
|
||||
return [value]
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _validate_constraints(cls, **values: Any) -> None:
|
||||
# Validate that only one text filter type is presented
|
||||
used_args = {key for key, value in values.items() if value is not None}
|
||||
if len(used_args) < 1:
|
||||
raise ValueError(f"Filter should contain one of arguments: {set(values.keys())}")
|
||||
if len(used_args) > 1:
|
||||
raise ValueError(f"Arguments {used_args} cannot be used together")
|
||||
|
||||
async def __call__(
|
||||
self, obj: Union[Message, CallbackQuery, InlineQuery, Poll]
|
||||
) -> Union[bool, Dict[str, Any]]:
|
||||
if isinstance(obj, Message):
|
||||
text = obj.text or obj.caption or ""
|
||||
if not text and obj.poll:
|
||||
text = obj.poll.question
|
||||
elif isinstance(obj, CallbackQuery) and obj.data:
|
||||
text = obj.data
|
||||
elif isinstance(obj, InlineQuery):
|
||||
text = obj.query
|
||||
elif isinstance(obj, Poll):
|
||||
text = obj.question
|
||||
else:
|
||||
return False
|
||||
|
||||
if not text:
|
||||
return False
|
||||
if self.ignore_case:
|
||||
text = text.lower()
|
||||
|
||||
if self.text is not None:
|
||||
equals = map(self.prepare_text, self.text)
|
||||
return text in equals
|
||||
|
||||
if self.contains is not None:
|
||||
contains = map(self.prepare_text, self.contains)
|
||||
return all(map(text.__contains__, contains))
|
||||
|
||||
if self.startswith is not None:
|
||||
startswith = map(self.prepare_text, self.startswith)
|
||||
return any(map(text.startswith, startswith))
|
||||
|
||||
if self.endswith is not None:
|
||||
endswith = map(self.prepare_text, self.endswith)
|
||||
return any(map(text.endswith, endswith))
|
||||
|
||||
# Impossible because the validator prevents this situation
|
||||
return False # pragma: no cover
|
||||
|
||||
def prepare_text(self, text: str) -> str:
|
||||
if self.ignore_case:
|
||||
return str(text).lower()
|
||||
return str(text)
|
||||
|
|
@ -47,25 +47,42 @@ class FSMContextMiddleware(BaseMiddleware):
|
|||
) -> Optional[FSMContext]:
|
||||
user = data.get("event_from_user")
|
||||
chat = data.get("event_chat")
|
||||
thread_id = data.get("event_thread_id")
|
||||
chat_id = chat.id if chat else None
|
||||
user_id = user.id if user else None
|
||||
return self.resolve_context(bot=bot, chat_id=chat_id, user_id=user_id, destiny=destiny)
|
||||
return self.resolve_context(
|
||||
bot=bot,
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
thread_id=thread_id,
|
||||
destiny=destiny,
|
||||
)
|
||||
|
||||
def resolve_context(
|
||||
self,
|
||||
bot: Bot,
|
||||
chat_id: Optional[int],
|
||||
user_id: Optional[int],
|
||||
thread_id: Optional[int] = None,
|
||||
destiny: str = DEFAULT_DESTINY,
|
||||
) -> Optional[FSMContext]:
|
||||
if chat_id is None:
|
||||
chat_id = user_id
|
||||
|
||||
if chat_id is not None and user_id is not None:
|
||||
chat_id, user_id = apply_strategy(
|
||||
chat_id=chat_id, user_id=user_id, strategy=self.strategy
|
||||
chat_id, user_id, thread_id = apply_strategy(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
thread_id=thread_id,
|
||||
strategy=self.strategy,
|
||||
)
|
||||
return self.get_context(
|
||||
bot=bot,
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
thread_id=thread_id,
|
||||
destiny=destiny,
|
||||
)
|
||||
return self.get_context(bot=bot, chat_id=chat_id, user_id=user_id, destiny=destiny)
|
||||
return None
|
||||
|
||||
def get_context(
|
||||
|
|
@ -73,6 +90,7 @@ class FSMContextMiddleware(BaseMiddleware):
|
|||
bot: Bot,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
thread_id: Optional[int] = None,
|
||||
destiny: str = DEFAULT_DESTINY,
|
||||
) -> FSMContext:
|
||||
return FSMContext(
|
||||
|
|
@ -81,6 +99,7 @@ class FSMContextMiddleware(BaseMiddleware):
|
|||
user_id=user_id,
|
||||
chat_id=chat_id,
|
||||
bot_id=bot.id,
|
||||
thread_id=thread_id,
|
||||
destiny=destiny,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class StorageKey:
|
|||
bot_id: int
|
||||
chat_id: int
|
||||
user_id: int
|
||||
thread_id: Optional[int] = None
|
||||
destiny: str = DEFAULT_DESTINY
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,10 @@ class DefaultKeyBuilder(KeyBuilder):
|
|||
parts = [self.prefix]
|
||||
if self.with_bot_id:
|
||||
parts.append(str(key.bot_id))
|
||||
parts.extend([str(key.chat_id), str(key.user_id)])
|
||||
parts.append(str(key.chat_id))
|
||||
if key.thread_id:
|
||||
parts.append(str(key.thread_id))
|
||||
parts.append(str(key.user_id))
|
||||
if self.with_destiny:
|
||||
parts.append(key.destiny)
|
||||
elif key.destiny != DEFAULT_DESTINY:
|
||||
|
|
|
|||
|
|
@ -1,16 +1,24 @@
|
|||
from enum import Enum, auto
|
||||
from typing import Tuple
|
||||
from typing import Optional, Tuple
|
||||
|
||||
|
||||
class FSMStrategy(Enum):
|
||||
USER_IN_CHAT = auto()
|
||||
CHAT = auto()
|
||||
GLOBAL_USER = auto()
|
||||
USER_IN_THREAD = auto()
|
||||
|
||||
|
||||
def apply_strategy(chat_id: int, user_id: int, strategy: FSMStrategy) -> Tuple[int, int]:
|
||||
def apply_strategy(
|
||||
strategy: FSMStrategy,
|
||||
chat_id: int,
|
||||
user_id: int,
|
||||
thread_id: Optional[int] = None,
|
||||
) -> Tuple[int, int, Optional[int]]:
|
||||
if strategy == FSMStrategy.CHAT:
|
||||
return chat_id, chat_id
|
||||
return chat_id, chat_id, None
|
||||
if strategy == FSMStrategy.GLOBAL_USER:
|
||||
return user_id, user_id
|
||||
return chat_id, user_id
|
||||
return user_id, user_id, None
|
||||
if strategy == FSMStrategy.USER_IN_THREAD:
|
||||
return chat_id, user_id, thread_id
|
||||
return chat_id, user_id, None
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ from .get_me import GetMe
|
|||
from .get_my_commands import GetMyCommands
|
||||
from .get_my_default_administrator_rights import GetMyDefaultAdministratorRights
|
||||
from .get_my_description import GetMyDescription
|
||||
from .get_my_name import GetMyName
|
||||
from .get_my_short_description import GetMyShortDescription
|
||||
from .get_sticker_set import GetStickerSet
|
||||
from .get_updates import GetUpdates
|
||||
|
|
@ -92,6 +93,7 @@ from .set_game_score import SetGameScore
|
|||
from .set_my_commands import SetMyCommands
|
||||
from .set_my_default_administrator_rights import SetMyDefaultAdministratorRights
|
||||
from .set_my_description import SetMyDescription
|
||||
from .set_my_name import SetMyName
|
||||
from .set_my_short_description import SetMyShortDescription
|
||||
from .set_passport_data_errors import SetPassportDataErrors
|
||||
from .set_sticker_emoji_list import SetStickerEmojiList
|
||||
|
|
@ -161,6 +163,7 @@ __all__ = (
|
|||
"GetMyCommands",
|
||||
"GetMyDefaultAdministratorRights",
|
||||
"GetMyDescription",
|
||||
"GetMyName",
|
||||
"GetMyShortDescription",
|
||||
"GetStickerSet",
|
||||
"GetUpdates",
|
||||
|
|
@ -207,6 +210,7 @@ __all__ = (
|
|||
"SetMyCommands",
|
||||
"SetMyDefaultAdministratorRights",
|
||||
"SetMyDescription",
|
||||
"SetMyName",
|
||||
"SetMyShortDescription",
|
||||
"SetPassportDataErrors",
|
||||
"SetStickerEmojiList",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ from __future__ import annotations
|
|||
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
|
||||
from ..types import InlineQueryResult
|
||||
from pydantic import Field
|
||||
|
||||
from ..types import InlineQueryResult, InlineQueryResultsButton
|
||||
from .base import TelegramMethod
|
||||
|
||||
|
||||
|
|
@ -25,10 +27,18 @@ class AnswerInlineQuery(TelegramMethod[bool]):
|
|||
cache_time: Optional[int] = None
|
||||
"""The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300."""
|
||||
is_personal: Optional[bool] = None
|
||||
"""Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query"""
|
||||
"""Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query."""
|
||||
next_offset: Optional[str] = None
|
||||
"""Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don't support pagination. Offset length can't exceed 64 bytes."""
|
||||
switch_pm_text: Optional[str] = None
|
||||
"""If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*"""
|
||||
switch_pm_parameter: Optional[str] = None
|
||||
"""`Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed."""
|
||||
button: Optional[InlineQueryResultsButton] = None
|
||||
"""A JSON-serialized object describing a button to be shown above inline query results"""
|
||||
switch_pm_parameter: Optional[str] = Field(None, deprecated=True)
|
||||
"""`Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.
|
||||
|
||||
.. deprecated:: API:6.7
|
||||
https://core.telegram.org/bots/api-changelog#april-21-2023"""
|
||||
switch_pm_text: Optional[str] = Field(None, deprecated=True)
|
||||
"""If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*
|
||||
|
||||
.. deprecated:: API:6.7
|
||||
https://core.telegram.org/bots/api-changelog#april-21-2023"""
|
||||
|
|
|
|||
18
aiogram/methods/get_my_name.py
Normal file
18
aiogram/methods/get_my_name.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
from typing import Optional
|
||||
|
||||
from ..types import BotName
|
||||
from .base import TelegramMethod
|
||||
|
||||
|
||||
class GetMyName(TelegramMethod[BotName]):
|
||||
"""
|
||||
Use this method to get the current bot name for the given user language. Returns :class:`aiogram.types.bot_name.BotName` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#getmyname
|
||||
"""
|
||||
|
||||
__returning__ = BotName
|
||||
__api_method__ = "getMyName"
|
||||
|
||||
language_code: Optional[str] = None
|
||||
"""A two-letter ISO 639-1 language code or an empty string"""
|
||||
19
aiogram/methods/set_my_name.py
Normal file
19
aiogram/methods/set_my_name.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from typing import Optional
|
||||
|
||||
from .base import TelegramMethod
|
||||
|
||||
|
||||
class SetMyName(TelegramMethod[bool]):
|
||||
"""
|
||||
Use this method to change the bot's name. Returns :code:`True` on success.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#setmyname
|
||||
"""
|
||||
|
||||
__returning__ = bool
|
||||
__api_method__ = "setMyName"
|
||||
|
||||
name: Optional[str] = None
|
||||
"""New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language."""
|
||||
language_code: Optional[str] = None
|
||||
"""A two-letter ISO 639-1 language code. If empty, the name will be shown to all users for whose language there is no dedicated name."""
|
||||
|
|
@ -15,6 +15,7 @@ from .bot_command_scope_chat_administrators import BotCommandScopeChatAdministra
|
|||
from .bot_command_scope_chat_member import BotCommandScopeChatMember
|
||||
from .bot_command_scope_default import BotCommandScopeDefault
|
||||
from .bot_description import BotDescription
|
||||
from .bot_name import BotName
|
||||
from .bot_short_description import BotShortDescription
|
||||
from .callback_game import CallbackGame
|
||||
from .callback_query import CallbackQuery
|
||||
|
|
@ -77,6 +78,7 @@ from .inline_query_result_photo import InlineQueryResultPhoto
|
|||
from .inline_query_result_venue import InlineQueryResultVenue
|
||||
from .inline_query_result_video import InlineQueryResultVideo
|
||||
from .inline_query_result_voice import InlineQueryResultVoice
|
||||
from .inline_query_results_button import InlineQueryResultsButton
|
||||
from .input_contact_message_content import InputContactMessageContent
|
||||
from .input_file import BufferedInputFile, FSInputFile, InputFile, URLInputFile
|
||||
from .input_invoice_message_content import InputInvoiceMessageContent
|
||||
|
|
@ -139,6 +141,7 @@ from .shipping_query import ShippingQuery
|
|||
from .sticker import Sticker
|
||||
from .sticker_set import StickerSet
|
||||
from .successful_payment import SuccessfulPayment
|
||||
from .switch_inline_query_chosen_chat import SwitchInlineQueryChosenChat
|
||||
from .update import Update
|
||||
from .user import User
|
||||
from .user_profile_photos import UserProfilePhotos
|
||||
|
|
@ -169,6 +172,7 @@ __all__ = (
|
|||
"BotCommandScopeChatMember",
|
||||
"BotCommandScopeDefault",
|
||||
"BotDescription",
|
||||
"BotName",
|
||||
"BotShortDescription",
|
||||
"BufferedInputFile",
|
||||
"CallbackGame",
|
||||
|
|
@ -234,6 +238,7 @@ __all__ = (
|
|||
"InlineQueryResultVenue",
|
||||
"InlineQueryResultVideo",
|
||||
"InlineQueryResultVoice",
|
||||
"InlineQueryResultsButton",
|
||||
"InputContactMessageContent",
|
||||
"InputFile",
|
||||
"InputInvoiceMessageContent",
|
||||
|
|
@ -294,6 +299,7 @@ __all__ = (
|
|||
"Sticker",
|
||||
"StickerSet",
|
||||
"SuccessfulPayment",
|
||||
"SwitchInlineQueryChosenChat",
|
||||
"TelegramObject",
|
||||
"UNSET_PARSE_MODE",
|
||||
"URLInputFile",
|
||||
|
|
|
|||
12
aiogram/types/bot_name.py
Normal file
12
aiogram/types/bot_name.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from .base import TelegramObject
|
||||
|
||||
|
||||
class BotName(TelegramObject):
|
||||
"""
|
||||
This object represents the bot's name.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#botname
|
||||
"""
|
||||
|
||||
name: str
|
||||
"""The bot's name"""
|
||||
|
|
@ -52,3 +52,5 @@ class ChatMemberUpdated(TelegramObject):
|
|||
"""New information about the chat member"""
|
||||
invite_link: Optional[ChatInviteLink] = None
|
||||
"""*Optional*. Chat invite link, which was used by the user to join the chat; for joining by invite link events only."""
|
||||
via_chat_folder_invite_link: Optional[bool] = None
|
||||
"""*Optional*. True, if the user joined the chat via a chat folder invite link"""
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from .base import MutableTelegramObject
|
|||
if TYPE_CHECKING:
|
||||
from .callback_game import CallbackGame
|
||||
from .login_url import LoginUrl
|
||||
from .switch_inline_query_chosen_chat import SwitchInlineQueryChosenChat
|
||||
from .web_app_info import WebAppInfo
|
||||
|
||||
|
||||
|
|
@ -31,6 +32,8 @@ class InlineKeyboardButton(MutableTelegramObject):
|
|||
"""*Optional*. If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot's username and the specified inline query in the input field. May be empty, in which case just the bot's username will be inserted."""
|
||||
switch_inline_query_current_chat: Optional[str] = None
|
||||
"""*Optional*. If set, pressing the button will insert the bot's username and the specified inline query in the current chat's input field. May be empty, in which case only the bot's username will be inserted."""
|
||||
switch_inline_query_chosen_chat: Optional[SwitchInlineQueryChosenChat] = None
|
||||
"""*Optional*. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field"""
|
||||
callback_game: Optional[CallbackGame] = None
|
||||
"""*Optional*. Description of the game that will be launched when the user presses the button."""
|
||||
pay: Optional[bool] = None
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from .base import TelegramObject
|
|||
if TYPE_CHECKING:
|
||||
from ..methods import AnswerInlineQuery
|
||||
from .inline_query_result import InlineQueryResult
|
||||
from .inline_query_results_button import InlineQueryResultsButton
|
||||
from .location import Location
|
||||
from .user import User
|
||||
|
||||
|
|
@ -39,8 +40,9 @@ class InlineQuery(TelegramObject):
|
|||
cache_time: Optional[int] = None,
|
||||
is_personal: Optional[bool] = None,
|
||||
next_offset: Optional[str] = None,
|
||||
switch_pm_text: Optional[str] = None,
|
||||
button: Optional[InlineQueryResultsButton] = None,
|
||||
switch_pm_parameter: Optional[str] = None,
|
||||
switch_pm_text: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
) -> AnswerInlineQuery:
|
||||
"""
|
||||
|
|
@ -57,10 +59,11 @@ class InlineQuery(TelegramObject):
|
|||
|
||||
:param results: A JSON-serialized array of results for the inline query
|
||||
:param cache_time: The maximum amount of time in seconds that the result of the inline query may be cached on the server. Defaults to 300.
|
||||
:param is_personal: Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query
|
||||
:param is_personal: Pass :code:`True` if results may be cached on the server side only for the user that sent the query. By default, results may be returned to any user who sends the same query.
|
||||
:param next_offset: Pass the offset that a client should send in the next query with the same text to receive more results. Pass an empty string if there are no more results or if you don't support pagination. Offset length can't exceed 64 bytes.
|
||||
:param switch_pm_text: If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*
|
||||
:param button: A JSON-serialized object describing a button to be shown above inline query results
|
||||
:param switch_pm_parameter: `Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when user presses the switch button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed.
|
||||
:param switch_pm_text: If passed, clients will display a button with specified text that switches the user to a private chat with the bot and sends the bot a start message with the parameter *switch_pm_parameter*
|
||||
:return: instance of method :class:`aiogram.methods.answer_inline_query.AnswerInlineQuery`
|
||||
"""
|
||||
# DO NOT EDIT MANUALLY!!!
|
||||
|
|
@ -74,7 +77,8 @@ class InlineQuery(TelegramObject):
|
|||
cache_time=cache_time,
|
||||
is_personal=is_personal,
|
||||
next_offset=next_offset,
|
||||
switch_pm_text=switch_pm_text,
|
||||
button=button,
|
||||
switch_pm_parameter=switch_pm_parameter,
|
||||
switch_pm_text=switch_pm_text,
|
||||
**kwargs,
|
||||
)
|
||||
|
|
|
|||
23
aiogram/types/inline_query_results_button.py
Normal file
23
aiogram/types/inline_query_results_button.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .web_app_info import WebAppInfo
|
||||
|
||||
|
||||
class InlineQueryResultsButton(TelegramObject):
|
||||
"""
|
||||
This object represents a button to be shown above inline query results. You **must** use exactly one of the optional fields.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inlinequeryresultsbutton
|
||||
"""
|
||||
|
||||
text: str
|
||||
"""Label text on the button"""
|
||||
web_app: Optional[WebAppInfo] = None
|
||||
"""*Optional*. Description of the `Web App <https://core.telegram.org/bots/webapps>`_ that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method *web_app_switch_inline_query* inside the Web App."""
|
||||
start_parameter: Optional[str] = None
|
||||
"""*Optional*. `Deep-linking <https://core.telegram.org/bots/features#deep-linking>`_ parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only :code:`A-Z`, :code:`a-z`, :code:`0-9`, :code:`_` and :code:`-` are allowed."""
|
||||
|
|
@ -317,6 +317,8 @@ class Message(TelegramObject):
|
|||
return ContentType.MESSAGE_AUTO_DELETE_TIMER_CHANGED
|
||||
if self.forum_topic_created:
|
||||
return ContentType.FORUM_TOPIC_CREATED
|
||||
if self.forum_topic_edited:
|
||||
return ContentType.FORUM_TOPIC_EDITED
|
||||
if self.forum_topic_closed:
|
||||
return ContentType.FORUM_TOPIC_CLOSED
|
||||
if self.forum_topic_reopened:
|
||||
|
|
|
|||
22
aiogram/types/switch_inline_query_chosen_chat.py
Normal file
22
aiogram/types/switch_inline_query_chosen_chat.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
from typing import Optional
|
||||
|
||||
from .base import TelegramObject
|
||||
|
||||
|
||||
class SwitchInlineQueryChosenChat(TelegramObject):
|
||||
"""
|
||||
This object represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#switchinlinequerychosenchat
|
||||
"""
|
||||
|
||||
query: Optional[str] = None
|
||||
"""*Optional*. The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted"""
|
||||
allow_user_chats: Optional[bool] = None
|
||||
"""*Optional*. True, if private chats with users can be chosen"""
|
||||
allow_bot_chats: Optional[bool] = None
|
||||
"""*Optional*. True, if private chats with bots can be chosen"""
|
||||
allow_group_chats: Optional[bool] = None
|
||||
"""*Optional*. True, if group and supergroup chats can be chosen"""
|
||||
allow_channel_chats: Optional[bool] = None
|
||||
"""*Optional*. True, if channel chats can be chosen"""
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
from typing import Optional
|
||||
|
||||
from aiogram.types import TelegramObject
|
||||
|
||||
|
||||
class WriteAccessAllowed(TelegramObject):
|
||||
"""
|
||||
This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.
|
||||
This object represents a service message about a user allowing a bot to write messages after adding the bot to the attachment menu or launching a Web App from a link.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#writeaccessallowed
|
||||
"""
|
||||
|
||||
web_app_name: Optional[str] = None
|
||||
"""*Optional*. Name of the Web App which was launched from a link"""
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from typing import Any, Awaitable, Callable, Dict, Optional, Set, cast
|
|||
try:
|
||||
from babel import Locale, UnknownLocaleError
|
||||
except ImportError: # pragma: no cover
|
||||
Locale = None
|
||||
Locale = None # type: ignore
|
||||
|
||||
class UnknownLocaleError(Exception): # type: ignore
|
||||
pass
|
||||
|
|
@ -127,7 +127,7 @@ class SimpleI18nMiddleware(I18nMiddleware):
|
|||
|
||||
if locale.language not in self.i18n.available_locales:
|
||||
return self.i18n.default_locale
|
||||
return cast(str, locale.language)
|
||||
return locale.language
|
||||
|
||||
|
||||
class ConstI18nMiddleware(I18nMiddleware):
|
||||
|
|
|
|||
|
|
@ -26,3 +26,49 @@ def create_tg_link(link: str, **kwargs: Any) -> str:
|
|||
|
||||
def create_telegram_link(*path: str, **kwargs: Any) -> str:
|
||||
return _format_url("https://t.me", *path, **kwargs)
|
||||
|
||||
|
||||
def create_channel_bot_link(
|
||||
username: str,
|
||||
parameter: Optional[str] = None,
|
||||
change_info: bool = False,
|
||||
post_messages: bool = False,
|
||||
edit_messages: bool = False,
|
||||
delete_messages: bool = False,
|
||||
restrict_members: bool = False,
|
||||
invite_users: bool = False,
|
||||
pin_messages: bool = False,
|
||||
promote_members: bool = False,
|
||||
manage_video_chats: bool = False,
|
||||
anonymous: bool = False,
|
||||
manage_chat: bool = False,
|
||||
) -> str:
|
||||
params = {}
|
||||
if parameter is not None:
|
||||
params["startgroup"] = parameter
|
||||
permissions = []
|
||||
if change_info:
|
||||
permissions.append("change_info")
|
||||
if post_messages:
|
||||
permissions.append("post_messages")
|
||||
if edit_messages:
|
||||
permissions.append("edit_messages")
|
||||
if delete_messages:
|
||||
permissions.append("delete_messages")
|
||||
if restrict_members:
|
||||
permissions.append("restrict_members")
|
||||
if invite_users:
|
||||
permissions.append("invite_users")
|
||||
if pin_messages:
|
||||
permissions.append("pin_messages")
|
||||
if promote_members:
|
||||
permissions.append("promote_members")
|
||||
if manage_video_chats:
|
||||
permissions.append("manage_video_chats")
|
||||
if anonymous:
|
||||
permissions.append("anonymous")
|
||||
if manage_chat:
|
||||
permissions.append("manage_chat")
|
||||
if permissions:
|
||||
params["admin"] = "+".join(permissions)
|
||||
return create_telegram_link(username, **params)
|
||||
|
|
|
|||
37
docs/api/methods/get_my_name.rst
Normal file
37
docs/api/methods/get_my_name.rst
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#########
|
||||
getMyName
|
||||
#########
|
||||
|
||||
Returns: :obj:`BotName`
|
||||
|
||||
.. automodule:: aiogram.methods.get_my_name
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As bot method
|
||||
-------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
result: BotName = await bot.get_my_name(...)
|
||||
|
||||
|
||||
Method as object
|
||||
----------------
|
||||
|
||||
Imports:
|
||||
|
||||
- :code:`from aiogram.methods.get_my_name import GetMyName`
|
||||
- alias: :code:`from aiogram.methods import GetMyName`
|
||||
|
||||
With specific bot
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: BotName = await bot(GetMyName(...))
|
||||
|
|
@ -42,6 +42,7 @@ Available methods
|
|||
get_my_commands
|
||||
get_my_default_administrator_rights
|
||||
get_my_description
|
||||
get_my_name
|
||||
get_my_short_description
|
||||
get_user_profile_photos
|
||||
hide_general_forum_topic
|
||||
|
|
@ -78,6 +79,7 @@ Available methods
|
|||
set_my_commands
|
||||
set_my_default_administrator_rights
|
||||
set_my_description
|
||||
set_my_name
|
||||
set_my_short_description
|
||||
unban_chat_member
|
||||
unban_chat_sender_chat
|
||||
|
|
|
|||
44
docs/api/methods/set_my_name.rst
Normal file
44
docs/api/methods/set_my_name.rst
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#########
|
||||
setMyName
|
||||
#########
|
||||
|
||||
Returns: :obj:`bool`
|
||||
|
||||
.. automodule:: aiogram.methods.set_my_name
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
As bot method
|
||||
-------------
|
||||
|
||||
.. code-block::
|
||||
|
||||
result: bool = await bot.set_my_name(...)
|
||||
|
||||
|
||||
Method as object
|
||||
----------------
|
||||
|
||||
Imports:
|
||||
|
||||
- :code:`from aiogram.methods.set_my_name import SetMyName`
|
||||
- alias: :code:`from aiogram.methods import SetMyName`
|
||||
|
||||
With specific bot
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
result: bool = await bot(SetMyName(...))
|
||||
|
||||
As reply into Webhook in handler
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
return SetMyName(...)
|
||||
9
docs/api/types/bot_name.rst
Normal file
9
docs/api/types/bot_name.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#######
|
||||
BotName
|
||||
#######
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.bot_name
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -34,6 +34,7 @@ Inline mode
|
|||
inline_query_result_venue
|
||||
inline_query_result_video
|
||||
inline_query_result_voice
|
||||
inline_query_results_button
|
||||
input_contact_message_content
|
||||
input_invoice_message_content
|
||||
input_location_message_content
|
||||
|
|
@ -60,6 +61,7 @@ Available types
|
|||
bot_command_scope_chat_member
|
||||
bot_command_scope_default
|
||||
bot_description
|
||||
bot_name
|
||||
bot_short_description
|
||||
callback_query
|
||||
chat
|
||||
|
|
@ -121,6 +123,7 @@ Available types
|
|||
reply_keyboard_markup
|
||||
reply_keyboard_remove
|
||||
response_parameters
|
||||
switch_inline_query_chosen_chat
|
||||
user
|
||||
user_profile_photos
|
||||
user_shared
|
||||
|
|
|
|||
9
docs/api/types/inline_query_results_button.rst
Normal file
9
docs/api/types/inline_query_results_button.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
########################
|
||||
InlineQueryResultsButton
|
||||
########################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.inline_query_results_button
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
9
docs/api/types/switch_inline_query_chosen_chat.rst
Normal file
9
docs/api/types/switch_inline_query_chosen_chat.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
###########################
|
||||
SwitchInlineQueryChosenChat
|
||||
###########################
|
||||
|
||||
|
||||
.. automodule:: aiogram.types.switch_inline_query_chosen_chat
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: True
|
||||
|
|
@ -16,7 +16,6 @@ Here is list of builtin filters:
|
|||
:maxdepth: 1
|
||||
|
||||
command
|
||||
text
|
||||
chat_member_updated
|
||||
magic_filters
|
||||
magic_data
|
||||
|
|
@ -69,7 +68,7 @@ If you specify multiple filters in a row, it will be checked with an "and" condi
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
@<router>.message(Text(startswith="show"), Text(endswith="example"))
|
||||
@<router>.message(F.text.startswith("show"), F.text.endswith("example"))
|
||||
|
||||
|
||||
Also, if you want to use two alternative ways to run the same handler ("or" condition)
|
||||
|
|
@ -77,7 +76,7 @@ you can register the handler twice or more times as you like
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
@<router>.message(Text(text="hi"))
|
||||
@<router>.message(F.text == "hi")
|
||||
@<router>.message(CommandStart())
|
||||
|
||||
|
||||
|
|
@ -96,7 +95,7 @@ An alternative way is to combine using special functions (:func:`and_f`, :func:`
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
and_f(Text(startswith="show"), Text(endswith="example"))
|
||||
or_f(Text(text="hi"), CommandStart())
|
||||
and_f(F.text.startswith("show"), F.text.endswith("example"))
|
||||
or_f(F.text(text="hi"), CommandStart())
|
||||
invert_f(IsAdmin())
|
||||
and_f(<A>, or_f(<B>, <C>))
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
====
|
||||
Text
|
||||
====
|
||||
|
||||
.. autoclass:: aiogram.filters.text.Text
|
||||
:members:
|
||||
:member-order: bysource
|
||||
:undoc-members: False
|
||||
|
||||
Can be imported:
|
||||
|
||||
- :code:`from aiogram.filters.text import Text`
|
||||
- :code:`from aiogram.filters import Text`
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
#. Text equals with the specified value: :code:`Text(text="text") # value == 'text'`
|
||||
#. Text starts with the specified value: :code:`Text(startswith="text") # value.startswith('text')`
|
||||
#. Text ends with the specified value: :code:`Text(endswith="text") # value.endswith('text')`
|
||||
#. Text contains the specified value: :code:`Text(contains="text") # value in 'text'`
|
||||
#. Any of previous listed filters can be list, set or tuple of strings that's mean any of listed value should be equals/startswith/endswith/contains: :code:`Text(text=["text", "spam"])`
|
||||
#. Ignore case can be combined with any previous listed filter: :code:`Text(text="Text", ignore_case=True) # value.lower() == 'text'.lower()`
|
||||
|
||||
Allowed handlers
|
||||
================
|
||||
|
||||
Allowed update types for this filter:
|
||||
|
||||
- :code:`message`
|
||||
- :code:`edited_message`
|
||||
- :code:`channel_post`
|
||||
- :code:`edited_channel_post`
|
||||
- :code:`inline_query`
|
||||
- :code:`callback_query`
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) 2022, aiogram Team
|
||||
# This file is distributed under the same license as the aiogram package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: aiogram\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-25 22:10+0300\n"
|
||||
"PO-Revision-Date: 2022-10-25 17:49+0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.10.3\n"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:3
|
||||
msgid "Text"
|
||||
msgstr "Текст"
|
||||
|
||||
#: aiogram.filters.text.Text:1 of
|
||||
msgid ""
|
||||
"Is useful for filtering text :class:`aiogram.types.message.Message`, any "
|
||||
":class:`aiogram.types.callback_query.CallbackQuery` with `data`, "
|
||||
":class:`aiogram.types.inline_query.InlineQuery` or "
|
||||
":class:`aiogram.types.poll.Poll` question."
|
||||
msgstr ""
|
||||
"Корисно для фільтрації тексту :class:`aiogram.types.message.Message`, "
|
||||
"будь-якого :class:`aiogram.types.callback_query.CallbackQuery` з `data`, "
|
||||
":class:`aiogram.types.inline_query.InlineQuery` або : "
|
||||
"class:`aiogram.types.poll.Poll` опитування."
|
||||
|
||||
#: aiogram.filters.text.Text:7 of
|
||||
msgid ""
|
||||
"Only one of `text`, `contains`, `startswith` or `endswith` argument can "
|
||||
"be used at once. Any of that arguments can be string, list, set or tuple "
|
||||
"of strings."
|
||||
msgstr ""
|
||||
"Одночасно можна використати лише один із аргументів `text`, `contains`, "
|
||||
"`startswith` або `endswith` . Будь-який із цих аргументів може бути "
|
||||
"рядком, списком, набором (set) або кортежем рядків."
|
||||
|
||||
#: aiogram.filters.text.Text:12 of
|
||||
msgid ""
|
||||
"use :ref:`magic-filter <magic-filters>`. For example do :pycode:`F.text "
|
||||
"== \"text\"` instead"
|
||||
msgstr ""
|
||||
"використати :ref:`magic-filter <magic-filters>`. Наприклад "
|
||||
":pycode:`F.text == \"text\"` instead"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:10
|
||||
msgid "Can be imported:"
|
||||
msgstr "Можна імпортувати:"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:12
|
||||
msgid ":code:`from aiogram.filters.text import Text`"
|
||||
msgstr ":code:`from aiogram.filters.text import Text`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:13
|
||||
msgid ":code:`from aiogram.filters import Text`"
|
||||
msgstr ":code:`from aiogram.filters import Text`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:16
|
||||
msgid "Usage"
|
||||
msgstr "Використання"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:18
|
||||
msgid ""
|
||||
"Text equals with the specified value: :code:`Text(text=\"text\") # value"
|
||||
" == 'text'`"
|
||||
msgstr ""
|
||||
"Текст дорівнює вказаному значенню: :code:`Text(text=\"text\") # value =="
|
||||
" 'text'`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:19
|
||||
msgid ""
|
||||
"Text starts with the specified value: :code:`Text(startswith=\"text\") #"
|
||||
" value.startswith('text')`"
|
||||
msgstr ""
|
||||
"Текст починається з указаного значення: :code:`Text(startswith=\"text\")"
|
||||
" # value.startswith('text')`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:20
|
||||
msgid ""
|
||||
"Text ends with the specified value: :code:`Text(endswith=\"text\") # "
|
||||
"value.endswith('text')`"
|
||||
msgstr ""
|
||||
"Текст закінчується вказаним значенням: :code:`Text(endswith=\"text\") # "
|
||||
"value.endswith('text')`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:21
|
||||
msgid ""
|
||||
"Text contains the specified value: :code:`Text(contains=\"text\") # "
|
||||
"value in 'text'`"
|
||||
msgstr ""
|
||||
"Текст містить вказане значення: :code:`Text(contains=\"text\") # value "
|
||||
"in 'text'`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:22
|
||||
msgid ""
|
||||
"Any of previous listed filters can be list, set or tuple of strings "
|
||||
"that's mean any of listed value should be "
|
||||
"equals/startswith/endswith/contains: :code:`Text(text=[\"text\", "
|
||||
"\"spam\"])`"
|
||||
msgstr ""
|
||||
"Будь-який із попередніх перерахованих фільтрів може бути списком, набором"
|
||||
" або кортежем рядків, що означає, що будь-яке значення у списку має "
|
||||
"дорівнювати/починатися/закінчуватися/містити: :code:`Text(text=[\"text\","
|
||||
" \"spam\"])`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:23
|
||||
msgid ""
|
||||
"Ignore case can be combined with any previous listed filter: "
|
||||
":code:`Text(text=\"Text\", ignore_case=True) # value.lower() == "
|
||||
"'text'.lower()`"
|
||||
msgstr ""
|
||||
"Ігнорування регістру можна поєднати з будь-яким фільтром із попереднього "
|
||||
"списку: :code:`Text(text=\"Text\", ignore_case=True) # value.lower() == "
|
||||
"'text'.lower()`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:26
|
||||
msgid "Allowed handlers"
|
||||
msgstr "Дозволені обробники (handlers)"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:28
|
||||
msgid "Allowed update types for this filter:"
|
||||
msgstr "Дозволені типи оновлень для цього фільтра:"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:30
|
||||
msgid ":code:`message`"
|
||||
msgstr ":code:`message`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:31
|
||||
msgid ":code:`edited_message`"
|
||||
msgstr ":code:`edited_message`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:32
|
||||
msgid ":code:`channel_post`"
|
||||
msgstr ":code:`channel_post`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:33
|
||||
msgid ":code:`edited_channel_post`"
|
||||
msgstr ":code:`edited_channel_post`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:34
|
||||
msgid ":code:`inline_query`"
|
||||
msgstr ":code:`inline_query`"
|
||||
|
||||
#: ../../dispatcher/filters/text.rst:35
|
||||
msgid ":code:`callback_query`"
|
||||
msgstr ":code:`callback_query`"
|
||||
|
|
@ -5,7 +5,7 @@ from aiogram import Bot, Dispatcher, Router, types
|
|||
from aiogram.filters import Command
|
||||
from aiogram.types import Message
|
||||
|
||||
# Bot token can be obtained via https://t.me/BotFahter
|
||||
# Bot token can be obtained via https://t.me/BotFather
|
||||
TOKEN = "42:TOKEN"
|
||||
|
||||
# All handlers should be attached to the Router (or Dispatcher)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ classifiers = [
|
|||
dependencies = [
|
||||
"magic-filter~=1.0.9",
|
||||
"aiohttp~=3.8.4",
|
||||
"pydantic~=1.10.4",
|
||||
"pydantic~=1.10.7",
|
||||
"aiofiles~=23.1.0",
|
||||
"certifi>=2022.9.24",
|
||||
]
|
||||
|
|
@ -56,24 +56,25 @@ fast = [
|
|||
"uvloop>=0.17.0; (sys_platform == 'darwin' or sys_platform == 'linux') and platform_python_implementation != 'PyPy'",
|
||||
]
|
||||
redis = [
|
||||
"redis~=4.5.1",
|
||||
"redis~=4.5.4",
|
||||
]
|
||||
proxy = [
|
||||
"aiohttp-socks~=0.7.1",
|
||||
"aiohttp-socks~=0.8.0",
|
||||
]
|
||||
i18n = [
|
||||
"Babel~=2.11.0",
|
||||
"Babel~=2.12.1",
|
||||
]
|
||||
test = [
|
||||
"pytest~=7.2.1",
|
||||
"pytest~=7.3.1",
|
||||
"pytest-html~=3.2.0",
|
||||
"pytest-asyncio~=0.20.3",
|
||||
"pytest-asyncio~=0.21.0",
|
||||
"pytest-lazy-fixture~=0.6.3",
|
||||
"pytest-mock~=3.10.0",
|
||||
"pytest-mypy~=0.10.0",
|
||||
"pytest-cov~=4.0.0",
|
||||
"pytest-aiohttp~=1.0.4",
|
||||
"aresponses~=2.1.6",
|
||||
"pytz~=2022.7.1"
|
||||
]
|
||||
docs = [
|
||||
"Sphinx~=5.2.3",
|
||||
|
|
@ -91,15 +92,15 @@ docs = [
|
|||
"sphinxcontrib-towncrier~=0.3.1a3",
|
||||
]
|
||||
dev = [
|
||||
"black~=23.1",
|
||||
"black~=23.3.0",
|
||||
"isort~=5.11",
|
||||
"ruff~=0.0.246",
|
||||
"mypy~=1.0.0",
|
||||
"ruff~=0.0.262",
|
||||
"mypy~=1.2.0",
|
||||
"toml~=0.10.2",
|
||||
"pre-commit~=3.0.4",
|
||||
"pre-commit~=3.2.2",
|
||||
"towncrier~=22.12.0",
|
||||
"packaging~=23.0",
|
||||
"typing-extensions~=4.4.0",
|
||||
"typing-extensions~=4.5.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
|
@ -148,7 +149,14 @@ features = [
|
|||
"test",
|
||||
]
|
||||
extra-dependencies = [
|
||||
"butcher @ git+https://github.com/aiogram/butcher.git@v0.1.13"
|
||||
"butcher @ git+https://github.com/aiogram/butcher.git@v0.1.14"
|
||||
]
|
||||
|
||||
[tool.hatch.envs.dev.scripts]
|
||||
update = [
|
||||
"butcher parse",
|
||||
"butcher refresh",
|
||||
"butcher apply all",
|
||||
]
|
||||
|
||||
[tool.hatch.envs.test]
|
||||
|
|
|
|||
11
tests/test_api/test_methods/test_get_my_name.py
Normal file
11
tests/test_api/test_methods/test_get_my_name.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from aiogram.methods import GetMyName
|
||||
from aiogram.types import BotDescription, BotName
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestGetMyName:
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(GetMyName, ok=True, result=BotName(name="Test"))
|
||||
|
||||
response: BotName = await bot.get_my_name()
|
||||
assert response == prepare_result.result
|
||||
10
tests/test_api/test_methods/test_set_my_name.py
Normal file
10
tests/test_api/test_methods/test_set_my_name.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from aiogram.methods import SetMyName
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestSetMyName:
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(SetMyName, ok=True, result=True)
|
||||
|
||||
response: bool = await bot.set_my_name()
|
||||
assert response == prepare_result.result
|
||||
|
|
@ -44,6 +44,7 @@ from aiogram.types import (
|
|||
EncryptedCredentials,
|
||||
ForumTopicClosed,
|
||||
ForumTopicCreated,
|
||||
ForumTopicEdited,
|
||||
ForumTopicReopened,
|
||||
Game,
|
||||
InlineKeyboardButton,
|
||||
|
|
@ -414,6 +415,16 @@ TEST_FORUM_TOPIC_CREATED = Message(
|
|||
icon_color=0xFFD67E,
|
||||
),
|
||||
)
|
||||
TEST_FORUM_TOPIC_EDITED = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
forum_topic_edited=ForumTopicEdited(
|
||||
name="test_edited",
|
||||
icon_color=0xFFD67E,
|
||||
),
|
||||
)
|
||||
TEST_FORUM_TOPIC_CLOSED = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
|
|
@ -484,6 +495,7 @@ class TestMessage:
|
|||
[TEST_MESSAGE_DICE, ContentType.DICE],
|
||||
[TEST_MESSAGE_WEB_APP_DATA, ContentType.WEB_APP_DATA],
|
||||
[TEST_FORUM_TOPIC_CREATED, ContentType.FORUM_TOPIC_CREATED],
|
||||
[TEST_FORUM_TOPIC_EDITED, ContentType.FORUM_TOPIC_EDITED],
|
||||
[TEST_FORUM_TOPIC_CLOSED, ContentType.FORUM_TOPIC_CLOSED],
|
||||
[TEST_FORUM_TOPIC_REOPENED, ContentType.FORUM_TOPIC_REOPENED],
|
||||
[TEST_MESSAGE_UNKNOWN, ContentType.UNKNOWN],
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from aiogram import Bot
|
|||
from aiogram.dispatcher.dispatcher import Dispatcher
|
||||
from aiogram.dispatcher.event.bases import UNHANDLED, SkipHandler
|
||||
from aiogram.dispatcher.router import Router
|
||||
from aiogram.methods import GetMe, GetUpdates, Request, SendMessage, TelegramMethod
|
||||
from aiogram.methods import GetMe, GetUpdates, SendMessage, TelegramMethod
|
||||
from aiogram.types import (
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
|
|
@ -462,9 +462,9 @@ class TestDispatcher:
|
|||
async def my_handler(event: Any, **kwargs: Any):
|
||||
assert event == getattr(update, event_type)
|
||||
if has_chat:
|
||||
assert Chat.get_current(False)
|
||||
assert kwargs["event_chat"]
|
||||
if has_user:
|
||||
assert User.get_current(False)
|
||||
assert kwargs["event_from_user"]
|
||||
return kwargs
|
||||
|
||||
result = await router.feed_update(bot, update, test="PASS")
|
||||
|
|
@ -708,10 +708,15 @@ class TestDispatcher:
|
|||
with pytest.raises(RuntimeError):
|
||||
await dispatcher.stop_polling()
|
||||
|
||||
assert not dispatcher._stop_signal.is_set()
|
||||
assert not dispatcher._stopped_signal.is_set()
|
||||
assert not dispatcher._stop_signal
|
||||
assert not dispatcher._stopped_signal
|
||||
with patch("asyncio.locks.Event.wait", new_callable=AsyncMock) as mocked_wait:
|
||||
async with dispatcher._running_lock:
|
||||
await dispatcher.stop_polling()
|
||||
assert not dispatcher._stop_signal
|
||||
|
||||
dispatcher._stop_signal = Event()
|
||||
dispatcher._stopped_signal = Event()
|
||||
await dispatcher.stop_polling()
|
||||
assert dispatcher._stop_signal.is_set()
|
||||
mocked_wait.assert_awaited()
|
||||
|
|
@ -723,6 +728,11 @@ class TestDispatcher:
|
|||
mocked_set.assert_not_called()
|
||||
|
||||
async with dispatcher._running_lock:
|
||||
dispatcher._signal_stop_polling(signal.SIGINT)
|
||||
mocked_set.assert_not_called()
|
||||
|
||||
dispatcher._stop_signal = Event()
|
||||
dispatcher._stopped_signal = Event()
|
||||
dispatcher._signal_stop_polling(signal.SIGINT)
|
||||
mocked_set.assert_called()
|
||||
|
||||
|
|
@ -764,12 +774,29 @@ class TestDispatcher:
|
|||
|
||||
def test_run_polling(self, bot: MockedBot):
|
||||
dispatcher = Dispatcher()
|
||||
|
||||
async def stop():
|
||||
await asyncio.sleep(0.5)
|
||||
await dispatcher.stop_polling()
|
||||
|
||||
start_called = False
|
||||
|
||||
@dispatcher.startup()
|
||||
async def startup():
|
||||
nonlocal start_called
|
||||
start_called = True
|
||||
asyncio.create_task(stop())
|
||||
|
||||
original_start_polling = dispatcher.start_polling
|
||||
with patch(
|
||||
"aiogram.dispatcher.dispatcher.Dispatcher.start_polling"
|
||||
"aiogram.dispatcher.dispatcher.Dispatcher.start_polling",
|
||||
side_effect=original_start_polling,
|
||||
) as patched_start_polling:
|
||||
dispatcher.run_polling(bot)
|
||||
patched_start_polling.assert_awaited_once()
|
||||
|
||||
assert start_called
|
||||
|
||||
async def test_feed_webhook_update_fast_process(self, bot: MockedBot):
|
||||
dispatcher = Dispatcher()
|
||||
dispatcher.message.register(simple_message_handler)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.dispatcher.middlewares.user_context import UserContextMiddleware
|
||||
from aiogram.types import Update
|
||||
|
||||
|
||||
async def next_handler(*args, **kwargs):
|
||||
|
|
@ -11,3 +14,13 @@ class TestUserContextMiddleware:
|
|||
async def test_unexpected_event_type(self):
|
||||
with pytest.raises(RuntimeError):
|
||||
await UserContextMiddleware()(next_handler, object(), {})
|
||||
|
||||
async def test_call(self):
|
||||
middleware = UserContextMiddleware()
|
||||
data = {}
|
||||
with patch.object(UserContextMiddleware, "resolve_event_context", return_value=[1, 2, 3]):
|
||||
await middleware(next_handler, Update(update_id=42), data)
|
||||
|
||||
assert data["event_chat"] == 1
|
||||
assert data["event_from_user"] == 2
|
||||
assert data["event_thread_id"] == 3
|
||||
|
|
|
|||
|
|
@ -49,34 +49,38 @@ class TestCallbackData:
|
|||
pass
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,success,expected",
|
||||
"value,expected",
|
||||
[
|
||||
[None, True, ""],
|
||||
[42, True, "42"],
|
||||
["test", True, "test"],
|
||||
[9.99, True, "9.99"],
|
||||
[Decimal("9.99"), True, "9.99"],
|
||||
[Fraction("3/2"), True, "3/2"],
|
||||
[
|
||||
UUID("123e4567-e89b-12d3-a456-426655440000"),
|
||||
True,
|
||||
"123e4567-e89b-12d3-a456-426655440000",
|
||||
],
|
||||
[MyIntEnum.FOO, True, "1"],
|
||||
[MyStringEnum.FOO, True, "FOO"],
|
||||
[..., False, "..."],
|
||||
[object, False, "..."],
|
||||
[object(), False, "..."],
|
||||
[User(id=42, is_bot=False, first_name="test"), False, "..."],
|
||||
[None, ""],
|
||||
[True, "1"],
|
||||
[False, "0"],
|
||||
[42, "42"],
|
||||
["test", "test"],
|
||||
[9.99, "9.99"],
|
||||
[Decimal("9.99"), "9.99"],
|
||||
[Fraction("3/2"), "3/2"],
|
||||
[UUID("123e4567-e89b-12d3-a456-426655440000"), "123e4567e89b12d3a456426655440000"],
|
||||
[MyIntEnum.FOO, "1"],
|
||||
[MyStringEnum.FOO, "FOO"],
|
||||
],
|
||||
)
|
||||
def test_encode_value(self, value, success, expected):
|
||||
def test_encode_value_positive(self, value, expected):
|
||||
callback = MyCallback(foo="test", bar=42)
|
||||
if success:
|
||||
assert callback._encode_value("test", value) == expected
|
||||
else:
|
||||
with pytest.raises(ValueError):
|
||||
assert callback._encode_value("test", value) == expected
|
||||
assert callback._encode_value("test", value) == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value",
|
||||
[
|
||||
...,
|
||||
object,
|
||||
object(),
|
||||
User(id=42, is_bot=False, first_name="test"),
|
||||
],
|
||||
)
|
||||
def test_encode_value_negative(self, value):
|
||||
callback = MyCallback(foo="test", bar=42)
|
||||
with pytest.raises(ValueError):
|
||||
callback._encode_value("test", value)
|
||||
|
||||
def test_pack(self):
|
||||
with pytest.raises(ValueError, match="Separator symbol .+"):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.filters import Text, and_f, invert_f, or_f
|
||||
from aiogram.filters import Command, and_f, invert_f, or_f
|
||||
from aiogram.filters.logic import _AndFilter, _InvertFilter, _OrFilter
|
||||
|
||||
|
||||
|
|
@ -28,10 +28,10 @@ class TestLogic:
|
|||
@pytest.mark.parametrize(
|
||||
"case,type_",
|
||||
[
|
||||
[or_f(Text(text="test"), Text(text="test")), _OrFilter],
|
||||
[and_f(Text(text="test"), Text(text="test")), _AndFilter],
|
||||
[invert_f(Text(text="test")), _InvertFilter],
|
||||
[~Text(text="test"), _InvertFilter],
|
||||
[or_f(Command("test"), Command("test")), _OrFilter],
|
||||
[and_f(Command("test"), Command("test")), _AndFilter],
|
||||
[invert_f(Command("test")), _InvertFilter],
|
||||
[~Command("test"), _InvertFilter],
|
||||
],
|
||||
)
|
||||
def test_dunder_methods(self, case, type_):
|
||||
|
|
|
|||
|
|
@ -1,245 +0,0 @@
|
|||
import datetime
|
||||
from itertools import permutations
|
||||
from typing import Sequence, Type
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.filters import Text
|
||||
from aiogram.types import (
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
InlineQuery,
|
||||
Message,
|
||||
Poll,
|
||||
PollOption,
|
||||
User,
|
||||
)
|
||||
|
||||
|
||||
class TestText:
|
||||
@pytest.mark.parametrize(
|
||||
"kwargs",
|
||||
[
|
||||
{},
|
||||
{"ignore_case": True},
|
||||
{"ignore_case": False},
|
||||
],
|
||||
)
|
||||
def test_not_enough_arguments(self, kwargs):
|
||||
with pytest.raises(ValueError):
|
||||
Text(**kwargs)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"first,last",
|
||||
permutations(["text", "contains", "startswith", "endswith"], 2),
|
||||
)
|
||||
@pytest.mark.parametrize("ignore_case", [True, False])
|
||||
def test_validator_too_few_arguments(self, first, last, ignore_case):
|
||||
kwargs = {first: "test", last: "test", "ignore_case": ignore_case}
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
Text(**kwargs)
|
||||
|
||||
@pytest.mark.parametrize("argument", ["text", "contains", "startswith", "endswith"])
|
||||
@pytest.mark.parametrize("input_type", [str, list, tuple])
|
||||
def test_validator_convert_to_list(self, argument: str, input_type: Type):
|
||||
text = Text(**{argument: input_type("test")})
|
||||
assert hasattr(text, argument)
|
||||
assert isinstance(getattr(text, argument), Sequence)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"argument,ignore_case,input_value,update_type,result",
|
||||
[
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"test",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
False,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"test",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
caption="test",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"test",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
True,
|
||||
"TEst",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="tesT",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"TEst",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="tesT",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
False,
|
||||
],
|
||||
[
|
||||
"startswith",
|
||||
False,
|
||||
"test",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test case",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"endswith",
|
||||
False,
|
||||
"case",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test case",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"contains",
|
||||
False,
|
||||
" ",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test case",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"startswith",
|
||||
True,
|
||||
"question",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
poll=Poll(
|
||||
id="poll id",
|
||||
question="Question?",
|
||||
options=[PollOption(text="A", voter_count=0)],
|
||||
is_closed=False,
|
||||
is_anonymous=False,
|
||||
type="regular",
|
||||
allows_multiple_answers=False,
|
||||
total_voter_count=0,
|
||||
),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"startswith",
|
||||
True,
|
||||
"callback:",
|
||||
CallbackQuery(
|
||||
id="query id",
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
chat_instance="instance",
|
||||
data="callback:data",
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"startswith",
|
||||
True,
|
||||
"query",
|
||||
InlineQuery(
|
||||
id="query id",
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
query="query line",
|
||||
offset="offset",
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
True,
|
||||
"question",
|
||||
Poll(
|
||||
id="poll id",
|
||||
question="Question",
|
||||
options=[PollOption(text="A", voter_count=0)],
|
||||
is_closed=False,
|
||||
is_anonymous=False,
|
||||
type="regular",
|
||||
allows_multiple_answers=False,
|
||||
total_voter_count=0,
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
True,
|
||||
["question", "another question"],
|
||||
Poll(
|
||||
id="poll id",
|
||||
question="Another question",
|
||||
options=[PollOption(text="A", voter_count=0)],
|
||||
is_closed=False,
|
||||
is_anonymous=False,
|
||||
type="quiz",
|
||||
allows_multiple_answers=False,
|
||||
total_voter_count=0,
|
||||
correct_option_id=0,
|
||||
),
|
||||
True,
|
||||
],
|
||||
["text", True, ["question", "another question"], object(), False],
|
||||
],
|
||||
)
|
||||
async def test_check_text(self, argument, ignore_case, input_value, result, update_type):
|
||||
text = Text(**{argument: input_value}, ignore_case=ignore_case)
|
||||
test = await text(update_type)
|
||||
assert test is result
|
||||
|
||||
def test_str(self):
|
||||
text = Text("test")
|
||||
assert str(text) == "Text(text=['test'], ignore_case=False)"
|
||||
|
|
@ -11,6 +11,7 @@ PREFIX = "test"
|
|||
BOT_ID = 42
|
||||
CHAT_ID = -1
|
||||
USER_ID = 2
|
||||
THREAD_ID = 3
|
||||
FIELD = "data"
|
||||
|
||||
|
||||
|
|
@ -46,6 +47,19 @@ class TestRedisDefaultKeyBuilder:
|
|||
with pytest.raises(ValueError):
|
||||
key_builder.build(key, FIELD)
|
||||
|
||||
def test_thread_id(self):
|
||||
key_builder = DefaultKeyBuilder(
|
||||
prefix=PREFIX,
|
||||
)
|
||||
key = StorageKey(
|
||||
chat_id=CHAT_ID,
|
||||
user_id=USER_ID,
|
||||
bot_id=BOT_ID,
|
||||
thread_id=THREAD_ID,
|
||||
destiny=DEFAULT_DESTINY,
|
||||
)
|
||||
assert key_builder.build(key, FIELD) == f"{PREFIX}:{CHAT_ID}:{THREAD_ID}:{USER_ID}:{FIELD}"
|
||||
|
||||
def test_create_isolation(self):
|
||||
fake_redis = object()
|
||||
storage = RedisStorage(redis=fake_redis)
|
||||
|
|
|
|||
|
|
@ -2,19 +2,41 @@ import pytest
|
|||
|
||||
from aiogram.fsm.strategy import FSMStrategy, apply_strategy
|
||||
|
||||
CHAT_ID = -42
|
||||
USER_ID = 42
|
||||
THREAD_ID = 1
|
||||
|
||||
PRIVATE = (USER_ID, USER_ID, None)
|
||||
CHAT = (CHAT_ID, USER_ID, None)
|
||||
THREAD = (CHAT_ID, USER_ID, THREAD_ID)
|
||||
|
||||
|
||||
class TestStrategy:
|
||||
@pytest.mark.parametrize(
|
||||
"strategy,case,expected",
|
||||
[
|
||||
[FSMStrategy.USER_IN_CHAT, (-42, 42), (-42, 42)],
|
||||
[FSMStrategy.CHAT, (-42, 42), (-42, -42)],
|
||||
[FSMStrategy.GLOBAL_USER, (-42, 42), (42, 42)],
|
||||
[FSMStrategy.USER_IN_CHAT, (42, 42), (42, 42)],
|
||||
[FSMStrategy.CHAT, (42, 42), (42, 42)],
|
||||
[FSMStrategy.GLOBAL_USER, (42, 42), (42, 42)],
|
||||
[FSMStrategy.USER_IN_CHAT, CHAT, CHAT],
|
||||
[FSMStrategy.USER_IN_CHAT, PRIVATE, PRIVATE],
|
||||
[FSMStrategy.USER_IN_CHAT, THREAD, CHAT],
|
||||
[FSMStrategy.CHAT, CHAT, (CHAT_ID, CHAT_ID, None)],
|
||||
[FSMStrategy.CHAT, PRIVATE, (USER_ID, USER_ID, None)],
|
||||
[FSMStrategy.CHAT, THREAD, (CHAT_ID, CHAT_ID, None)],
|
||||
[FSMStrategy.GLOBAL_USER, CHAT, PRIVATE],
|
||||
[FSMStrategy.GLOBAL_USER, PRIVATE, PRIVATE],
|
||||
[FSMStrategy.GLOBAL_USER, THREAD, PRIVATE],
|
||||
[FSMStrategy.USER_IN_THREAD, CHAT, CHAT],
|
||||
[FSMStrategy.USER_IN_THREAD, PRIVATE, PRIVATE],
|
||||
[FSMStrategy.USER_IN_THREAD, THREAD, THREAD],
|
||||
],
|
||||
)
|
||||
def test_strategy(self, strategy, case, expected):
|
||||
chat_id, user_id = case
|
||||
assert apply_strategy(chat_id=chat_id, user_id=user_id, strategy=strategy) == expected
|
||||
chat_id, user_id, thread_id = case
|
||||
assert (
|
||||
apply_strategy(
|
||||
chat_id=chat_id,
|
||||
user_id=user_id,
|
||||
thread_id=thread_id,
|
||||
strategy=strategy,
|
||||
)
|
||||
== expected
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,22 @@
|
|||
from itertools import product
|
||||
from typing import Any, Dict
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.utils.link import BRANCH, create_telegram_link, create_tg_link, docs_url
|
||||
from aiogram.utils.link import (
|
||||
BRANCH,
|
||||
create_telegram_link,
|
||||
create_tg_link,
|
||||
docs_url,
|
||||
create_channel_bot_link,
|
||||
)
|
||||
|
||||
|
||||
class TestLink:
|
||||
@pytest.mark.parametrize(
|
||||
"base,params,result",
|
||||
[["user", dict(id=42), "tg://user?id=42"]],
|
||||
[["user", {"id": 42}, "tg://user?id=42"]],
|
||||
)
|
||||
def test_create_tg_link(self, base: str, params: Dict[str, Any], result: str):
|
||||
assert create_tg_link(base, **params) == result
|
||||
|
|
@ -16,8 +24,8 @@ class TestLink:
|
|||
@pytest.mark.parametrize(
|
||||
"base,params,result",
|
||||
[
|
||||
["username", dict(), "https://t.me/username"],
|
||||
["username", dict(start="test"), "https://t.me/username?start=test"],
|
||||
["username", {}, "https://t.me/username"],
|
||||
["username", {"start": "test"}, "https://t.me/username?start=test"],
|
||||
],
|
||||
)
|
||||
def test_create_telegram_link(self, base: str, params: Dict[str, Any], result: str):
|
||||
|
|
@ -31,3 +39,45 @@ class TestLink:
|
|||
|
||||
def test_docs(self):
|
||||
assert docs_url("test.html") == f"https://docs.aiogram.dev/en/{BRANCH}/test.html"
|
||||
|
||||
|
||||
class TestCreateChannelBotLink:
|
||||
def test_without_params(self):
|
||||
assert create_channel_bot_link("test_bot") == "https://t.me/test_bot"
|
||||
|
||||
def test_parameter(self):
|
||||
assert (
|
||||
create_channel_bot_link("test_bot", parameter="parameter in group")
|
||||
== "https://t.me/test_bot?startgroup=parameter+in+group"
|
||||
)
|
||||
|
||||
def test_permissions(self):
|
||||
# Is bad idea to put over 2k cases into parameterized test,
|
||||
# so I've preferred to implement it inside the test
|
||||
|
||||
params = {
|
||||
"change_info",
|
||||
"post_messages",
|
||||
"edit_messages",
|
||||
"delete_messages",
|
||||
"restrict_members",
|
||||
"invite_users",
|
||||
"pin_messages",
|
||||
"promote_members",
|
||||
"manage_video_chats",
|
||||
"anonymous",
|
||||
"manage_chat",
|
||||
}
|
||||
|
||||
variants = product([True, False], repeat=len(params))
|
||||
for index, variants in enumerate(variants):
|
||||
kwargs = {k: v for k, v in zip(params, variants) if v}
|
||||
if not kwargs:
|
||||
# Variant without additional arguments is already covered
|
||||
continue
|
||||
|
||||
link = create_channel_bot_link("test", **kwargs)
|
||||
query = parse_qs(link.split("?", maxsplit=1)[-1], max_num_fields=1)
|
||||
assert "admin" in query
|
||||
admin = query["admin"][0]
|
||||
assert set(admin.split("+")) == set(kwargs)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue