diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index fc260046..1247ec56 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,7 +19,7 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip poetry==1.0 + python -m pip install --upgrade pip poetry==1.1.4 poetry install mkdir -p reports diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6061286a..d42ee11e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: max-parallel: 6 + fail-fast: false matrix: os: - ubuntu-latest @@ -21,6 +22,7 @@ jobs: python-version: - 3.7 - 3.8 + - 3.9 steps: - uses: actions/checkout@master @@ -32,7 +34,7 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip poetry==1.0 + python -m pip install --upgrade pip poetry==1.1.4 poetry install - name: Lint code diff --git a/.gitignore b/.gitignore index 43d848ee..1f3e1971 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,12 @@ .idea/ +.vscode/ __pycache__/ *.py[cod] env/ build/ +_build/ dist/ site/ *.egg-info/ diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..523f4428 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,14 @@ +version: 2 + +sphinx: + configuration: docs2/conf.py + +formats: all + +python: + version: 3.8 + install: + - method: pip + path: . + extra_requirements: + - docs diff --git a/Makefile b/Makefile index 6a2f442a..ca31515a 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ clean: .PHONY: isort isort: - $(py) isort -rc aiogram tests + $(py) isort aiogram tests .PHONY: black black: diff --git a/aiogram/__init__.py b/aiogram/__init__.py index 435b4eaf..0d751e51 100644 --- a/aiogram/__init__.py +++ b/aiogram/__init__.py @@ -1,6 +1,5 @@ -from .api import methods, types -from .api.client import session -from .api.client.bot import Bot +from .client import session +from .client.bot import Bot from .dispatcher import filters, handler from .dispatcher.dispatcher import Dispatcher from .dispatcher.middlewares.base import BaseMiddleware @@ -28,5 +27,5 @@ __all__ = ( "handler", ) -__version__ = "3.0.0a5" -__api_version__ = "4.9" +__version__ = "3.0.0a6" +__api_version__ = "5.0" diff --git a/aiogram/api/client/session/__init__.py b/aiogram/api/client/session/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/aiogram/api/client/telegram.py b/aiogram/api/client/telegram.py deleted file mode 100644 index 97775e03..00000000 --- a/aiogram/api/client/telegram.py +++ /dev/null @@ -1,38 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class TelegramAPIServer: - """ - Base config for API Endpoints - """ - - base: str - file: str - - def api_url(self, token: str, method: str) -> str: - """ - Generate URL for API methods - - :param token: Bot token - :param method: API method name (case insensitive) - :return: URL - """ - return self.base.format(token=token, method=method) - - def file_url(self, token: str, path: str) -> str: - """ - Generate URL for downloading files - - :param token: Bot token - :param path: file path - :return: URL - """ - return self.file.format(token=token, path=path) - - -# Main API server -PRODUCTION = TelegramAPIServer( - base="https://api.telegram.org/bot{token}/{method}", - file="https://api.telegram.org/file/bot{token}/{path}", -) diff --git a/aiogram/api/methods/answer_callback_query.py b/aiogram/api/methods/answer_callback_query.py deleted file mode 100644 index 1f407c70..00000000 --- a/aiogram/api/methods/answer_callback_query.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional - -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class AnswerCallbackQuery(TelegramMethod[bool]): - """ - Use this method to send answers to callback queries sent from inline keyboards. The answer - will be displayed to the user as a notification at the top of the chat screen or as an alert. - On success, True is returned. - Alternatively, the user can be redirected to the specified Game URL. For this option to work, - you must first create a game for your bot via @Botfather and accept the terms. Otherwise, you - may use links like t.me/your_bot?start=XXXX that open your bot with a parameter. - - Source: https://core.telegram.org/bots/api#answercallbackquery - """ - - __returning__ = bool - - callback_query_id: str - """Unique identifier for the query to be answered""" - text: Optional[str] = None - """Text of the notification. If not specified, nothing will be shown to the user, 0-200 - characters""" - show_alert: Optional[bool] = None - """If true, an alert will be shown by the client instead of a notification at the top of the - chat screen. Defaults to false.""" - url: Optional[str] = None - """URL that will be opened by the user's client. If you have created a Game and accepted the - conditions via @Botfather, specify the URL that opens your game — note that this will only - work if the query comes from a callback_game button.""" - cache_time: Optional[int] = None - """The maximum amount of time in seconds that the result of the callback query may be cached - client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="answerCallbackQuery", data=data) diff --git a/aiogram/api/methods/answer_pre_checkout_query.py b/aiogram/api/methods/answer_pre_checkout_query.py deleted file mode 100644 index 8ffae74e..00000000 --- a/aiogram/api/methods/answer_pre_checkout_query.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional - -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class AnswerPreCheckoutQuery(TelegramMethod[bool]): - """ - Once the user has confirmed their payment and shipping details, the Bot API sends the final - confirmation in the form of an Update with the field pre_checkout_query. Use this method to - respond to such pre-checkout queries. On success, True is returned. Note: The Bot API must - receive an answer within 10 seconds after the pre-checkout query was sent. - - Source: https://core.telegram.org/bots/api#answerprecheckoutquery - """ - - __returning__ = bool - - pre_checkout_query_id: str - """Unique identifier for the query to be answered""" - ok: bool - """Specify True if everything is alright (goods are available, etc.) and the bot is ready to - proceed with the order. Use False if there are any problems.""" - error_message: Optional[str] = None - """Required if ok is False. Error message in human readable form that explains the reason for - failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our - amazing black T-shirts while you were busy filling out your payment details. Please choose - a different color or garment!"). Telegram will display this message to the user.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="answerPreCheckoutQuery", data=data) diff --git a/aiogram/api/methods/edit_message_caption.py b/aiogram/api/methods/edit_message_caption.py deleted file mode 100644 index 3359c45f..00000000 --- a/aiogram/api/methods/edit_message_caption.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import UNSET, InlineKeyboardMarkup, Message -from .base import Request, TelegramMethod, prepare_parse_mode - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class EditMessageCaption(TelegramMethod[Union[Message, bool]]): - """ - Use this method to edit captions of messages. On success, if edited message is sent by the - bot, the edited Message is returned, otherwise True is returned. - - Source: https://core.telegram.org/bots/api#editmessagecaption - """ - - __returning__ = Union[Message, bool] - - chat_id: Optional[Union[int, str]] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat or - username of the target channel (in the format @channelusername)""" - message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the message to edit""" - inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" - caption: Optional[str] = None - """New caption of the message, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the message caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for an inline keyboard.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - prepare_parse_mode(bot, data) - - return Request(method="editMessageCaption", data=data) diff --git a/aiogram/api/methods/edit_message_live_location.py b/aiogram/api/methods/edit_message_live_location.py deleted file mode 100644 index ddd95d74..00000000 --- a/aiogram/api/methods/edit_message_live_location.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import InlineKeyboardMarkup, Message -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class EditMessageLiveLocation(TelegramMethod[Union[Message, bool]]): - """ - Use this method to edit live location messages. A location can be edited until its live_period - expires or editing is explicitly disabled by a call to stopMessageLiveLocation. On success, if - the edited message was sent by the bot, the edited Message is returned, otherwise True is - returned. - - Source: https://core.telegram.org/bots/api#editmessagelivelocation - """ - - __returning__ = Union[Message, bool] - - latitude: float - """Latitude of new location""" - longitude: float - """Longitude of new location""" - chat_id: Optional[Union[int, str]] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat or - username of the target channel (in the format @channelusername)""" - message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the message to edit""" - inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for a new inline keyboard.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="editMessageLiveLocation", data=data) diff --git a/aiogram/api/methods/edit_message_text.py b/aiogram/api/methods/edit_message_text.py deleted file mode 100644 index d30fc82e..00000000 --- a/aiogram/api/methods/edit_message_text.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import UNSET, InlineKeyboardMarkup, Message -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class EditMessageText(TelegramMethod[Union[Message, bool]]): - """ - Use this method to edit text and game messages. On success, if edited message is sent by the - bot, the edited Message is returned, otherwise True is returned. - - Source: https://core.telegram.org/bots/api#editmessagetext - """ - - __returning__ = Union[Message, bool] - - text: str - """New text of the message, 1-4096 characters after entities parsing""" - chat_id: Optional[Union[int, str]] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat or - username of the target channel (in the format @channelusername)""" - message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the message to edit""" - inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the message text. See formatting options for more details.""" - disable_web_page_preview: Optional[bool] = None - """Disables link previews for links in this message""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for an inline keyboard.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="editMessageText", data=data) diff --git a/aiogram/api/methods/export_chat_invite_link.py b/aiogram/api/methods/export_chat_invite_link.py deleted file mode 100644 index b77b01a5..00000000 --- a/aiogram/api/methods/export_chat_invite_link.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Union - -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class ExportChatInviteLink(TelegramMethod[str]): - """ - Use this method to generate a new invite link for a chat; any previously generated link is - revoked. The bot must be an administrator in the chat for this to work and must have the - appropriate admin rights. Returns the new invite link as String on success. - Note: Each administrator in a chat generates their own invite links. Bots can't use invite - links generated by other administrators. If you want your bot to work with invite links, it - will need to generate its own link using exportChatInviteLink — after this the link will - become available to the bot via the getChat method. If your bot needs to generate a new invite - link replacing its previous one, use exportChatInviteLink again. - - Source: https://core.telegram.org/bots/api#exportchatinvitelink - """ - - __returning__ = str - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="exportChatInviteLink", data=data) diff --git a/aiogram/api/methods/get_file.py b/aiogram/api/methods/get_file.py deleted file mode 100644 index 9fce6eb0..00000000 --- a/aiogram/api/methods/get_file.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict - -from ..types import File -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class GetFile(TelegramMethod[File]): - """ - Use this method to get basic info about a file and prepare it for downloading. For the moment, - bots can download files of up to 20MB in size. On success, a File object is returned. The file - can then be downloaded via the link https://api.telegram.org/file/bot/, - where is taken from the response. It is guaranteed that the link will be valid for - at least 1 hour. When the link expires, a new one can be requested by calling getFile again. - Note: This function may not preserve the original file name and MIME type. You should save the - file's MIME type and name (if available) when the File object is received. - - Source: https://core.telegram.org/bots/api#getfile - """ - - __returning__ = File - - file_id: str - """File identifier to get info about""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="getFile", data=data) diff --git a/aiogram/api/methods/get_updates.py b/aiogram/api/methods/get_updates.py deleted file mode 100644 index ffb74c92..00000000 --- a/aiogram/api/methods/get_updates.py +++ /dev/null @@ -1,48 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, List, Optional - -from ..types import Update -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class GetUpdates(TelegramMethod[List[Update]]): - """ - Use this method to receive incoming updates using long polling (wiki). An Array of Update - objects is returned. - Notes - 1. This method will not work if an outgoing webhook is set up. - 2. In order to avoid getting duplicate updates, recalculate offset after each server response. - - Source: https://core.telegram.org/bots/api#getupdates - """ - - __returning__ = List[Update] - - offset: Optional[int] = None - """Identifier of the first update to be returned. Must be greater by one than the highest - among the identifiers of previously received updates. By default, updates starting with the - earliest unconfirmed update are returned. An update is considered confirmed as soon as - getUpdates is called with an offset higher than its update_id. The negative offset can be - specified to retrieve updates starting from -offset update from the end of the updates - queue. All previous updates will forgotten.""" - limit: Optional[int] = None - """Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults - to 100.""" - timeout: Optional[int] = None - """Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be - positive, short polling should be used for testing purposes only.""" - allowed_updates: Optional[List[str]] = None - """A JSON-serialized list of the update types you want your bot to receive. For example, - specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of - these types. See Update for a complete list of available update types. Specify an empty - list to receive all updates regardless of type (default). If not specified, the previous - setting will be used.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="getUpdates", data=data) diff --git a/aiogram/api/methods/send_animation.py b/aiogram/api/methods/send_animation.py deleted file mode 100644 index dcb8e91c..00000000 --- a/aiogram/api/methods/send_animation.py +++ /dev/null @@ -1,74 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import ( - UNSET, - ForceReply, - InlineKeyboardMarkup, - InputFile, - Message, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, -) -from .base import Request, TelegramMethod, prepare_file - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendAnimation(TelegramMethod[Message]): - """ - Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On - success, the sent Message is returned. Bots can currently send animation files of up to 50 MB - in size, this limit may be changed in the future. - - Source: https://core.telegram.org/bots/api#sendanimation - """ - - __returning__ = Message - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - animation: Union[InputFile, str] - """Animation to send. Pass a file_id as String to send an animation that exists on the - Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an - animation from the Internet, or upload a new animation using multipart/form-data.""" - duration: Optional[int] = None - """Duration of sent animation in seconds""" - width: Optional[int] = None - """Animation width""" - height: Optional[int] = None - """Animation height""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - caption: Optional[str] = None - """Animation caption (may also be used when resending animation by file_id), 0-1024 characters - after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the animation caption. See formatting options for more - details.""" - disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the message is a reply, ID of the original message""" - reply_markup: Optional[ - Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] - ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict(exclude={"animation", "thumb"}) - - files: Dict[str, InputFile] = {} - prepare_file(data=data, files=files, name="animation", value=self.animation) - prepare_file(data=data, files=files, name="thumb", value=self.thumb) - - return Request(method="sendAnimation", data=data, files=files) diff --git a/aiogram/api/methods/send_audio.py b/aiogram/api/methods/send_audio.py deleted file mode 100644 index 739fadcb..00000000 --- a/aiogram/api/methods/send_audio.py +++ /dev/null @@ -1,74 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import ( - UNSET, - ForceReply, - InlineKeyboardMarkup, - InputFile, - Message, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, -) -from .base import Request, TelegramMethod, prepare_file - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendAudio(TelegramMethod[Message]): - """ - Use this method to send audio files, if you want Telegram clients to display them in the music - player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message is - returned. Bots can currently send audio files of up to 50 MB in size, this limit may be - changed in the future. - For sending voice messages, use the sendVoice method instead. - - Source: https://core.telegram.org/bots/api#sendaudio - """ - - __returning__ = Message - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - audio: Union[InputFile, str] - """Audio file to send. Pass a file_id as String to send an audio file that exists on the - Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an audio - file from the Internet, or upload a new one using multipart/form-data.""" - caption: Optional[str] = None - """Audio caption, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the audio caption. See formatting options for more details.""" - duration: Optional[int] = None - """Duration of the audio in seconds""" - performer: Optional[str] = None - """Performer""" - title: Optional[str] = None - """Track name""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the message is a reply, ID of the original message""" - reply_markup: Optional[ - Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] - ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict(exclude={"audio", "thumb"}) - - files: Dict[str, InputFile] = {} - prepare_file(data=data, files=files, name="audio", value=self.audio) - prepare_file(data=data, files=files, name="thumb", value=self.thumb) - - return Request(method="sendAudio", data=data, files=files) diff --git a/aiogram/api/methods/send_chat_action.py b/aiogram/api/methods/send_chat_action.py deleted file mode 100644 index bff4283c..00000000 --- a/aiogram/api/methods/send_chat_action.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Union - -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendChatAction(TelegramMethod[bool]): - """ - Use this method when you need to tell the user that something is happening on the bot's side. - The status is set for 5 seconds or less (when a message arrives from your bot, Telegram - clients clear its typing status). Returns True on success. - Example: The ImageBot needs some time to process a request and upload the image. Instead of - sending a text message along the lines of 'Retrieving image, please wait…', the bot may use - sendChatAction with action = upload_photo. The user will see a 'sending photo' status for the - bot. - We only recommend using this method when a response from the bot will take a noticeable amount - of time to arrive. - - Source: https://core.telegram.org/bots/api#sendchataction - """ - - __returning__ = bool - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - action: str - """Type of action to broadcast. Choose one, depending on what the user is about to receive: - typing for text messages, upload_photo for photos, record_video or upload_video for videos, - record_audio or upload_audio for audio files, upload_document for general files, - find_location for location data, record_video_note or upload_video_note for video notes.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="sendChatAction", data=data) diff --git a/aiogram/api/methods/send_document.py b/aiogram/api/methods/send_document.py deleted file mode 100644 index 9ee44b54..00000000 --- a/aiogram/api/methods/send_document.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import ( - UNSET, - ForceReply, - InlineKeyboardMarkup, - InputFile, - Message, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, -) -from .base import Request, TelegramMethod, prepare_file - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendDocument(TelegramMethod[Message]): - """ - Use this method to send general files. On success, the sent Message is returned. Bots can - currently send files of any type of up to 50 MB in size, this limit may be changed in the - future. - - Source: https://core.telegram.org/bots/api#senddocument - """ - - __returning__ = Message - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - document: Union[InputFile, str] - """File to send. Pass a file_id as String to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, - or upload a new one using multipart/form-data.""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - caption: Optional[str] = None - """Document caption (may also be used when resending documents by file_id), 0-1024 characters - after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the document caption. See formatting options for more details.""" - disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the message is a reply, ID of the original message""" - reply_markup: Optional[ - Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] - ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict(exclude={"document", "thumb"}) - - files: Dict[str, InputFile] = {} - prepare_file(data=data, files=files, name="document", value=self.document) - prepare_file(data=data, files=files, name="thumb", value=self.thumb) - - return Request(method="sendDocument", data=data, files=files) diff --git a/aiogram/api/methods/send_invoice.py b/aiogram/api/methods/send_invoice.py deleted file mode 100644 index fa9f0615..00000000 --- a/aiogram/api/methods/send_invoice.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, List, Optional - -from ..types import InlineKeyboardMarkup, LabeledPrice, Message -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendInvoice(TelegramMethod[Message]): - """ - Use this method to send invoices. On success, the sent Message is returned. - - Source: https://core.telegram.org/bots/api#sendinvoice - """ - - __returning__ = Message - - chat_id: int - """Unique identifier for the target private chat""" - title: str - """Product name, 1-32 characters""" - description: str - """Product description, 1-255 characters""" - payload: str - """Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for - your internal processes.""" - provider_token: str - """Payments provider token, obtained via Botfather""" - start_parameter: str - """Unique deep-linking parameter that can be used to generate this invoice when used as a - start parameter""" - currency: str - """Three-letter ISO 4217 currency code, see more on currencies""" - prices: List[LabeledPrice] - """Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, - delivery cost, delivery tax, bonus, etc.)""" - provider_data: Optional[str] = None - """JSON-encoded data about the invoice, which will be shared with the payment provider. A - detailed description of required fields should be provided by the payment provider.""" - photo_url: Optional[str] = None - """URL of the product photo for the invoice. Can be a photo of the goods or a marketing image - for a service. People like it better when they see what they are paying for.""" - photo_size: Optional[int] = None - """Photo size""" - photo_width: Optional[int] = None - """Photo width""" - photo_height: Optional[int] = None - """Photo height""" - need_name: Optional[bool] = None - """Pass True, if you require the user's full name to complete the order""" - need_phone_number: Optional[bool] = None - """Pass True, if you require the user's phone number to complete the order""" - need_email: Optional[bool] = None - """Pass True, if you require the user's email address to complete the order""" - need_shipping_address: Optional[bool] = None - """Pass True, if you require the user's shipping address to complete the order""" - send_phone_number_to_provider: Optional[bool] = None - """Pass True, if user's phone number should be sent to provider""" - send_email_to_provider: Optional[bool] = None - """Pass True, if user's email address should be sent to provider""" - is_flexible: Optional[bool] = None - """Pass True, if the final price depends on the shipping method""" - disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the message is a reply, ID of the original message""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for an inline keyboard. If empty, one 'Pay total price' button - will be shown. If not empty, the first button must be a Pay button.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="sendInvoice", data=data) diff --git a/aiogram/api/methods/send_media_group.py b/aiogram/api/methods/send_media_group.py deleted file mode 100644 index 70c804e1..00000000 --- a/aiogram/api/methods/send_media_group.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union - -from ..types import InputFile, InputMediaPhoto, InputMediaVideo, Message -from .base import Request, TelegramMethod, prepare_input_media, prepare_parse_mode - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendMediaGroup(TelegramMethod[List[Message]]): - """ - Use this method to send a group of photos or videos as an album. On success, an array of the - sent Messages is returned. - - Source: https://core.telegram.org/bots/api#sendmediagroup - """ - - __returning__ = List[Message] - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - media: List[Union[InputMediaPhoto, InputMediaVideo]] - """A JSON-serialized array describing photos and videos to be sent, must include 2-10 items""" - disable_notification: Optional[bool] = None - """Sends the messages silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the messages are a reply, ID of the original message""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - prepare_parse_mode(bot, data["media"]) - - files: Dict[str, InputFile] = {} - prepare_input_media(data, files) - - return Request(method="sendMediaGroup", data=data, files=files) diff --git a/aiogram/api/methods/send_photo.py b/aiogram/api/methods/send_photo.py deleted file mode 100644 index b35cc165..00000000 --- a/aiogram/api/methods/send_photo.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import ( - UNSET, - ForceReply, - InlineKeyboardMarkup, - InputFile, - Message, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, -) -from .base import Request, TelegramMethod, prepare_file - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendPhoto(TelegramMethod[Message]): - """ - Use this method to send photos. On success, the sent Message is returned. - - Source: https://core.telegram.org/bots/api#sendphoto - """ - - __returning__ = Message - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - photo: Union[InputFile, str] - """Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet, - or upload a new photo using multipart/form-data.""" - caption: Optional[str] = None - """Photo caption (may also be used when resending photos by file_id), 0-1024 characters after - entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the photo caption. See formatting options for more details.""" - disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the message is a reply, ID of the original message""" - reply_markup: Optional[ - Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] - ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict(exclude={"photo"}) - - files: Dict[str, InputFile] = {} - prepare_file(data=data, files=files, name="photo", value=self.photo) - - return Request(method="sendPhoto", data=data, files=files) diff --git a/aiogram/api/methods/send_video.py b/aiogram/api/methods/send_video.py deleted file mode 100644 index c9c5acb2..00000000 --- a/aiogram/api/methods/send_video.py +++ /dev/null @@ -1,75 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import ( - UNSET, - ForceReply, - InlineKeyboardMarkup, - InputFile, - Message, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, -) -from .base import Request, TelegramMethod, prepare_file - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendVideo(TelegramMethod[Message]): - """ - Use this method to send video files, Telegram clients support mp4 videos (other formats may be - sent as Document). On success, the sent Message is returned. Bots can currently send video - files of up to 50 MB in size, this limit may be changed in the future. - - Source: https://core.telegram.org/bots/api#sendvideo - """ - - __returning__ = Message - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - video: Union[InputFile, str] - """Video to send. Pass a file_id as String to send a video that exists on the Telegram servers - (recommended), pass an HTTP URL as a String for Telegram to get a video from the Internet, - or upload a new video using multipart/form-data.""" - duration: Optional[int] = None - """Duration of sent video in seconds""" - width: Optional[int] = None - """Video width""" - height: Optional[int] = None - """Video height""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - caption: Optional[str] = None - """Video caption (may also be used when resending videos by file_id), 0-1024 characters after - entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the video caption. See formatting options for more details.""" - supports_streaming: Optional[bool] = None - """Pass True, if the uploaded video is suitable for streaming""" - disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the message is a reply, ID of the original message""" - reply_markup: Optional[ - Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] - ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict(exclude={"video", "thumb"}) - - files: Dict[str, InputFile] = {} - prepare_file(data=data, files=files, name="video", value=self.video) - prepare_file(data=data, files=files, name="thumb", value=self.thumb) - - return Request(method="sendVideo", data=data, files=files) diff --git a/aiogram/api/methods/send_voice.py b/aiogram/api/methods/send_voice.py deleted file mode 100644 index 2a464439..00000000 --- a/aiogram/api/methods/send_voice.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Optional, Union - -from ..types import ( - UNSET, - ForceReply, - InlineKeyboardMarkup, - InputFile, - Message, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, -) -from .base import Request, TelegramMethod, prepare_file - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SendVoice(TelegramMethod[Message]): - """ - Use this method to send audio files, if you want Telegram clients to display the file as a - playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS - (other formats may be sent as Audio or Document). On success, the sent Message is returned. - Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in - the future. - - Source: https://core.telegram.org/bots/api#sendvoice - """ - - __returning__ = Message - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - voice: Union[InputFile, str] - """Audio file to send. Pass a file_id as String to send a file that exists on the Telegram - servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the - Internet, or upload a new one using multipart/form-data.""" - caption: Optional[str] = None - """Voice message caption, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the voice message caption. See formatting options for more - details.""" - duration: Optional[int] = None - """Duration of the voice message in seconds""" - disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" - reply_to_message_id: Optional[int] = None - """If the message is a reply, ID of the original message""" - reply_markup: Optional[ - Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] - ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict(exclude={"voice"}) - - files: Dict[str, InputFile] = {} - prepare_file(data=data, files=files, name="voice", value=self.voice) - - return Request(method="sendVoice", data=data, files=files) diff --git a/aiogram/api/methods/set_webhook.py b/aiogram/api/methods/set_webhook.py deleted file mode 100644 index 49c7617d..00000000 --- a/aiogram/api/methods/set_webhook.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, List, Optional - -from ..types import InputFile -from .base import Request, TelegramMethod, prepare_file - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class SetWebhook(TelegramMethod[bool]): - """ - Use this method to specify a url and receive incoming updates via an outgoing webhook. - Whenever there is an update for the bot, we will send an HTTPS POST request to the specified - url, containing a JSON-serialized Update. In case of an unsuccessful request, we will give up - after a reasonable amount of attempts. Returns True on success. - If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a - secret path in the URL, e.g. https://www.example.com/. Since nobody else knows your - bot's token, you can be pretty sure it's us. - Notes - 1. You will not be able to receive updates using getUpdates for as long as an outgoing webhook - is set up. - 2. To use a self-signed certificate, you need to upload your public key certificate using - certificate parameter. Please upload as InputFile, sending a String will not work. - 3. Ports currently supported for Webhooks: 443, 80, 88, 8443. - NEW! If you're having any trouble setting up webhooks, please check out this amazing guide to - Webhooks. - - Source: https://core.telegram.org/bots/api#setwebhook - """ - - __returning__ = bool - - url: str - """HTTPS url to send updates to. Use an empty string to remove webhook integration""" - certificate: Optional[InputFile] = None - """Upload your public key certificate so that the root certificate in use can be checked. See - our self-signed guide for details.""" - max_connections: Optional[int] = None - """Maximum allowed number of simultaneous HTTPS connections to the webhook for update - delivery, 1-100. Defaults to 40. Use lower values to limit the load on your bot's server, - and higher values to increase your bot's throughput.""" - allowed_updates: Optional[List[str]] = None - """A JSON-serialized list of the update types you want your bot to receive. For example, - specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of - these types. See Update for a complete list of available update types. Specify an empty - list to receive all updates regardless of type (default). If not specified, the previous - setting will be used.""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict(exclude={"certificate"}) - - files: Dict[str, InputFile] = {} - prepare_file(data=data, files=files, name="certificate", value=self.certificate) - - return Request(method="setWebhook", data=data, files=files) diff --git a/aiogram/api/methods/unban_chat_member.py b/aiogram/api/methods/unban_chat_member.py deleted file mode 100644 index 56a30bd6..00000000 --- a/aiogram/api/methods/unban_chat_member.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Union - -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class UnbanChatMember(TelegramMethod[bool]): - """ - Use this method to unban a previously kicked user in a supergroup or channel. The user will - not return to the group or channel automatically, but will be able to join via link, etc. The - bot must be an administrator for this to work. Returns True on success. - - Source: https://core.telegram.org/bots/api#unbanchatmember - """ - - __returning__ = bool - - chat_id: Union[int, str] - """Unique identifier for the target group or username of the target supergroup or channel (in - the format @username)""" - user_id: int - """Unique identifier of the target user""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="unbanChatMember", data=data) diff --git a/aiogram/api/methods/unpin_chat_message.py b/aiogram/api/methods/unpin_chat_message.py deleted file mode 100644 index 419a7edd..00000000 --- a/aiogram/api/methods/unpin_chat_message.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, Dict, Union - -from .base import Request, TelegramMethod - -if TYPE_CHECKING: # pragma: no cover - from ..client.bot import Bot - - -class UnpinChatMessage(TelegramMethod[bool]): - """ - Use this method to unpin a message in a group, a supergroup, or a channel. The bot must be an - administrator in the chat for this to work and must have the 'can_pin_messages' admin right in - the supergroup or 'can_edit_messages' admin right in the channel. Returns True on success. - - Source: https://core.telegram.org/bots/api#unpinchatmessage - """ - - __returning__ = bool - - chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" - - def build_request(self, bot: Bot) -> Request: - data: Dict[str, Any] = self.dict() - - return Request(method="unpinChatMessage", data=data) diff --git a/aiogram/api/types/callback_query.py b/aiogram/api/types/callback_query.py deleted file mode 100644 index 1991ca8d..00000000 --- a/aiogram/api/types/callback_query.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .message import Message - from .user import User - from ..methods import AnswerCallbackQuery - - -class CallbackQuery(TelegramObject): - """ - This object represents an incoming callback query from a callback button in an inline - keyboard. If the button that originated the query was attached to a message sent by the bot, - the field message will be present. If the button was attached to a message sent via the bot - (in inline mode), the field inline_message_id will be present. Exactly one of the fields data - or game_short_name will be present. - NOTE: After the user presses a callback button, Telegram clients will display a progress bar - until you call answerCallbackQuery. It is, therefore, necessary to react by calling - answerCallbackQuery even if no notification to the user is needed (e.g., without specifying - any of the optional parameters). - - Source: https://core.telegram.org/bots/api#callbackquery - """ - - id: str - """Unique identifier for this query""" - from_user: User = Field(..., alias="from") - """Sender""" - chat_instance: str - """Global identifier, uniquely corresponding to the chat to which the message with the - callback button was sent. Useful for high scores in games.""" - message: Optional[Message] = None - """Message with the callback button that originated the query. Note that message content and - message date will not be available if the message is too old""" - inline_message_id: Optional[str] = None - """Identifier of the message sent via the bot in inline mode, that originated the query.""" - data: Optional[str] = None - """Data associated with the callback button. Be aware that a bad client can send arbitrary - data in this field.""" - game_short_name: Optional[str] = None - """Short name of a Game to be returned, serves as the unique identifier for the game""" - - def answer( - self, - text: Optional[str] = None, - show_alert: Optional[bool] = None, - url: Optional[str] = None, - cache_time: Optional[int] = None, - ) -> AnswerCallbackQuery: - """ - Answer to callback query - - :param text: - :param show_alert: - :param url: - :param cache_time: - :return: - """ - from ..methods import AnswerCallbackQuery - - return AnswerCallbackQuery( - callback_query_id=self.id, - text=text, - show_alert=show_alert, - url=url, - cache_time=cache_time, - ) diff --git a/aiogram/api/types/chat.py b/aiogram/api/types/chat.py deleted file mode 100644 index 2e3163b2..00000000 --- a/aiogram/api/types/chat.py +++ /dev/null @@ -1,53 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .chat_permissions import ChatPermissions - from .chat_photo import ChatPhoto - from .message import Message - - -class Chat(TelegramObject): - """ - This object represents a chat. - - Source: https://core.telegram.org/bots/api#chat - """ - - id: int - """Unique identifier for this chat. This number may be greater than 32 bits and some - programming languages may have difficulty/silent defects in interpreting it. But it is - smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe - for storing this identifier.""" - type: str - """Type of chat, can be either 'private', 'group', 'supergroup' or 'channel'""" - title: Optional[str] = None - """Title, for supergroups, channels and group chats""" - username: Optional[str] = None - """Username, for private chats, supergroups and channels if available""" - first_name: Optional[str] = None - """First name of the other party in a private chat""" - last_name: Optional[str] = None - """Last name of the other party in a private chat""" - photo: Optional[ChatPhoto] = None - """Chat photo. Returned only in getChat.""" - description: Optional[str] = None - """Description, for groups, supergroups and channel chats. Returned only in getChat.""" - invite_link: Optional[str] = None - """Chat invite link, for groups, supergroups and channel chats. Each administrator in a chat - generates their own invite links, so the bot must first generate the link using - exportChatInviteLink. Returned only in getChat.""" - pinned_message: Optional[Message] = None - """Pinned message, for groups, supergroups and channels. Returned only in getChat.""" - permissions: Optional[ChatPermissions] = None - """Default chat member permissions, for groups and supergroups. Returned only in getChat.""" - slow_mode_delay: Optional[int] = None - """For supergroups, the minimum allowed delay between consecutive messages sent by each - unpriviledged user. Returned only in getChat.""" - sticker_set_name: Optional[str] = None - """For supergroups, name of group sticker set. Returned only in getChat.""" - can_set_sticker_set: Optional[bool] = None - """True, if the bot can change the group sticker set. Returned only in getChat.""" diff --git a/aiogram/api/types/chat_member.py b/aiogram/api/types/chat_member.py deleted file mode 100644 index dca3029e..00000000 --- a/aiogram/api/types/chat_member.py +++ /dev/null @@ -1,91 +0,0 @@ -from __future__ import annotations - -import datetime -from typing import TYPE_CHECKING, Optional, Union - -from ...utils import helper -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .user import User - - -class ChatMember(TelegramObject): - """ - This object contains information about one member of a chat. - - Source: https://core.telegram.org/bots/api#chatmember - """ - - user: User - """Information about the user""" - status: str - """The member's status in the chat. Can be 'creator', 'administrator', 'member', 'restricted', - 'left' or 'kicked'""" - custom_title: Optional[str] = None - """Owner and administrators only. Custom title for this user""" - until_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None - """Restricted and kicked only. Date when restrictions will be lifted for this user; unix time""" - can_be_edited: Optional[bool] = None - """Administrators only. True, if the bot is allowed to edit administrator privileges of that - user""" - can_post_messages: Optional[bool] = None - """Administrators only. True, if the administrator can post in the channel; channels only""" - can_edit_messages: Optional[bool] = None - """Administrators only. True, if the administrator can edit messages of other users and can - pin messages; channels only""" - can_delete_messages: Optional[bool] = None - """Administrators only. True, if the administrator can delete messages of other users""" - can_restrict_members: Optional[bool] = None - """Administrators only. True, if the administrator can restrict, ban or unban chat members""" - can_promote_members: Optional[bool] = None - """Administrators only. True, if the administrator can add new administrators with a subset of - their own privileges or demote administrators that he has promoted, directly or indirectly - (promoted by administrators that were appointed by the user)""" - can_change_info: Optional[bool] = None - """Administrators and restricted only. True, if the user is allowed to change the chat title, - photo and other settings""" - can_invite_users: Optional[bool] = None - """Administrators and restricted only. True, if the user is allowed to invite new users to the - chat""" - can_pin_messages: Optional[bool] = None - """Administrators and restricted only. True, if the user is allowed to pin messages; groups - and supergroups only""" - is_member: Optional[bool] = None - """Restricted only. True, if the user is a member of the chat at the moment of the request""" - can_send_messages: Optional[bool] = None - """Restricted only. True, if the user is allowed to send text messages, contacts, locations - and venues""" - can_send_media_messages: Optional[bool] = None - """Restricted only. True, if the user is allowed to send audios, documents, photos, videos, - video notes and voice notes""" - can_send_polls: Optional[bool] = None - """Restricted only. True, if the user is allowed to send polls""" - can_send_other_messages: Optional[bool] = None - """Restricted only. True, if the user is allowed to send animations, games, stickers and use - inline bots""" - can_add_web_page_previews: Optional[bool] = None - """Restricted only. True, if the user is allowed to add web page previews to their messages""" - - @property - def is_chat_admin(self) -> bool: - return self.status in {ChatMemberStatus.CREATOR, ChatMemberStatus.ADMINISTRATOR} - - @property - def is_chat_member(self) -> bool: - return self.status not in {ChatMemberStatus.LEFT, ChatMemberStatus.KICKED} - - -class ChatMemberStatus(helper.Helper): - """ - Chat member status - """ - - mode = helper.HelperMode.lowercase - - CREATOR = helper.Item() # creator - ADMINISTRATOR = helper.Item() # administrator - MEMBER = helper.Item() # member - RESTRICTED = helper.Item() # restricted - LEFT = helper.Item() # left - KICKED = helper.Item() # kicked diff --git a/aiogram/api/types/chat_permissions.py b/aiogram/api/types/chat_permissions.py deleted file mode 100644 index aef8d9ff..00000000 --- a/aiogram/api/types/chat_permissions.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .base import MutableTelegramObject - - -class ChatPermissions(MutableTelegramObject): - """ - Describes actions that a non-administrator user is allowed to take in a chat. - - Source: https://core.telegram.org/bots/api#chatpermissions - """ - - can_send_messages: Optional[bool] = None - """True, if the user is allowed to send text messages, contacts, locations and venues""" - can_send_media_messages: Optional[bool] = None - """True, if the user is allowed to send audios, documents, photos, videos, video notes and - voice notes, implies can_send_messages""" - can_send_polls: Optional[bool] = None - """True, if the user is allowed to send polls, implies can_send_messages""" - can_send_other_messages: Optional[bool] = None - """True, if the user is allowed to send animations, games, stickers and use inline bots, - implies can_send_media_messages""" - can_add_web_page_previews: Optional[bool] = None - """True, if the user is allowed to add web page previews to their messages, implies - can_send_media_messages""" - can_change_info: Optional[bool] = None - """True, if the user is allowed to change the chat title, photo and other settings. Ignored in - public supergroups""" - can_invite_users: Optional[bool] = None - """True, if the user is allowed to invite new users to the chat""" - can_pin_messages: Optional[bool] = None - """True, if the user is allowed to pin messages. Ignored in public supergroups""" diff --git a/aiogram/api/types/chosen_inline_result.py b/aiogram/api/types/chosen_inline_result.py deleted file mode 100644 index f6dfdede..00000000 --- a/aiogram/api/types/chosen_inline_result.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .location import Location - from .user import User - - -class ChosenInlineResult(TelegramObject): - """ - Represents a result of an inline query that was chosen by the user and sent to their chat - partner. - Note: It is necessary to enable inline feedback via @Botfather in order to receive these - objects in updates. - - Source: https://core.telegram.org/bots/api#choseninlineresult - """ - - result_id: str - """The unique identifier for the result that was chosen""" - from_user: User = Field(..., alias="from") - """The user that chose the result""" - query: str - """The query that was used to obtain the result""" - location: Optional[Location] = None - """Sender location, only for bots that require user location""" - inline_message_id: Optional[str] = None - """Identifier of the sent inline message. Available only if there is an inline keyboard - attached to the message. Will be also received in callback queries and can be used to edit - the message.""" diff --git a/aiogram/api/types/downloadable.py b/aiogram/api/types/downloadable.py deleted file mode 100644 index 0b0ee4cf..00000000 --- a/aiogram/api/types/downloadable.py +++ /dev/null @@ -1,5 +0,0 @@ -from typing_extensions import Protocol - - -class Downloadable(Protocol): - file_id: str diff --git a/aiogram/api/types/encrypted_credentials.py b/aiogram/api/types/encrypted_credentials.py deleted file mode 100644 index 7cf2c1e1..00000000 --- a/aiogram/api/types/encrypted_credentials.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations - -from .base import TelegramObject - - -class EncryptedCredentials(TelegramObject): - """ - Contains data required for decrypting and authenticating EncryptedPassportElement. See the - Telegram Passport Documentation for a complete description of the data decryption and - authentication processes. - - Source: https://core.telegram.org/bots/api#encryptedcredentials - """ - - data: str - """Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and - secrets required for EncryptedPassportElement decryption and authentication""" - hash: str - """Base64-encoded data hash for data authentication""" - secret: str - """Base64-encoded secret, encrypted with the bot's public RSA key, required for data - decryption""" diff --git a/aiogram/api/types/encrypted_passport_element.py b/aiogram/api/types/encrypted_passport_element.py deleted file mode 100644 index 2e786009..00000000 --- a/aiogram/api/types/encrypted_passport_element.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, List, Optional - -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .passport_file import PassportFile - - -class EncryptedPassportElement(TelegramObject): - """ - Contains information about documents or other Telegram Passport elements shared with the bot - by the user. - - Source: https://core.telegram.org/bots/api#encryptedpassportelement - """ - - type: str - """Element type. One of 'personal_details', 'passport', 'driver_license', 'identity_card', - 'internal_passport', 'address', 'utility_bill', 'bank_statement', 'rental_agreement', - 'passport_registration', 'temporary_registration', 'phone_number', 'email'.""" - hash: str - """Base64-encoded element hash for using in PassportElementErrorUnspecified""" - data: Optional[str] = None - """Base64-encoded encrypted Telegram Passport element data provided by the user, available for - 'personal_details', 'passport', 'driver_license', 'identity_card', 'internal_passport' and - 'address' types. Can be decrypted and verified using the accompanying EncryptedCredentials.""" - phone_number: Optional[str] = None - """User's verified phone number, available only for 'phone_number' type""" - email: Optional[str] = None - """User's verified email address, available only for 'email' type""" - files: Optional[List[PassportFile]] = None - """Array of encrypted files with documents provided by the user, available for 'utility_bill', - 'bank_statement', 'rental_agreement', 'passport_registration' and 'temporary_registration' - types. Files can be decrypted and verified using the accompanying EncryptedCredentials.""" - front_side: Optional[PassportFile] = None - """Encrypted file with the front side of the document, provided by the user. Available for - 'passport', 'driver_license', 'identity_card' and 'internal_passport'. The file can be - decrypted and verified using the accompanying EncryptedCredentials.""" - reverse_side: Optional[PassportFile] = None - """Encrypted file with the reverse side of the document, provided by the user. Available for - 'driver_license' and 'identity_card'. The file can be decrypted and verified using the - accompanying EncryptedCredentials.""" - selfie: Optional[PassportFile] = None - """Encrypted file with the selfie of the user holding a document, provided by the user; - available for 'passport', 'driver_license', 'identity_card' and 'internal_passport'. The - file can be decrypted and verified using the accompanying EncryptedCredentials.""" - translation: Optional[List[PassportFile]] = None - """Array of encrypted files with translated versions of documents provided by the user. - Available if requested for 'passport', 'driver_license', 'identity_card', - 'internal_passport', 'utility_bill', 'bank_statement', 'rental_agreement', - 'passport_registration' and 'temporary_registration' types. Files can be decrypted and - verified using the accompanying EncryptedCredentials.""" diff --git a/aiogram/api/types/file.py b/aiogram/api/types/file.py deleted file mode 100644 index f914fe00..00000000 --- a/aiogram/api/types/file.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .base import TelegramObject - - -class File(TelegramObject): - """ - This object represents a file ready to be downloaded. The file can be downloaded via the link - https://api.telegram.org/file/bot/. It is guaranteed that the link will be - valid for at least 1 hour. When the link expires, a new one can be requested by calling - getFile. - Maximum file size to download is 20 MB - - Source: https://core.telegram.org/bots/api#file - """ - - file_id: str - """Identifier for this file, which can be used to download or reuse the file""" - file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" - file_size: Optional[int] = None - """File size, if known""" - file_path: Optional[str] = None - """File path. Use https://api.telegram.org/file/bot/ to get the file.""" diff --git a/aiogram/api/types/force_reply.py b/aiogram/api/types/force_reply.py deleted file mode 100644 index 8cbc1653..00000000 --- a/aiogram/api/types/force_reply.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .base import MutableTelegramObject - - -class ForceReply(MutableTelegramObject): - """ - Upon receiving a message with this object, Telegram clients will display a reply interface to - the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be - extremely useful if you want to create user-friendly step-by-step interfaces without having to - sacrifice privacy mode. - Example: A poll bot for groups runs in privacy mode (only receives commands, replies to its - messages and mentions). There could be two ways to create a new poll: - - Explain the user how to send a command with parameters (e.g. /newpoll question answer1 - answer2). May be appealing for hardcore users but lacks modern day polish. - Guide the user through a step-by-step process. 'Please send me your question', 'Cool, now - let's add the first answer option', 'Great. Keep adding answer options, then send /done when - you're ready'. - The last option is definitely more attractive. And if you use ForceReply in your bot's - questions, it will receive the user's answers even if it only receives replies, commands and - mentions — without any extra work for the user. - - Source: https://core.telegram.org/bots/api#forcereply - """ - - force_reply: bool - """Shows reply interface to the user, as if they manually selected the bot's message and - tapped 'Reply'""" - selective: Optional[bool] = None - """Use this parameter if you want to force reply from specific users only. Targets: 1) users - that are @mentioned in the text of the Message object; 2) if the bot's message is a reply - (has reply_to_message_id), sender of the original message.""" diff --git a/aiogram/api/types/inline_keyboard_button.py b/aiogram/api/types/inline_keyboard_button.py deleted file mode 100644 index 33ade442..00000000 --- a/aiogram/api/types/inline_keyboard_button.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from .base import MutableTelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .callback_game import CallbackGame - from .login_url import LoginUrl - - -class InlineKeyboardButton(MutableTelegramObject): - """ - This object represents one button of an inline keyboard. You must use exactly one of the - optional fields. - - Source: https://core.telegram.org/bots/api#inlinekeyboardbutton - """ - - text: str - """Label text on the button""" - url: Optional[str] = None - """HTTP or tg:// url to be opened when button is pressed""" - login_url: Optional[LoginUrl] = None - """An HTTP URL used to automatically authorize the user. Can be used as a replacement for the - Telegram Login Widget.""" - callback_data: Optional[str] = None - """Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes""" - switch_inline_query: Optional[str] = None - """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. Can - be empty, in which case just the bot's username will be inserted.""" - switch_inline_query_current_chat: Optional[str] = None - """If set, pressing the button will insert the bot's username and the specified inline query - in the current chat's input field. Can be empty, in which case only the bot's username will - be inserted.""" - callback_game: Optional[CallbackGame] = None - """Description of the game that will be launched when the user presses the button.""" - pay: Optional[bool] = None - """Specify True, to send a Pay button.""" diff --git a/aiogram/api/types/inline_keyboard_markup.py b/aiogram/api/types/inline_keyboard_markup.py deleted file mode 100644 index 73c43f0d..00000000 --- a/aiogram/api/types/inline_keyboard_markup.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, List - -from .base import MutableTelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_button import InlineKeyboardButton - - -class InlineKeyboardMarkup(MutableTelegramObject): - """ - This object represents an inline keyboard that appears right next to the message it belongs - to. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will display unsupported message. - - Source: https://core.telegram.org/bots/api#inlinekeyboardmarkup - """ - - inline_keyboard: List[List[InlineKeyboardButton]] - """Array of button rows, each represented by an Array of InlineKeyboardButton objects""" diff --git a/aiogram/api/types/inline_query_result.py b/aiogram/api/types/inline_query_result.py deleted file mode 100644 index 9df3573b..00000000 --- a/aiogram/api/types/inline_query_result.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations - -from .base import MutableTelegramObject - - -class InlineQueryResult(MutableTelegramObject): - """ - This object represents one result of an inline query. Telegram clients currently support - results of the following 20 types: - - InlineQueryResultCachedAudio - - InlineQueryResultCachedDocument - - InlineQueryResultCachedGif - - InlineQueryResultCachedMpeg4Gif - - InlineQueryResultCachedPhoto - - InlineQueryResultCachedSticker - - InlineQueryResultCachedVideo - - InlineQueryResultCachedVoice - - InlineQueryResultArticle - - InlineQueryResultAudio - - InlineQueryResultContact - - InlineQueryResultGame - - InlineQueryResultDocument - - InlineQueryResultGif - - InlineQueryResultLocation - - InlineQueryResultMpeg4Gif - - InlineQueryResultPhoto - - InlineQueryResultVenue - - InlineQueryResultVideo - - InlineQueryResultVoice - - Source: https://core.telegram.org/bots/api#inlinequeryresult - """ diff --git a/aiogram/api/types/inline_query_result_audio.py b/aiogram/api/types/inline_query_result_audio.py deleted file mode 100644 index b236f26d..00000000 --- a/aiogram/api/types/inline_query_result_audio.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultAudio(InlineQueryResult): - """ - Represents a link to an MP3 audio file. By default, this audio file will be sent by the user. - Alternatively, you can use input_message_content to send a message with the specified content - instead of the audio. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. - - Source: https://core.telegram.org/bots/api#inlinequeryresultaudio - """ - - type: str = Field("audio", const=True) - """Type of the result, must be audio""" - id: str - """Unique identifier for this result, 1-64 bytes""" - audio_url: str - """A valid URL for the audio file""" - title: str - """Title""" - caption: Optional[str] = None - """Caption, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the audio caption. See formatting options for more details.""" - performer: Optional[str] = None - """Performer""" - audio_duration: Optional[int] = None - """Audio duration in seconds""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the audio""" diff --git a/aiogram/api/types/inline_query_result_cached_audio.py b/aiogram/api/types/inline_query_result_cached_audio.py deleted file mode 100644 index b26edfed..00000000 --- a/aiogram/api/types/inline_query_result_cached_audio.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultCachedAudio(InlineQueryResult): - """ - Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio - file will be sent by the user. Alternatively, you can use input_message_content to send a - message with the specified content instead of the audio. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. - - Source: https://core.telegram.org/bots/api#inlinequeryresultcachedaudio - """ - - type: str = Field("audio", const=True) - """Type of the result, must be audio""" - id: str - """Unique identifier for this result, 1-64 bytes""" - audio_file_id: str - """A valid file identifier for the audio file""" - caption: Optional[str] = None - """Caption, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the audio caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the audio""" diff --git a/aiogram/api/types/inline_query_result_cached_document.py b/aiogram/api/types/inline_query_result_cached_document.py deleted file mode 100644 index 74fa2ecf..00000000 --- a/aiogram/api/types/inline_query_result_cached_document.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultCachedDocument(InlineQueryResult): - """ - Represents a link to a file stored on the Telegram servers. By default, this file will be sent - by the user with an optional caption. Alternatively, you can use input_message_content to send - a message with the specified content instead of the file. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. - - Source: https://core.telegram.org/bots/api#inlinequeryresultcacheddocument - """ - - type: str = Field("document", const=True) - """Type of the result, must be document""" - id: str - """Unique identifier for this result, 1-64 bytes""" - title: str - """Title for the result""" - document_file_id: str - """A valid file identifier for the file""" - description: Optional[str] = None - """Short description of the result""" - caption: Optional[str] = None - """Caption of the document to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the document caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the file""" diff --git a/aiogram/api/types/inline_query_result_cached_gif.py b/aiogram/api/types/inline_query_result_cached_gif.py deleted file mode 100644 index cb66af96..00000000 --- a/aiogram/api/types/inline_query_result_cached_gif.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultCachedGif(InlineQueryResult): - """ - Represents a link to an animated GIF file stored on the Telegram servers. By default, this - animated GIF file will be sent by the user with an optional caption. Alternatively, you can - use input_message_content to send a message with specified content instead of the animation. - - Source: https://core.telegram.org/bots/api#inlinequeryresultcachedgif - """ - - type: str = Field("gif", const=True) - """Type of the result, must be gif""" - id: str - """Unique identifier for this result, 1-64 bytes""" - gif_file_id: str - """A valid file identifier for the GIF file""" - title: Optional[str] = None - """Title for the result""" - caption: Optional[str] = None - """Caption of the GIF file to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the GIF animation""" diff --git a/aiogram/api/types/inline_query_result_cached_mpeg4_gif.py b/aiogram/api/types/inline_query_result_cached_mpeg4_gif.py deleted file mode 100644 index a43de25f..00000000 --- a/aiogram/api/types/inline_query_result_cached_mpeg4_gif.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): - """ - Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the - Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an - optional caption. Alternatively, you can use input_message_content to send a message with the - specified content instead of the animation. - - Source: https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif - """ - - type: str = Field("mpeg4_gif", const=True) - """Type of the result, must be mpeg4_gif""" - id: str - """Unique identifier for this result, 1-64 bytes""" - mpeg4_file_id: str - """A valid file identifier for the MP4 file""" - title: Optional[str] = None - """Title for the result""" - caption: Optional[str] = None - """Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the video animation""" diff --git a/aiogram/api/types/inline_query_result_cached_photo.py b/aiogram/api/types/inline_query_result_cached_photo.py deleted file mode 100644 index b3a9f8a1..00000000 --- a/aiogram/api/types/inline_query_result_cached_photo.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultCachedPhoto(InlineQueryResult): - """ - Represents a link to a photo stored on the Telegram servers. By default, this photo will be - sent by the user with an optional caption. Alternatively, you can use input_message_content to - send a message with the specified content instead of the photo. - - Source: https://core.telegram.org/bots/api#inlinequeryresultcachedphoto - """ - - type: str = Field("photo", const=True) - """Type of the result, must be photo""" - id: str - """Unique identifier for this result, 1-64 bytes""" - photo_file_id: str - """A valid file identifier of the photo""" - title: Optional[str] = None - """Title for the result""" - description: Optional[str] = None - """Short description of the result""" - caption: Optional[str] = None - """Caption of the photo to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the photo caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the photo""" diff --git a/aiogram/api/types/inline_query_result_cached_video.py b/aiogram/api/types/inline_query_result_cached_video.py deleted file mode 100644 index ef561bb4..00000000 --- a/aiogram/api/types/inline_query_result_cached_video.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultCachedVideo(InlineQueryResult): - """ - Represents a link to a video file stored on the Telegram servers. By default, this video file - will be sent by the user with an optional caption. Alternatively, you can use - input_message_content to send a message with the specified content instead of the video. - - Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvideo - """ - - type: str = Field("video", const=True) - """Type of the result, must be video""" - id: str - """Unique identifier for this result, 1-64 bytes""" - video_file_id: str - """A valid file identifier for the video file""" - title: str - """Title for the result""" - description: Optional[str] = None - """Short description of the result""" - caption: Optional[str] = None - """Caption of the video to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the video caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the video""" diff --git a/aiogram/api/types/inline_query_result_cached_voice.py b/aiogram/api/types/inline_query_result_cached_voice.py deleted file mode 100644 index aec92d44..00000000 --- a/aiogram/api/types/inline_query_result_cached_voice.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultCachedVoice(InlineQueryResult): - """ - Represents a link to a voice message stored on the Telegram servers. By default, this voice - message will be sent by the user. Alternatively, you can use input_message_content to send a - message with the specified content instead of the voice message. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. - - Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvoice - """ - - type: str = Field("voice", const=True) - """Type of the result, must be voice""" - id: str - """Unique identifier for this result, 1-64 bytes""" - voice_file_id: str - """A valid file identifier for the voice message""" - title: str - """Voice message title""" - caption: Optional[str] = None - """Caption, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the voice message caption. See formatting options for more - details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the voice message""" diff --git a/aiogram/api/types/inline_query_result_document.py b/aiogram/api/types/inline_query_result_document.py deleted file mode 100644 index 01e389ff..00000000 --- a/aiogram/api/types/inline_query_result_document.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultDocument(InlineQueryResult): - """ - Represents a link to a file. By default, this file will be sent by the user with an optional - caption. Alternatively, you can use input_message_content to send a message with the specified - content instead of the file. Currently, only .PDF and .ZIP files can be sent using this - method. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. - - Source: https://core.telegram.org/bots/api#inlinequeryresultdocument - """ - - type: str = Field("document", const=True) - """Type of the result, must be document""" - id: str - """Unique identifier for this result, 1-64 bytes""" - title: str - """Title for the result""" - document_url: str - """A valid URL for the file""" - mime_type: str - """Mime type of the content of the file, either 'application/pdf' or 'application/zip'""" - caption: Optional[str] = None - """Caption of the document to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the document caption. See formatting options for more details.""" - description: Optional[str] = None - """Short description of the result""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the file""" - thumb_url: Optional[str] = None - """URL of the thumbnail (jpeg only) for the file""" - thumb_width: Optional[int] = None - """Thumbnail width""" - thumb_height: Optional[int] = None - """Thumbnail height""" diff --git a/aiogram/api/types/inline_query_result_gif.py b/aiogram/api/types/inline_query_result_gif.py deleted file mode 100644 index 8812c43a..00000000 --- a/aiogram/api/types/inline_query_result_gif.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultGif(InlineQueryResult): - """ - Represents a link to an animated GIF file. By default, this animated GIF file will be sent by - the user with optional caption. Alternatively, you can use input_message_content to send a - message with the specified content instead of the animation. - - Source: https://core.telegram.org/bots/api#inlinequeryresultgif - """ - - type: str = Field("gif", const=True) - """Type of the result, must be gif""" - id: str - """Unique identifier for this result, 1-64 bytes""" - gif_url: str - """A valid URL for the GIF file. File size must not exceed 1MB""" - thumb_url: str - """URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result""" - gif_width: Optional[int] = None - """Width of the GIF""" - gif_height: Optional[int] = None - """Height of the GIF""" - gif_duration: Optional[int] = None - """Duration of the GIF""" - thumb_mime_type: Optional[str] = None - """MIME type of the thumbnail, must be one of 'image/jpeg', 'image/gif', or 'video/mp4'. - Defaults to 'image/jpeg'""" - title: Optional[str] = None - """Title for the result""" - caption: Optional[str] = None - """Caption of the GIF file to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the GIF animation""" diff --git a/aiogram/api/types/inline_query_result_location.py b/aiogram/api/types/inline_query_result_location.py deleted file mode 100644 index 22498ebb..00000000 --- a/aiogram/api/types/inline_query_result_location.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultLocation(InlineQueryResult): - """ - Represents a location on a map. By default, the location will be sent by the user. - Alternatively, you can use input_message_content to send a message with the specified content - instead of the location. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. - - Source: https://core.telegram.org/bots/api#inlinequeryresultlocation - """ - - type: str = Field("location", const=True) - """Type of the result, must be location""" - id: str - """Unique identifier for this result, 1-64 Bytes""" - latitude: float - """Location latitude in degrees""" - longitude: float - """Location longitude in degrees""" - title: str - """Location title""" - live_period: Optional[int] = None - """Period in seconds for which the location can be updated, should be between 60 and 86400.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the location""" - thumb_url: Optional[str] = None - """Url of the thumbnail for the result""" - thumb_width: Optional[int] = None - """Thumbnail width""" - thumb_height: Optional[int] = None - """Thumbnail height""" diff --git a/aiogram/api/types/inline_query_result_mpeg4_gif.py b/aiogram/api/types/inline_query_result_mpeg4_gif.py deleted file mode 100644 index 76f97e0a..00000000 --- a/aiogram/api/types/inline_query_result_mpeg4_gif.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultMpeg4Gif(InlineQueryResult): - """ - Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, - this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you - can use input_message_content to send a message with the specified content instead of the - animation. - - Source: https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif - """ - - type: str = Field("mpeg4_gif", const=True) - """Type of the result, must be mpeg4_gif""" - id: str - """Unique identifier for this result, 1-64 bytes""" - mpeg4_url: str - """A valid URL for the MP4 file. File size must not exceed 1MB""" - thumb_url: str - """URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result""" - mpeg4_width: Optional[int] = None - """Video width""" - mpeg4_height: Optional[int] = None - """Video height""" - mpeg4_duration: Optional[int] = None - """Video duration""" - thumb_mime_type: Optional[str] = None - """MIME type of the thumbnail, must be one of 'image/jpeg', 'image/gif', or 'video/mp4'. - Defaults to 'image/jpeg'""" - title: Optional[str] = None - """Title for the result""" - caption: Optional[str] = None - """Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the video animation""" diff --git a/aiogram/api/types/inline_query_result_photo.py b/aiogram/api/types/inline_query_result_photo.py deleted file mode 100644 index 31cddee2..00000000 --- a/aiogram/api/types/inline_query_result_photo.py +++ /dev/null @@ -1,47 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultPhoto(InlineQueryResult): - """ - Represents a link to a photo. By default, this photo will be sent by the user with optional - caption. Alternatively, you can use input_message_content to send a message with the specified - content instead of the photo. - - Source: https://core.telegram.org/bots/api#inlinequeryresultphoto - """ - - type: str = Field("photo", const=True) - """Type of the result, must be photo""" - id: str - """Unique identifier for this result, 1-64 bytes""" - photo_url: str - """A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB""" - thumb_url: str - """URL of the thumbnail for the photo""" - photo_width: Optional[int] = None - """Width of the photo""" - photo_height: Optional[int] = None - """Height of the photo""" - title: Optional[str] = None - """Title for the result""" - description: Optional[str] = None - """Short description of the result""" - caption: Optional[str] = None - """Caption of the photo to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the photo caption. See formatting options for more details.""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the photo""" diff --git a/aiogram/api/types/inline_query_result_video.py b/aiogram/api/types/inline_query_result_video.py deleted file mode 100644 index 3002b8be..00000000 --- a/aiogram/api/types/inline_query_result_video.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultVideo(InlineQueryResult): - """ - Represents a link to a page containing an embedded video player or a video file. By default, - this video file will be sent by the user with an optional caption. Alternatively, you can use - input_message_content to send a message with the specified content instead of the video. - If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must - replace its content using input_message_content. - - Source: https://core.telegram.org/bots/api#inlinequeryresultvideo - """ - - type: str = Field("video", const=True) - """Type of the result, must be video""" - id: str - """Unique identifier for this result, 1-64 bytes""" - video_url: str - """A valid URL for the embedded video player or video file""" - mime_type: str - """Mime type of the content of video url, 'text/html' or 'video/mp4'""" - thumb_url: str - """URL of the thumbnail (jpeg only) for the video""" - title: str - """Title for the result""" - caption: Optional[str] = None - """Caption of the video to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the video caption. See formatting options for more details.""" - video_width: Optional[int] = None - """Video width""" - video_height: Optional[int] = None - """Video height""" - video_duration: Optional[int] = None - """Video duration in seconds""" - description: Optional[str] = None - """Short description of the result""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the video. This field is required if - InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video).""" diff --git a/aiogram/api/types/inline_query_result_voice.py b/aiogram/api/types/inline_query_result_voice.py deleted file mode 100644 index 724d46bc..00000000 --- a/aiogram/api/types/inline_query_result_voice.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from pydantic import Field - -from .base import UNSET -from .inline_query_result import InlineQueryResult - -if TYPE_CHECKING: # pragma: no cover - from .inline_keyboard_markup import InlineKeyboardMarkup - from .input_message_content import InputMessageContent - - -class InlineQueryResultVoice(InlineQueryResult): - """ - Represents a link to a voice recording in an .OGG container encoded with OPUS. By default, - this voice recording will be sent by the user. Alternatively, you can use - input_message_content to send a message with the specified content instead of the the voice - message. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. - - Source: https://core.telegram.org/bots/api#inlinequeryresultvoice - """ - - type: str = Field("voice", const=True) - """Type of the result, must be voice""" - id: str - """Unique identifier for this result, 1-64 bytes""" - voice_url: str - """A valid URL for the voice recording""" - title: str - """Recording title""" - caption: Optional[str] = None - """Caption, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the voice message caption. See formatting options for more - details.""" - voice_duration: Optional[int] = None - """Recording duration in seconds""" - reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" - input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the voice recording""" diff --git a/aiogram/api/types/input_location_message_content.py b/aiogram/api/types/input_location_message_content.py deleted file mode 100644 index c1ee650f..00000000 --- a/aiogram/api/types/input_location_message_content.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .input_message_content import InputMessageContent - - -class InputLocationMessageContent(InputMessageContent): - """ - Represents the content of a location message to be sent as the result of an inline query. - - Source: https://core.telegram.org/bots/api#inputlocationmessagecontent - """ - - latitude: float - """Latitude of the location in degrees""" - longitude: float - """Longitude of the location in degrees""" - live_period: Optional[int] = None - """Period in seconds for which the location can be updated, should be between 60 and 86400.""" diff --git a/aiogram/api/types/input_media.py b/aiogram/api/types/input_media.py deleted file mode 100644 index a9e7b2be..00000000 --- a/aiogram/api/types/input_media.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -from .base import MutableTelegramObject - - -class InputMedia(MutableTelegramObject): - """ - This object represents the content of a media message to be sent. It should be one of - - InputMediaAnimation - - InputMediaDocument - - InputMediaAudio - - InputMediaPhoto - - InputMediaVideo - - Source: https://core.telegram.org/bots/api#inputmedia - """ diff --git a/aiogram/api/types/input_media_animation.py b/aiogram/api/types/input_media_animation.py deleted file mode 100644 index d30084a2..00000000 --- a/aiogram/api/types/input_media_animation.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional, Union - -from pydantic import Field - -from .base import UNSET -from .input_media import InputMedia - -if TYPE_CHECKING: # pragma: no cover - from .input_file import InputFile - - -class InputMediaAnimation(InputMedia): - """ - Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. - - Source: https://core.telegram.org/bots/api#inputmediaanimation - """ - - type: str = Field("animation", const=True) - """Type of the result, must be animation""" - media: Union[str, InputFile] - """File to send. Pass a file_id to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass - 'attach://' to upload a new one using multipart/form-data under - name.""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - caption: Optional[str] = None - """Caption of the animation to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the animation caption. See formatting options for more - details.""" - width: Optional[int] = None - """Animation width""" - height: Optional[int] = None - """Animation height""" - duration: Optional[int] = None - """Animation duration""" diff --git a/aiogram/api/types/input_media_audio.py b/aiogram/api/types/input_media_audio.py deleted file mode 100644 index 098087d1..00000000 --- a/aiogram/api/types/input_media_audio.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional, Union - -from pydantic import Field - -from .base import UNSET -from .input_media import InputMedia - -if TYPE_CHECKING: # pragma: no cover - from .input_file import InputFile - - -class InputMediaAudio(InputMedia): - """ - Represents an audio file to be treated as music to be sent. - - Source: https://core.telegram.org/bots/api#inputmediaaudio - """ - - type: str = Field("audio", const=True) - """Type of the result, must be audio""" - media: Union[str, InputFile] - """File to send. Pass a file_id to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass - 'attach://' to upload a new one using multipart/form-data under - name.""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - caption: Optional[str] = None - """Caption of the audio to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the audio caption. See formatting options for more details.""" - duration: Optional[int] = None - """Duration of the audio in seconds""" - performer: Optional[str] = None - """Performer of the audio""" - title: Optional[str] = None - """Title of the audio""" diff --git a/aiogram/api/types/input_media_document.py b/aiogram/api/types/input_media_document.py deleted file mode 100644 index e29a5014..00000000 --- a/aiogram/api/types/input_media_document.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional, Union - -from pydantic import Field - -from .base import UNSET -from .input_media import InputMedia - -if TYPE_CHECKING: # pragma: no cover - from .input_file import InputFile - - -class InputMediaDocument(InputMedia): - """ - Represents a general file to be sent. - - Source: https://core.telegram.org/bots/api#inputmediadocument - """ - - type: str = Field("document", const=True) - """Type of the result, must be document""" - media: Union[str, InputFile] - """File to send. Pass a file_id to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass - 'attach://' to upload a new one using multipart/form-data under - name.""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - caption: Optional[str] = None - """Caption of the document to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the document caption. See formatting options for more details.""" diff --git a/aiogram/api/types/input_media_photo.py b/aiogram/api/types/input_media_photo.py deleted file mode 100644 index 6ef61006..00000000 --- a/aiogram/api/types/input_media_photo.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional, Union - -from pydantic import Field - -from .base import UNSET -from .input_media import InputMedia - -if TYPE_CHECKING: # pragma: no cover - from .input_file import InputFile - - -class InputMediaPhoto(InputMedia): - """ - Represents a photo to be sent. - - Source: https://core.telegram.org/bots/api#inputmediaphoto - """ - - type: str = Field("photo", const=True) - """Type of the result, must be photo""" - media: Union[str, InputFile] - """File to send. Pass a file_id to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass - 'attach://' to upload a new one using multipart/form-data under - name.""" - caption: Optional[str] = None - """Caption of the photo to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the photo caption. See formatting options for more details.""" diff --git a/aiogram/api/types/input_media_video.py b/aiogram/api/types/input_media_video.py deleted file mode 100644 index d8dc0adf..00000000 --- a/aiogram/api/types/input_media_video.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional, Union - -from pydantic import Field - -from .base import UNSET -from .input_media import InputMedia - -if TYPE_CHECKING: # pragma: no cover - from .input_file import InputFile - - -class InputMediaVideo(InputMedia): - """ - Represents a video to be sent. - - Source: https://core.telegram.org/bots/api#inputmediavideo - """ - - type: str = Field("video", const=True) - """Type of the result, must be video""" - media: Union[str, InputFile] - """File to send. Pass a file_id to send a file that exists on the Telegram servers - (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass - 'attach://' to upload a new one using multipart/form-data under - name.""" - thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" - caption: Optional[str] = None - """Caption of the video to be sent, 0-1024 characters after entities parsing""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the video caption. See formatting options for more details.""" - width: Optional[int] = None - """Video width""" - height: Optional[int] = None - """Video height""" - duration: Optional[int] = None - """Video duration""" - supports_streaming: Optional[bool] = None - """Pass True, if the uploaded video is suitable for streaming""" diff --git a/aiogram/api/types/input_message_content.py b/aiogram/api/types/input_message_content.py deleted file mode 100644 index fd002b02..00000000 --- a/aiogram/api/types/input_message_content.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -from .base import MutableTelegramObject - - -class InputMessageContent(MutableTelegramObject): - """ - This object represents the content of a message to be sent as a result of an inline query. - Telegram clients currently support the following 4 types: - - InputTextMessageContent - - InputLocationMessageContent - - InputVenueMessageContent - - InputContactMessageContent - - Source: https://core.telegram.org/bots/api#inputmessagecontent - """ diff --git a/aiogram/api/types/input_text_message_content.py b/aiogram/api/types/input_text_message_content.py deleted file mode 100644 index 389cb48e..00000000 --- a/aiogram/api/types/input_text_message_content.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .base import UNSET -from .input_message_content import InputMessageContent - - -class InputTextMessageContent(InputMessageContent): - """ - Represents the content of a text message to be sent as the result of an inline query. - - Source: https://core.telegram.org/bots/api#inputtextmessagecontent - """ - - message_text: str - """Text of the message to be sent, 1-4096 characters""" - parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the message text. See formatting options for more details.""" - disable_web_page_preview: Optional[bool] = None - """Disables link previews for links in the sent message""" diff --git a/aiogram/api/types/input_venue_message_content.py b/aiogram/api/types/input_venue_message_content.py deleted file mode 100644 index 2e8c1796..00000000 --- a/aiogram/api/types/input_venue_message_content.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .input_message_content import InputMessageContent - - -class InputVenueMessageContent(InputMessageContent): - """ - Represents the content of a venue message to be sent as the result of an inline query. - - Source: https://core.telegram.org/bots/api#inputvenuemessagecontent - """ - - latitude: float - """Latitude of the venue in degrees""" - longitude: float - """Longitude of the venue in degrees""" - title: str - """Name of the venue""" - address: str - """Address of the venue""" - foursquare_id: Optional[str] = None - """Foursquare identifier of the venue, if known""" - foursquare_type: Optional[str] = None - """Foursquare type of the venue, if known. (For example, 'arts_entertainment/default', - 'arts_entertainment/aquarium' or 'food/icecream'.)""" diff --git a/aiogram/api/types/invoice.py b/aiogram/api/types/invoice.py deleted file mode 100644 index 2d8d74ff..00000000 --- a/aiogram/api/types/invoice.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import annotations - -from .base import TelegramObject - - -class Invoice(TelegramObject): - """ - This object contains basic information about an invoice. - - Source: https://core.telegram.org/bots/api#invoice - """ - - title: str - """Product name""" - description: str - """Product description""" - start_parameter: str - """Unique bot deep-linking parameter that can be used to generate this invoice""" - currency: str - """Three-letter ISO 4217 currency code""" - total_amount: int - """Total price in the smallest units of the currency (integer, not float/double). For example, - for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it - shows the number of digits past the decimal point for each currency (2 for the majority of - currencies).""" diff --git a/aiogram/api/types/keyboard_button.py b/aiogram/api/types/keyboard_button.py deleted file mode 100644 index d04fa016..00000000 --- a/aiogram/api/types/keyboard_button.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from .base import MutableTelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .keyboard_button_poll_type import KeyboardButtonPollType - - -class KeyboardButton(MutableTelegramObject): - """ - This object represents one button of the reply keyboard. For simple text buttons String can be - used instead of this object to specify text of the button. Optional fields request_contact, - request_location, and request_poll are mutually exclusive. - Note: request_contact and request_location options will only work in Telegram versions - released after 9 April, 2016. Older clients will display unsupported message. - Note: request_poll option will only work in Telegram versions released after 23 January, 2020. - Older clients will display unsupported message. - - Source: https://core.telegram.org/bots/api#keyboardbutton - """ - - text: str - """Text of the button. If none of the optional fields are used, it will be sent as a message - when the button is pressed""" - request_contact: Optional[bool] = None - """If True, the user's phone number will be sent as a contact when the button is pressed. - Available in private chats only""" - request_location: Optional[bool] = None - """If True, the user's current location will be sent when the button is pressed. Available in - private chats only""" - request_poll: Optional[KeyboardButtonPollType] = None - """If specified, the user will be asked to create a poll and send it to the bot when the - button is pressed. Available in private chats only""" diff --git a/aiogram/api/types/labeled_price.py b/aiogram/api/types/labeled_price.py deleted file mode 100644 index 1b057f38..00000000 --- a/aiogram/api/types/labeled_price.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import annotations - -from .base import MutableTelegramObject - - -class LabeledPrice(MutableTelegramObject): - """ - This object represents a portion of the price for goods or services. - - Source: https://core.telegram.org/bots/api#labeledprice - """ - - label: str - """Portion label""" - amount: int - """Price of the product in the smallest units of the currency (integer, not float/double). For - example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in - currencies.json, it shows the number of digits past the decimal point for each currency (2 - for the majority of currencies).""" diff --git a/aiogram/api/types/location.py b/aiogram/api/types/location.py deleted file mode 100644 index 99c7ffc4..00000000 --- a/aiogram/api/types/location.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -from .base import TelegramObject - - -class Location(TelegramObject): - """ - This object represents a point on the map. - - Source: https://core.telegram.org/bots/api#location - """ - - longitude: float - """Longitude as defined by sender""" - latitude: float - """Latitude as defined by sender""" diff --git a/aiogram/api/types/login_url.py b/aiogram/api/types/login_url.py deleted file mode 100644 index cf422252..00000000 --- a/aiogram/api/types/login_url.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .base import TelegramObject - - -class LoginUrl(TelegramObject): - """ - This object represents a parameter of the inline keyboard button used to automatically - authorize a user. Serves as a great replacement for the Telegram Login Widget when the user is - coming from Telegram. All the user needs to do is tap/click a button and confirm that they - want to log in: - Telegram apps support these buttons as of version 5.7. - Sample bot: @discussbot - - Source: https://core.telegram.org/bots/api#loginurl - """ - - url: str - """An HTTP URL to be opened with user authorization data added to the query string when the - button is pressed. If the user refuses to provide authorization data, the original URL - without information about the user will be opened. The data added is the same as described - in Receiving authorization data.""" - forward_text: Optional[str] = None - """New text of the button in forwarded messages.""" - bot_username: Optional[str] = None - """Username of a bot, which will be used for user authorization. See Setting up a bot for more - details. If not specified, the current bot's username will be assumed. The url's domain - must be the same as the domain linked with the bot. See Linking your domain to the bot for - more details.""" - request_write_access: Optional[bool] = None - """Pass True to request the permission for your bot to send messages to the user.""" diff --git a/aiogram/api/types/message_entity.py b/aiogram/api/types/message_entity.py deleted file mode 100644 index 54c11a46..00000000 --- a/aiogram/api/types/message_entity.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .user import User - - -class MessageEntity(TelegramObject): - """ - This object represents one special entity in a text message. For example, hashtags, usernames, - URLs, etc. - - Source: https://core.telegram.org/bots/api#messageentity - """ - - type: str - """Type of the entity. Can be 'mention' (@username), 'hashtag' (#hashtag), 'cashtag' ($USD), - 'bot_command' (/start@jobs_bot), 'url' (https://telegram.org), 'email' - (do-not-reply@telegram.org), 'phone_number' (+1-212-555-0123), 'bold' (bold text), 'italic' - (italic text), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'code' - (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), - 'text_mention' (for users without usernames)""" - offset: int - """Offset in UTF-16 code units to the start of the entity""" - length: int - """Length of the entity in UTF-16 code units""" - url: Optional[str] = None - """For 'text_link' only, url that will be opened after user taps on the text""" - user: Optional[User] = None - """For 'text_mention' only, the mentioned user""" - language: Optional[str] = None - """For 'pre' only, the programming language of the entity text""" diff --git a/aiogram/api/types/passport_element_error.py b/aiogram/api/types/passport_element_error.py deleted file mode 100644 index 691cc348..00000000 --- a/aiogram/api/types/passport_element_error.py +++ /dev/null @@ -1,21 +0,0 @@ -from __future__ import annotations - -from .base import MutableTelegramObject - - -class PassportElementError(MutableTelegramObject): - """ - This object represents an error in the Telegram Passport element which was submitted that - should be resolved by the user. It should be one of: - - PassportElementErrorDataField - - PassportElementErrorFrontSide - - PassportElementErrorReverseSide - - PassportElementErrorSelfie - - PassportElementErrorFile - - PassportElementErrorFiles - - PassportElementErrorTranslationFile - - PassportElementErrorTranslationFiles - - PassportElementErrorUnspecified - - Source: https://core.telegram.org/bots/api#passportelementerror - """ diff --git a/aiogram/api/types/reply_keyboard_markup.py b/aiogram/api/types/reply_keyboard_markup.py deleted file mode 100644 index 5d30cc9b..00000000 --- a/aiogram/api/types/reply_keyboard_markup.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, List, Optional - -from .base import MutableTelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .keyboard_button import KeyboardButton - - -class ReplyKeyboardMarkup(MutableTelegramObject): - """ - This object represents a custom keyboard with reply options (see Introduction to bots for - details and examples). - - Source: https://core.telegram.org/bots/api#replykeyboardmarkup - """ - - keyboard: List[List[KeyboardButton]] - """Array of button rows, each represented by an Array of KeyboardButton objects""" - resize_keyboard: Optional[bool] = None - """Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard - smaller if there are just two rows of buttons). Defaults to false, in which case the custom - keyboard is always of the same height as the app's standard keyboard.""" - one_time_keyboard: Optional[bool] = None - """Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be - available, but clients will automatically display the usual letter-keyboard in the chat – - the user can press a special button in the input field to see the custom keyboard again. - Defaults to false.""" - selective: Optional[bool] = None - """Use this parameter if you want to show the keyboard to specific users only. Targets: 1) - users that are @mentioned in the text of the Message object; 2) if the bot's message is a - reply (has reply_to_message_id), sender of the original message.""" diff --git a/aiogram/api/types/reply_keyboard_remove.py b/aiogram/api/types/reply_keyboard_remove.py deleted file mode 100644 index ae092d94..00000000 --- a/aiogram/api/types/reply_keyboard_remove.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .base import MutableTelegramObject - - -class ReplyKeyboardRemove(MutableTelegramObject): - """ - Upon receiving a message with this object, Telegram clients will remove the current custom - keyboard and display the default letter-keyboard. By default, custom keyboards are displayed - until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are - hidden immediately after the user presses a button (see ReplyKeyboardMarkup). - - Source: https://core.telegram.org/bots/api#replykeyboardremove - """ - - remove_keyboard: bool = True - """Requests clients to remove the custom keyboard (user will not be able to summon this - keyboard; if you want to hide the keyboard from sight but keep it accessible, use - one_time_keyboard in ReplyKeyboardMarkup)""" - selective: Optional[bool] = None - """Use this parameter if you want to remove the keyboard for specific users only. Targets: 1) - users that are @mentioned in the text of the Message object; 2) if the bot's message is a - reply (has reply_to_message_id), sender of the original message.""" diff --git a/aiogram/api/types/response_parameters.py b/aiogram/api/types/response_parameters.py deleted file mode 100644 index 2e1e4eb8..00000000 --- a/aiogram/api/types/response_parameters.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations - -from typing import Optional - -from .base import TelegramObject - - -class ResponseParameters(TelegramObject): - """ - Contains information about why a request was unsuccessful. - - Source: https://core.telegram.org/bots/api#responseparameters - """ - - migrate_to_chat_id: Optional[int] = None - """The group has been migrated to a supergroup with the specified identifier. This number may - be greater than 32 bits and some programming languages may have difficulty/silent defects - in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or - double-precision float type are safe for storing this identifier.""" - retry_after: Optional[int] = None - """In case of exceeding flood control, the number of seconds left to wait before the request - can be repeated""" diff --git a/aiogram/api/types/update.py b/aiogram/api/types/update.py deleted file mode 100644 index 84803baa..00000000 --- a/aiogram/api/types/update.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .callback_query import CallbackQuery - from .chosen_inline_result import ChosenInlineResult - from .inline_query import InlineQuery - from .message import Message - from .poll import Poll - from .poll_answer import PollAnswer - from .pre_checkout_query import PreCheckoutQuery - from .shipping_query import ShippingQuery - - -class Update(TelegramObject): - """ - This object represents an incoming update. - At most one of the optional parameters can be present in any given update. - - Source: https://core.telegram.org/bots/api#update - """ - - update_id: int - """The update's unique identifier. Update identifiers start from a certain positive number and - increase sequentially. This ID becomes especially handy if you're using Webhooks, since it - allows you to ignore repeated updates or to restore the correct update sequence, should - they get out of order. If there are no new updates for at least a week, then identifier of - the next update will be chosen randomly instead of sequentially.""" - message: Optional[Message] = None - """New incoming message of any kind — text, photo, sticker, etc.""" - edited_message: Optional[Message] = None - """New version of a message that is known to the bot and was edited""" - channel_post: Optional[Message] = None - """New incoming channel post of any kind — text, photo, sticker, etc.""" - edited_channel_post: Optional[Message] = None - """New version of a channel post that is known to the bot and was edited""" - inline_query: Optional[InlineQuery] = None - """New incoming inline query""" - chosen_inline_result: Optional[ChosenInlineResult] = None - """The result of an inline query that was chosen by a user and sent to their chat partner. - Please see our documentation on the feedback collecting for details on how to enable these - updates for your bot.""" - callback_query: Optional[CallbackQuery] = None - """New incoming callback query""" - shipping_query: Optional[ShippingQuery] = None - """New incoming shipping query. Only for invoices with flexible price""" - pre_checkout_query: Optional[PreCheckoutQuery] = None - """New incoming pre-checkout query. Contains full information about checkout""" - poll: Optional[Poll] = None - """New poll state. Bots receive only updates about stopped polls and polls, which are sent by - the bot""" - poll_answer: Optional[PollAnswer] = None - """A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls - that were sent by the bot itself.""" diff --git a/aiogram/api/types/venue.py b/aiogram/api/types/venue.py deleted file mode 100644 index 4e8769ab..00000000 --- a/aiogram/api/types/venue.py +++ /dev/null @@ -1,28 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Optional - -from .base import TelegramObject - -if TYPE_CHECKING: # pragma: no cover - from .location import Location - - -class Venue(TelegramObject): - """ - This object represents a venue. - - Source: https://core.telegram.org/bots/api#venue - """ - - location: Location - """Venue location""" - title: str - """Name of the venue""" - address: str - """Address of the venue""" - foursquare_id: Optional[str] = None - """Foursquare identifier of the venue""" - foursquare_type: Optional[str] = None - """Foursquare type of the venue. (For example, 'arts_entertainment/default', - 'arts_entertainment/aquarium' or 'food/icecream'.)""" diff --git a/aiogram/api/__init__.py b/aiogram/client/__init__.py similarity index 100% rename from aiogram/api/__init__.py rename to aiogram/client/__init__.py diff --git a/aiogram/api/client/bot.py b/aiogram/client/bot.py similarity index 51% rename from aiogram/api/client/bot.py rename to aiogram/client/bot.py index ce12de07..695b7b28 100644 --- a/aiogram/api/client/bot.py +++ b/aiogram/client/bot.py @@ -19,14 +19,17 @@ from typing import ( import aiofiles from async_lru import alru_cache -from ...utils.mixins import ContextInstanceMixin -from ...utils.token import extract_bot_id, validate_token +from aiogram.utils.mixins import ContextInstanceMixin +from aiogram.utils.token import extract_bot_id, validate_token + from ..methods import ( AddStickerToSet, AnswerCallbackQuery, AnswerInlineQuery, AnswerPreCheckoutQuery, AnswerShippingQuery, + Close, + CopyMessage, CreateNewStickerSet, DeleteChatPhoto, DeleteChatStickerSet, @@ -54,6 +57,7 @@ from ..methods import ( GetWebhookInfo, KickChatMember, LeaveChat, + LogOut, PinChatMessage, PromoteChatMember, RestrictChatMember, @@ -91,6 +95,7 @@ from ..methods import ( StopPoll, TelegramMethod, UnbanChatMember, + UnpinAllChatMessages, UnpinChatMessage, UploadStickerFile, ) @@ -108,11 +113,15 @@ from ..types import ( InlineQueryResult, InputFile, InputMedia, + InputMediaAudio, + InputMediaDocument, InputMediaPhoto, InputMediaVideo, LabeledPrice, MaskPosition, Message, + MessageEntity, + MessageId, PassportElementError, Poll, ReplyKeyboardMarkup, @@ -131,13 +140,23 @@ T = TypeVar("T") class Bot(ContextInstanceMixin["Bot"]): - """ - Main bot class - """ - def __init__( - self, token: str, session: Optional[BaseSession] = None, parse_mode: Optional[str] = None, + self, + token: str, + session: Optional[BaseSession] = None, + parse_mode: Optional[str] = None, ) -> None: + """ + Bot class + + :param token: Telegram Bot token `Obtained from @BotFather `_ + :param session: HTTP Client session (For example AiohttpSession). + If not specified it will be automatically created. + :param parse_mode: Default parse mode. + If specified it will be propagated into the API methods at runtime. + :raise TokenValidationError: When token has invalid format this exception will be raised + """ + validate_token(token) if session is None: @@ -173,19 +192,13 @@ class Bot(ContextInstanceMixin["Bot"]): yield self finally: if auto_close: - await self.close() + await self.session.close() self.reset_current(token) @alru_cache() # type: ignore async def me(self) -> User: return await self.get_me() - async def close(self) -> None: - """ - Close bot session - """ - await self.session.close() - @classmethod async def __download_file_binary_io( cls, destination: BinaryIO, seek: bool, stream: AsyncGenerator[bytes, None] @@ -265,9 +278,9 @@ class Bot(ContextInstanceMixin["Bot"]): if file_id is None: raise TypeError("file can only be of the string or Downloadable type") - _file = await self.get_file(file_id) + file_ = await self.get_file(file_id) # https://github.com/aiogram/aiogram/pull/282/files#r394110017 - file_path = cast(str, _file.file_path) + file_path = cast(str, file_.file_path) return await self.download_file( file_path, destination=destination, timeout=timeout, chunk_size=chunk_size, seek=seek @@ -317,38 +330,28 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> List[Update]: """ - Use this method to receive incoming updates using long polling (wiki). An Array of Update - objects is returned. - Notes - 1. This method will not work if an outgoing webhook is set up. - 2. In order to avoid getting duplicate updates, recalculate offset after each server - response. + Use this method to receive incoming updates using long polling (`wiki `_). An Array of :class:`aiogram.types.update.Update` objects is returned. + + **Notes** + + **1.** This method will not work if an outgoing webhook is set up. + + **2.** In order to avoid getting duplicate updates, recalculate *offset* after each server response. Source: https://core.telegram.org/bots/api#getupdates - :param offset: Identifier of the first update to be returned. Must be greater by one than - the highest among the identifiers of previously received updates. By - default, updates starting with the earliest unconfirmed update are - returned. An update is considered confirmed as soon as getUpdates is called - with an offset higher than its update_id. The negative offset can be - specified to retrieve updates starting from -offset update from the end of - the updates queue. All previous updates will forgotten. - :param limit: Limits the number of updates to be retrieved. Values between 1-100 are - accepted. Defaults to 100. - :param timeout: Timeout in seconds for long polling. Defaults to 0, i.e. usual short - polling. Should be positive, short polling should be used for testing - purposes only. - :param allowed_updates: A JSON-serialized list of the update types you want your bot to - receive. For example, specify ['message', 'edited_channel_post', - 'callback_query'] to only receive updates of these types. See - Update for a complete list of available update types. Specify an - empty list to receive all updates regardless of type (default). If - not specified, the previous setting will be used. + :param offset: Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as :class:`aiogram.methods.get_updates.GetUpdates` is called with an *offset* higher than its *update_id*. The negative offset can be specified to retrieve updates starting from *-offset* update from the end of the updates queue. All previous updates will forgotten. + :param limit: Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100. + :param timeout: Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be positive, short polling should be used for testing purposes only. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of these types. See :class:`aiogram.types.update.Update` for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. :param request_timeout: Request timeout :return: An Array of Update objects is returned. """ call = GetUpdates( - offset=offset, limit=limit, timeout=timeout, allowed_updates=allowed_updates, + offset=offset, + limit=limit, + timeout=timeout, + allowed_updates=allowed_updates, ) return await self(call, request_timeout=request_timeout) @@ -356,78 +359,77 @@ class Bot(ContextInstanceMixin["Bot"]): self, url: str, certificate: Optional[InputFile] = None, + ip_address: Optional[str] = None, max_connections: Optional[int] = None, allowed_updates: Optional[List[str]] = None, + drop_pending_updates: Optional[bool] = None, request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to specify a url and receive incoming updates via an outgoing webhook. - Whenever there is an update for the bot, we will send an HTTPS POST request to the - specified url, containing a JSON-serialized Update. In case of an unsuccessful request, we - will give up after a reasonable amount of attempts. Returns True on success. - If you'd like to make sure that the Webhook request comes from Telegram, we recommend - using a secret path in the URL, e.g. https://www.example.com/. Since nobody else - knows your bot's token, you can be pretty sure it's us. - Notes - 1. You will not be able to receive updates using getUpdates for as long as an outgoing - webhook is set up. - 2. To use a self-signed certificate, you need to upload your public key certificate using - certificate parameter. Please upload as InputFile, sending a String will not work. - 3. Ports currently supported for Webhooks: 443, 80, 88, 8443. - NEW! If you're having any trouble setting up webhooks, please check out this amazing guide - to Webhooks. + Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized :class:`aiogram.types.update.Update`. In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns :code:`True` on success. + If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. :code:`https://www.example.com/`. Since nobody else knows your bot's token, you can be pretty sure it's us. + + **Notes** + + **1.** You will not be able to receive updates using :class:`aiogram.methods.get_updates.GetUpdates` for as long as an outgoing webhook is set up. + + **2.** To use a self-signed certificate, you need to upload your `public key certificate `_ using *certificate* parameter. Please upload as InputFile, sending a String will not work. + + **3.** Ports currently supported *for Webhooks*: **443, 80, 88, 8443**. + **NEW!** If you're having any trouble setting up webhooks, please check out this `amazing guide to Webhooks `_. Source: https://core.telegram.org/bots/api#setwebhook - :param url: HTTPS url to send updates to. Use an empty string to remove webhook - integration - :param certificate: Upload your public key certificate so that the root certificate in use - can be checked. See our self-signed guide for details. - :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the - webhook for update delivery, 1-100. Defaults to 40. Use lower - values to limit the load on your bot's server, and higher values - to increase your bot's throughput. - :param allowed_updates: A JSON-serialized list of the update types you want your bot to - receive. For example, specify ['message', 'edited_channel_post', - 'callback_query'] to only receive updates of these types. See - Update for a complete list of available update types. Specify an - empty list to receive all updates regardless of type (default). If - not specified, the previous setting will be used. + :param url: HTTPS url to send updates to. Use an empty string to remove webhook integration + :param certificate: Upload your public key certificate so that the root certificate in use can be checked. See our `self-signed guide `_ for details. + :param ip_address: The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS + :param max_connections: Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to *40*. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput. + :param allowed_updates: A JSON-serialized list of the update types you want your bot to receive. For example, specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of these types. See :class:`aiogram.types.update.Update` for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used. + :param drop_pending_updates: Pass :code:`True` to drop all pending updates :param request_timeout: Request timeout :return: Returns True on success. """ call = SetWebhook( url=url, certificate=certificate, + ip_address=ip_address, max_connections=max_connections, allowed_updates=allowed_updates, + drop_pending_updates=drop_pending_updates, ) return await self(call, request_timeout=request_timeout) - async def delete_webhook(self, request_timeout: Optional[int] = None,) -> bool: + async def delete_webhook( + self, + drop_pending_updates: Optional[bool] = None, + request_timeout: Optional[int] = None, + ) -> bool: """ - Use this method to remove webhook integration if you decide to switch back to getUpdates. - Returns True on success. Requires no parameters. + Use this method to remove webhook integration if you decide to switch back to :class:`aiogram.methods.get_updates.GetUpdates`. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletewebhook + :param drop_pending_updates: Pass :code:`True` to drop all pending updates :param request_timeout: Request timeout :return: Returns True on success. """ - call = DeleteWebhook() + call = DeleteWebhook( + drop_pending_updates=drop_pending_updates, + ) return await self(call, request_timeout=request_timeout) - async def get_webhook_info(self, request_timeout: Optional[int] = None,) -> WebhookInfo: + async def get_webhook_info( + self, + request_timeout: Optional[int] = None, + ) -> WebhookInfo: """ - Use this method to get current webhook status. Requires no parameters. On success, returns - a WebhookInfo object. If the bot is using getUpdates, will return an object with the url - field empty. + Use this method to get current webhook status. Requires no parameters. On success, returns a :class:`aiogram.types.webhook_info.WebhookInfo` object. If the bot is using :class:`aiogram.methods.get_updates.GetUpdates`, will return an object with the *url* field empty. Source: https://core.telegram.org/bots/api#getwebhookinfo :param request_timeout: Request timeout :return: On success, returns a WebhookInfo object. If the bot is using getUpdates, will - return an object with the url field empty. + return an object with the url field empty. """ call = GetWebhookInfo() return await self(call, request_timeout=request_timeout) @@ -437,10 +439,12 @@ class Bot(ContextInstanceMixin["Bot"]): # Source: https://core.telegram.org/bots/api#available-methods # ============================================================================================= - async def get_me(self, request_timeout: Optional[int] = None,) -> User: + async def get_me( + self, + request_timeout: Optional[int] = None, + ) -> User: """ - A simple method for testing your bot's auth token. Requires no parameters. Returns basic - information about the bot in form of a User object. + A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a :class:`aiogram.types.user.User` object. Source: https://core.telegram.org/bots/api#getme @@ -450,36 +454,66 @@ class Bot(ContextInstanceMixin["Bot"]): call = GetMe() return await self(call, request_timeout=request_timeout) + async def log_out( + self, + request_timeout: Optional[int] = None, + ) -> bool: + """ + Use this method to log out from the cloud Bot API server before launching the bot locally. You **must** log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. After a successful call, you can immediately log in on a local server, but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns :code:`True` on success. Requires no parameters. + + Source: https://core.telegram.org/bots/api#logout + + :param request_timeout: Request timeout + :return: Returns True on success. + """ + call = LogOut() + return await self(call, request_timeout=request_timeout) + + async def close( + self, + request_timeout: Optional[int] = None, + ) -> bool: + """ + Use this method to close the bot instance before moving it from one local server to another. You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. The method will return error 429 in the first 10 minutes after the bot is launched. Returns :code:`True` on success. Requires no parameters. + + Source: https://core.telegram.org/bots/api#close + + :param request_timeout: Request timeout + :return: The method will return error 429 in the first 10 minutes after the bot is + launched. Returns True on success. + """ + call = Close() + return await self(call, request_timeout=request_timeout) + async def send_message( self, chat_id: Union[int, str], text: str, parse_mode: Optional[str] = UNSET, + entities: Optional[List[MessageEntity]] = None, disable_web_page_preview: Optional[bool] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send text messages. On success, the sent Message is returned. + Use this method to send text messages. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendmessage - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param text: Text of the message to be sent, 1-4096 characters after entities parsing - :param parse_mode: Mode for parsing entities in the message text. See formatting options - for more details. + :param parse_mode: Mode for parsing entities in the message text. See `formatting options `_ for more details. + :param entities: List of special entities that appear in message text, which can be specified instead of *parse_mode* :param disable_web_page_preview: Disables link previews for links in this message - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -487,9 +521,11 @@ class Bot(ContextInstanceMixin["Bot"]): chat_id=chat_id, text=text, parse_mode=parse_mode, + entities=entities, disable_web_page_preview=disable_web_page_preview, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -503,17 +539,14 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to forward messages of any kind. On success, the sent Message is returned. + Use this method to forward messages of any kind. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#forwardmessage - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param from_chat_id: Unique identifier for the chat where the original message was sent - (or channel username in the format @channelusername) - :param message_id: Message identifier in the chat specified in from_chat_id - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format :code:`@channelusername`) + :param message_id: Message identifier in the chat specified in *from_chat_id* + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -525,40 +558,83 @@ class Bot(ContextInstanceMixin["Bot"]): ) return await self(call, request_timeout=request_timeout) + async def copy_message( + self, + chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: Optional[str] = None, + parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, + disable_notification: Optional[bool] = None, + reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None, + request_timeout: Optional[int] = None, + ) -> MessageId: + """ + Use this method to copy messages of any kind. The method is analogous to the method :class:`aiogram.methods.forward_messages.ForwardMessages`, but the copied message doesn't have a link to the original message. Returns the :class:`aiogram.types.message_id.MessageId` of the sent message on success. + + Source: https://core.telegram.org/bots/api#copymessage + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param from_chat_id: Unique identifier for the chat where the original message was sent (or channel username in the format :code:`@channelusername`) + :param message_id: Message identifier in the chat specified in *from_chat_id* + :param caption: New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept + :param parse_mode: Mode for parsing entities in the new caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the new caption, which can be specified instead of *parse_mode* + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. + :param reply_to_message_id: If the message is a reply, ID of the original message + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. + :param request_timeout: Request timeout + :return: Returns the MessageId of the sent message on success. + """ + call = CopyMessage( + chat_id=chat_id, + from_chat_id=from_chat_id, + message_id=message_id, + caption=caption, + parse_mode=parse_mode, + caption_entities=caption_entities, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, + reply_markup=reply_markup, + ) + return await self(call, request_timeout=request_timeout) + async def send_photo( self, chat_id: Union[int, str], photo: Union[InputFile, str], caption: Optional[str] = None, parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send photos. On success, the sent Message is returned. + Use this method to send photos. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendphoto - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param photo: Photo to send. Pass a file_id as String to send a photo that exists on the - Telegram servers (recommended), pass an HTTP URL as a String for Telegram to - get a photo from the Internet, or upload a new photo using - multipart/form-data. - :param caption: Photo caption (may also be used when resending photos by file_id), 0-1024 - characters after entities parsing - :param parse_mode: Mode for parsing entities in the photo caption. See formatting options - for more details. - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param photo: Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data. The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20. :ref:`More info on Sending Files » ` + :param caption: Photo caption (may also be used when resending photos by *file_id*), 0-1024 characters after entities parsing + :param parse_mode: Mode for parsing entities in the photo caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of *parse_mode* + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -567,8 +643,10 @@ class Bot(ContextInstanceMixin["Bot"]): photo=photo, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -579,51 +657,38 @@ class Bot(ContextInstanceMixin["Bot"]): audio: Union[InputFile, str], caption: Optional[str] = None, parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, duration: Optional[int] = None, performer: Optional[str] = None, title: Optional[str] = None, thumb: Optional[Union[InputFile, str]] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send audio files, if you want Telegram clients to display them in the - music player. Your audio must be in the .MP3 or .M4A format. On success, the sent Message - is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be - changed in the future. - For sending voice messages, use the sendVoice method instead. + Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future. + For sending voice messages, use the :class:`aiogram.methods.send_voice.SendVoice` method instead. Source: https://core.telegram.org/bots/api#sendaudio - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param audio: Audio file to send. Pass a file_id as String to send an audio file that - exists on the Telegram servers (recommended), pass an HTTP URL as a String - for Telegram to get an audio file from the Internet, or upload a new one - using multipart/form-data. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param audio: Audio file to send. Pass a file_id as String to send an audio file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » ` :param caption: Audio caption, 0-1024 characters after entities parsing - :param parse_mode: Mode for parsing entities in the audio caption. See formatting options - for more details. + :param parse_mode: Mode for parsing entities in the audio caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of *parse_mode* :param duration: Duration of the audio in seconds :param performer: Performer :param title: Track name - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the - file is supported server-side. The thumbnail should be in JPEG format and - less than 200 kB in size. A thumbnail's width and height should not exceed - 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file, so you - can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under . - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » ` + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -632,12 +697,14 @@ class Bot(ContextInstanceMixin["Bot"]): audio=audio, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, duration=duration, performer=performer, title=title, thumb=thumb, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -649,43 +716,32 @@ class Bot(ContextInstanceMixin["Bot"]): thumb: Optional[Union[InputFile, str]] = None, caption: Optional[str] = None, parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, + disable_content_type_detection: Optional[bool] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send general files. On success, the sent Message is returned. Bots can - currently send files of any type of up to 50 MB in size, this limit may be changed in the - future. + Use this method to send general files. On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future. Source: https://core.telegram.org/bots/api#senddocument - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param document: File to send. Pass a file_id as String to send a file that exists on the - Telegram servers (recommended), pass an HTTP URL as a String for Telegram - to get a file from the Internet, or upload a new one using - multipart/form-data. - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the - file is supported server-side. The thumbnail should be in JPEG format and - less than 200 kB in size. A thumbnail's width and height should not exceed - 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file, so you - can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under . - :param caption: Document caption (may also be used when resending documents by file_id), - 0-1024 characters after entities parsing - :param parse_mode: Mode for parsing entities in the document caption. See formatting - options for more details. - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param document: File to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » ` + :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » ` + :param caption: Document caption (may also be used when resending documents by *file_id*), 0-1024 characters after entities parsing + :param parse_mode: Mode for parsing entities in the document caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of *parse_mode* + :param disable_content_type_detection: Disables automatic server-side content type detection for files uploaded using multipart/form-data + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -695,8 +751,11 @@ class Bot(ContextInstanceMixin["Bot"]): thumb=thumb, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, + disable_content_type_detection=disable_content_type_detection, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -711,48 +770,35 @@ class Bot(ContextInstanceMixin["Bot"]): thumb: Optional[Union[InputFile, str]] = None, caption: Optional[str] = None, parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, supports_streaming: Optional[bool] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send video files, Telegram clients support mp4 videos (other formats - may be sent as Document). On success, the sent Message is returned. Bots can currently - send video files of up to 50 MB in size, this limit may be changed in the future. + Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as :class:`aiogram.types.document.Document`). On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future. Source: https://core.telegram.org/bots/api#sendvideo - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param video: Video to send. Pass a file_id as String to send a video that exists on the - Telegram servers (recommended), pass an HTTP URL as a String for Telegram to - get a video from the Internet, or upload a new video using - multipart/form-data. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param video: Video to send. Pass a file_id as String to send a video that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a video from the Internet, or upload a new video using multipart/form-data. :ref:`More info on Sending Files » ` :param duration: Duration of sent video in seconds :param width: Video width :param height: Video height - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the - file is supported server-side. The thumbnail should be in JPEG format and - less than 200 kB in size. A thumbnail's width and height should not exceed - 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file, so you - can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under . - :param caption: Video caption (may also be used when resending videos by file_id), 0-1024 - characters after entities parsing - :param parse_mode: Mode for parsing entities in the video caption. See formatting options - for more details. - :param supports_streaming: Pass True, if the uploaded video is suitable for streaming - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » ` + :param caption: Video caption (may also be used when resending videos by *file_id*), 0-1024 characters after entities parsing + :param parse_mode: Mode for parsing entities in the video caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of *parse_mode* + :param supports_streaming: Pass :code:`True`, if the uploaded video is suitable for streaming + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -765,9 +811,11 @@ class Bot(ContextInstanceMixin["Bot"]): thumb=thumb, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, supports_streaming=supports_streaming, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -782,46 +830,33 @@ class Bot(ContextInstanceMixin["Bot"]): thumb: Optional[Union[InputFile, str]] = None, caption: Optional[str] = None, parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On - success, the sent Message is returned. Bots can currently send animation files of up to 50 - MB in size, this limit may be changed in the future. + Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. Source: https://core.telegram.org/bots/api#sendanimation - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param animation: Animation to send. Pass a file_id as String to send an animation that - exists on the Telegram servers (recommended), pass an HTTP URL as a - String for Telegram to get an animation from the Internet, or upload a - new animation using multipart/form-data. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param animation: Animation to send. Pass a file_id as String to send an animation that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an animation from the Internet, or upload a new animation using multipart/form-data. :ref:`More info on Sending Files » ` :param duration: Duration of sent animation in seconds :param width: Animation width :param height: Animation height - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the - file is supported server-side. The thumbnail should be in JPEG format and - less than 200 kB in size. A thumbnail's width and height should not exceed - 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file, so you - can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under . - :param caption: Animation caption (may also be used when resending animation by file_id), - 0-1024 characters after entities parsing - :param parse_mode: Mode for parsing entities in the animation caption. See formatting - options for more details. - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » ` + :param caption: Animation caption (may also be used when resending animation by *file_id*), 0-1024 characters after entities parsing + :param parse_mode: Mode for parsing entities in the animation caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of *parse_mode* + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -834,8 +869,10 @@ class Bot(ContextInstanceMixin["Bot"]): thumb=thumb, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -846,39 +883,31 @@ class Bot(ContextInstanceMixin["Bot"]): voice: Union[InputFile, str], caption: Optional[str] = None, parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, duration: Optional[int] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send audio files, if you want Telegram clients to display the file as a - playable voice message. For this to work, your audio must be in an .OGG file encoded with - OPUS (other formats may be sent as Audio or Document). On success, the sent Message is - returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be - changed in the future. + Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS (other formats may be sent as :class:`aiogram.types.audio.Audio` or :class:`aiogram.types.document.Document`). On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. Source: https://core.telegram.org/bots/api#sendvoice - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param voice: Audio file to send. Pass a file_id as String to send a file that exists on - the Telegram servers (recommended), pass an HTTP URL as a String for - Telegram to get a file from the Internet, or upload a new one using - multipart/form-data. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param voice: Audio file to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » ` :param caption: Voice message caption, 0-1024 characters after entities parsing - :param parse_mode: Mode for parsing entities in the voice message caption. See formatting - options for more details. + :param parse_mode: Mode for parsing entities in the voice message caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of *parse_mode* :param duration: Duration of the voice message in seconds - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -887,9 +916,11 @@ class Bot(ContextInstanceMixin["Bot"]): voice=voice, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, duration=duration, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -903,38 +934,26 @@ class Bot(ContextInstanceMixin["Bot"]): thumb: Optional[Union[InputFile, str]] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. - Use this method to send video messages. On success, the sent Message is returned. + As of `v.4.0 `_, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendvideonote - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param video_note: Video note to send. Pass a file_id as String to send a video note that - exists on the Telegram servers (recommended) or upload a new video - using multipart/form-data.. Sending video notes by a URL is currently - unsupported + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param video_note: Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. :ref:`More info on Sending Files » `. Sending video notes by a URL is currently unsupported :param duration: Duration of sent video in seconds :param length: Video width and height, i.e. diameter of the video message - :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the - file is supported server-side. The thumbnail should be in JPEG format and - less than 200 kB in size. A thumbnail's width and height should not exceed - 320. Ignored if the file is not uploaded using multipart/form-data. - Thumbnails can't be reused and can be only uploaded as a new file, so you - can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under . - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param thumb: Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » ` + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -946,6 +965,7 @@ class Bot(ContextInstanceMixin["Bot"]): thumb=thumb, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -953,32 +973,31 @@ class Bot(ContextInstanceMixin["Bot"]): async def send_media_group( self, chat_id: Union[int, str], - media: List[Union[InputMediaPhoto, InputMediaVideo]], + media: List[Union[InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo]], disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, request_timeout: Optional[int] = None, ) -> List[Message]: """ - Use this method to send a group of photos or videos as an album. On success, an array of - the sent Messages is returned. + Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of `Messages `_ that were sent is returned. Source: https://core.telegram.org/bots/api#sendmediagroup - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param media: A JSON-serialized array describing photos and videos to be sent, must - include 2-10 items - :param disable_notification: Sends the messages silently. Users will receive a - notification with no sound. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param media: A JSON-serialized array describing messages to be sent, must include 2-10 items + :param disable_notification: Sends messages `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the messages are a reply, ID of the original message + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found :param request_timeout: Request timeout - :return: On success, an array of the sent Messages is returned. + :return: On success, an array of Messages that were sent is returned. """ call = SendMediaGroup( chat_id=chat_id, media=media, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, ) return await self(call, request_timeout=request_timeout) @@ -987,31 +1006,34 @@ class Bot(ContextInstanceMixin["Bot"]): chat_id: Union[int, str], latitude: float, longitude: float, + horizontal_accuracy: Optional[float] = None, live_period: Optional[int] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send point on the map. On success, the sent Message is returned. + Use this method to send point on the map. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendlocation - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param latitude: Latitude of the location :param longitude: Longitude of the location - :param live_period: Period in seconds for which the location will be updated (see Live - Locations, should be between 60 and 86400. - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param horizontal_accuracy: The radius of uncertainty for the location, measured in meters; 0-1500 + :param live_period: Period in seconds for which the location will be updated (see `Live Locations `_, should be between 60 and 86400. + :param heading: For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. + :param proximity_alert_radius: For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -1019,9 +1041,13 @@ class Bot(ContextInstanceMixin["Bot"]): chat_id=chat_id, latitude=latitude, longitude=longitude, + horizontal_accuracy=horizontal_accuracy, live_period=live_period, + heading=heading, + proximity_alert_radius=proximity_alert_radius, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -1033,30 +1059,29 @@ class Bot(ContextInstanceMixin["Bot"]): chat_id: Optional[Union[int, str]] = None, message_id: Optional[int] = None, inline_message_id: Optional[str] = None, + horizontal_accuracy: Optional[float] = None, + heading: Optional[int] = None, + proximity_alert_radius: Optional[int] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, request_timeout: Optional[int] = None, ) -> Union[Message, bool]: """ - Use this method to edit live location messages. A location can be edited until its - live_period expires or editing is explicitly disabled by a call to - stopMessageLiveLocation. On success, if the edited message was sent by the bot, the edited - Message is returned, otherwise True is returned. + Use this method to edit live location messages. A location can be edited until its *live_period* expires or editing is explicitly disabled by a call to :class:`aiogram.methods.stop_message_live_location.StopMessageLiveLocation`. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#editmessagelivelocation :param latitude: Latitude of new location :param longitude: Longitude of new location - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat or username of the target channel (in the format - @channelusername) - :param message_id: Required if inline_message_id is not specified. Identifier of the - message to edit - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message - :param reply_markup: A JSON-serialized object for a new inline keyboard. + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param message_id: Required if *inline_message_id* is not specified. Identifier of the message to edit + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message + :param horizontal_accuracy: The radius of uncertainty for the location, measured in meters; 0-1500 + :param heading: Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified. + :param proximity_alert_radius: Maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified. + :param reply_markup: A JSON-serialized object for a new `inline keyboard `_. :param request_timeout: Request timeout - :return: On success, if the edited message was sent by the bot, the edited Message is - returned, otherwise True is returned. + :return: On success, if the edited message is not an inline message, the edited Message is + returned, otherwise True is returned. """ call = EditMessageLiveLocation( latitude=latitude, @@ -1064,6 +1089,9 @@ class Bot(ContextInstanceMixin["Bot"]): chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, + horizontal_accuracy=horizontal_accuracy, + heading=heading, + proximity_alert_radius=proximity_alert_radius, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -1077,23 +1105,17 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> Union[Message, bool]: """ - Use this method to stop updating a live location message before live_period expires. On - success, if the message was sent by the bot, the sent Message is returned, otherwise True - is returned. + Use this method to stop updating a live location message before *live_period* expires. On success, if the message was sent by the bot, the sent :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#stopmessagelivelocation - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat or username of the target channel (in the format - @channelusername) - :param message_id: Required if inline_message_id is not specified. Identifier of the - message with live location to stop - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message - :param reply_markup: A JSON-serialized object for a new inline keyboard. + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param message_id: Required if *inline_message_id* is not specified. Identifier of the message with live location to stop + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message + :param reply_markup: A JSON-serialized object for a new `inline keyboard `_. :param request_timeout: Request timeout :return: On success, if the message was sent by the bot, the sent Message is returned, - otherwise True is returned. + otherwise True is returned. """ call = StopMessageLiveLocation( chat_id=chat_id, @@ -1112,35 +1134,34 @@ class Bot(ContextInstanceMixin["Bot"]): address: str, foursquare_id: Optional[str] = None, foursquare_type: Optional[str] = None, + google_place_id: Optional[str] = None, + google_place_type: Optional[str] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send information about a venue. On success, the sent Message is - returned. + Use this method to send information about a venue. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendvenue - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param latitude: Latitude of the venue :param longitude: Longitude of the venue :param title: Name of the venue :param address: Address of the venue :param foursquare_id: Foursquare identifier of the venue - :param foursquare_type: Foursquare type of the venue, if known. (For example, - 'arts_entertainment/default', 'arts_entertainment/aquarium' or - 'food/icecream'.) - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param foursquare_type: Foursquare type of the venue, if known. (For example, 'arts_entertainment/default', 'arts_entertainment/aquarium' or 'food/icecream'.) + :param google_place_id: Google Places identifier of the venue + :param google_place_type: Google Places type of the venue. (See `supported types `_.) + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -1152,8 +1173,11 @@ class Bot(ContextInstanceMixin["Bot"]): address=address, foursquare_id=foursquare_id, foursquare_type=foursquare_type, + google_place_id=google_place_id, + google_place_type=google_place_type, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -1167,28 +1191,26 @@ class Bot(ContextInstanceMixin["Bot"]): vcard: Optional[str] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send phone contacts. On success, the sent Message is returned. + Use this method to send phone contacts. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendcontact - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param phone_number: Contact's phone number :param first_name: Contact's first name :param last_name: Contact's last name - :param vcard: Additional data about the contact in the form of a vCard, 0-2048 bytes - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param vcard: Additional data about the contact in the form of a `vCard `_, 0-2048 bytes + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove keyboard or - to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -1200,6 +1222,7 @@ class Bot(ContextInstanceMixin["Bot"]): vcard=vcard, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -1215,50 +1238,40 @@ class Bot(ContextInstanceMixin["Bot"]): correct_option_id: Optional[int] = None, explanation: Optional[str] = None, explanation_parse_mode: Optional[str] = UNSET, + explanation_entities: Optional[List[MessageEntity]] = None, open_period: Optional[int] = None, close_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None, is_closed: Optional[bool] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send a native poll. On success, the sent Message is returned. + Use this method to send a native poll. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendpoll - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param question: Poll question, 1-255 characters - :param options: A JSON-serialized list of answer options, 2-10 strings 1-100 characters - each - :param is_anonymous: True, if the poll needs to be anonymous, defaults to True + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param question: Poll question, 1-300 characters + :param options: A JSON-serialized list of answer options, 2-10 strings 1-100 characters each + :param is_anonymous: True, if the poll needs to be anonymous, defaults to :code:`True` :param type: Poll type, 'quiz' or 'regular', defaults to 'regular' - :param allows_multiple_answers: True, if the poll allows multiple answers, ignored for - polls in quiz mode, defaults to False - :param correct_option_id: 0-based identifier of the correct answer option, required for - polls in quiz mode - :param explanation: Text that is shown when a user chooses an incorrect answer or taps on - the lamp icon in a quiz-style poll, 0-200 characters with at most 2 - line feeds after entities parsing - :param explanation_parse_mode: Mode for parsing entities in the explanation. See - formatting options for more details. - :param open_period: Amount of time in seconds the poll will be active after creation, - 5-600. Can't be used together with close_date. - :param close_date: Point in time (Unix timestamp) when the poll will be automatically - closed. Must be at least 5 and no more than 600 seconds in the future. - Can't be used together with open_period. - :param is_closed: Pass True, if the poll needs to be immediately closed. This can be - useful for poll preview. - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param allows_multiple_answers: True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False` + :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode + :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing + :param explanation_parse_mode: Mode for parsing entities in the explanation. See `formatting options `_ for more details. + :param explanation_entities: List of special entities that appear in the poll explanation, which can be specified instead of *parse_mode* + :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. + :param is_closed: Pass :code:`True`, if the poll needs to be immediately closed. This can be useful for poll preview. + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -1272,11 +1285,13 @@ class Bot(ContextInstanceMixin["Bot"]): correct_option_id=correct_option_id, explanation=explanation, explanation_parse_mode=explanation_parse_mode, + explanation_entities=explanation_entities, open_period=open_period, close_date=close_date, is_closed=is_closed, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -1287,28 +1302,23 @@ class Bot(ContextInstanceMixin["Bot"]): emoji: Optional[str] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send an animated emoji that will display a random value. On success, - the sent Message is returned. + Use this method to send an animated emoji that will display a random value. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#senddice - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of - '', '', or ''. Dice can have values 1-6 for '' and '', and values 1-5 for - ''. Defaults to '' - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of '🎲', '🎯', '🏀', '⚽', or '🎰'. Dice can have values 1-6 for '🎲' and '🎯', values 1-5 for '🏀' and '⚽', and values 1-64 for '🎰'. Defaults to '🎲' + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -1317,37 +1327,35 @@ class Bot(ContextInstanceMixin["Bot"]): emoji=emoji, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) async def send_chat_action( - self, chat_id: Union[int, str], action: str, request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + action: str, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method when you need to tell the user that something is happening on the bot's - side. The status is set for 5 seconds or less (when a message arrives from your bot, - Telegram clients clear its typing status). Returns True on success. - Example: The ImageBot needs some time to process a request and upload the image. Instead - of sending a text message along the lines of 'Retrieving image, please wait…', the bot may - use sendChatAction with action = upload_photo. The user will see a 'sending photo' status - for the bot. - We only recommend using this method when a response from the bot will take a noticeable - amount of time to arrive. + Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). Returns :code:`True` on success. + + Example: The `ImageBot `_ needs some time to process a request and upload the image. Instead of sending a text message along the lines of 'Retrieving image, please wait…', the bot may use :class:`aiogram.methods.send_chat_action.SendChatAction` with *action* = *upload_photo*. The user will see a 'sending photo' status for the bot. + + We only recommend using this method when a response from the bot will take a **noticeable** amount of time to arrive. Source: https://core.telegram.org/bots/api#sendchataction - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param action: Type of action to broadcast. Choose one, depending on what the user is - about to receive: typing for text messages, upload_photo for photos, - record_video or upload_video for videos, record_audio or upload_audio for - audio files, upload_document for general files, find_location for location - data, record_video_note or upload_video_note for video notes. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param action: Type of action to broadcast. Choose one, depending on what the user is about to receive: *typing* for `text messages `_, *upload_photo* for `photos `_, *record_video* or *upload_video* for `videos `_, *record_voice* or *upload_voice* for `voice notes `_, *upload_document* for `general files `_, *find_location* for `location data `_, *record_video_note* or *upload_video_note* for `video notes `_. :param request_timeout: Request timeout :return: Returns True on success. """ - call = SendChatAction(chat_id=chat_id, action=action,) + call = SendChatAction( + chat_id=chat_id, + action=action, + ) return await self(call, request_timeout=request_timeout) async def get_user_profile_photos( @@ -1358,32 +1366,31 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> UserProfilePhotos: """ - Use this method to get a list of profile pictures for a user. Returns a UserProfilePhotos - object. + Use this method to get a list of profile pictures for a user. Returns a :class:`aiogram.types.user_profile_photos.UserProfilePhotos` object. Source: https://core.telegram.org/bots/api#getuserprofilephotos :param user_id: Unique identifier of the target user - :param offset: Sequential number of the first photo to be returned. By default, all photos - are returned. - :param limit: Limits the number of photos to be retrieved. Values between 1-100 are - accepted. Defaults to 100. + :param offset: Sequential number of the first photo to be returned. By default, all photos are returned. + :param limit: Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100. :param request_timeout: Request timeout :return: Returns a UserProfilePhotos object. """ - call = GetUserProfilePhotos(user_id=user_id, offset=offset, limit=limit,) + call = GetUserProfilePhotos( + user_id=user_id, + offset=offset, + limit=limit, + ) return await self(call, request_timeout=request_timeout) - async def get_file(self, file_id: str, request_timeout: Optional[int] = None,) -> File: + async def get_file( + self, + file_id: str, + request_timeout: Optional[int] = None, + ) -> File: """ - Use this method to get basic info about a file and prepare it for downloading. For the - moment, bots can download files of up to 20MB in size. On success, a File object is - returned. The file can then be downloaded via the link - https://api.telegram.org/file/bot/, where is taken from the - response. It is guaranteed that the link will be valid for at least 1 hour. When the link - expires, a new one can be requested by calling getFile again. - Note: This function may not preserve the original file name and MIME type. You should save - the file's MIME type and name (if available) when the File object is received. + Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. On success, a :class:`aiogram.types.file.File` object is returned. The file can then be downloaded via the link :code:`https://api.telegram.org/file/bot/`, where :code:`` is taken from the response. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling :class:`aiogram.methods.get_file.GetFile` again. + **Note:** This function may not preserve the original file name and MIME type. You should save the file's MIME type and name (if available) when the File object is received. Source: https://core.telegram.org/bots/api#getfile @@ -1391,7 +1398,9 @@ class Bot(ContextInstanceMixin["Bot"]): :param request_timeout: Request timeout :return: On success, a File object is returned. """ - call = GetFile(file_id=file_id,) + call = GetFile( + file_id=file_id, + ) return await self(call, request_timeout=request_timeout) async def kick_chat_member( @@ -1402,44 +1411,48 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to kick a user from a group, a supergroup or a channel. In the case of - supergroups and channels, the user will not be able to return to the group on their own - using invite links, etc., unless unbanned first. The bot must be an administrator in the - chat for this to work and must have the appropriate admin rights. Returns True on success. + Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless `unbanned `_ first. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#kickchatmember - :param chat_id: Unique identifier for the target group or username of the target - supergroup or channel (in the format @channelusername) + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format :code:`@channelusername`) :param user_id: Unique identifier of the target user - :param until_date: Date when the user will be unbanned, unix time. If user is banned for - more than 366 days or less than 30 seconds from the current time they - are considered to be banned forever + :param until_date: Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever. Applied for supergroups and channels only. :param request_timeout: Request timeout :return: In the case of supergroups and channels, the user will not be able to return to - the group on their own using invite links, etc. Returns True on success. + the chat on their own using invite links, etc. Returns True on success. """ - call = KickChatMember(chat_id=chat_id, user_id=user_id, until_date=until_date,) + call = KickChatMember( + chat_id=chat_id, + user_id=user_id, + until_date=until_date, + ) return await self(call, request_timeout=request_timeout) async def unban_chat_member( - self, chat_id: Union[int, str], user_id: int, request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + user_id: int, + only_if_banned: Optional[bool] = None, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to unban a previously kicked user in a supergroup or channel. The user - will not return to the group or channel automatically, but will be able to join via link, - etc. The bot must be an administrator for this to work. Returns True on success. + Use this method to unban a previously kicked user in a supergroup or channel. The user will **not** return to the group or channel automatically, but will be able to join via link, etc. The bot must be an administrator for this to work. By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be **removed** from the chat. If you don't want this, use the parameter *only_if_banned*. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#unbanchatmember - :param chat_id: Unique identifier for the target group or username of the target - supergroup or channel (in the format @username) + :param chat_id: Unique identifier for the target group or username of the target supergroup or channel (in the format :code:`@username`) :param user_id: Unique identifier of the target user + :param only_if_banned: Do nothing if the user is not banned :param request_timeout: Request timeout :return: The user will not return to the group or channel automatically, but will be able - to join via link, etc. Returns True on success. + to join via link, etc. Returns True on success. """ - call = UnbanChatMember(chat_id=chat_id, user_id=user_id,) + call = UnbanChatMember( + chat_id=chat_id, + user_id=user_id, + only_if_banned=only_if_banned, + ) return await self(call, request_timeout=request_timeout) async def restrict_chat_member( @@ -1451,24 +1464,22 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to restrict a user in a supergroup. The bot must be an administrator in - the supergroup for this to work and must have the appropriate admin rights. Pass True for - all permissions to lift restrictions from a user. Returns True on success. + Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass :code:`True` for all permissions to lift restrictions from a user. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#restrictchatmember - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`) :param user_id: Unique identifier of the target user - :param permissions: New user permissions - :param until_date: Date when restrictions will be lifted for the user, unix time. If user - is restricted for more than 366 days or less than 30 seconds from the - current time, they are considered to be restricted forever + :param permissions: A JSON-serialized object for new user permissions + :param until_date: Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever :param request_timeout: Request timeout :return: Returns True on success. """ call = RestrictChatMember( - chat_id=chat_id, user_id=user_id, permissions=permissions, until_date=until_date, + chat_id=chat_id, + user_id=user_id, + permissions=permissions, + until_date=until_date, ) return await self(call, request_timeout=request_timeout) @@ -1476,6 +1487,7 @@ class Bot(ContextInstanceMixin["Bot"]): self, chat_id: Union[int, str], user_id: int, + is_anonymous: Optional[bool] = None, can_change_info: Optional[bool] = None, can_post_messages: Optional[bool] = None, can_edit_messages: Optional[bool] = None, @@ -1487,38 +1499,28 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to promote or demote a user in a supergroup or a channel. The bot must be - an administrator in the chat for this to work and must have the appropriate admin rights. - Pass False for all boolean parameters to demote a user. Returns True on success. + Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Pass :code:`False` for all boolean parameters to demote a user. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#promotechatmember - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param user_id: Unique identifier of the target user - :param can_change_info: Pass True, if the administrator can change chat title, photo and - other settings - :param can_post_messages: Pass True, if the administrator can create channel posts, - channels only - :param can_edit_messages: Pass True, if the administrator can edit messages of other users - and can pin messages, channels only - :param can_delete_messages: Pass True, if the administrator can delete messages of other - users + :param is_anonymous: Pass :code:`True`, if the administrator's presence in the chat is hidden + :param can_change_info: Pass True, if the administrator can change chat title, photo and other settings + :param can_post_messages: Pass True, if the administrator can create channel posts, channels only + :param can_edit_messages: Pass True, if the administrator can edit messages of other users and can pin messages, channels only + :param can_delete_messages: Pass True, if the administrator can delete messages of other users :param can_invite_users: Pass True, if the administrator can invite new users to the chat - :param can_restrict_members: Pass True, if the administrator can restrict, ban or unban - chat members - :param can_pin_messages: Pass True, if the administrator can pin messages, supergroups - only - :param can_promote_members: Pass True, if the administrator can add new administrators - with a subset of their own privileges or demote administrators - that he has promoted, directly or indirectly (promoted by - administrators that were appointed by him) + :param can_restrict_members: Pass True, if the administrator can restrict, ban or unban chat members + :param can_pin_messages: Pass True, if the administrator can pin messages, supergroups only + :param can_promote_members: Pass True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him) :param request_timeout: Request timeout :return: Returns True on success. """ call = PromoteChatMember( chat_id=chat_id, user_id=user_id, + is_anonymous=is_anonymous, can_change_info=can_change_info, can_post_messages=can_post_messages, can_edit_messages=can_edit_messages, @@ -1538,21 +1540,20 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to set a custom title for an administrator in a supergroup promoted by the - bot. Returns True on success. + Use this method to set a custom title for an administrator in a supergroup promoted by the bot. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatadministratorcustomtitle - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`) :param user_id: Unique identifier of the target user - :param custom_title: New custom title for the administrator; 0-16 characters, emoji are - not allowed + :param custom_title: New custom title for the administrator; 0-16 characters, emoji are not allowed :param request_timeout: Request timeout :return: Returns True on success. """ call = SetChatAdministratorCustomTitle( - chat_id=chat_id, user_id=user_id, custom_title=custom_title, + chat_id=chat_id, + user_id=user_id, + custom_title=custom_title, ) return await self(call, request_timeout=request_timeout) @@ -1563,98 +1564,103 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to set default chat permissions for all members. The bot must be an - administrator in the group or a supergroup for this to work and must have the - can_restrict_members admin rights. Returns True on success. + Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work and must have the *can_restrict_members* admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatpermissions - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`) :param permissions: New default chat permissions :param request_timeout: Request timeout :return: Returns True on success. """ - call = SetChatPermissions(chat_id=chat_id, permissions=permissions,) + call = SetChatPermissions( + chat_id=chat_id, + permissions=permissions, + ) return await self(call, request_timeout=request_timeout) async def export_chat_invite_link( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, ) -> str: """ - Use this method to generate a new invite link for a chat; any previously generated link is - revoked. The bot must be an administrator in the chat for this to work and must have the - appropriate admin rights. Returns the new invite link as String on success. - Note: Each administrator in a chat generates their own invite links. Bots can't use invite - links generated by other administrators. If you want your bot to work with invite links, - it will need to generate its own link using exportChatInviteLink — after this the link - will become available to the bot via the getChat method. If your bot needs to generate a - new invite link replacing its previous one, use exportChatInviteLink again. + Use this method to generate a new invite link for a chat; any previously generated link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the new invite link as *String* on success. + + Note: Each administrator in a chat generates their own invite links. Bots can't use invite links generated by other administrators. If you want your bot to work with invite links, it will need to generate its own link using :class:`aiogram.methods.export_chat_invite_link.ExportChatInviteLink` — after this the link will become available to the bot via the :class:`aiogram.methods.get_chat.GetChat` method. If your bot needs to generate a new invite link replacing its previous one, use :class:`aiogram.methods.export_chat_invite_link.ExportChatInviteLink` again. Source: https://core.telegram.org/bots/api#exportchatinvitelink - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param request_timeout: Request timeout :return: Returns the new invite link as String on success. """ - call = ExportChatInviteLink(chat_id=chat_id,) + call = ExportChatInviteLink( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def set_chat_photo( - self, chat_id: Union[int, str], photo: InputFile, request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + photo: InputFile, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to set a new profile photo for the chat. Photos can't be changed for - private chats. The bot must be an administrator in the chat for this to work and must have - the appropriate admin rights. Returns True on success. + Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatphoto - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param photo: New chat photo, uploaded using multipart/form-data :param request_timeout: Request timeout :return: Returns True on success. """ - call = SetChatPhoto(chat_id=chat_id, photo=photo,) + call = SetChatPhoto( + chat_id=chat_id, + photo=photo, + ) return await self(call, request_timeout=request_timeout) async def delete_chat_photo( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to delete a chat photo. Photos can't be changed for private chats. The bot - must be an administrator in the chat for this to work and must have the appropriate admin - rights. Returns True on success. + Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletechatphoto - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param request_timeout: Request timeout :return: Returns True on success. """ - call = DeleteChatPhoto(chat_id=chat_id,) + call = DeleteChatPhoto( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def set_chat_title( - self, chat_id: Union[int, str], title: str, request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + title: str, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to change the title of a chat. Titles can't be changed for private chats. - The bot must be an administrator in the chat for this to work and must have the - appropriate admin rights. Returns True on success. + Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchattitle - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param title: New chat title, 1-255 characters :param request_timeout: Request timeout :return: Returns True on success. """ - call = SetChatTitle(chat_id=chat_id, title=title,) + call = SetChatTitle( + chat_id=chat_id, + title=title, + ) return await self(call, request_timeout=request_timeout) async def set_chat_description( @@ -1664,19 +1670,19 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to change the description of a group, a supergroup or a channel. The bot - must be an administrator in the chat for this to work and must have the appropriate admin - rights. Returns True on success. + Use this method to change the description of a group, a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatdescription - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param description: New chat description, 0-255 characters :param request_timeout: Request timeout :return: Returns True on success. """ - call = SetChatDescription(chat_id=chat_id, description=description,) + call = SetChatDescription( + chat_id=chat_id, + description=description, + ) return await self(call, request_timeout=request_timeout) async def pin_chat_message( @@ -1687,135 +1693,163 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to pin a message in a group, a supergroup, or a channel. The bot must be - an administrator in the chat for this to work and must have the 'can_pin_messages' admin - right in the supergroup or 'can_edit_messages' admin right in the channel. Returns True on - success. + Use this method to add a message to the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#pinchatmessage - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param message_id: Identifier of a message to pin - :param disable_notification: Pass True, if it is not necessary to send a notification to - all chat members about the new pinned message. Notifications - are always disabled in channels. + :param disable_notification: Pass :code:`True`, if it is not necessary to send a notification to all chat members about the new pinned message. Notifications are always disabled in channels and private chats. :param request_timeout: Request timeout :return: Returns True on success. """ call = PinChatMessage( - chat_id=chat_id, message_id=message_id, disable_notification=disable_notification, + chat_id=chat_id, + message_id=message_id, + disable_notification=disable_notification, ) return await self(call, request_timeout=request_timeout) async def unpin_chat_message( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + message_id: Optional[int] = None, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to unpin a message in a group, a supergroup, or a channel. The bot must be - an administrator in the chat for this to work and must have the 'can_pin_messages' admin - right in the supergroup or 'can_edit_messages' admin right in the channel. Returns True on - success. + Use this method to remove a message from the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#unpinchatmessage - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param message_id: Identifier of a message to unpin. If not specified, the most recent pinned message (by sending date) will be unpinned. :param request_timeout: Request timeout :return: Returns True on success. """ - call = UnpinChatMessage(chat_id=chat_id,) + call = UnpinChatMessage( + chat_id=chat_id, + message_id=message_id, + ) + return await self(call, request_timeout=request_timeout) + + async def unpin_all_chat_messages( + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, + ) -> bool: + """ + Use this method to clear the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns :code:`True` on success. + + Source: https://core.telegram.org/bots/api#unpinallchatmessages + + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param request_timeout: Request timeout + :return: Returns True on success. + """ + call = UnpinAllChatMessages( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def leave_chat( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method for your bot to leave a group, supergroup or channel. Returns True on - success. + Use this method for your bot to leave a group, supergroup or channel. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#leavechat - :param chat_id: Unique identifier for the target chat or username of the target supergroup - or channel (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`) :param request_timeout: Request timeout :return: Returns True on success. """ - call = LeaveChat(chat_id=chat_id,) + call = LeaveChat( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def get_chat( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, ) -> Chat: """ - Use this method to get up to date information about the chat (current name of the user for - one-on-one conversations, current username of a user, group or channel, etc.). Returns a - Chat object on success. + Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). Returns a :class:`aiogram.types.chat.Chat` object on success. Source: https://core.telegram.org/bots/api#getchat - :param chat_id: Unique identifier for the target chat or username of the target supergroup - or channel (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`) :param request_timeout: Request timeout :return: Returns a Chat object on success. """ - call = GetChat(chat_id=chat_id,) + call = GetChat( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def get_chat_administrators( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, ) -> List[ChatMember]: """ - Use this method to get a list of administrators in a chat. On success, returns an Array of - ChatMember objects that contains information about all chat administrators except other - bots. If the chat is a group or a supergroup and no administrators were appointed, only - the creator will be returned. + Use this method to get a list of administrators in a chat. On success, returns an Array of :class:`aiogram.types.chat_member.ChatMember` objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned. Source: https://core.telegram.org/bots/api#getchatadministrators - :param chat_id: Unique identifier for the target chat or username of the target supergroup - or channel (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`) :param request_timeout: Request timeout :return: On success, returns an Array of ChatMember objects that contains information - about all chat administrators except other bots. If the chat is a group or a - supergroup and no administrators were appointed, only the creator will be - returned. + about all chat administrators except other bots. If the chat is a group or a + supergroup and no administrators were appointed, only the creator will be + returned. """ - call = GetChatAdministrators(chat_id=chat_id,) + call = GetChatAdministrators( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def get_chat_members_count( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, ) -> int: """ - Use this method to get the number of members in a chat. Returns Int on success. + Use this method to get the number of members in a chat. Returns *Int* on success. Source: https://core.telegram.org/bots/api#getchatmemberscount - :param chat_id: Unique identifier for the target chat or username of the target supergroup - or channel (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`) :param request_timeout: Request timeout :return: Returns Int on success. """ - call = GetChatMembersCount(chat_id=chat_id,) + call = GetChatMembersCount( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def get_chat_member( - self, chat_id: Union[int, str], user_id: int, request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + user_id: int, + request_timeout: Optional[int] = None, ) -> ChatMember: """ - Use this method to get information about a member of a chat. Returns a ChatMember object - on success. + Use this method to get information about a member of a chat. Returns a :class:`aiogram.types.chat_member.ChatMember` object on success. Source: https://core.telegram.org/bots/api#getchatmember - :param chat_id: Unique identifier for the target chat or username of the target supergroup - or channel (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`) :param user_id: Unique identifier of the target user :param request_timeout: Request timeout :return: Returns a ChatMember object on success. """ - call = GetChatMember(chat_id=chat_id, user_id=user_id,) + call = GetChatMember( + chat_id=chat_id, + user_id=user_id, + ) return await self(call, request_timeout=request_timeout) async def set_chat_sticker_set( @@ -1825,41 +1859,40 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to set a new group sticker set for a supergroup. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. Use - the field can_set_sticker_set optionally returned in getChat requests to check if the bot - can use this method. Returns True on success. + Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field *can_set_sticker_set* optionally returned in :class:`aiogram.methods.get_chat.GetChat` requests to check if the bot can use this method. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatstickerset - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`) :param sticker_set_name: Name of the sticker set to be set as the group sticker set :param request_timeout: Request timeout :return: Use the field can_set_sticker_set optionally returned in getChat requests to - check if the bot can use this method. Returns True on success. + check if the bot can use this method. Returns True on success. """ - call = SetChatStickerSet(chat_id=chat_id, sticker_set_name=sticker_set_name,) + call = SetChatStickerSet( + chat_id=chat_id, + sticker_set_name=sticker_set_name, + ) return await self(call, request_timeout=request_timeout) async def delete_chat_sticker_set( - self, chat_id: Union[int, str], request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to delete a group sticker set from a supergroup. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. Use - the field can_set_sticker_set optionally returned in getChat requests to check if the bot - can use this method. Returns True on success. + Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field *can_set_sticker_set* optionally returned in :class:`aiogram.methods.get_chat.GetChat` requests to check if the bot can use this method. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletechatstickerset - :param chat_id: Unique identifier for the target chat or username of the target supergroup - (in the format @supergroupusername) + :param chat_id: Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`) :param request_timeout: Request timeout :return: Use the field can_set_sticker_set optionally returned in getChat requests to - check if the bot can use this method. Returns True on success. + check if the bot can use this method. Returns True on success. """ - call = DeleteChatStickerSet(chat_id=chat_id,) + call = DeleteChatStickerSet( + chat_id=chat_id, + ) return await self(call, request_timeout=request_timeout) async def answer_callback_query( @@ -1872,27 +1905,17 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to send answers to callback queries sent from inline keyboards. The answer - will be displayed to the user as a notification at the top of the chat screen or as an - alert. On success, True is returned. - Alternatively, the user can be redirected to the specified Game URL. For this option to - work, you must first create a game for your bot via @Botfather and accept the terms. - Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a - parameter. + Use this method to send answers to callback queries sent from `inline keyboards `_. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, :code:`True` is returned. + + Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via `@Botfather `_ and accept the terms. Otherwise, you may use links like :code:`t.me/your_bot?start=XXXX` that open your bot with a parameter. Source: https://core.telegram.org/bots/api#answercallbackquery :param callback_query_id: Unique identifier for the query to be answered - :param text: Text of the notification. If not specified, nothing will be shown to the - user, 0-200 characters - :param show_alert: If true, an alert will be shown by the client instead of a notification - at the top of the chat screen. Defaults to false. - :param url: URL that will be opened by the user's client. If you have created a Game and - accepted the conditions via @Botfather, specify the URL that opens your game — - note that this will only work if the query comes from a callback_game button. - :param cache_time: The maximum amount of time in seconds that the result of the callback - query may be cached client-side. Telegram apps will support caching - starting in version 3.14. Defaults to 0. + :param text: Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters + :param show_alert: If *true*, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to *false*. + :param url: URL that will be opened by the user's client. If you have created a :class:`aiogram.types.game.Game` and accepted the conditions via `@Botfather `_, specify the URL that opens your game — note that this will only work if the query comes from a `https://core.telegram.org/bots/api#inlinekeyboardbutton `_ *callback_game* button. + :param cache_time: The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0. :param request_timeout: Request timeout :return: On success, True is returned. """ @@ -1906,25 +1929,30 @@ class Bot(ContextInstanceMixin["Bot"]): return await self(call, request_timeout=request_timeout) async def set_my_commands( - self, commands: List[BotCommand], request_timeout: Optional[int] = None, + self, + commands: List[BotCommand], + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to change the list of the bot's commands. Returns True on success. + Use this method to change the list of the bot's commands. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setmycommands - :param commands: A JSON-serialized list of bot commands to be set as the list of the bot's - commands. At most 100 commands can be specified. + :param commands: A JSON-serialized list of bot commands to be set as the list of the bot's commands. At most 100 commands can be specified. :param request_timeout: Request timeout :return: Returns True on success. """ - call = SetMyCommands(commands=commands,) + call = SetMyCommands( + commands=commands, + ) return await self(call, request_timeout=request_timeout) - async def get_my_commands(self, request_timeout: Optional[int] = None,) -> List[BotCommand]: + async def get_my_commands( + self, + request_timeout: Optional[int] = None, + ) -> List[BotCommand]: """ - Use this method to get the current list of the bot's commands. Requires no parameters. - Returns Array of BotCommand on success. + Use this method to get the current list of the bot's commands. Requires no parameters. Returns Array of :class:`aiogram.types.bot_command.BotCommand` on success. Source: https://core.telegram.org/bots/api#getmycommands @@ -1946,31 +1974,27 @@ class Bot(ContextInstanceMixin["Bot"]): message_id: Optional[int] = None, inline_message_id: Optional[str] = None, parse_mode: Optional[str] = UNSET, + entities: Optional[List[MessageEntity]] = None, disable_web_page_preview: Optional[bool] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, request_timeout: Optional[int] = None, ) -> Union[Message, bool]: """ - Use this method to edit text and game messages. On success, if edited message is sent by - the bot, the edited Message is returned, otherwise True is returned. + Use this method to edit text and `game `_ messages. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#editmessagetext :param text: New text of the message, 1-4096 characters after entities parsing - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat or username of the target channel (in the format - @channelusername) - :param message_id: Required if inline_message_id is not specified. Identifier of the - message to edit - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message - :param parse_mode: Mode for parsing entities in the message text. See formatting options - for more details. + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param message_id: Required if *inline_message_id* is not specified. Identifier of the message to edit + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message + :param parse_mode: Mode for parsing entities in the message text. See `formatting options `_ for more details. + :param entities: List of special entities that appear in message text, which can be specified instead of *parse_mode* :param disable_web_page_preview: Disables link previews for links in this message - :param reply_markup: A JSON-serialized object for an inline keyboard. + :param reply_markup: A JSON-serialized object for an `inline keyboard `_. :param request_timeout: Request timeout - :return: On success, if edited message is sent by the bot, the edited Message is returned, - otherwise True is returned. + :return: On success, if the edited message is not an inline message, the edited Message is + returned, otherwise True is returned. """ call = EditMessageText( text=text, @@ -1978,6 +2002,7 @@ class Bot(ContextInstanceMixin["Bot"]): message_id=message_id, inline_message_id=inline_message_id, parse_mode=parse_mode, + entities=entities, disable_web_page_preview=disable_web_page_preview, reply_markup=reply_markup, ) @@ -1990,29 +2015,25 @@ class Bot(ContextInstanceMixin["Bot"]): inline_message_id: Optional[str] = None, caption: Optional[str] = None, parse_mode: Optional[str] = UNSET, + caption_entities: Optional[List[MessageEntity]] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, request_timeout: Optional[int] = None, ) -> Union[Message, bool]: """ - Use this method to edit captions of messages. On success, if edited message is sent by the - bot, the edited Message is returned, otherwise True is returned. + Use this method to edit captions of messages. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#editmessagecaption - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat or username of the target channel (in the format - @channelusername) - :param message_id: Required if inline_message_id is not specified. Identifier of the - message to edit - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param message_id: Required if *inline_message_id* is not specified. Identifier of the message to edit + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message :param caption: New caption of the message, 0-1024 characters after entities parsing - :param parse_mode: Mode for parsing entities in the message caption. See formatting - options for more details. - :param reply_markup: A JSON-serialized object for an inline keyboard. + :param parse_mode: Mode for parsing entities in the message caption. See `formatting options `_ for more details. + :param caption_entities: List of special entities that appear in the caption, which can be specified instead of *parse_mode* + :param reply_markup: A JSON-serialized object for an `inline keyboard `_. :param request_timeout: Request timeout - :return: On success, if edited message is sent by the bot, the edited Message is returned, - otherwise True is returned. + :return: On success, if the edited message is not an inline message, the edited Message is + returned, otherwise True is returned. """ call = EditMessageCaption( chat_id=chat_id, @@ -2020,6 +2041,7 @@ class Bot(ContextInstanceMixin["Bot"]): inline_message_id=inline_message_id, caption=caption, parse_mode=parse_mode, + caption_entities=caption_entities, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -2034,27 +2056,18 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> Union[Message, bool]: """ - Use this method to edit animation, audio, document, photo, or video messages. If a message - is a part of a message album, then it can be edited only to a photo or a video. Otherwise, - message type can be changed arbitrarily. When inline message is edited, new file can't be - uploaded. Use previously uploaded file via its file_id or specify a URL. On success, if - the edited message was sent by the bot, the edited Message is returned, otherwise True is - returned. + Use this method to edit animation, audio, document, photo, or video messages. If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. When an inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id or specify a URL. On success, if the edited message was sent by the bot, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#editmessagemedia :param media: A JSON-serialized object for a new media content of the message - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat or username of the target channel (in the format - @channelusername) - :param message_id: Required if inline_message_id is not specified. Identifier of the - message to edit - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message - :param reply_markup: A JSON-serialized object for a new inline keyboard. + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param message_id: Required if *inline_message_id* is not specified. Identifier of the message to edit + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message + :param reply_markup: A JSON-serialized object for a new `inline keyboard `_. :param request_timeout: Request timeout :return: On success, if the edited message was sent by the bot, the edited Message is - returned, otherwise True is returned. + returned, otherwise True is returned. """ call = EditMessageMedia( media=media, @@ -2074,22 +2087,17 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> Union[Message, bool]: """ - Use this method to edit only the reply markup of messages. On success, if edited message - is sent by the bot, the edited Message is returned, otherwise True is returned. + Use this method to edit only the reply markup of messages. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#editmessagereplymarkup - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat or username of the target channel (in the format - @channelusername) - :param message_id: Required if inline_message_id is not specified. Identifier of the - message to edit - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message - :param reply_markup: A JSON-serialized object for an inline keyboard. + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param message_id: Required if *inline_message_id* is not specified. Identifier of the message to edit + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message + :param reply_markup: A JSON-serialized object for an `inline keyboard `_. :param request_timeout: Request timeout - :return: On success, if edited message is sent by the bot, the edited Message is returned, - otherwise True is returned. + :return: On success, if the edited message is not an inline message, the edited Message is + returned, otherwise True is returned. """ call = EditMessageReplyMarkup( chat_id=chat_id, @@ -2107,47 +2115,59 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> Poll: """ - Use this method to stop a poll which was sent by the bot. On success, the stopped Poll - with the final results is returned. + Use this method to stop a poll which was sent by the bot. On success, the stopped :class:`aiogram.types.poll.Poll` with the final results is returned. Source: https://core.telegram.org/bots/api#stoppoll - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param message_id: Identifier of the original message with the poll - :param reply_markup: A JSON-serialized object for a new message inline keyboard. + :param reply_markup: A JSON-serialized object for a new message `inline keyboard `_. :param request_timeout: Request timeout :return: On success, the stopped Poll with the final results is returned. """ - call = StopPoll(chat_id=chat_id, message_id=message_id, reply_markup=reply_markup,) + call = StopPoll( + chat_id=chat_id, + message_id=message_id, + reply_markup=reply_markup, + ) return await self(call, request_timeout=request_timeout) async def delete_message( - self, chat_id: Union[int, str], message_id: int, request_timeout: Optional[int] = None, + self, + chat_id: Union[int, str], + message_id: int, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to delete a message, including service messages, with the following - limitations: + Use this method to delete a message, including service messages, with the following limitations: + - A message can only be deleted if it was sent less than 48 hours ago. - - A dice message in a private chat can only be deleted if it was sent more than 24 hours - ago. + + - A dice message in a private chat can only be deleted if it was sent more than 24 hours ago. + - Bots can delete outgoing messages in private chats, groups, and supergroups. + - Bots can delete incoming messages in private chats. - - Bots granted can_post_messages permissions can delete outgoing messages in channels. + + - Bots granted *can_post_messages* permissions can delete outgoing messages in channels. + - If the bot is an administrator of a group, it can delete any message there. - - If the bot has can_delete_messages permission in a supergroup or a channel, it can - delete any message there. - Returns True on success. + + - If the bot has *can_delete_messages* permission in a supergroup or a channel, it can delete any message there. + + Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletemessage - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) :param message_id: Identifier of the message to delete :param request_timeout: Request timeout :return: Returns True on success. """ - call = DeleteMessage(chat_id=chat_id, message_id=message_id,) + call = DeleteMessage( + chat_id=chat_id, + message_id=message_id, + ) return await self(call, request_timeout=request_timeout) # ============================================================================================= @@ -2161,29 +2181,23 @@ class Bot(ContextInstanceMixin["Bot"]): sticker: Union[InputFile, str], disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send static .WEBP or animated .TGS stickers. On success, the sent - Message is returned. + Use this method to send static .WEBP or `animated `_ .TGS stickers. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendsticker - :param chat_id: Unique identifier for the target chat or username of the target channel - (in the format @channelusername) - :param sticker: Sticker to send. Pass a file_id as String to send a file that exists on - the Telegram servers (recommended), pass an HTTP URL as a String for - Telegram to get a .WEBP file from the Internet, or upload a new one using - multipart/form-data. - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param chat_id: Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`) + :param sticker: Sticker to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a .WEBP file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » ` + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: Additional interface options. A JSON-serialized object for an inline - keyboard, custom reply keyboard, instructions to remove reply - keyboard or to force a reply from the user. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -2192,15 +2206,18 @@ class Bot(ContextInstanceMixin["Bot"]): sticker=sticker, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) async def get_sticker_set( - self, name: str, request_timeout: Optional[int] = None, + self, + name: str, + request_timeout: Optional[int] = None, ) -> StickerSet: """ - Use this method to get a sticker set. On success, a StickerSet object is returned. + Use this method to get a sticker set. On success, a :class:`aiogram.types.sticker_set.StickerSet` object is returned. Source: https://core.telegram.org/bots/api#getstickerset @@ -2208,27 +2225,31 @@ class Bot(ContextInstanceMixin["Bot"]): :param request_timeout: Request timeout :return: On success, a StickerSet object is returned. """ - call = GetStickerSet(name=name,) + call = GetStickerSet( + name=name, + ) return await self(call, request_timeout=request_timeout) async def upload_sticker_file( - self, user_id: int, png_sticker: InputFile, request_timeout: Optional[int] = None, + self, + user_id: int, + png_sticker: InputFile, + request_timeout: Optional[int] = None, ) -> File: """ - Use this method to upload a .PNG file with a sticker for later use in createNewStickerSet - and addStickerToSet methods (can be used multiple times). Returns the uploaded File on - success. + Use this method to upload a .PNG file with a sticker for later use in *createNewStickerSet* and *addStickerToSet* methods (can be used multiple times). Returns the uploaded :class:`aiogram.types.file.File` on success. Source: https://core.telegram.org/bots/api#uploadstickerfile :param user_id: User identifier of sticker file owner - :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, - dimensions must not exceed 512px, and either width or height must be - exactly 512px. + :param png_sticker: **PNG** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. :ref:`More info on Sending Files » ` :param request_timeout: Request timeout :return: Returns the uploaded File on success. """ - call = UploadStickerFile(user_id=user_id, png_sticker=png_sticker,) + call = UploadStickerFile( + user_id=user_id, + png_sticker=png_sticker, + ) return await self(call, request_timeout=request_timeout) async def create_new_sticker_set( @@ -2244,31 +2265,18 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to create a new sticker set owned by a user. The bot will be able to edit - the sticker set thus created. You must use exactly one of the fields png_sticker or - tgs_sticker. Returns True on success. + Use this method to create a new sticker set owned by a user. The bot will be able to edit the sticker set thus created. You **must** use exactly one of the fields *png_sticker* or *tgs_sticker*. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#createnewstickerset :param user_id: User identifier of created sticker set owner - :param name: Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., - animals). Can contain only english letters, digits and underscores. Must - begin with a letter, can't contain consecutive underscores and must end in - '_by_'. is case insensitive. 1-64 characters. + :param name: Short name of sticker set, to be used in :code:`t.me/addstickers/` URLs (e.g., *animals*). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in *'_by_'*. ** is case insensitive. 1-64 characters. :param title: Sticker set title, 1-64 characters :param emojis: One or more emoji corresponding to the sticker - :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, - dimensions must not exceed 512px, and either width or height must be - exactly 512px. Pass a file_id as a String to send a file that already - exists on the Telegram servers, pass an HTTP URL as a String for - Telegram to get a file from the Internet, or upload a new one using - multipart/form-data. - :param tgs_sticker: TGS animation with the sticker, uploaded using multipart/form-data. - See https://core.telegram.org/animated_stickers#technical-requirements - for technical requirements - :param contains_masks: Pass True, if a set of mask stickers should be created - :param mask_position: A JSON-serialized object for position where the mask should be - placed on faces + :param png_sticker: **PNG** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a *file_id* as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » ` + :param tgs_sticker: **TGS** animation with the sticker, uploaded using multipart/form-data. See `https://core.telegram.org/animated_stickers#technical-requirements `_`https://core.telegram.org/animated_stickers#technical-requirements `_ for technical requirements + :param contains_masks: Pass :code:`True`, if a set of mask stickers should be created + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :param request_timeout: Request timeout :return: Returns True on success. """ @@ -2295,27 +2303,16 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to add a new sticker to a set created by the bot. You must use exactly one - of the fields png_sticker or tgs_sticker. Animated stickers can be added to animated - sticker sets and only to them. Animated sticker sets can have up to 50 stickers. Static - sticker sets can have up to 120 stickers. Returns True on success. + Use this method to add a new sticker to a set created by the bot. You **must** use exactly one of the fields *png_sticker* or *tgs_sticker*. Animated stickers can be added to animated sticker sets and only to them. Animated sticker sets can have up to 50 stickers. Static sticker sets can have up to 120 stickers. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#addstickertoset :param user_id: User identifier of sticker set owner :param name: Sticker set name :param emojis: One or more emoji corresponding to the sticker - :param png_sticker: PNG image with the sticker, must be up to 512 kilobytes in size, - dimensions must not exceed 512px, and either width or height must be - exactly 512px. Pass a file_id as a String to send a file that already - exists on the Telegram servers, pass an HTTP URL as a String for - Telegram to get a file from the Internet, or upload a new one using - multipart/form-data. - :param tgs_sticker: TGS animation with the sticker, uploaded using multipart/form-data. - See https://core.telegram.org/animated_stickers#technical-requirements - for technical requirements - :param mask_position: A JSON-serialized object for position where the mask should be - placed on faces + :param png_sticker: **PNG** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a *file_id* as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » ` + :param tgs_sticker: **TGS** animation with the sticker, uploaded using multipart/form-data. See `https://core.telegram.org/animated_stickers#technical-requirements `_`https://core.telegram.org/animated_stickers#technical-requirements `_ for technical requirements + :param mask_position: A JSON-serialized object for position where the mask should be placed on faces :param request_timeout: Request timeout :return: Returns True on success. """ @@ -2330,11 +2327,13 @@ class Bot(ContextInstanceMixin["Bot"]): return await self(call, request_timeout=request_timeout) async def set_sticker_position_in_set( - self, sticker: str, position: int, request_timeout: Optional[int] = None, + self, + sticker: str, + position: int, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to move a sticker in a set created by the bot to a specific position. - Returns True on success. + Use this method to move a sticker in a set created by the bot to a specific position. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setstickerpositioninset @@ -2343,15 +2342,19 @@ class Bot(ContextInstanceMixin["Bot"]): :param request_timeout: Request timeout :return: Returns True on success. """ - call = SetStickerPositionInSet(sticker=sticker, position=position,) + call = SetStickerPositionInSet( + sticker=sticker, + position=position, + ) return await self(call, request_timeout=request_timeout) async def delete_sticker_from_set( - self, sticker: str, request_timeout: Optional[int] = None, + self, + sticker: str, + request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to delete a sticker from a set created by the bot. Returns True on - success. + Use this method to delete a sticker from a set created by the bot. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletestickerfromset @@ -2359,7 +2362,9 @@ class Bot(ContextInstanceMixin["Bot"]): :param request_timeout: Request timeout :return: Returns True on success. """ - call = DeleteStickerFromSet(sticker=sticker,) + call = DeleteStickerFromSet( + sticker=sticker, + ) return await self(call, request_timeout=request_timeout) async def set_sticker_set_thumb( @@ -2370,26 +2375,21 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for - animated sticker sets only. Returns True on success. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setstickersetthumb :param name: Sticker set name :param user_id: User identifier of the sticker set owner - :param thumb: A PNG image with the thumbnail, must be up to 128 kilobytes in size and have - width and height exactly 100px, or a TGS animation with the thumbnail up to - 32 kilobytes in size; see - https://core.telegram.org/animated_stickers#technical-requirements for - animated sticker technical requirements. Pass a file_id as a String to send - a file that already exists on the Telegram servers, pass an HTTP URL as a - String for Telegram to get a file from the Internet, or upload a new one - using multipart/form-data.. Animated sticker set thumbnail can't be uploaded - via HTTP URL. + :param thumb: A **PNG** image with the thumbnail, must be up to 128 kilobytes in size and have width and height exactly 100px, or a **TGS** animation with the thumbnail up to 32 kilobytes in size; see `https://core.telegram.org/animated_stickers#technical-requirements `_`https://core.telegram.org/animated_stickers#technical-requirements `_ for animated sticker technical requirements. Pass a *file_id* as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `. Animated sticker set thumbnail can't be uploaded via HTTP URL. :param request_timeout: Request timeout :return: Returns True on success. """ - call = SetStickerSetThumb(name=name, user_id=user_id, thumb=thumb,) + call = SetStickerSetThumb( + name=name, + user_id=user_id, + thumb=thumb, + ) return await self(call, request_timeout=request_timeout) # ============================================================================================= @@ -2409,28 +2409,19 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Use this method to send answers to an inline query. On success, True is returned. - No more than 50 results per query are allowed. + Use this method to send answers to an inline query. On success, :code:`True` is returned. + + No more than **50** results per query are allowed. Source: https://core.telegram.org/bots/api#answerinlinequery :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 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 switch_pm_parameter: 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. + :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 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 switch_pm_parameter: `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 request_timeout: Request timeout :return: On success, True is returned. """ @@ -2474,52 +2465,39 @@ class Bot(ContextInstanceMixin["Bot"]): is_flexible: Optional[bool] = None, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send invoices. On success, the sent Message is returned. + Use this method to send invoices. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendinvoice :param chat_id: Unique identifier for the target private chat :param title: Product name, 1-32 characters :param description: Product description, 1-255 characters - :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to - the user, use for your internal processes. - :param provider_token: Payments provider token, obtained via Botfather - :param start_parameter: Unique deep-linking parameter that can be used to generate this - invoice when used as a start parameter - :param currency: Three-letter ISO 4217 currency code, see more on currencies - :param prices: Price breakdown, a JSON-serialized list of components (e.g. product price, - tax, discount, delivery cost, delivery tax, bonus, etc.) - :param provider_data: JSON-encoded data about the invoice, which will be shared with the - payment provider. A detailed description of required fields should - be provided by the payment provider. - :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or - a marketing image for a service. People like it better when they see - what they are paying for. + :param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes. + :param provider_token: Payments provider token, obtained via `Botfather `_ + :param start_parameter: Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter + :param currency: Three-letter ISO 4217 currency code, see `more on currencies `_ + :param prices: Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) + :param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider. + :param photo_url: URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for. :param photo_size: Photo size :param photo_width: Photo width :param photo_height: Photo height - :param need_name: Pass True, if you require the user's full name to complete the order - :param need_phone_number: Pass True, if you require the user's phone number to complete - the order - :param need_email: Pass True, if you require the user's email address to complete the - order - :param need_shipping_address: Pass True, if you require the user's shipping address to - complete the order - :param send_phone_number_to_provider: Pass True, if user's phone number should be sent to - provider - :param send_email_to_provider: Pass True, if user's email address should be sent to - provider - :param is_flexible: Pass True, if the final price depends on the shipping method - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param need_name: Pass :code:`True`, if you require the user's full name to complete the order + :param need_phone_number: Pass :code:`True`, if you require the user's phone number to complete the order + :param need_email: Pass :code:`True`, if you require the user's email address to complete the order + :param need_shipping_address: Pass :code:`True`, if you require the user's shipping address to complete the order + :param send_phone_number_to_provider: Pass :code:`True`, if user's phone number should be sent to provider + :param send_email_to_provider: Pass :code:`True`, if user's email address should be sent to provider + :param is_flexible: Pass :code:`True`, if the final price depends on the shipping method + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Pay - total price' button will be shown. If not empty, the first button - must be a Pay button. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: A JSON-serialized object for an `inline keyboard `_. If empty, one 'Pay :code:`total price`' button will be shown. If not empty, the first button must be a Pay button. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -2546,6 +2524,7 @@ class Bot(ContextInstanceMixin["Bot"]): is_flexible=is_flexible, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -2559,22 +2538,14 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - If you sent an invoice requesting a shipping address and the parameter is_flexible was - specified, the Bot API will send an Update with a shipping_query field to the bot. Use - this method to reply to shipping queries. On success, True is returned. + If you sent an invoice requesting a shipping address and the parameter *is_flexible* was specified, the Bot API will send an :class:`aiogram.types.update.Update` with a *shipping_query* field to the bot. Use this method to reply to shipping queries. On success, True is returned. Source: https://core.telegram.org/bots/api#answershippingquery :param shipping_query_id: Unique identifier for the query to be answered - :param ok: Specify True if delivery to the specified address is possible and False if - there are any problems (for example, if delivery to the specified address is - not possible) - :param shipping_options: Required if ok is True. A JSON-serialized array of available - shipping options. - :param error_message: Required if ok is False. Error message in human readable form that - explains why it is impossible to complete the order (e.g. "Sorry, - delivery to your desired address is unavailable'). Telegram will - display this message to the user. + :param ok: Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible) + :param shipping_options: Required if *ok* is True. A JSON-serialized array of available shipping options. + :param error_message: Required if *ok* is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user. :param request_timeout: Request timeout :return: On success, True is returned. """ @@ -2594,27 +2565,20 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Once the user has confirmed their payment and shipping details, the Bot API sends the - final confirmation in the form of an Update with the field pre_checkout_query. Use this - method to respond to such pre-checkout queries. On success, True is returned. Note: The - Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. + Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an :class:`aiogram.types.update.Update` with the field *pre_checkout_query*. Use this method to respond to such pre-checkout queries. On success, True is returned. **Note:** The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. Source: https://core.telegram.org/bots/api#answerprecheckoutquery :param pre_checkout_query_id: Unique identifier for the query to be answered - :param ok: Specify True if everything is alright (goods are available, etc.) and the bot - is ready to proceed with the order. Use False if there are any problems. - :param error_message: Required if ok is False. Error message in human readable form that - explains the reason for failure to proceed with the checkout (e.g. - "Sorry, somebody just bought the last of our amazing black T-shirts - while you were busy filling out your payment details. Please choose - a different color or garment!"). Telegram will display this message - to the user. + :param ok: Specify :code:`True` if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use :code:`False` if there are any problems. + :param error_message: Required if *ok* is :code:`False`. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user. :param request_timeout: Request timeout :return: On success, True is returned. """ call = AnswerPreCheckoutQuery( - pre_checkout_query_id=pre_checkout_query_id, ok=ok, error_message=error_message, + pre_checkout_query_id=pre_checkout_query_id, + ok=ok, + error_message=error_message, ) return await self(call, request_timeout=request_timeout) @@ -2630,14 +2594,8 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> bool: """ - Informs a user that some of the Telegram Passport elements they provided contains errors. - The user will not be able to re-submit their Passport to you until the errors are fixed - (the contents of the field for which you returned the error must change). Returns True on - success. - Use this if the data submitted by the user doesn't satisfy the standards your service - requires for any reason. For example, if a birthday date seems invalid, a submitted - document is blurry, a scan shows evidence of tampering, etc. Supply some details in the - error message to make sure the user knows how to correct the issues. + Informs a user that some of the Telegram Passport elements they provided contains errors. The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for which you returned the error must change). Returns :code:`True` on success. + Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues. Source: https://core.telegram.org/bots/api#setpassportdataerrors @@ -2645,10 +2603,13 @@ class Bot(ContextInstanceMixin["Bot"]): :param errors: A JSON-serialized array describing the errors :param request_timeout: Request timeout :return: The user will not be able to re-submit their Passport to you until the errors are - fixed (the contents of the field for which you returned the error must change). - Returns True on success. + fixed (the contents of the field for which you returned the error must change). + Returns True on success. """ - call = SetPassportDataErrors(user_id=user_id, errors=errors,) + call = SetPassportDataErrors( + user_id=user_id, + errors=errors, + ) return await self(call, request_timeout=request_timeout) # ============================================================================================= @@ -2662,23 +2623,21 @@ class Bot(ContextInstanceMixin["Bot"]): game_short_name: str, disable_notification: Optional[bool] = None, reply_to_message_id: Optional[int] = None, + allow_sending_without_reply: Optional[bool] = None, reply_markup: Optional[InlineKeyboardMarkup] = None, request_timeout: Optional[int] = None, ) -> Message: """ - Use this method to send a game. On success, the sent Message is returned. + Use this method to send a game. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendgame :param chat_id: Unique identifier for the target chat - :param game_short_name: Short name of the game, serves as the unique identifier for the - game. Set up your games via Botfather. - :param disable_notification: Sends the message silently. Users will receive a notification - with no sound. + :param game_short_name: Short name of the game, serves as the unique identifier for the game. Set up your games via `Botfather `_. + :param disable_notification: Sends the message `silently `_. Users will receive a notification with no sound. :param reply_to_message_id: If the message is a reply, ID of the original message - :param reply_markup: A JSON-serialized object for an inline keyboard. If empty, one 'Play - game_title' button will be shown. If not empty, the first button must - launch the game. + :param allow_sending_without_reply: Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found + :param reply_markup: A JSON-serialized object for an `inline keyboard `_. If empty, one 'Play game_title' button will be shown. If not empty, the first button must launch the game. :param request_timeout: Request timeout :return: On success, the sent Message is returned. """ @@ -2687,6 +2646,7 @@ class Bot(ContextInstanceMixin["Bot"]): game_short_name=game_short_name, disable_notification=disable_notification, reply_to_message_id=reply_to_message_id, + allow_sending_without_reply=allow_sending_without_reply, reply_markup=reply_markup, ) return await self(call, request_timeout=request_timeout) @@ -2703,29 +2663,21 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> Union[Message, bool]: """ - Use this method to set the score of the specified user in a game. On success, if the - message was sent by the bot, returns the edited Message, otherwise returns True. Returns - an error, if the new score is not greater than the user's current score in the chat and - force is False. + Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited :class:`aiogram.types.message.Message`, otherwise returns :code:`True`. Returns an error, if the new score is not greater than the user's current score in the chat and *force* is :code:`False`. Source: https://core.telegram.org/bots/api#setgamescore :param user_id: User identifier :param score: New score, must be non-negative - :param force: Pass True, if the high score is allowed to decrease. This can be useful when - fixing mistakes or banning cheaters - :param disable_edit_message: Pass True, if the game message should not be automatically - edited to include the current scoreboard - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat - :param message_id: Required if inline_message_id is not specified. Identifier of the sent - message - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message + :param force: Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters + :param disable_edit_message: Pass True, if the game message should not be automatically edited to include the current scoreboard + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat + :param message_id: Required if *inline_message_id* is not specified. Identifier of the sent message + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message :param request_timeout: Request timeout :return: On success, if the message was sent by the bot, returns the edited Message, - otherwise returns True. Returns an error, if the new score is not greater than - the user's current score in the chat and force is False. + otherwise returns True. Returns an error, if the new score is not greater than + the user's current score in the chat and force is False. """ call = SetGameScore( user_id=user_id, @@ -2747,28 +2699,22 @@ class Bot(ContextInstanceMixin["Bot"]): request_timeout: Optional[int] = None, ) -> List[GameHighScore]: """ - Use this method to get data for high score tables. Will return the score of the specified - user and several of their neighbors in a game. On success, returns an Array of - GameHighScore objects. - This method will currently return scores for the target user, plus two of their closest - neighbors on each side. Will also return the top three users if the user and his neighbors - are not among them. Please note that this behavior is subject to change. + Use this method to get data for high score tables. Will return the score of the specified user and several of their neighbors in a game. On success, returns an *Array* of :class:`aiogram.types.game_high_score.GameHighScore` objects. + + This method will currently return scores for the target user, plus two of their closest neighbors on each side. Will also return the top three users if the user and his neighbors are not among them. Please note that this behavior is subject to change. Source: https://core.telegram.org/bots/api#getgamehighscores :param user_id: Target user id - :param chat_id: Required if inline_message_id is not specified. Unique identifier for the - target chat - :param message_id: Required if inline_message_id is not specified. Identifier of the sent - message - :param inline_message_id: Required if chat_id and message_id are not specified. Identifier - of the inline message + :param chat_id: Required if *inline_message_id* is not specified. Unique identifier for the target chat + :param message_id: Required if *inline_message_id* is not specified. Identifier of the sent message + :param inline_message_id: Required if *chat_id* and *message_id* are not specified. Identifier of the inline message :param request_timeout: Request timeout :return: Will return the score of the specified user and several of their neighbors in a - game. On success, returns an Array of GameHighScore objects. This method will - currently return scores for the target user, plus two of their closest neighbors - on each side. Will also return the top three users if the user and his neighbors - are not among them. + game. On success, returns an Array of GameHighScore objects. This method will + currently return scores for the target user, plus two of their closest neighbors + on each side. Will also return the top three users if the user and his neighbors + are not among them. """ call = GetGameHighScores( user_id=user_id, diff --git a/aiogram/api/client/__init__.py b/aiogram/client/session/__init__.py similarity index 100% rename from aiogram/api/client/__init__.py rename to aiogram/client/session/__init__.py diff --git a/aiogram/api/client/session/aiohttp.py b/aiogram/client/session/aiohttp.py similarity index 97% rename from aiogram/api/client/session/aiohttp.py rename to aiogram/client/session/aiohttp.py index adb8a987..1dd7bb42 100644 --- a/aiogram/api/client/session/aiohttp.py +++ b/aiogram/client/session/aiohttp.py @@ -17,7 +17,7 @@ from typing import ( from aiohttp import BasicAuth, ClientSession, FormData, TCPConnector -from aiogram.api.methods import Request, TelegramMethod +from aiogram.methods import Request, TelegramMethod from .base import BaseSession, UNSET @@ -56,7 +56,7 @@ def _retrieve_basic(basic: _ProxyBasic) -> Dict[str, Any]: def _prepare_connector(chain_or_plain: _ProxyType) -> Tuple[Type["TCPConnector"], Dict[str, Any]]: - from aiohttp_socks import ProxyInfo, ProxyConnector, ChainProxyConnector # type: ignore + from aiohttp_socks import ChainProxyConnector, ProxyConnector, ProxyInfo # type: ignore # since tuple is Iterable(compatible with _ProxyChain) object, we assume that # user wants chained proxies if tuple is a pair of string(url) and BasicAuth diff --git a/aiogram/api/client/session/base.py b/aiogram/client/session/base.py similarity index 78% rename from aiogram/api/client/session/base.py rename to aiogram/client/session/base.py index d1249733..7a8edd4a 100644 --- a/aiogram/api/client/session/base.py +++ b/aiogram/client/session/base.py @@ -17,8 +17,8 @@ from typing import ( ) from aiogram.utils.exceptions import TelegramAPIError +from aiogram.utils.helper import Default -from ....utils.helper import Default from ...methods import Response, TelegramMethod from ...types import UNSET from ..telegram import PRODUCTION, TelegramAPIServer @@ -32,35 +32,63 @@ _JsonDumps = Callable[..., str] class BaseSession(abc.ABC): - default_timeout: ClassVar[float] = 60.0 api: Default[TelegramAPIServer] = Default(PRODUCTION) + """Telegra Bot API URL patterns""" json_loads: Default[_JsonLoads] = Default(json.loads) + """JSON loader""" json_dumps: Default[_JsonDumps] = Default(json.dumps) + """JSON dumper""" + default_timeout: ClassVar[float] = 60.0 + """Default timeout""" timeout: Default[float] = Default(fget=lambda self: float(self.__class__.default_timeout)) + """Session scope request timeout""" @classmethod def raise_for_status(cls, response: Response[T]) -> None: + """ + Check response status + + :param response: Response instance + """ if response.ok: return raise TelegramAPIError(response.description) @abc.abstractmethod async def close(self) -> None: # pragma: no cover + """ + Close client session + """ pass @abc.abstractmethod async def make_request( self, bot: Bot, method: TelegramMethod[T], timeout: Optional[int] = UNSET ) -> T: # pragma: no cover + """ + Make request to Telegram Bot API + + :param bot: Bot instance + :param method: Method instance + :param timeout: Request timeout + :return: + :raise TelegramApiError: + """ pass @abc.abstractmethod async def stream_content( self, url: str, timeout: int, chunk_size: int ) -> AsyncGenerator[bytes, None]: # pragma: no cover + """ + Stream reader + """ yield b"" def prepare_value(self, value: Any) -> Union[str, int, bool]: + """ + Prepare value before send + """ if isinstance(value, str): return value if isinstance(value, (list, dict)): @@ -74,6 +102,9 @@ class BaseSession(abc.ABC): return str(value) def clean_json(self, value: Any) -> Any: + """ + Clean data before send + """ if isinstance(value, list): return [self.clean_json(v) for v in value if v is not None] elif isinstance(value, dict): diff --git a/aiogram/client/telegram.py b/aiogram/client/telegram.py new file mode 100644 index 00000000..b8e89a89 --- /dev/null +++ b/aiogram/client/telegram.py @@ -0,0 +1,52 @@ +from dataclasses import dataclass + + +@dataclass(frozen=True) +class TelegramAPIServer: + """ + Base config for API Endpoints + """ + + base: str + file: str + is_local: bool = False + + def api_url(self, token: str, method: str) -> str: + """ + Generate URL for API methods + + :param token: Bot token + :param method: API method name (case insensitive) + :return: URL + """ + return self.base.format(token=token, method=method) + + def file_url(self, token: str, path: str) -> str: + """ + Generate URL for downloading files + + :param token: Bot token + :param path: file path + :return: URL + """ + return self.file.format(token=token, path=path) + + @classmethod + def from_base(cls, base: str, is_local: bool = False) -> "TelegramAPIServer": + """ + Use this method to auto-generate TelegramAPIServer instance from base URL + + :param base: Base URL + :param is_local: Mark this server is in `local mode `_. + :return: instance of :class:`TelegramAPIServer` + """ + base = base.rstrip("/") + return cls( + base=f"{base}/bot{{token}}/{{method}}", + file=f"{base}/file/bot{{token}}/{{path}}", + is_local=is_local, + ) + + +# Main API server +PRODUCTION = TelegramAPIServer.from_base("https://api.telegram.org") diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index 699f746d..ff0bf1a7 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -7,11 +7,13 @@ from asyncio import CancelledError, Future, Lock from typing import Any, AsyncGenerator, Dict, Optional, Union from .. import loggers -from ..api.client.bot import Bot -from ..api.methods import TelegramMethod -from ..api.types import Update, User +from ..client.bot import Bot +from ..methods import TelegramMethod +from ..types import TelegramObject, Update, User from ..utils.exceptions import TelegramAPIError -from .event.bases import NOT_HANDLED +from .event.bases import UNHANDLED, SkipHandler +from .event.telegram import TelegramEventObserver +from .middlewares.error import ErrorsMiddleware from .middlewares.user_context import UserContextMiddleware from .router import Router @@ -23,10 +25,15 @@ class Dispatcher(Router): def __init__(self, **kwargs: Any) -> None: super(Dispatcher, self).__init__(**kwargs) - self._running_lock = Lock() - # Default middleware is needed for contextual features + self.update = TelegramEventObserver(router=self, event_name="update") + self.observers["update"] = self.update + + self.update.register(self._listen_update) self.update.outer_middleware(UserContextMiddleware()) + self.update.outer_middleware(ErrorsMiddleware(self)) + + self._running_lock = Lock() @property def parent_router(self) -> None: @@ -61,7 +68,7 @@ class Dispatcher(Router): Bot.set_current(bot) try: response = await self.update.trigger(update, bot=bot, **kwargs) - handled = response is not NOT_HANDLED + handled = response is not UNHANDLED return response finally: finish_time = loop.time() @@ -97,6 +104,74 @@ class Dispatcher(Router): yield update update_id = update.update_id + 1 + async def _listen_update(self, update: Update, **kwargs: Any) -> Any: + """ + Main updates listener + + Workflow: + - Detect content type and propagate to observers in current router + - If no one filter is pass - propagate update to child routers as Update + + :param update: + :param kwargs: + :return: + """ + event: TelegramObject + if update.message: + update_type = "message" + event = update.message + elif update.edited_message: + update_type = "edited_message" + event = update.edited_message + elif update.channel_post: + update_type = "channel_post" + event = update.channel_post + elif update.edited_channel_post: + update_type = "edited_channel_post" + event = update.edited_channel_post + elif update.inline_query: + update_type = "inline_query" + event = update.inline_query + elif update.chosen_inline_result: + update_type = "chosen_inline_result" + event = update.chosen_inline_result + elif update.callback_query: + update_type = "callback_query" + event = update.callback_query + elif update.shipping_query: + update_type = "shipping_query" + event = update.shipping_query + elif update.pre_checkout_query: + update_type = "pre_checkout_query" + event = update.pre_checkout_query + elif update.poll: + update_type = "poll" + event = update.poll + elif update.poll_answer: + update_type = "poll_answer" + event = update.poll_answer + else: + warnings.warn( + "Detected unknown update type.\n" + "Seems like Telegram Bot API was updated and you have " + "installed not latest version of aiogram framework", + RuntimeWarning, + ) + raise SkipHandler + + kwargs.update(event_update=update) + + for router in self.chain: + kwargs.update(event_router=router) + observer = router.observers[update_type] + response = await observer.trigger(event, update=update, **kwargs) + if response is not UNHANDLED: + break + else: + response = UNHANDLED + + return response + @classmethod async def _silent_call_request(cls, bot: Bot, result: TelegramMethod[Any]) -> None: """ @@ -129,7 +204,7 @@ class Dispatcher(Router): handled = False try: response = await self.feed_update(bot, update, **kwargs) - handled = handled is not NOT_HANDLED + handled = handled is not UNHANDLED if call_answer and isinstance(response, TelegramMethod): await self._silent_call_request(bot=bot, result=response) return handled @@ -172,7 +247,7 @@ class Dispatcher(Router): raise async def feed_webhook_update( - self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: int = 55, **kwargs: Any + self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: float = 55, **kwargs: Any ) -> Optional[Dict[str, Any]]: if not isinstance(update, Update): # Allow to use raw updates update = Update(**update) @@ -255,7 +330,7 @@ class Dispatcher(Router): await asyncio.gather(*coro_list) finally: for bot in bots: # Close sessions - await bot.close() + await bot.session.close() loggers.dispatcher.info("Polling stopped") await self.emit_shutdown(**workflow_data) diff --git a/aiogram/dispatcher/event/bases.py b/aiogram/dispatcher/event/bases.py index d255e3ae..cb5fb2cf 100644 --- a/aiogram/dispatcher/event/bases.py +++ b/aiogram/dispatcher/event/bases.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import Any, Awaitable, Callable, Dict, NoReturn, Optional, Union from unittest.mock import sentinel -from ...api.types import TelegramObject +from ...types import TelegramObject from ..middlewares.base import BaseMiddleware NextMiddlewareType = Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]] @@ -11,7 +11,7 @@ MiddlewareType = Union[ BaseMiddleware, Callable[[NextMiddlewareType, TelegramObject, Dict[str, Any]], Awaitable[Any]] ] -NOT_HANDLED = sentinel.NOT_HANDLED +UNHANDLED = sentinel.UNHANDLED class SkipHandler(Exception): diff --git a/aiogram/dispatcher/event/event.py b/aiogram/dispatcher/event/event.py index 29aa4580..ef87d329 100644 --- a/aiogram/dispatcher/event/event.py +++ b/aiogram/dispatcher/event/event.py @@ -8,6 +8,19 @@ from .handler import CallbackType, HandlerObject, HandlerType class EventObserver: """ Simple events observer + + Is used for managing events is not related with Telegram (For example startup/shutdown processes) + + Handlers can be registered via decorator or method + + .. code-block:: python + + .register(my_handler) + + .. code-block:: python + + @() + async def my_handler(*args, **kwargs): ... """ def __init__(self) -> None: diff --git a/aiogram/dispatcher/event/handler.py b/aiogram/dispatcher/event/handler.py index 8314c52b..bc7ee10d 100644 --- a/aiogram/dispatcher/event/handler.py +++ b/aiogram/dispatcher/event/handler.py @@ -24,12 +24,6 @@ class CallableMixin: def __post_init__(self) -> None: callback = inspect.unwrap(self.callback) self.awaitable = inspect.isawaitable(callback) or inspect.iscoroutinefunction(callback) - if isinstance(callback, BaseFilter): - # Pydantic 1.5 has incorrect signature generator - # Issue: https://github.com/samuelcolvin/pydantic/issues/1419 - # Fixes: https://github.com/samuelcolvin/pydantic/pull/1427 - # TODO: Remove this temporary fix - callback = inspect.unwrap(callback.__call__) self.spec = inspect.getfullargspec(callback) def _prepare_kwargs(self, kwargs: Dict[str, Any]) -> Dict[str, Any]: diff --git a/aiogram/dispatcher/event/telegram.py b/aiogram/dispatcher/event/telegram.py index e72d5db3..ab185043 100644 --- a/aiogram/dispatcher/event/telegram.py +++ b/aiogram/dispatcher/event/telegram.py @@ -6,9 +6,9 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional from pydantic import ValidationError -from ...api.types import TelegramObject +from ...types import TelegramObject from ..filters.base import BaseFilter -from .bases import NOT_HANDLED, MiddlewareType, NextMiddlewareType, SkipHandler +from .bases import UNHANDLED, MiddlewareType, NextMiddlewareType, SkipHandler from .handler import CallbackType, FilterObject, FilterType, HandlerObject, HandlerType if TYPE_CHECKING: # pragma: no cover @@ -18,6 +18,9 @@ if TYPE_CHECKING: # pragma: no cover class TelegramEventObserver: """ Event observer for Telegram events + + Here you can register handler with filters or bounded filters which can be used as keyword arguments instead of writing full references when you register new handlers. + This observer will stops event propagation when first handler is pass. """ def __init__(self, router: Router, event_name: str) -> None: @@ -57,6 +60,17 @@ class TelegramEventObserver: yield filter_ registry.append(filter_) + def _resolve_inner_middlewares(self) -> List[MiddlewareType]: + """ + Get all inner middlewares in an tree + """ + middlewares = [] + + for router in self.router.chain_head: + observer = router.observers[self.event_name] + middlewares.extend(observer.middlewares) + return middlewares + def resolve_filters(self, full_config: Dict[str, Any]) -> List[BaseFilter]: """ Resolve keyword filters via filters factory @@ -126,12 +140,14 @@ class TelegramEventObserver: if result: kwargs.update(data) try: - wrapped_inner = self._wrap_middleware(self.middlewares, handler.call) + wrapped_inner = self._wrap_middleware( + self._resolve_inner_middlewares(), handler.call + ) return await wrapped_inner(event, kwargs) except SkipHandler: continue - return NOT_HANDLED + return UNHANDLED def __call__( self, *args: FilterType, **bound_filters: BaseFilter @@ -147,16 +163,26 @@ class TelegramEventObserver: return wrapper def middleware( - self, middleware: Optional[MiddlewareType] = None, + self, + middleware: Optional[MiddlewareType] = None, ) -> Union[Callable[[MiddlewareType], MiddlewareType], MiddlewareType]: """ Decorator for registering inner middlewares Usage: - >>> @.middleware() # via decorator (variant 1) - >>> @.middleware # via decorator (variant 2) - >>> async def my_middleware(handler, event, data): ... - >>> .middleware(middleware) # via method + + .. code-block:: python + + @.middleware() # via decorator (variant 1) + + .. code-block:: python + + @.middleware # via decorator (variant 2) + + .. code-block:: python + + async def my_middleware(handler, event, data): ... + .middleware(my_middleware) # via method """ def wrapper(m: MiddlewareType) -> MiddlewareType: @@ -168,16 +194,26 @@ class TelegramEventObserver: return wrapper(middleware) def outer_middleware( - self, middleware: Optional[MiddlewareType] = None, + self, + middleware: Optional[MiddlewareType] = None, ) -> Union[Callable[[MiddlewareType], MiddlewareType], MiddlewareType]: """ Decorator for registering outer middlewares Usage: - >>> @.outer_middleware() # via decorator (variant 1) - >>> @.outer_middleware # via decorator (variant 2) - >>> async def my_middleware(handler, event, data): ... - >>> .outer_middleware(my_middleware) # via method + + .. code-block:: python + + @.outer_middleware() # via decorator (variant 1) + + .. code-block:: python + + @.outer_middleware # via decorator (variant 2) + + .. code-block:: python + + async def my_middleware(handler, event, data): ... + .outer_middleware(my_middleware) # via method """ def wrapper(m: MiddlewareType) -> MiddlewareType: diff --git a/aiogram/dispatcher/filters/__init__.py b/aiogram/dispatcher/filters/__init__.py index e59259be..1452d0a3 100644 --- a/aiogram/dispatcher/filters/__init__.py +++ b/aiogram/dispatcher/filters/__init__.py @@ -18,7 +18,6 @@ __all__ = ( ) BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = { - "update": (), "message": (Text, Command, ContentTypesFilter), "edited_message": (Text, Command, ContentTypesFilter), "channel_post": (Text, ContentTypesFilter), diff --git a/aiogram/dispatcher/filters/command.py b/aiogram/dispatcher/filters/command.py index 7d0c05f8..1d48c711 100644 --- a/aiogram/dispatcher/filters/command.py +++ b/aiogram/dispatcher/filters/command.py @@ -7,8 +7,8 @@ from typing import Any, Dict, Match, Optional, Pattern, Sequence, Union, cast from pydantic import validator from aiogram import Bot -from aiogram.api.types import Message from aiogram.dispatcher.filters import BaseFilter +from aiogram.types import Message CommandPatterType = Union[str, re.Pattern] diff --git a/aiogram/dispatcher/filters/content_types.py b/aiogram/dispatcher/filters/content_types.py index 1f195663..c8cc4392 100644 --- a/aiogram/dispatcher/filters/content_types.py +++ b/aiogram/dispatcher/filters/content_types.py @@ -2,8 +2,9 @@ from typing import Any, Dict, Optional, Sequence, Union from pydantic import validator -from ...api.types import Message -from ...api.types.message import ContentType +from aiogram.types.message import ContentType + +from ...types import Message from .base import BaseFilter diff --git a/aiogram/dispatcher/filters/text.py b/aiogram/dispatcher/filters/text.py index 8ab3822d..8141e07c 100644 --- a/aiogram/dispatcher/filters/text.py +++ b/aiogram/dispatcher/filters/text.py @@ -2,8 +2,8 @@ from typing import Any, Dict, Optional, Sequence, Union from pydantic import root_validator -from aiogram.api.types import CallbackQuery, InlineQuery, Message, Poll from aiogram.dispatcher.filters import BaseFilter +from aiogram.types import CallbackQuery, InlineQuery, Message, Poll TextType = str diff --git a/aiogram/dispatcher/handler/base.py b/aiogram/dispatcher/handler/base.py index 90b8da45..6e26250b 100644 --- a/aiogram/dispatcher/handler/base.py +++ b/aiogram/dispatcher/handler/base.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, Generic, TypeVar, cast from aiogram import Bot -from aiogram.api.types import Update +from aiogram.types import Update T = TypeVar("T") diff --git a/aiogram/dispatcher/handler/callback_query.py b/aiogram/dispatcher/handler/callback_query.py index a7faa0d3..8f129b21 100644 --- a/aiogram/dispatcher/handler/callback_query.py +++ b/aiogram/dispatcher/handler/callback_query.py @@ -1,8 +1,8 @@ from abc import ABC from typing import Optional -from aiogram.api.types import CallbackQuery, Message, User from aiogram.dispatcher.handler import BaseHandler +from aiogram.types import CallbackQuery, Message, User class CallbackQueryHandler(BaseHandler[CallbackQuery], ABC): diff --git a/aiogram/dispatcher/handler/chosen_inline_result.py b/aiogram/dispatcher/handler/chosen_inline_result.py index b15469bf..58b75a32 100644 --- a/aiogram/dispatcher/handler/chosen_inline_result.py +++ b/aiogram/dispatcher/handler/chosen_inline_result.py @@ -1,7 +1,7 @@ from abc import ABC -from aiogram.api.types import ChosenInlineResult, User from aiogram.dispatcher.handler import BaseHandler +from aiogram.types import ChosenInlineResult, User class ChosenInlineResultHandler(BaseHandler[ChosenInlineResult], ABC): diff --git a/aiogram/dispatcher/handler/inline_query.py b/aiogram/dispatcher/handler/inline_query.py index faae2d23..13800523 100644 --- a/aiogram/dispatcher/handler/inline_query.py +++ b/aiogram/dispatcher/handler/inline_query.py @@ -1,7 +1,7 @@ from abc import ABC -from aiogram.api.types import InlineQuery, User from aiogram.dispatcher.handler import BaseHandler +from aiogram.types import InlineQuery, User class InlineQueryHandler(BaseHandler[InlineQuery], ABC): diff --git a/aiogram/dispatcher/handler/message.py b/aiogram/dispatcher/handler/message.py index a25daddc..91f39cff 100644 --- a/aiogram/dispatcher/handler/message.py +++ b/aiogram/dispatcher/handler/message.py @@ -1,9 +1,9 @@ from abc import ABC from typing import Optional, cast -from aiogram.api.types import Chat, Message, User from aiogram.dispatcher.filters import CommandObject from aiogram.dispatcher.handler.base import BaseHandler, BaseHandlerMixin +from aiogram.types import Chat, Message, User class MessageHandler(BaseHandler[Message], ABC): diff --git a/aiogram/dispatcher/handler/poll.py b/aiogram/dispatcher/handler/poll.py index 27883738..de0e29a4 100644 --- a/aiogram/dispatcher/handler/poll.py +++ b/aiogram/dispatcher/handler/poll.py @@ -1,8 +1,8 @@ from abc import ABC from typing import List -from aiogram.api.types import Poll, PollOption from aiogram.dispatcher.handler import BaseHandler +from aiogram.types import Poll, PollOption class PollHandler(BaseHandler[Poll], ABC): diff --git a/aiogram/dispatcher/handler/pre_checkout_query.py b/aiogram/dispatcher/handler/pre_checkout_query.py index 02fae71c..92486257 100644 --- a/aiogram/dispatcher/handler/pre_checkout_query.py +++ b/aiogram/dispatcher/handler/pre_checkout_query.py @@ -1,7 +1,7 @@ from abc import ABC -from aiogram.api.types import PreCheckoutQuery, User from aiogram.dispatcher.handler import BaseHandler +from aiogram.types import PreCheckoutQuery, User class PreCheckoutQueryHandler(BaseHandler[PreCheckoutQuery], ABC): diff --git a/aiogram/dispatcher/handler/shipping_query.py b/aiogram/dispatcher/handler/shipping_query.py index ea079a23..4abb9b9f 100644 --- a/aiogram/dispatcher/handler/shipping_query.py +++ b/aiogram/dispatcher/handler/shipping_query.py @@ -1,7 +1,7 @@ from abc import ABC -from aiogram.api.types import ShippingQuery, User from aiogram.dispatcher.handler import BaseHandler +from aiogram.types import ShippingQuery, User class ShippingQueryHandler(BaseHandler[ShippingQuery], ABC): diff --git a/aiogram/dispatcher/middlewares/error.py b/aiogram/dispatcher/middlewares/error.py index 438277b1..bd3cdcd4 100644 --- a/aiogram/dispatcher/middlewares/error.py +++ b/aiogram/dispatcher/middlewares/error.py @@ -2,8 +2,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict -from ...api.types import Update -from ..event.bases import NOT_HANDLED, CancelHandler, SkipHandler +from ...types import Update +from ..event.bases import UNHANDLED, CancelHandler, SkipHandler from .base import BaseMiddleware if TYPE_CHECKING: # pragma: no cover @@ -25,7 +25,9 @@ class ErrorsMiddleware(BaseMiddleware[Update]): except (SkipHandler, CancelHandler): # pragma: no cover raise except Exception as e: - response = await self.router.errors.trigger(event, exception=e, **data) - if response is NOT_HANDLED: - raise - return response + for router in self.router.chain: + observer = router.observers["error"] + response = await observer.trigger(event, exception=e, **data) + if response is not UNHANDLED: + return response + raise diff --git a/aiogram/dispatcher/middlewares/user_context.py b/aiogram/dispatcher/middlewares/user_context.py index 09abd10f..1434eec4 100644 --- a/aiogram/dispatcher/middlewares/user_context.py +++ b/aiogram/dispatcher/middlewares/user_context.py @@ -1,8 +1,8 @@ from contextlib import contextmanager from typing import Any, Awaitable, Callable, Dict, Iterator, Optional, Tuple -from aiogram.api.types import Chat, Update, User from aiogram.dispatcher.middlewares.base import BaseMiddleware +from aiogram.types import Chat, Update, User class UserContextMiddleware(BaseMiddleware[Update]): diff --git a/aiogram/dispatcher/router.py b/aiogram/dispatcher/router.py index 7680454e..3370d3c0 100644 --- a/aiogram/dispatcher/router.py +++ b/aiogram/dispatcher/router.py @@ -3,29 +3,37 @@ from __future__ import annotations import warnings from typing import Any, Dict, Generator, List, Optional, Union -from ..api.types import TelegramObject, Update from ..utils.imports import import_module from ..utils.warnings import CodeHasNoEffect -from .event.bases import NOT_HANDLED, SkipHandler from .event.event import EventObserver from .event.telegram import TelegramEventObserver from .filters import BUILTIN_FILTERS -from .middlewares.error import ErrorsMiddleware class Router: """ - Events router + Router can route update and it nested update types like messages, callback query, polls and all other event types. + Here is used event-observer pattern. + + Event handlers can be registered in observer by two ways: + + - By observer method - :obj:`router..register(handler, )` + - By decorator - :obj:`@router.()` + """ def __init__(self, use_builtin_filters: bool = True) -> None: + """ + + :param use_builtin_filters: `aiogram` has many builtin filters and you can controll automatic registration of this filters in factory + """ + self.use_builtin_filters = use_builtin_filters self._parent_router: Optional[Router] = None self.sub_routers: List[Router] = [] # Observers - self.update = TelegramEventObserver(router=self, event_name="update") self.message = TelegramEventObserver(router=self, event_name="message") self.edited_message = TelegramEventObserver(router=self, event_name="edited_message") self.channel_post = TelegramEventObserver(router=self, event_name="channel_post") @@ -49,7 +57,6 @@ class Router: self.shutdown = EventObserver() self.observers: Dict[str, TelegramEventObserver] = { - "update": self.update, "message": self.message, "edited_message": self.edited_message, "channel_post": self.channel_post, @@ -64,11 +71,6 @@ class Router: "error": self.errors, } - # Root handler - self.update.register(self._listen_update) - - self.update.outer_middleware(ErrorsMiddleware(self)) - # Builtin filters if use_builtin_filters: for name, observer in self.observers.items(): @@ -126,8 +128,9 @@ class Router: if not self.use_builtin_filters and parent.use_builtin_filters: warnings.warn( - f"Router(use_builtin_filters=False) has no effect for router {self} " - f"in due to builtin filters is already registered in parent router", + f"{self.__class__.__name__}(use_builtin_filters=False) has no effect" + f" for router {self} in due to builtin filters is already registered" + f" in parent router", CodeHasNoEffect, stacklevel=3, ) @@ -155,73 +158,6 @@ class Router: router.parent_router = self return router - async def _listen_update(self, update: Update, **kwargs: Any) -> Any: - """ - Main updates listener - - Workflow: - - Detect content type and propagate to observers in current router - - If no one filter is pass - propagate update to child routers as Update - - :param update: - :param kwargs: - :return: - """ - event: TelegramObject - if update.message: - update_type = "message" - event = update.message - elif update.edited_message: - update_type = "edited_message" - event = update.edited_message - elif update.channel_post: - update_type = "channel_post" - event = update.channel_post - elif update.edited_channel_post: - update_type = "edited_channel_post" - event = update.edited_channel_post - elif update.inline_query: - update_type = "inline_query" - event = update.inline_query - elif update.chosen_inline_result: - update_type = "chosen_inline_result" - event = update.chosen_inline_result - elif update.callback_query: - update_type = "callback_query" - event = update.callback_query - elif update.shipping_query: - update_type = "shipping_query" - event = update.shipping_query - elif update.pre_checkout_query: - update_type = "pre_checkout_query" - event = update.pre_checkout_query - elif update.poll: - update_type = "poll" - event = update.poll - elif update.poll_answer: - update_type = "poll_answer" - event = update.poll_answer - else: - warnings.warn( - "Detected unknown update type.\n" - "Seems like Telegram Bot API was updated and you have " - "installed not latest version of aiogram framework", - RuntimeWarning, - ) - raise SkipHandler - - kwargs.update(event_update=update, event_router=self) - observer = self.observers[update_type] - response = await observer.trigger(event, update=update, **kwargs) - - if response is NOT_HANDLED: # Resolve nested routers - for router in self.sub_routers: - response = await router.update.trigger(event=update, **kwargs) - if response is NOT_HANDLED: - continue - - return response - async def emit_startup(self, *args: Any, **kwargs: Any) -> None: """ Recursively call startup callbacks @@ -248,17 +184,6 @@ class Router: for router in self.sub_routers: await router.emit_shutdown(*args, **kwargs) - @property - def update_handler(self) -> TelegramEventObserver: - warnings.warn( - "`Router.update_handler(...)` is deprecated and will be removed in version 3.2 " - "use `Router.update(...)`", - DeprecationWarning, - stacklevel=2, - ) - - return self.update - @property def message_handler(self) -> TelegramEventObserver: warnings.warn( diff --git a/aiogram/api/methods/__init__.py b/aiogram/methods/__init__.py similarity index 95% rename from aiogram/api/methods/__init__.py rename to aiogram/methods/__init__.py index 52785ed0..9d498162 100644 --- a/aiogram/api/methods/__init__.py +++ b/aiogram/methods/__init__.py @@ -4,6 +4,8 @@ from .answer_inline_query import AnswerInlineQuery from .answer_pre_checkout_query import AnswerPreCheckoutQuery from .answer_shipping_query import AnswerShippingQuery from .base import Request, Response, TelegramMethod +from .close import Close +from .copy_message import CopyMessage from .create_new_sticker_set import CreateNewStickerSet from .delete_chat_photo import DeleteChatPhoto from .delete_chat_sticker_set import DeleteChatStickerSet @@ -31,6 +33,7 @@ from .get_user_profile_photos import GetUserProfilePhotos from .get_webhook_info import GetWebhookInfo from .kick_chat_member import KickChatMember from .leave_chat import LeaveChat +from .log_out import LogOut from .pin_chat_message import PinChatMessage from .promote_chat_member import PromoteChatMember from .restrict_chat_member import RestrictChatMember @@ -67,6 +70,7 @@ from .set_webhook import SetWebhook from .stop_message_live_location import StopMessageLiveLocation from .stop_poll import StopPoll from .unban_chat_member import UnbanChatMember +from .unpin_all_chat_messages import UnpinAllChatMessages from .unpin_chat_message import UnpinChatMessage from .upload_sticker_file import UploadStickerFile @@ -79,8 +83,11 @@ __all__ = ( "DeleteWebhook", "GetWebhookInfo", "GetMe", + "LogOut", + "Close", "SendMessage", "ForwardMessage", + "CopyMessage", "SendPhoto", "SendAudio", "SendDocument", @@ -112,6 +119,7 @@ __all__ = ( "SetChatDescription", "PinChatMessage", "UnpinChatMessage", + "UnpinAllChatMessages", "LeaveChat", "GetChat", "GetChatAdministrators", diff --git a/aiogram/api/methods/add_sticker_to_set.py b/aiogram/methods/add_sticker_to_set.py similarity index 54% rename from aiogram/api/methods/add_sticker_to_set.py rename to aiogram/methods/add_sticker_to_set.py index 735371e4..12120964 100644 --- a/aiogram/api/methods/add_sticker_to_set.py +++ b/aiogram/methods/add_sticker_to_set.py @@ -11,10 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class AddStickerToSet(TelegramMethod[bool]): """ - Use this method to add a new sticker to a set created by the bot. You must use exactly one of - the fields png_sticker or tgs_sticker. Animated stickers can be added to animated sticker sets - and only to them. Animated sticker sets can have up to 50 stickers. Static sticker sets can - have up to 120 stickers. Returns True on success. + Use this method to add a new sticker to a set created by the bot. You **must** use exactly one of the fields *png_sticker* or *tgs_sticker*. Animated stickers can be added to animated sticker sets and only to them. Animated sticker sets can have up to 50 stickers. Static sticker sets can have up to 120 stickers. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#addstickertoset """ @@ -28,14 +25,9 @@ class AddStickerToSet(TelegramMethod[bool]): emojis: str """One or more emoji corresponding to the sticker""" png_sticker: Optional[Union[InputFile, str]] = None - """PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed - 512px, and either width or height must be exactly 512px. Pass a file_id as a String to send - a file that already exists on the Telegram servers, pass an HTTP URL as a String for - Telegram to get a file from the Internet, or upload a new one using multipart/form-data.""" + """**PNG** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a *file_id* as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `""" tgs_sticker: Optional[InputFile] = None - """TGS animation with the sticker, uploaded using multipart/form-data. See - https://core.telegram.org/animated_stickers#technical-requirements for technical - requirements""" + """**TGS** animation with the sticker, uploaded using multipart/form-data. See `https://core.telegram.org/animated_stickers#technical-requirements `_`https://core.telegram.org/animated_stickers#technical-requirements `_ for technical requirements""" mask_position: Optional[MaskPosition] = None """A JSON-serialized object for position where the mask should be placed on faces""" diff --git a/aiogram/methods/answer_callback_query.py b/aiogram/methods/answer_callback_query.py new file mode 100644 index 00000000..7ab79207 --- /dev/null +++ b/aiogram/methods/answer_callback_query.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Optional + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class AnswerCallbackQuery(TelegramMethod[bool]): + """ + Use this method to send answers to callback queries sent from `inline keyboards `_. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, :code:`True` is returned. + + Alternatively, the user can be redirected to the specified Game URL. For this option to work, you must first create a game for your bot via `@Botfather `_ and accept the terms. Otherwise, you may use links like :code:`t.me/your_bot?start=XXXX` that open your bot with a parameter. + + Source: https://core.telegram.org/bots/api#answercallbackquery + """ + + __returning__ = bool + + callback_query_id: str + """Unique identifier for the query to be answered""" + text: Optional[str] = None + """Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters""" + show_alert: Optional[bool] = None + """If *true*, an alert will be shown by the client instead of a notification at the top of the chat screen. Defaults to *false*.""" + url: Optional[str] = None + """URL that will be opened by the user's client. If you have created a :class:`aiogram.types.game.Game` and accepted the conditions via `@Botfather `_, specify the URL that opens your game — note that this will only work if the query comes from a `https://core.telegram.org/bots/api#inlinekeyboardbutton `_ *callback_game* button.""" + cache_time: Optional[int] = None + """The maximum amount of time in seconds that the result of the callback query may be cached client-side. Telegram apps will support caching starting in version 3.14. Defaults to 0.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="answerCallbackQuery", data=data) diff --git a/aiogram/api/methods/answer_inline_query.py b/aiogram/methods/answer_inline_query.py similarity index 58% rename from aiogram/api/methods/answer_inline_query.py rename to aiogram/methods/answer_inline_query.py index c5c2709d..95c9b66d 100644 --- a/aiogram/api/methods/answer_inline_query.py +++ b/aiogram/methods/answer_inline_query.py @@ -11,8 +11,9 @@ if TYPE_CHECKING: # pragma: no cover class AnswerInlineQuery(TelegramMethod[bool]): """ - Use this method to send answers to an inline query. On success, True is returned. - No more than 50 results per query are allowed. + Use this method to send answers to an inline query. On success, :code:`True` is returned. + + No more than **50** results per query are allowed. Source: https://core.telegram.org/bots/api#answerinlinequery """ @@ -24,25 +25,17 @@ class AnswerInlineQuery(TelegramMethod[bool]): results: List[InlineQueryResult] """A JSON-serialized array of results for the inline query""" 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.""" + """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 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.""" + """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""" + """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 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.""" + """`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.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() prepare_parse_mode(bot, data["results"]) - return Request(method="answerInlineQuery", data=data) diff --git a/aiogram/methods/answer_pre_checkout_query.py b/aiogram/methods/answer_pre_checkout_query.py new file mode 100644 index 00000000..fd47fa15 --- /dev/null +++ b/aiogram/methods/answer_pre_checkout_query.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Optional + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class AnswerPreCheckoutQuery(TelegramMethod[bool]): + """ + Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an :class:`aiogram.types.update.Update` with the field *pre_checkout_query*. Use this method to respond to such pre-checkout queries. On success, True is returned. **Note:** The Bot API must receive an answer within 10 seconds after the pre-checkout query was sent. + + Source: https://core.telegram.org/bots/api#answerprecheckoutquery + """ + + __returning__ = bool + + pre_checkout_query_id: str + """Unique identifier for the query to be answered""" + ok: bool + """Specify :code:`True` if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use :code:`False` if there are any problems.""" + error_message: Optional[str] = None + """Required if *ok* is :code:`False`. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="answerPreCheckoutQuery", data=data) diff --git a/aiogram/api/methods/answer_shipping_query.py b/aiogram/methods/answer_shipping_query.py similarity index 57% rename from aiogram/api/methods/answer_shipping_query.py rename to aiogram/methods/answer_shipping_query.py index da79adb5..6c3c7b6d 100644 --- a/aiogram/api/methods/answer_shipping_query.py +++ b/aiogram/methods/answer_shipping_query.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class AnswerShippingQuery(TelegramMethod[bool]): """ - If you sent an invoice requesting a shipping address and the parameter is_flexible was - specified, the Bot API will send an Update with a shipping_query field to the bot. Use this - method to reply to shipping queries. On success, True is returned. + If you sent an invoice requesting a shipping address and the parameter *is_flexible* was specified, the Bot API will send an :class:`aiogram.types.update.Update` with a *shipping_query* field to the bot. Use this method to reply to shipping queries. On success, True is returned. Source: https://core.telegram.org/bots/api#answershippingquery """ @@ -23,14 +21,11 @@ class AnswerShippingQuery(TelegramMethod[bool]): shipping_query_id: str """Unique identifier for the query to be answered""" ok: bool - """Specify True if delivery to the specified address is possible and False if there are any - problems (for example, if delivery to the specified address is not possible)""" + """Specify True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible)""" shipping_options: Optional[List[ShippingOption]] = None - """Required if ok is True. A JSON-serialized array of available shipping options.""" + """Required if *ok* is True. A JSON-serialized array of available shipping options.""" error_message: Optional[str] = None - """Required if ok is False. Error message in human readable form that explains why it is - impossible to complete the order (e.g. "Sorry, delivery to your desired address is - unavailable'). Telegram will display this message to the user.""" + """Required if *ok* is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/base.py b/aiogram/methods/base.py similarity index 90% rename from aiogram/api/methods/base.py rename to aiogram/methods/base.py index dfdfda34..ce73a35b 100644 --- a/aiogram/api/methods/base.py +++ b/aiogram/methods/base.py @@ -84,7 +84,7 @@ class TelegramMethod(abc.ABC, BaseModel, Generic[T]): return await bot(self) def __await__(self) -> Generator[Any, None, T]: - from aiogram.api.client.bot import Bot + from aiogram.client.bot import Bot bot = Bot.get_current(no_error=False) return self.emit(bot).__await__() @@ -126,7 +126,12 @@ def prepare_media_file(data: Dict[str, Any], files: Dict[str, InputFile]) -> Non data["media"]["media"] = f"attach://{tag}" -def prepare_parse_mode(bot: Bot, root: Any, parse_mode_property: str = "parse_mode") -> None: +def prepare_parse_mode( + bot: Bot, + root: Any, + parse_mode_property: str = "parse_mode", + entities_property: str = "entities", +) -> None: """ Find and set parse_mode with highest priority. @@ -137,9 +142,14 @@ def prepare_parse_mode(bot: Bot, root: Any, parse_mode_property: str = "parse_mo """ if isinstance(root, list): for item in root: - prepare_parse_mode(bot=bot, root=item, parse_mode_property=parse_mode_property) + prepare_parse_mode( + bot=bot, + root=item, + parse_mode_property=parse_mode_property, + entities_property=entities_property, + ) elif root.get(parse_mode_property, UNSET) is UNSET: - if bot.parse_mode: + if bot.parse_mode and root.get(entities_property, None) is None: root[parse_mode_property] = bot.parse_mode else: root[parse_mode_property] = None diff --git a/aiogram/methods/close.py b/aiogram/methods/close.py new file mode 100644 index 00000000..18f00eac --- /dev/null +++ b/aiogram/methods/close.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class Close(TelegramMethod[bool]): + """ + Use this method to close the bot instance before moving it from one local server to another. You need to delete the webhook before calling this method to ensure that the bot isn't launched again after server restart. The method will return error 429 in the first 10 minutes after the bot is launched. Returns :code:`True` on success. Requires no parameters. + + Source: https://core.telegram.org/bots/api#close + """ + + __returning__ = bool + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="close", data=data) diff --git a/aiogram/methods/copy_message.py b/aiogram/methods/copy_message.py new file mode 100644 index 00000000..dddf2c92 --- /dev/null +++ b/aiogram/methods/copy_message.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + UNSET, + ForceReply, + InlineKeyboardMarkup, + MessageEntity, + MessageId, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from .base import Request, TelegramMethod, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class CopyMessage(TelegramMethod[MessageId]): + """ + Use this method to copy messages of any kind. The method is analogous to the method :class:`aiogram.methods.forward_messages.ForwardMessages`, but the copied message doesn't have a link to the original message. Returns the :class:`aiogram.types.message_id.MessageId` of the sent message on success. + + Source: https://core.telegram.org/bots/api#copymessage + """ + + __returning__ = MessageId + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + from_chat_id: Union[int, str] + """Unique identifier for the chat where the original message was sent (or channel username in the format :code:`@channelusername`)""" + message_id: int + """Message identifier in the chat specified in *from_chat_id*""" + caption: Optional[str] = None + """New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the new caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the new caption, which can be specified instead of *parse_mode*""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + return Request(method="copyMessage", data=data) diff --git a/aiogram/api/methods/create_new_sticker_set.py b/aiogram/methods/create_new_sticker_set.py similarity index 50% rename from aiogram/api/methods/create_new_sticker_set.py rename to aiogram/methods/create_new_sticker_set.py index e6a222ae..9c0750e2 100644 --- a/aiogram/api/methods/create_new_sticker_set.py +++ b/aiogram/methods/create_new_sticker_set.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class CreateNewStickerSet(TelegramMethod[bool]): """ - Use this method to create a new sticker set owned by a user. The bot will be able to edit the - sticker set thus created. You must use exactly one of the fields png_sticker or tgs_sticker. - Returns True on success. + Use this method to create a new sticker set owned by a user. The bot will be able to edit the sticker set thus created. You **must** use exactly one of the fields *png_sticker* or *tgs_sticker*. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#createnewstickerset """ @@ -23,25 +21,17 @@ class CreateNewStickerSet(TelegramMethod[bool]): user_id: int """User identifier of created sticker set owner""" name: str - """Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). Can - contain only english letters, digits and underscores. Must begin with a letter, can't - contain consecutive underscores and must end in '_by_'. is - case insensitive. 1-64 characters.""" + """Short name of sticker set, to be used in :code:`t.me/addstickers/` URLs (e.g., *animals*). Can contain only english letters, digits and underscores. Must begin with a letter, can't contain consecutive underscores and must end in *'_by_'*. ** is case insensitive. 1-64 characters.""" title: str """Sticker set title, 1-64 characters""" emojis: str """One or more emoji corresponding to the sticker""" png_sticker: Optional[Union[InputFile, str]] = None - """PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed - 512px, and either width or height must be exactly 512px. Pass a file_id as a String to send - a file that already exists on the Telegram servers, pass an HTTP URL as a String for - Telegram to get a file from the Internet, or upload a new one using multipart/form-data.""" + """**PNG** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. Pass a *file_id* as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `""" tgs_sticker: Optional[InputFile] = None - """TGS animation with the sticker, uploaded using multipart/form-data. See - https://core.telegram.org/animated_stickers#technical-requirements for technical - requirements""" + """**TGS** animation with the sticker, uploaded using multipart/form-data. See `https://core.telegram.org/animated_stickers#technical-requirements `_`https://core.telegram.org/animated_stickers#technical-requirements `_ for technical requirements""" contains_masks: Optional[bool] = None - """Pass True, if a set of mask stickers should be created""" + """Pass :code:`True`, if a set of mask stickers should be created""" mask_position: Optional[MaskPosition] = None """A JSON-serialized object for position where the mask should be placed on faces""" diff --git a/aiogram/api/methods/delete_chat_photo.py b/aiogram/methods/delete_chat_photo.py similarity index 75% rename from aiogram/api/methods/delete_chat_photo.py rename to aiogram/methods/delete_chat_photo.py index 8be35986..dac07e6f 100644 --- a/aiogram/api/methods/delete_chat_photo.py +++ b/aiogram/methods/delete_chat_photo.py @@ -10,9 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class DeleteChatPhoto(TelegramMethod[bool]): """ - Use this method to delete a chat photo. Photos can't be changed for private chats. The bot - must be an administrator in the chat for this to work and must have the appropriate admin - rights. Returns True on success. + Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletechatphoto """ @@ -20,8 +18,7 @@ class DeleteChatPhoto(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/delete_chat_sticker_set.py b/aiogram/methods/delete_chat_sticker_set.py similarity index 65% rename from aiogram/api/methods/delete_chat_sticker_set.py rename to aiogram/methods/delete_chat_sticker_set.py index 14801ec4..fb9f92d0 100644 --- a/aiogram/api/methods/delete_chat_sticker_set.py +++ b/aiogram/methods/delete_chat_sticker_set.py @@ -10,10 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class DeleteChatStickerSet(TelegramMethod[bool]): """ - Use this method to delete a group sticker set from a supergroup. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. Use the - field can_set_sticker_set optionally returned in getChat requests to check if the bot can use - this method. Returns True on success. + Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field *can_set_sticker_set* optionally returned in :class:`aiogram.methods.get_chat.GetChat` requests to check if the bot can use this method. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletechatstickerset """ @@ -21,8 +18,7 @@ class DeleteChatStickerSet(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup (in the format - @supergroupusername)""" + """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/delete_message.py b/aiogram/methods/delete_message.py similarity index 76% rename from aiogram/api/methods/delete_message.py rename to aiogram/methods/delete_message.py index da0edce1..7fe18d55 100644 --- a/aiogram/api/methods/delete_message.py +++ b/aiogram/methods/delete_message.py @@ -10,17 +10,23 @@ if TYPE_CHECKING: # pragma: no cover class DeleteMessage(TelegramMethod[bool]): """ - Use this method to delete a message, including service messages, with the following - limitations: + Use this method to delete a message, including service messages, with the following limitations: + - A message can only be deleted if it was sent less than 48 hours ago. + - A dice message in a private chat can only be deleted if it was sent more than 24 hours ago. + - Bots can delete outgoing messages in private chats, groups, and supergroups. + - Bots can delete incoming messages in private chats. - - Bots granted can_post_messages permissions can delete outgoing messages in channels. + + - Bots granted *can_post_messages* permissions can delete outgoing messages in channels. + - If the bot is an administrator of a group, it can delete any message there. - - If the bot has can_delete_messages permission in a supergroup or a channel, it can delete - any message there. - Returns True on success. + + - If the bot has *can_delete_messages* permission in a supergroup or a channel, it can delete any message there. + + Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletemessage """ @@ -28,8 +34,7 @@ class DeleteMessage(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" message_id: int """Identifier of the message to delete""" diff --git a/aiogram/api/methods/delete_sticker_from_set.py b/aiogram/methods/delete_sticker_from_set.py similarity index 94% rename from aiogram/api/methods/delete_sticker_from_set.py rename to aiogram/methods/delete_sticker_from_set.py index 30ac3d4b..25499a7e 100644 --- a/aiogram/api/methods/delete_sticker_from_set.py +++ b/aiogram/methods/delete_sticker_from_set.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class DeleteStickerFromSet(TelegramMethod[bool]): """ - Use this method to delete a sticker from a set created by the bot. Returns True on success. + Use this method to delete a sticker from a set created by the bot. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletestickerfromset """ diff --git a/aiogram/api/methods/delete_webhook.py b/aiogram/methods/delete_webhook.py similarity index 66% rename from aiogram/api/methods/delete_webhook.py rename to aiogram/methods/delete_webhook.py index 9242a634..4672645f 100644 --- a/aiogram/api/methods/delete_webhook.py +++ b/aiogram/methods/delete_webhook.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Dict +from typing import TYPE_CHECKING, Any, Dict, Optional from .base import Request, TelegramMethod @@ -10,14 +10,16 @@ if TYPE_CHECKING: # pragma: no cover class DeleteWebhook(TelegramMethod[bool]): """ - Use this method to remove webhook integration if you decide to switch back to getUpdates. - Returns True on success. Requires no parameters. + Use this method to remove webhook integration if you decide to switch back to :class:`aiogram.methods.get_updates.GetUpdates`. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#deletewebhook """ __returning__ = bool + drop_pending_updates: Optional[bool] = None + """Pass :code:`True` to drop all pending updates""" + def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/edit_message_caption.py b/aiogram/methods/edit_message_caption.py new file mode 100644 index 00000000..6e7a1661 --- /dev/null +++ b/aiogram/methods/edit_message_caption.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import UNSET, InlineKeyboardMarkup, Message, MessageEntity +from .base import Request, TelegramMethod, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class EditMessageCaption(TelegramMethod[Union[Message, bool]]): + """ + Use this method to edit captions of messages. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. + + Source: https://core.telegram.org/bots/api#editmessagecaption + """ + + __returning__ = Union[Message, bool] + + chat_id: Optional[Union[int, str]] = None + """Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + message_id: Optional[int] = None + """Required if *inline_message_id* is not specified. Identifier of the message to edit""" + inline_message_id: Optional[str] = None + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" + caption: Optional[str] = None + """New caption of the message, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the message caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """A JSON-serialized object for an `inline keyboard `_.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + return Request(method="editMessageCaption", data=data) diff --git a/aiogram/methods/edit_message_live_location.py b/aiogram/methods/edit_message_live_location.py new file mode 100644 index 00000000..99207cf3 --- /dev/null +++ b/aiogram/methods/edit_message_live_location.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Optional, Union + +from ..types import InlineKeyboardMarkup, Message +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class EditMessageLiveLocation(TelegramMethod[Union[Message, bool]]): + """ + Use this method to edit live location messages. A location can be edited until its *live_period* expires or editing is explicitly disabled by a call to :class:`aiogram.methods.stop_message_live_location.StopMessageLiveLocation`. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. + + Source: https://core.telegram.org/bots/api#editmessagelivelocation + """ + + __returning__ = Union[Message, bool] + + latitude: float + """Latitude of new location""" + longitude: float + """Longitude of new location""" + chat_id: Optional[Union[int, str]] = None + """Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + message_id: Optional[int] = None + """Required if *inline_message_id* is not specified. Identifier of the message to edit""" + inline_message_id: Optional[str] = None + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" + horizontal_accuracy: Optional[float] = None + """The radius of uncertainty for the location, measured in meters; 0-1500""" + heading: Optional[int] = None + """Direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.""" + proximity_alert_radius: Optional[int] = None + """Maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """A JSON-serialized object for a new `inline keyboard `_.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="editMessageLiveLocation", data=data) diff --git a/aiogram/api/methods/edit_message_media.py b/aiogram/methods/edit_message_media.py similarity index 54% rename from aiogram/api/methods/edit_message_media.py rename to aiogram/methods/edit_message_media.py index db8e344f..be34133c 100644 --- a/aiogram/api/methods/edit_message_media.py +++ b/aiogram/methods/edit_message_media.py @@ -11,12 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class EditMessageMedia(TelegramMethod[Union[Message, bool]]): """ - Use this method to edit animation, audio, document, photo, or video messages. If a message is - a part of a message album, then it can be edited only to a photo or a video. Otherwise, - message type can be changed arbitrarily. When inline message is edited, new file can't be - uploaded. Use previously uploaded file via its file_id or specify a URL. On success, if the - edited message was sent by the bot, the edited Message is returned, otherwise True is - returned. + Use this method to edit animation, audio, document, photo, or video messages. If a message is part of a message album, then it can be edited only to an audio for audio albums, only to a document for document albums and to a photo or a video otherwise. When an inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id or specify a URL. On success, if the edited message was sent by the bot, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#editmessagemedia """ @@ -26,19 +21,17 @@ class EditMessageMedia(TelegramMethod[Union[Message, bool]]): media: InputMedia """A JSON-serialized object for a new media content of the message""" chat_id: Optional[Union[int, str]] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat or - username of the target channel (in the format @channelusername)""" + """Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the message to edit""" + """Required if *inline_message_id* is not specified. Identifier of the message to edit""" inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for a new inline keyboard.""" + """A JSON-serialized object for a new `inline keyboard `_.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() prepare_parse_mode(bot, data["media"]) - files: Dict[str, InputFile] = {} prepare_media_file(data=data, files=files) diff --git a/aiogram/api/methods/edit_message_reply_markup.py b/aiogram/methods/edit_message_reply_markup.py similarity index 56% rename from aiogram/api/methods/edit_message_reply_markup.py rename to aiogram/methods/edit_message_reply_markup.py index b9973e40..eb16e43c 100644 --- a/aiogram/api/methods/edit_message_reply_markup.py +++ b/aiogram/methods/edit_message_reply_markup.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class EditMessageReplyMarkup(TelegramMethod[Union[Message, bool]]): """ - Use this method to edit only the reply markup of messages. On success, if edited message is - sent by the bot, the edited Message is returned, otherwise True is returned. + Use this method to edit only the reply markup of messages. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#editmessagereplymarkup """ @@ -20,14 +19,13 @@ class EditMessageReplyMarkup(TelegramMethod[Union[Message, bool]]): __returning__ = Union[Message, bool] chat_id: Optional[Union[int, str]] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat or - username of the target channel (in the format @channelusername)""" + """Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the message to edit""" + """Required if *inline_message_id* is not specified. Identifier of the message to edit""" inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for an inline keyboard.""" + """A JSON-serialized object for an `inline keyboard `_.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/edit_message_text.py b/aiogram/methods/edit_message_text.py new file mode 100644 index 00000000..9bd578ff --- /dev/null +++ b/aiogram/methods/edit_message_text.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import UNSET, InlineKeyboardMarkup, Message, MessageEntity +from .base import Request, TelegramMethod, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class EditMessageText(TelegramMethod[Union[Message, bool]]): + """ + Use this method to edit text and `game `_ messages. On success, if the edited message is not an inline message, the edited :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. + + Source: https://core.telegram.org/bots/api#editmessagetext + """ + + __returning__ = Union[Message, bool] + + text: str + """New text of the message, 1-4096 characters after entities parsing""" + chat_id: Optional[Union[int, str]] = None + """Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + message_id: Optional[int] = None + """Required if *inline_message_id* is not specified. Identifier of the message to edit""" + inline_message_id: Optional[str] = None + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the message text. See `formatting options `_ for more details.""" + entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in message text, which can be specified instead of *parse_mode*""" + disable_web_page_preview: Optional[bool] = None + """Disables link previews for links in this message""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """A JSON-serialized object for an `inline keyboard `_.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="entities" + ) + + return Request(method="editMessageText", data=data) diff --git a/aiogram/methods/export_chat_invite_link.py b/aiogram/methods/export_chat_invite_link.py new file mode 100644 index 00000000..5ee6b6d9 --- /dev/null +++ b/aiogram/methods/export_chat_invite_link.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Union + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class ExportChatInviteLink(TelegramMethod[str]): + """ + Use this method to generate a new invite link for a chat; any previously generated link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the new invite link as *String* on success. + + Note: Each administrator in a chat generates their own invite links. Bots can't use invite links generated by other administrators. If you want your bot to work with invite links, it will need to generate its own link using :class:`aiogram.methods.export_chat_invite_link.ExportChatInviteLink` — after this the link will become available to the bot via the :class:`aiogram.methods.get_chat.GetChat` method. If your bot needs to generate a new invite link replacing its previous one, use :class:`aiogram.methods.export_chat_invite_link.ExportChatInviteLink` again. + + Source: https://core.telegram.org/bots/api#exportchatinvitelink + """ + + __returning__ = str + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="exportChatInviteLink", data=data) diff --git a/aiogram/api/methods/forward_message.py b/aiogram/methods/forward_message.py similarity index 68% rename from aiogram/api/methods/forward_message.py rename to aiogram/methods/forward_message.py index 06f19956..24b68919 100644 --- a/aiogram/api/methods/forward_message.py +++ b/aiogram/methods/forward_message.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class ForwardMessage(TelegramMethod[Message]): """ - Use this method to forward messages of any kind. On success, the sent Message is returned. + Use this method to forward messages of any kind. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#forwardmessage """ @@ -19,15 +19,13 @@ class ForwardMessage(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" from_chat_id: Union[int, str] - """Unique identifier for the chat where the original message was sent (or channel username in - the format @channelusername)""" + """Unique identifier for the chat where the original message was sent (or channel username in the format :code:`@channelusername`)""" message_id: int - """Message identifier in the chat specified in from_chat_id""" + """Message identifier in the chat specified in *from_chat_id*""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/get_chat.py b/aiogram/methods/get_chat.py similarity index 73% rename from aiogram/api/methods/get_chat.py rename to aiogram/methods/get_chat.py index 6131c00d..ef84aa1e 100644 --- a/aiogram/api/methods/get_chat.py +++ b/aiogram/methods/get_chat.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetChat(TelegramMethod[Chat]): """ - Use this method to get up to date information about the chat (current name of the user for - one-on-one conversations, current username of a user, group or channel, etc.). Returns a Chat - object on success. + Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). Returns a :class:`aiogram.types.chat.Chat` object on success. Source: https://core.telegram.org/bots/api#getchat """ @@ -21,8 +19,7 @@ class GetChat(TelegramMethod[Chat]): __returning__ = Chat chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup or channel (in - the format @channelusername)""" + """Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`)""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/get_chat_administrators.py b/aiogram/methods/get_chat_administrators.py similarity index 68% rename from aiogram/api/methods/get_chat_administrators.py rename to aiogram/methods/get_chat_administrators.py index 584d6f9a..961fb959 100644 --- a/aiogram/api/methods/get_chat_administrators.py +++ b/aiogram/methods/get_chat_administrators.py @@ -11,10 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetChatAdministrators(TelegramMethod[List[ChatMember]]): """ - Use this method to get a list of administrators in a chat. On success, returns an Array of - ChatMember objects that contains information about all chat administrators except other bots. - If the chat is a group or a supergroup and no administrators were appointed, only the creator - will be returned. + Use this method to get a list of administrators in a chat. On success, returns an Array of :class:`aiogram.types.chat_member.ChatMember` objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned. Source: https://core.telegram.org/bots/api#getchatadministrators """ @@ -22,8 +19,7 @@ class GetChatAdministrators(TelegramMethod[List[ChatMember]]): __returning__ = List[ChatMember] chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup or channel (in - the format @channelusername)""" + """Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`)""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/get_chat_member.py b/aiogram/methods/get_chat_member.py similarity index 83% rename from aiogram/api/methods/get_chat_member.py rename to aiogram/methods/get_chat_member.py index 903fd803..cb21e1ff 100644 --- a/aiogram/api/methods/get_chat_member.py +++ b/aiogram/methods/get_chat_member.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetChatMember(TelegramMethod[ChatMember]): """ - Use this method to get information about a member of a chat. Returns a ChatMember object on - success. + Use this method to get information about a member of a chat. Returns a :class:`aiogram.types.chat_member.ChatMember` object on success. Source: https://core.telegram.org/bots/api#getchatmember """ @@ -20,8 +19,7 @@ class GetChatMember(TelegramMethod[ChatMember]): __returning__ = ChatMember chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup or channel (in - the format @channelusername)""" + """Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`)""" user_id: int """Unique identifier of the target user""" diff --git a/aiogram/api/methods/get_chat_members_count.py b/aiogram/methods/get_chat_members_count.py similarity index 87% rename from aiogram/api/methods/get_chat_members_count.py rename to aiogram/methods/get_chat_members_count.py index 9b830af0..2ec638f4 100644 --- a/aiogram/api/methods/get_chat_members_count.py +++ b/aiogram/methods/get_chat_members_count.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class GetChatMembersCount(TelegramMethod[int]): """ - Use this method to get the number of members in a chat. Returns Int on success. + Use this method to get the number of members in a chat. Returns *Int* on success. Source: https://core.telegram.org/bots/api#getchatmemberscount """ @@ -18,8 +18,7 @@ class GetChatMembersCount(TelegramMethod[int]): __returning__ = int chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup or channel (in - the format @channelusername)""" + """Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`)""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/get_file.py b/aiogram/methods/get_file.py new file mode 100644 index 00000000..57d84c3a --- /dev/null +++ b/aiogram/methods/get_file.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict + +from ..types import File +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class GetFile(TelegramMethod[File]): + """ + Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. On success, a :class:`aiogram.types.file.File` object is returned. The file can then be downloaded via the link :code:`https://api.telegram.org/file/bot/`, where :code:`` is taken from the response. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling :class:`aiogram.methods.get_file.GetFile` again. + **Note:** This function may not preserve the original file name and MIME type. You should save the file's MIME type and name (if available) when the File object is received. + + Source: https://core.telegram.org/bots/api#getfile + """ + + __returning__ = File + + file_id: str + """File identifier to get info about""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="getFile", data=data) diff --git a/aiogram/api/methods/get_game_high_scores.py b/aiogram/methods/get_game_high_scores.py similarity index 52% rename from aiogram/api/methods/get_game_high_scores.py rename to aiogram/methods/get_game_high_scores.py index c2dd0671..66427ed9 100644 --- a/aiogram/api/methods/get_game_high_scores.py +++ b/aiogram/methods/get_game_high_scores.py @@ -11,12 +11,9 @@ if TYPE_CHECKING: # pragma: no cover class GetGameHighScores(TelegramMethod[List[GameHighScore]]): """ - Use this method to get data for high score tables. Will return the score of the specified user - and several of their neighbors in a game. On success, returns an Array of GameHighScore - objects. - This method will currently return scores for the target user, plus two of their closest - neighbors on each side. Will also return the top three users if the user and his neighbors are - not among them. Please note that this behavior is subject to change. + Use this method to get data for high score tables. Will return the score of the specified user and several of their neighbors in a game. On success, returns an *Array* of :class:`aiogram.types.game_high_score.GameHighScore` objects. + + This method will currently return scores for the target user, plus two of their closest neighbors on each side. Will also return the top three users if the user and his neighbors are not among them. Please note that this behavior is subject to change. Source: https://core.telegram.org/bots/api#getgamehighscores """ @@ -26,11 +23,11 @@ class GetGameHighScores(TelegramMethod[List[GameHighScore]]): user_id: int """Target user id""" chat_id: Optional[int] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat""" + """Required if *inline_message_id* is not specified. Unique identifier for the target chat""" message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the sent message""" + """Required if *inline_message_id* is not specified. Identifier of the sent message""" inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/get_me.py b/aiogram/methods/get_me.py similarity index 83% rename from aiogram/api/methods/get_me.py rename to aiogram/methods/get_me.py index c9171d47..4b0c07c9 100644 --- a/aiogram/api/methods/get_me.py +++ b/aiogram/methods/get_me.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetMe(TelegramMethod[User]): """ - A simple method for testing your bot's auth token. Requires no parameters. Returns basic - information about the bot in form of a User object. + A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a :class:`aiogram.types.user.User` object. Source: https://core.telegram.org/bots/api#getme """ diff --git a/aiogram/api/methods/get_my_commands.py b/aiogram/methods/get_my_commands.py similarity index 85% rename from aiogram/api/methods/get_my_commands.py rename to aiogram/methods/get_my_commands.py index c748cb92..19adb9bd 100644 --- a/aiogram/api/methods/get_my_commands.py +++ b/aiogram/methods/get_my_commands.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetMyCommands(TelegramMethod[List[BotCommand]]): """ - Use this method to get the current list of the bot's commands. Requires no parameters. Returns - Array of BotCommand on success. + Use this method to get the current list of the bot's commands. Requires no parameters. Returns Array of :class:`aiogram.types.bot_command.BotCommand` on success. Source: https://core.telegram.org/bots/api#getmycommands """ diff --git a/aiogram/api/methods/get_sticker_set.py b/aiogram/methods/get_sticker_set.py similarity index 82% rename from aiogram/api/methods/get_sticker_set.py rename to aiogram/methods/get_sticker_set.py index 56e54577..30a8f35b 100644 --- a/aiogram/api/methods/get_sticker_set.py +++ b/aiogram/methods/get_sticker_set.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetStickerSet(TelegramMethod[StickerSet]): """ - Use this method to get a sticker set. On success, a StickerSet object is returned. + Use this method to get a sticker set. On success, a :class:`aiogram.types.sticker_set.StickerSet` object is returned. Source: https://core.telegram.org/bots/api#getstickerset """ diff --git a/aiogram/methods/get_updates.py b/aiogram/methods/get_updates.py new file mode 100644 index 00000000..e47dcbd9 --- /dev/null +++ b/aiogram/methods/get_updates.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional + +from ..types import Update +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class GetUpdates(TelegramMethod[List[Update]]): + """ + Use this method to receive incoming updates using long polling (`wiki `_). An Array of :class:`aiogram.types.update.Update` objects is returned. + + **Notes** + + **1.** This method will not work if an outgoing webhook is set up. + + **2.** In order to avoid getting duplicate updates, recalculate *offset* after each server response. + + Source: https://core.telegram.org/bots/api#getupdates + """ + + __returning__ = List[Update] + + offset: Optional[int] = None + """Identifier of the first update to be returned. Must be greater by one than the highest among the identifiers of previously received updates. By default, updates starting with the earliest unconfirmed update are returned. An update is considered confirmed as soon as :class:`aiogram.methods.get_updates.GetUpdates` is called with an *offset* higher than its *update_id*. The negative offset can be specified to retrieve updates starting from *-offset* update from the end of the updates queue. All previous updates will forgotten.""" + limit: Optional[int] = None + """Limits the number of updates to be retrieved. Values between 1-100 are accepted. Defaults to 100.""" + timeout: Optional[int] = None + """Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. Should be positive, short polling should be used for testing purposes only.""" + allowed_updates: Optional[List[str]] = None + """A JSON-serialized list of the update types you want your bot to receive. For example, specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of these types. See :class:`aiogram.types.update.Update` for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="getUpdates", data=data) diff --git a/aiogram/api/methods/get_user_profile_photos.py b/aiogram/methods/get_user_profile_photos.py similarity index 88% rename from aiogram/api/methods/get_user_profile_photos.py rename to aiogram/methods/get_user_profile_photos.py index bce33116..b37f8a0e 100644 --- a/aiogram/api/methods/get_user_profile_photos.py +++ b/aiogram/methods/get_user_profile_photos.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetUserProfilePhotos(TelegramMethod[UserProfilePhotos]): """ - Use this method to get a list of profile pictures for a user. Returns a UserProfilePhotos - object. + Use this method to get a list of profile pictures for a user. Returns a :class:`aiogram.types.user_profile_photos.UserProfilePhotos` object. Source: https://core.telegram.org/bots/api#getuserprofilephotos """ @@ -24,8 +23,7 @@ class GetUserProfilePhotos(TelegramMethod[UserProfilePhotos]): offset: Optional[int] = None """Sequential number of the first photo to be returned. By default, all photos are returned.""" limit: Optional[int] = None - """Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to - 100.""" + """Limits the number of photos to be retrieved. Values between 1-100 are accepted. Defaults to 100.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/get_webhook_info.py b/aiogram/methods/get_webhook_info.py similarity index 74% rename from aiogram/api/methods/get_webhook_info.py rename to aiogram/methods/get_webhook_info.py index 218569bb..60c8ef52 100644 --- a/aiogram/api/methods/get_webhook_info.py +++ b/aiogram/methods/get_webhook_info.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class GetWebhookInfo(TelegramMethod[WebhookInfo]): """ - Use this method to get current webhook status. Requires no parameters. On success, returns a - WebhookInfo object. If the bot is using getUpdates, will return an object with the url field - empty. + Use this method to get current webhook status. Requires no parameters. On success, returns a :class:`aiogram.types.webhook_info.WebhookInfo` object. If the bot is using :class:`aiogram.methods.get_updates.GetUpdates`, will return an object with the *url* field empty. Source: https://core.telegram.org/bots/api#getwebhookinfo """ diff --git a/aiogram/api/methods/kick_chat_member.py b/aiogram/methods/kick_chat_member.py similarity index 60% rename from aiogram/api/methods/kick_chat_member.py rename to aiogram/methods/kick_chat_member.py index 45880c4c..1c18497e 100644 --- a/aiogram/api/methods/kick_chat_member.py +++ b/aiogram/methods/kick_chat_member.py @@ -11,10 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class KickChatMember(TelegramMethod[bool]): """ - Use this method to kick a user from a group, a supergroup or a channel. In the case of - supergroups and channels, the user will not be able to return to the group on their own using - invite links, etc., unless unbanned first. The bot must be an administrator in the chat for - this to work and must have the appropriate admin rights. Returns True on success. + Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the chat on their own using invite links, etc., unless `unbanned `_ first. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#kickchatmember """ @@ -22,13 +19,11 @@ class KickChatMember(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target group or username of the target supergroup or channel (in - the format @channelusername)""" + """Unique identifier for the target group or username of the target supergroup or channel (in the format :code:`@channelusername`)""" user_id: int """Unique identifier of the target user""" until_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None - """Date when the user will be unbanned, unix time. If user is banned for more than 366 days or - less than 30 seconds from the current time they are considered to be banned forever""" + """Date when the user will be unbanned, unix time. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever. Applied for supergroups and channels only.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/leave_chat.py b/aiogram/methods/leave_chat.py similarity index 84% rename from aiogram/api/methods/leave_chat.py rename to aiogram/methods/leave_chat.py index 7af04143..f247caa0 100644 --- a/aiogram/api/methods/leave_chat.py +++ b/aiogram/methods/leave_chat.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class LeaveChat(TelegramMethod[bool]): """ - Use this method for your bot to leave a group, supergroup or channel. Returns True on success. + Use this method for your bot to leave a group, supergroup or channel. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#leavechat """ @@ -18,8 +18,7 @@ class LeaveChat(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup or channel (in - the format @channelusername)""" + """Unique identifier for the target chat or username of the target supergroup or channel (in the format :code:`@channelusername`)""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/log_out.py b/aiogram/methods/log_out.py new file mode 100644 index 00000000..c2bbe6d3 --- /dev/null +++ b/aiogram/methods/log_out.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class LogOut(TelegramMethod[bool]): + """ + Use this method to log out from the cloud Bot API server before launching the bot locally. You **must** log out the bot before running it locally, otherwise there is no guarantee that the bot will receive updates. After a successful call, you can immediately log in on a local server, but will not be able to log in back to the cloud Bot API server for 10 minutes. Returns :code:`True` on success. Requires no parameters. + + Source: https://core.telegram.org/bots/api#logout + """ + + __returning__ = bool + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="logOut", data=data) diff --git a/aiogram/api/methods/pin_chat_message.py b/aiogram/methods/pin_chat_message.py similarity index 55% rename from aiogram/api/methods/pin_chat_message.py rename to aiogram/methods/pin_chat_message.py index dbf0d013..51726dd5 100644 --- a/aiogram/api/methods/pin_chat_message.py +++ b/aiogram/methods/pin_chat_message.py @@ -10,9 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class PinChatMessage(TelegramMethod[bool]): """ - Use this method to pin a message in a group, a supergroup, or a channel. The bot must be an - administrator in the chat for this to work and must have the 'can_pin_messages' admin right in - the supergroup or 'can_edit_messages' admin right in the channel. Returns True on success. + Use this method to add a message to the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#pinchatmessage """ @@ -20,13 +18,11 @@ class PinChatMessage(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" message_id: int """Identifier of a message to pin""" disable_notification: Optional[bool] = None - """Pass True, if it is not necessary to send a notification to all chat members about the new - pinned message. Notifications are always disabled in channels.""" + """Pass :code:`True`, if it is not necessary to send a notification to all chat members about the new pinned message. Notifications are always disabled in channels and private chats.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/promote_chat_member.py b/aiogram/methods/promote_chat_member.py similarity index 73% rename from aiogram/api/methods/promote_chat_member.py rename to aiogram/methods/promote_chat_member.py index 1b4ca625..335dd522 100644 --- a/aiogram/api/methods/promote_chat_member.py +++ b/aiogram/methods/promote_chat_member.py @@ -10,9 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class PromoteChatMember(TelegramMethod[bool]): """ - Use this method to promote or demote a user in a supergroup or a channel. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. Pass - False for all boolean parameters to demote a user. Returns True on success. + Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Pass :code:`False` for all boolean parameters to demote a user. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#promotechatmember """ @@ -20,17 +18,17 @@ class PromoteChatMember(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" user_id: int """Unique identifier of the target user""" + is_anonymous: Optional[bool] = None + """Pass :code:`True`, if the administrator's presence in the chat is hidden""" can_change_info: Optional[bool] = None """Pass True, if the administrator can change chat title, photo and other settings""" can_post_messages: Optional[bool] = None """Pass True, if the administrator can create channel posts, channels only""" can_edit_messages: Optional[bool] = None - """Pass True, if the administrator can edit messages of other users and can pin messages, - channels only""" + """Pass True, if the administrator can edit messages of other users and can pin messages, channels only""" can_delete_messages: Optional[bool] = None """Pass True, if the administrator can delete messages of other users""" can_invite_users: Optional[bool] = None @@ -40,9 +38,7 @@ class PromoteChatMember(TelegramMethod[bool]): can_pin_messages: Optional[bool] = None """Pass True, if the administrator can pin messages, supergroups only""" can_promote_members: Optional[bool] = None - """Pass True, if the administrator can add new administrators with a subset of their own - privileges or demote administrators that he has promoted, directly or indirectly (promoted - by administrators that were appointed by him)""" + """Pass True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by him)""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/restrict_chat_member.py b/aiogram/methods/restrict_chat_member.py similarity index 67% rename from aiogram/api/methods/restrict_chat_member.py rename to aiogram/methods/restrict_chat_member.py index 9626e05a..dec75404 100644 --- a/aiogram/api/methods/restrict_chat_member.py +++ b/aiogram/methods/restrict_chat_member.py @@ -12,9 +12,7 @@ if TYPE_CHECKING: # pragma: no cover class RestrictChatMember(TelegramMethod[bool]): """ - Use this method to restrict a user in a supergroup. The bot must be an administrator in the - supergroup for this to work and must have the appropriate admin rights. Pass True for all - permissions to lift restrictions from a user. Returns True on success. + Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass :code:`True` for all permissions to lift restrictions from a user. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#restrictchatmember """ @@ -22,16 +20,13 @@ class RestrictChatMember(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup (in the format - @supergroupusername)""" + """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" user_id: int """Unique identifier of the target user""" permissions: ChatPermissions - """New user permissions""" + """A JSON-serialized object for new user permissions""" until_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None - """Date when restrictions will be lifted for the user, unix time. If user is restricted for - more than 366 days or less than 30 seconds from the current time, they are considered to be - restricted forever""" + """Date when restrictions will be lifted for the user, unix time. If user is restricted for more than 366 days or less than 30 seconds from the current time, they are considered to be restricted forever""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/send_animation.py b/aiogram/methods/send_animation.py new file mode 100644 index 00000000..2399ee0d --- /dev/null +++ b/aiogram/methods/send_animation.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + UNSET, + ForceReply, + InlineKeyboardMarkup, + InputFile, + Message, + MessageEntity, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from .base import Request, TelegramMethod, prepare_file, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendAnimation(TelegramMethod[Message]): + """ + Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future. + + Source: https://core.telegram.org/bots/api#sendanimation + """ + + __returning__ = Message + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + animation: Union[InputFile, str] + """Animation to send. Pass a file_id as String to send an animation that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an animation from the Internet, or upload a new animation using multipart/form-data. :ref:`More info on Sending Files » `""" + duration: Optional[int] = None + """Duration of sent animation in seconds""" + width: Optional[int] = None + """Animation width""" + height: Optional[int] = None + """Animation height""" + thumb: Optional[Union[InputFile, str]] = None + """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """Animation caption (may also be used when resending animation by *file_id*), 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the animation caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict(exclude={"animation", "thumb"}) + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + files: Dict[str, InputFile] = {} + prepare_file(data=data, files=files, name="animation", value=self.animation) + prepare_file(data=data, files=files, name="thumb", value=self.thumb) + + return Request(method="sendAnimation", data=data, files=files) diff --git a/aiogram/methods/send_audio.py b/aiogram/methods/send_audio.py new file mode 100644 index 00000000..58899f81 --- /dev/null +++ b/aiogram/methods/send_audio.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + UNSET, + ForceReply, + InlineKeyboardMarkup, + InputFile, + Message, + MessageEntity, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from .base import Request, TelegramMethod, prepare_file, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendAudio(TelegramMethod[Message]): + """ + Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .MP3 or .M4A format. On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future. + For sending voice messages, use the :class:`aiogram.methods.send_voice.SendVoice` method instead. + + Source: https://core.telegram.org/bots/api#sendaudio + """ + + __returning__ = Message + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + audio: Union[InputFile, str] + """Audio file to send. Pass a file_id as String to send an audio file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get an audio file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """Audio caption, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the audio caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + duration: Optional[int] = None + """Duration of the audio in seconds""" + performer: Optional[str] = None + """Performer""" + title: Optional[str] = None + """Track name""" + thumb: Optional[Union[InputFile, str]] = None + """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict(exclude={"audio", "thumb"}) + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + files: Dict[str, InputFile] = {} + prepare_file(data=data, files=files, name="audio", value=self.audio) + prepare_file(data=data, files=files, name="thumb", value=self.thumb) + + return Request(method="sendAudio", data=data, files=files) diff --git a/aiogram/methods/send_chat_action.py b/aiogram/methods/send_chat_action.py new file mode 100644 index 00000000..2db43414 --- /dev/null +++ b/aiogram/methods/send_chat_action.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Union + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendChatAction(TelegramMethod[bool]): + """ + Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). Returns :code:`True` on success. + + Example: The `ImageBot `_ needs some time to process a request and upload the image. Instead of sending a text message along the lines of 'Retrieving image, please wait…', the bot may use :class:`aiogram.methods.send_chat_action.SendChatAction` with *action* = *upload_photo*. The user will see a 'sending photo' status for the bot. + + We only recommend using this method when a response from the bot will take a **noticeable** amount of time to arrive. + + Source: https://core.telegram.org/bots/api#sendchataction + """ + + __returning__ = bool + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + action: str + """Type of action to broadcast. Choose one, depending on what the user is about to receive: *typing* for `text messages `_, *upload_photo* for `photos `_, *record_video* or *upload_video* for `videos `_, *record_voice* or *upload_voice* for `voice notes `_, *upload_document* for `general files `_, *find_location* for `location data `_, *record_video_note* or *upload_video_note* for `video notes `_.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="sendChatAction", data=data) diff --git a/aiogram/api/methods/send_contact.py b/aiogram/methods/send_contact.py similarity index 63% rename from aiogram/api/methods/send_contact.py rename to aiogram/methods/send_contact.py index 90c1fd7d..226a091d 100644 --- a/aiogram/api/methods/send_contact.py +++ b/aiogram/methods/send_contact.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: # pragma: no cover class SendContact(TelegramMethod[Message]): """ - Use this method to send phone contacts. On success, the sent Message is returned. + Use this method to send phone contacts. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendcontact """ @@ -25,8 +25,7 @@ class SendContact(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" phone_number: str """Contact's phone number""" first_name: str @@ -34,16 +33,17 @@ class SendContact(TelegramMethod[Message]): last_name: Optional[str] = None """Contact's last name""" vcard: Optional[str] = None - """Additional data about the contact in the form of a vCard, 0-2048 bytes""" + """Additional data about the contact in the form of a `vCard `_, 0-2048 bytes""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/send_dice.py b/aiogram/methods/send_dice.py similarity index 57% rename from aiogram/api/methods/send_dice.py rename to aiogram/methods/send_dice.py index 1b69fed4..ca8e0c12 100644 --- a/aiogram/api/methods/send_dice.py +++ b/aiogram/methods/send_dice.py @@ -17,8 +17,7 @@ if TYPE_CHECKING: # pragma: no cover class SendDice(TelegramMethod[Message]): """ - Use this method to send an animated emoji that will display a random value. On success, the - sent Message is returned. + Use this method to send an animated emoji that will display a random value. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#senddice """ @@ -26,20 +25,19 @@ class SendDice(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" emoji: Optional[str] = None - """Emoji on which the dice throw animation is based. Currently, must be one of '', '', or ''. - Dice can have values 1-6 for '' and '', and values 1-5 for ''. Defaults to ''""" + """Emoji on which the dice throw animation is based. Currently, must be one of '🎲', '🎯', '🏀', '⚽', or '🎰'. Dice can have values 1-6 for '🎲' and '🎯', values 1-5 for '🏀' and '⚽', and values 1-64 for '🎰'. Defaults to '🎲'""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/send_document.py b/aiogram/methods/send_document.py new file mode 100644 index 00000000..5cb155c2 --- /dev/null +++ b/aiogram/methods/send_document.py @@ -0,0 +1,66 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + UNSET, + ForceReply, + InlineKeyboardMarkup, + InputFile, + Message, + MessageEntity, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from .base import Request, TelegramMethod, prepare_file, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendDocument(TelegramMethod[Message]): + """ + Use this method to send general files. On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future. + + Source: https://core.telegram.org/bots/api#senddocument + """ + + __returning__ = Message + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + document: Union[InputFile, str] + """File to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `""" + thumb: Optional[Union[InputFile, str]] = None + """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """Document caption (may also be used when resending documents by *file_id*), 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the document caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + disable_content_type_detection: Optional[bool] = None + """Disables automatic server-side content type detection for files uploaded using multipart/form-data""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict(exclude={"document", "thumb"}) + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + files: Dict[str, InputFile] = {} + prepare_file(data=data, files=files, name="document", value=self.document) + prepare_file(data=data, files=files, name="thumb", value=self.thumb) + + return Request(method="sendDocument", data=data, files=files) diff --git a/aiogram/api/methods/send_game.py b/aiogram/methods/send_game.py similarity index 55% rename from aiogram/api/methods/send_game.py rename to aiogram/methods/send_game.py index 15257073..690bf101 100644 --- a/aiogram/api/methods/send_game.py +++ b/aiogram/methods/send_game.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class SendGame(TelegramMethod[Message]): """ - Use this method to send a game. On success, the sent Message is returned. + Use this method to send a game. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendgame """ @@ -21,15 +21,15 @@ class SendGame(TelegramMethod[Message]): chat_id: int """Unique identifier for the target chat""" game_short_name: str - """Short name of the game, serves as the unique identifier for the game. Set up your games via - Botfather.""" + """Short name of the game, serves as the unique identifier for the game. Set up your games via `Botfather `_.""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for an inline keyboard. If empty, one 'Play game_title' button - will be shown. If not empty, the first button must launch the game.""" + """A JSON-serialized object for an `inline keyboard `_. If empty, one 'Play game_title' button will be shown. If not empty, the first button must launch the game.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/send_invoice.py b/aiogram/methods/send_invoice.py new file mode 100644 index 00000000..99bacb6c --- /dev/null +++ b/aiogram/methods/send_invoice.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional + +from ..types import InlineKeyboardMarkup, LabeledPrice, Message +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendInvoice(TelegramMethod[Message]): + """ + Use this method to send invoices. On success, the sent :class:`aiogram.types.message.Message` is returned. + + Source: https://core.telegram.org/bots/api#sendinvoice + """ + + __returning__ = Message + + chat_id: int + """Unique identifier for the target private chat""" + title: str + """Product name, 1-32 characters""" + description: str + """Product description, 1-255 characters""" + payload: str + """Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.""" + provider_token: str + """Payments provider token, obtained via `Botfather `_""" + start_parameter: str + """Unique deep-linking parameter that can be used to generate this invoice when used as a start parameter""" + currency: str + """Three-letter ISO 4217 currency code, see `more on currencies `_""" + prices: List[LabeledPrice] + """Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)""" + provider_data: Optional[str] = None + """A JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.""" + photo_url: Optional[str] = None + """URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.""" + photo_size: Optional[int] = None + """Photo size""" + photo_width: Optional[int] = None + """Photo width""" + photo_height: Optional[int] = None + """Photo height""" + need_name: Optional[bool] = None + """Pass :code:`True`, if you require the user's full name to complete the order""" + need_phone_number: Optional[bool] = None + """Pass :code:`True`, if you require the user's phone number to complete the order""" + need_email: Optional[bool] = None + """Pass :code:`True`, if you require the user's email address to complete the order""" + need_shipping_address: Optional[bool] = None + """Pass :code:`True`, if you require the user's shipping address to complete the order""" + send_phone_number_to_provider: Optional[bool] = None + """Pass :code:`True`, if user's phone number should be sent to provider""" + send_email_to_provider: Optional[bool] = None + """Pass :code:`True`, if user's email address should be sent to provider""" + is_flexible: Optional[bool] = None + """Pass :code:`True`, if the final price depends on the shipping method""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """A JSON-serialized object for an `inline keyboard `_. If empty, one 'Pay :code:`total price`' button will be shown. If not empty, the first button must be a Pay button.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="sendInvoice", data=data) diff --git a/aiogram/api/methods/send_location.py b/aiogram/methods/send_location.py similarity index 51% rename from aiogram/api/methods/send_location.py rename to aiogram/methods/send_location.py index 76cdda91..f9646468 100644 --- a/aiogram/api/methods/send_location.py +++ b/aiogram/methods/send_location.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: # pragma: no cover class SendLocation(TelegramMethod[Message]): """ - Use this method to send point on the map. On success, the sent Message is returned. + Use this method to send point on the map. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendlocation """ @@ -25,24 +25,29 @@ class SendLocation(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" latitude: float """Latitude of the location""" longitude: float """Longitude of the location""" + horizontal_accuracy: Optional[float] = None + """The radius of uncertainty for the location, measured in meters; 0-1500""" live_period: Optional[int] = None - """Period in seconds for which the location will be updated (see Live Locations, should be - between 60 and 86400.""" + """Period in seconds for which the location will be updated (see `Live Locations `_, should be between 60 and 86400.""" + heading: Optional[int] = None + """For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.""" + proximity_alert_radius: Optional[int] = None + """For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/send_media_group.py b/aiogram/methods/send_media_group.py new file mode 100644 index 00000000..46da358e --- /dev/null +++ b/aiogram/methods/send_media_group.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + InputFile, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + Message, +) +from .base import Request, TelegramMethod, prepare_input_media, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendMediaGroup(TelegramMethod[List[Message]]): + """ + Use this method to send a group of photos, videos, documents or audios as an album. Documents and audio files can be only grouped in an album with messages of the same type. On success, an array of `Messages `_ that were sent is returned. + + Source: https://core.telegram.org/bots/api#sendmediagroup + """ + + __returning__ = List[Message] + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + media: List[Union[InputMediaAudio, InputMediaDocument, InputMediaPhoto, InputMediaVideo]] + """A JSON-serialized array describing messages to be sent, must include 2-10 items""" + disable_notification: Optional[bool] = None + """Sends messages `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the messages are a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + prepare_parse_mode(bot, data["media"]) + + files: Dict[str, InputFile] = {} + prepare_input_media(data, files) + + return Request(method="sendMediaGroup", data=data, files=files) diff --git a/aiogram/api/methods/send_message.py b/aiogram/methods/send_message.py similarity index 53% rename from aiogram/api/methods/send_message.py rename to aiogram/methods/send_message.py index 9dbeaa16..bbacb6af 100644 --- a/aiogram/api/methods/send_message.py +++ b/aiogram/methods/send_message.py @@ -1,12 +1,13 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Dict, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union from ..types import ( UNSET, ForceReply, InlineKeyboardMarkup, Message, + MessageEntity, ReplyKeyboardMarkup, ReplyKeyboardRemove, ) @@ -18,7 +19,7 @@ if TYPE_CHECKING: # pragma: no cover class SendMessage(TelegramMethod[Message]): """ - Use this method to send text messages. On success, the sent Message is returned. + Use this method to send text messages. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendmessage """ @@ -26,26 +27,31 @@ class SendMessage(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" text: str """Text of the message to be sent, 1-4096 characters after entities parsing""" parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the message text. See formatting options for more details.""" + """Mode for parsing entities in the message text. See `formatting options `_ for more details.""" + entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in message text, which can be specified instead of *parse_mode*""" disable_web_page_preview: Optional[bool] = None """Disables link previews for links in this message""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() - prepare_parse_mode(bot, data) + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="entities" + ) return Request(method="sendMessage", data=data) diff --git a/aiogram/methods/send_photo.py b/aiogram/methods/send_photo.py new file mode 100644 index 00000000..46b2a808 --- /dev/null +++ b/aiogram/methods/send_photo.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + UNSET, + ForceReply, + InlineKeyboardMarkup, + InputFile, + Message, + MessageEntity, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from .base import Request, TelegramMethod, prepare_file, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendPhoto(TelegramMethod[Message]): + """ + Use this method to send photos. On success, the sent :class:`aiogram.types.message.Message` is returned. + + Source: https://core.telegram.org/bots/api#sendphoto + """ + + __returning__ = Message + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + photo: Union[InputFile, str] + """Photo to send. Pass a file_id as String to send a photo that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a photo from the Internet, or upload a new photo using multipart/form-data. The photo must be at most 10 MB in size. The photo's width and height must not exceed 10000 in total. Width and height ratio must be at most 20. :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """Photo caption (may also be used when resending photos by *file_id*), 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the photo caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict(exclude={"photo"}) + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + files: Dict[str, InputFile] = {} + prepare_file(data=data, files=files, name="photo", value=self.photo) + + return Request(method="sendPhoto", data=data, files=files) diff --git a/aiogram/api/methods/send_poll.py b/aiogram/methods/send_poll.py similarity index 54% rename from aiogram/api/methods/send_poll.py rename to aiogram/methods/send_poll.py index 35fa45be..c9a6fdd0 100644 --- a/aiogram/api/methods/send_poll.py +++ b/aiogram/methods/send_poll.py @@ -8,6 +8,7 @@ from ..types import ( ForceReply, InlineKeyboardMarkup, Message, + MessageEntity, ReplyKeyboardMarkup, ReplyKeyboardRemove, ) @@ -19,7 +20,7 @@ if TYPE_CHECKING: # pragma: no cover class SendPoll(TelegramMethod[Message]): """ - Use this method to send a native poll. On success, the sent Message is returned. + Use this method to send a native poll. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendpoll """ @@ -27,46 +28,50 @@ class SendPoll(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" question: str - """Poll question, 1-255 characters""" + """Poll question, 1-300 characters""" options: List[str] """A JSON-serialized list of answer options, 2-10 strings 1-100 characters each""" is_anonymous: Optional[bool] = None - """True, if the poll needs to be anonymous, defaults to True""" + """True, if the poll needs to be anonymous, defaults to :code:`True`""" type: Optional[str] = None """Poll type, 'quiz' or 'regular', defaults to 'regular'""" allows_multiple_answers: Optional[bool] = None - """True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to - False""" + """True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to :code:`False`""" correct_option_id: Optional[int] = None """0-based identifier of the correct answer option, required for polls in quiz mode""" explanation: Optional[str] = None - """Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a - quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing""" + """Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing""" explanation_parse_mode: Optional[str] = UNSET - """Mode for parsing entities in the explanation. See formatting options for more details.""" + """Mode for parsing entities in the explanation. See `formatting options `_ for more details.""" + explanation_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the poll explanation, which can be specified instead of *parse_mode*""" open_period: Optional[int] = None - """Amount of time in seconds the poll will be active after creation, 5-600. Can't be used - together with close_date.""" + """Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*.""" close_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None - """Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least - 5 and no more than 600 seconds in the future. Can't be used together with open_period.""" + """Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*.""" is_closed: Optional[bool] = None - """Pass True, if the poll needs to be immediately closed. This can be useful for poll preview.""" + """Pass :code:`True`, if the poll needs to be immediately closed. This can be useful for poll preview.""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() - prepare_parse_mode(bot, data, parse_mode_property="explanation_parse_mode") + + prepare_parse_mode( + bot, + data, + parse_mode_property="explanation_parse_mode", + entities_property="explanation_entities", + ) return Request(method="sendPoll", data=data) diff --git a/aiogram/api/methods/send_sticker.py b/aiogram/methods/send_sticker.py similarity index 56% rename from aiogram/api/methods/send_sticker.py rename to aiogram/methods/send_sticker.py index a2adbffa..05f0cf8d 100644 --- a/aiogram/api/methods/send_sticker.py +++ b/aiogram/methods/send_sticker.py @@ -18,8 +18,7 @@ if TYPE_CHECKING: # pragma: no cover class SendSticker(TelegramMethod[Message]): """ - Use this method to send static .WEBP or animated .TGS stickers. On success, the sent Message - is returned. + Use this method to send static .WEBP or `animated `_ .TGS stickers. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendsticker """ @@ -27,21 +26,19 @@ class SendSticker(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" sticker: Union[InputFile, str] - """Sticker to send. Pass a file_id as String to send a file that exists on the Telegram - servers (recommended), pass an HTTP URL as a String for Telegram to get a .WEBP file from - the Internet, or upload a new one using multipart/form-data.""" + """Sticker to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a .WEBP file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict(exclude={"sticker"}) diff --git a/aiogram/api/methods/send_venue.py b/aiogram/methods/send_venue.py similarity index 59% rename from aiogram/api/methods/send_venue.py rename to aiogram/methods/send_venue.py index e81455e4..cdadf416 100644 --- a/aiogram/api/methods/send_venue.py +++ b/aiogram/methods/send_venue.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: # pragma: no cover class SendVenue(TelegramMethod[Message]): """ - Use this method to send information about a venue. On success, the sent Message is returned. + Use this method to send information about a venue. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendvenue """ @@ -25,8 +25,7 @@ class SendVenue(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" latitude: float """Latitude of the venue""" longitude: float @@ -38,17 +37,21 @@ class SendVenue(TelegramMethod[Message]): foursquare_id: Optional[str] = None """Foursquare identifier of the venue""" foursquare_type: Optional[str] = None - """Foursquare type of the venue, if known. (For example, 'arts_entertainment/default', - 'arts_entertainment/aquarium' or 'food/icecream'.)""" + """Foursquare type of the venue, if known. (For example, 'arts_entertainment/default', 'arts_entertainment/aquarium' or 'food/icecream'.)""" + google_place_id: Optional[str] = None + """Google Places identifier of the venue""" + google_place_type: Optional[str] = None + """Google Places type of the venue. (See `supported types `_.)""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/send_video.py b/aiogram/methods/send_video.py new file mode 100644 index 00000000..0105beaa --- /dev/null +++ b/aiogram/methods/send_video.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + UNSET, + ForceReply, + InlineKeyboardMarkup, + InputFile, + Message, + MessageEntity, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from .base import Request, TelegramMethod, prepare_file, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendVideo(TelegramMethod[Message]): + """ + Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as :class:`aiogram.types.document.Document`). On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future. + + Source: https://core.telegram.org/bots/api#sendvideo + """ + + __returning__ = Message + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + video: Union[InputFile, str] + """Video to send. Pass a file_id as String to send a video that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a video from the Internet, or upload a new video using multipart/form-data. :ref:`More info on Sending Files » `""" + duration: Optional[int] = None + """Duration of sent video in seconds""" + width: Optional[int] = None + """Video width""" + height: Optional[int] = None + """Video height""" + thumb: Optional[Union[InputFile, str]] = None + """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """Video caption (may also be used when resending videos by *file_id*), 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the video caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + supports_streaming: Optional[bool] = None + """Pass :code:`True`, if the uploaded video is suitable for streaming""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict(exclude={"video", "thumb"}) + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + files: Dict[str, InputFile] = {} + prepare_file(data=data, files=files, name="video", value=self.video) + prepare_file(data=data, files=files, name="thumb", value=self.thumb) + + return Request(method="sendVideo", data=data, files=files) diff --git a/aiogram/api/methods/send_video_note.py b/aiogram/methods/send_video_note.py similarity index 52% rename from aiogram/api/methods/send_video_note.py rename to aiogram/methods/send_video_note.py index f3b75574..4e475222 100644 --- a/aiogram/api/methods/send_video_note.py +++ b/aiogram/methods/send_video_note.py @@ -18,8 +18,7 @@ if TYPE_CHECKING: # pragma: no cover class SendVideoNote(TelegramMethod[Message]): """ - As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use - this method to send video messages. On success, the sent Message is returned. + As of `v.4.0 `_, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. On success, the sent :class:`aiogram.types.message.Message` is returned. Source: https://core.telegram.org/bots/api#sendvideonote """ @@ -27,32 +26,25 @@ class SendVideoNote(TelegramMethod[Message]): __returning__ = Message chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" video_note: Union[InputFile, str] - """Video note to send. Pass a file_id as String to send a video note that exists on the - Telegram servers (recommended) or upload a new video using multipart/form-data.. Sending - video notes by a URL is currently unsupported""" + """Video note to send. Pass a file_id as String to send a video note that exists on the Telegram servers (recommended) or upload a new video using multipart/form-data. :ref:`More info on Sending Files » `. Sending video notes by a URL is currently unsupported""" duration: Optional[int] = None """Duration of sent video in seconds""" length: Optional[int] = None """Video width and height, i.e. diameter of the video message""" thumb: Optional[Union[InputFile, str]] = None - """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is - supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. - A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded - using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new - file, so you can pass 'attach://' if the thumbnail was uploaded using - multipart/form-data under .""" + """Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" disable_notification: Optional[bool] = None - """Sends the message silently. Users will receive a notification with no sound.""" + """Sends the message `silently `_. Users will receive a notification with no sound.""" reply_to_message_id: Optional[int] = None """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" reply_markup: Optional[ Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] ] = None - """Additional interface options. A JSON-serialized object for an inline keyboard, custom reply - keyboard, instructions to remove reply keyboard or to force a reply from the user.""" + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict(exclude={"video_note", "thumb"}) diff --git a/aiogram/methods/send_voice.py b/aiogram/methods/send_voice.py new file mode 100644 index 00000000..abd16217 --- /dev/null +++ b/aiogram/methods/send_voice.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from ..types import ( + UNSET, + ForceReply, + InlineKeyboardMarkup, + InputFile, + Message, + MessageEntity, + ReplyKeyboardMarkup, + ReplyKeyboardRemove, +) +from .base import Request, TelegramMethod, prepare_file, prepare_parse_mode + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SendVoice(TelegramMethod[Message]): + """ + Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .OGG file encoded with OPUS (other formats may be sent as :class:`aiogram.types.audio.Audio` or :class:`aiogram.types.document.Document`). On success, the sent :class:`aiogram.types.message.Message` is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future. + + Source: https://core.telegram.org/bots/api#sendvoice + """ + + __returning__ = Message + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + voice: Union[InputFile, str] + """Audio file to send. Pass a file_id as String to send a file that exists on the Telegram servers (recommended), pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """Voice message caption, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """Mode for parsing entities in the voice message caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + duration: Optional[int] = None + """Duration of the voice message in seconds""" + disable_notification: Optional[bool] = None + """Sends the message `silently `_. Users will receive a notification with no sound.""" + reply_to_message_id: Optional[int] = None + """If the message is a reply, ID of the original message""" + allow_sending_without_reply: Optional[bool] = None + """Pass :code:`True`, if the message should be sent even if the specified replied-to message is not found""" + reply_markup: Optional[ + Union[InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply] + ] = None + """Additional interface options. A JSON-serialized object for an `inline keyboard `_, `custom reply keyboard `_, instructions to remove reply keyboard or to force a reply from the user.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict(exclude={"voice"}) + + prepare_parse_mode( + bot, data, parse_mode_property="parse_mode", entities_property="caption_entities" + ) + + files: Dict[str, InputFile] = {} + prepare_file(data=data, files=files, name="voice", value=self.voice) + + return Request(method="sendVoice", data=data, files=files) diff --git a/aiogram/api/methods/set_chat_administrator_custom_title.py b/aiogram/methods/set_chat_administrator_custom_title.py similarity index 86% rename from aiogram/api/methods/set_chat_administrator_custom_title.py rename to aiogram/methods/set_chat_administrator_custom_title.py index e4929c51..cb570940 100644 --- a/aiogram/api/methods/set_chat_administrator_custom_title.py +++ b/aiogram/methods/set_chat_administrator_custom_title.py @@ -10,8 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class SetChatAdministratorCustomTitle(TelegramMethod[bool]): """ - Use this method to set a custom title for an administrator in a supergroup promoted by the - bot. Returns True on success. + Use this method to set a custom title for an administrator in a supergroup promoted by the bot. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatadministratorcustomtitle """ @@ -19,8 +18,7 @@ class SetChatAdministratorCustomTitle(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup (in the format - @supergroupusername)""" + """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" user_id: int """Unique identifier of the target user""" custom_title: str diff --git a/aiogram/api/methods/set_chat_description.py b/aiogram/methods/set_chat_description.py similarity index 77% rename from aiogram/api/methods/set_chat_description.py rename to aiogram/methods/set_chat_description.py index 2a7ac937..109aa270 100644 --- a/aiogram/api/methods/set_chat_description.py +++ b/aiogram/methods/set_chat_description.py @@ -10,9 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class SetChatDescription(TelegramMethod[bool]): """ - Use this method to change the description of a group, a supergroup or a channel. The bot must - be an administrator in the chat for this to work and must have the appropriate admin rights. - Returns True on success. + Use this method to change the description of a group, a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatdescription """ @@ -20,8 +18,7 @@ class SetChatDescription(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" description: Optional[str] = None """New chat description, 0-255 characters""" diff --git a/aiogram/api/methods/set_chat_permissions.py b/aiogram/methods/set_chat_permissions.py similarity index 76% rename from aiogram/api/methods/set_chat_permissions.py rename to aiogram/methods/set_chat_permissions.py index f4739c5c..75181e88 100644 --- a/aiogram/api/methods/set_chat_permissions.py +++ b/aiogram/methods/set_chat_permissions.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class SetChatPermissions(TelegramMethod[bool]): """ - Use this method to set default chat permissions for all members. The bot must be an - administrator in the group or a supergroup for this to work and must have the - can_restrict_members admin rights. Returns True on success. + Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work and must have the *can_restrict_members* admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatpermissions """ @@ -21,8 +19,7 @@ class SetChatPermissions(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup (in the format - @supergroupusername)""" + """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" permissions: ChatPermissions """New default chat permissions""" diff --git a/aiogram/api/methods/set_chat_photo.py b/aiogram/methods/set_chat_photo.py similarity index 79% rename from aiogram/api/methods/set_chat_photo.py rename to aiogram/methods/set_chat_photo.py index 94e8cf55..2aaa6cca 100644 --- a/aiogram/api/methods/set_chat_photo.py +++ b/aiogram/methods/set_chat_photo.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class SetChatPhoto(TelegramMethod[bool]): """ - Use this method to set a new profile photo for the chat. Photos can't be changed for private - chats. The bot must be an administrator in the chat for this to work and must have the - appropriate admin rights. Returns True on success. + Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatphoto """ @@ -21,8 +19,7 @@ class SetChatPhoto(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" photo: InputFile """New chat photo, uploaded using multipart/form-data""" diff --git a/aiogram/api/methods/set_chat_sticker_set.py b/aiogram/methods/set_chat_sticker_set.py similarity index 68% rename from aiogram/api/methods/set_chat_sticker_set.py rename to aiogram/methods/set_chat_sticker_set.py index 7f37c7ff..91a8896b 100644 --- a/aiogram/api/methods/set_chat_sticker_set.py +++ b/aiogram/methods/set_chat_sticker_set.py @@ -10,10 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class SetChatStickerSet(TelegramMethod[bool]): """ - Use this method to set a new group sticker set for a supergroup. The bot must be an - administrator in the chat for this to work and must have the appropriate admin rights. Use the - field can_set_sticker_set optionally returned in getChat requests to check if the bot can use - this method. Returns True on success. + Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field *can_set_sticker_set* optionally returned in :class:`aiogram.methods.get_chat.GetChat` requests to check if the bot can use this method. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchatstickerset """ @@ -21,8 +18,7 @@ class SetChatStickerSet(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target supergroup (in the format - @supergroupusername)""" + """Unique identifier for the target chat or username of the target supergroup (in the format :code:`@supergroupusername`)""" sticker_set_name: str """Name of the sticker set to be set as the group sticker set""" diff --git a/aiogram/api/methods/set_chat_title.py b/aiogram/methods/set_chat_title.py similarity index 75% rename from aiogram/api/methods/set_chat_title.py rename to aiogram/methods/set_chat_title.py index 4b3bd8b4..f86ace4f 100644 --- a/aiogram/api/methods/set_chat_title.py +++ b/aiogram/methods/set_chat_title.py @@ -10,9 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class SetChatTitle(TelegramMethod[bool]): """ - Use this method to change the title of a chat. Titles can't be changed for private chats. The - bot must be an administrator in the chat for this to work and must have the appropriate admin - rights. Returns True on success. + Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setchattitle """ @@ -20,8 +18,7 @@ class SetChatTitle(TelegramMethod[bool]): __returning__ = bool chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" title: str """New chat title, 1-255 characters""" diff --git a/aiogram/api/methods/set_game_score.py b/aiogram/methods/set_game_score.py similarity index 61% rename from aiogram/api/methods/set_game_score.py rename to aiogram/methods/set_game_score.py index 41d3f054..9965dced 100644 --- a/aiogram/api/methods/set_game_score.py +++ b/aiogram/methods/set_game_score.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class SetGameScore(TelegramMethod[Union[Message, bool]]): """ - Use this method to set the score of the specified user in a game. On success, if the message - was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if - the new score is not greater than the user's current score in the chat and force is False. + Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited :class:`aiogram.types.message.Message`, otherwise returns :code:`True`. Returns an error, if the new score is not greater than the user's current score in the chat and *force* is :code:`False`. Source: https://core.telegram.org/bots/api#setgamescore """ @@ -25,17 +23,15 @@ class SetGameScore(TelegramMethod[Union[Message, bool]]): score: int """New score, must be non-negative""" force: Optional[bool] = None - """Pass True, if the high score is allowed to decrease. This can be useful when fixing - mistakes or banning cheaters""" + """Pass True, if the high score is allowed to decrease. This can be useful when fixing mistakes or banning cheaters""" disable_edit_message: Optional[bool] = None - """Pass True, if the game message should not be automatically edited to include the current - scoreboard""" + """Pass True, if the game message should not be automatically edited to include the current scoreboard""" chat_id: Optional[int] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat""" + """Required if *inline_message_id* is not specified. Unique identifier for the target chat""" message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the sent message""" + """Required if *inline_message_id* is not specified. Identifier of the sent message""" inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/set_my_commands.py b/aiogram/methods/set_my_commands.py similarity index 87% rename from aiogram/api/methods/set_my_commands.py rename to aiogram/methods/set_my_commands.py index f7fd7b9a..a739b228 100644 --- a/aiogram/api/methods/set_my_commands.py +++ b/aiogram/methods/set_my_commands.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class SetMyCommands(TelegramMethod[bool]): """ - Use this method to change the list of the bot's commands. Returns True on success. + Use this method to change the list of the bot's commands. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setmycommands """ @@ -19,8 +19,7 @@ class SetMyCommands(TelegramMethod[bool]): __returning__ = bool commands: List[BotCommand] - """A JSON-serialized list of bot commands to be set as the list of the bot's commands. At most - 100 commands can be specified.""" + """A JSON-serialized list of bot commands to be set as the list of the bot's commands. At most 100 commands can be specified.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/set_passport_data_errors.py b/aiogram/methods/set_passport_data_errors.py similarity index 62% rename from aiogram/api/methods/set_passport_data_errors.py rename to aiogram/methods/set_passport_data_errors.py index 280187a9..e3c215ce 100644 --- a/aiogram/api/methods/set_passport_data_errors.py +++ b/aiogram/methods/set_passport_data_errors.py @@ -11,13 +11,8 @@ if TYPE_CHECKING: # pragma: no cover class SetPassportDataErrors(TelegramMethod[bool]): """ - Informs a user that some of the Telegram Passport elements they provided contains errors. The - user will not be able to re-submit their Passport to you until the errors are fixed (the - contents of the field for which you returned the error must change). Returns True on success. - Use this if the data submitted by the user doesn't satisfy the standards your service requires - for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, - a scan shows evidence of tampering, etc. Supply some details in the error message to make sure - the user knows how to correct the issues. + Informs a user that some of the Telegram Passport elements they provided contains errors. The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for which you returned the error must change). Returns :code:`True` on success. + Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues. Source: https://core.telegram.org/bots/api#setpassportdataerrors """ diff --git a/aiogram/api/methods/set_sticker_position_in_set.py b/aiogram/methods/set_sticker_position_in_set.py similarity index 92% rename from aiogram/api/methods/set_sticker_position_in_set.py rename to aiogram/methods/set_sticker_position_in_set.py index 54c27bfd..8c60c0ea 100644 --- a/aiogram/api/methods/set_sticker_position_in_set.py +++ b/aiogram/methods/set_sticker_position_in_set.py @@ -10,8 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class SetStickerPositionInSet(TelegramMethod[bool]): """ - Use this method to move a sticker in a set created by the bot to a specific position. Returns - True on success. + Use this method to move a sticker in a set created by the bot to a specific position. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setstickerpositioninset """ diff --git a/aiogram/api/methods/set_sticker_set_thumb.py b/aiogram/methods/set_sticker_set_thumb.py similarity index 50% rename from aiogram/api/methods/set_sticker_set_thumb.py rename to aiogram/methods/set_sticker_set_thumb.py index 67d3e50e..ab97d663 100644 --- a/aiogram/api/methods/set_sticker_set_thumb.py +++ b/aiogram/methods/set_sticker_set_thumb.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class SetStickerSetThumb(TelegramMethod[bool]): """ - Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for - animated sticker sets only. Returns True on success. + Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns :code:`True` on success. Source: https://core.telegram.org/bots/api#setstickersetthumb """ @@ -24,13 +23,7 @@ class SetStickerSetThumb(TelegramMethod[bool]): user_id: int """User identifier of the sticker set owner""" thumb: Optional[Union[InputFile, str]] = None - """A PNG image with the thumbnail, must be up to 128 kilobytes in size and have width and - height exactly 100px, or a TGS animation with the thumbnail up to 32 kilobytes in size; see - https://core.telegram.org/animated_stickers#technical-requirements for animated sticker - technical requirements. Pass a file_id as a String to send a file that already exists on - the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the - Internet, or upload a new one using multipart/form-data.. Animated sticker set thumbnail - can't be uploaded via HTTP URL.""" + """A **PNG** image with the thumbnail, must be up to 128 kilobytes in size and have width and height exactly 100px, or a **TGS** animation with the thumbnail up to 32 kilobytes in size; see `https://core.telegram.org/animated_stickers#technical-requirements `_`https://core.telegram.org/animated_stickers#technical-requirements `_ for animated sticker technical requirements. Pass a *file_id* as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. :ref:`More info on Sending Files » `. Animated sticker set thumbnail can't be uploaded via HTTP URL.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict(exclude={"thumb"}) diff --git a/aiogram/methods/set_webhook.py b/aiogram/methods/set_webhook.py new file mode 100644 index 00000000..fc7539c4 --- /dev/null +++ b/aiogram/methods/set_webhook.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, List, Optional + +from ..types import InputFile +from .base import Request, TelegramMethod, prepare_file + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class SetWebhook(TelegramMethod[bool]): + """ + Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized :class:`aiogram.types.update.Update`. In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns :code:`True` on success. + If you'd like to make sure that the Webhook request comes from Telegram, we recommend using a secret path in the URL, e.g. :code:`https://www.example.com/`. Since nobody else knows your bot's token, you can be pretty sure it's us. + + **Notes** + + **1.** You will not be able to receive updates using :class:`aiogram.methods.get_updates.GetUpdates` for as long as an outgoing webhook is set up. + + **2.** To use a self-signed certificate, you need to upload your `public key certificate `_ using *certificate* parameter. Please upload as InputFile, sending a String will not work. + + **3.** Ports currently supported *for Webhooks*: **443, 80, 88, 8443**. + **NEW!** If you're having any trouble setting up webhooks, please check out this `amazing guide to Webhooks `_. + + Source: https://core.telegram.org/bots/api#setwebhook + """ + + __returning__ = bool + + url: str + """HTTPS url to send updates to. Use an empty string to remove webhook integration""" + certificate: Optional[InputFile] = None + """Upload your public key certificate so that the root certificate in use can be checked. See our `self-signed guide `_ for details.""" + ip_address: Optional[str] = None + """The fixed IP address which will be used to send webhook requests instead of the IP address resolved through DNS""" + max_connections: Optional[int] = None + """Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery, 1-100. Defaults to *40*. Use lower values to limit the load on your bot's server, and higher values to increase your bot's throughput.""" + allowed_updates: Optional[List[str]] = None + """A JSON-serialized list of the update types you want your bot to receive. For example, specify ['message', 'edited_channel_post', 'callback_query'] to only receive updates of these types. See :class:`aiogram.types.update.Update` for a complete list of available update types. Specify an empty list to receive all updates regardless of type (default). If not specified, the previous setting will be used.""" + drop_pending_updates: Optional[bool] = None + """Pass :code:`True` to drop all pending updates""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict(exclude={"certificate"}) + + files: Dict[str, InputFile] = {} + prepare_file(data=data, files=files, name="certificate", value=self.certificate) + + return Request(method="setWebhook", data=data, files=files) diff --git a/aiogram/api/methods/stop_message_live_location.py b/aiogram/methods/stop_message_live_location.py similarity index 55% rename from aiogram/api/methods/stop_message_live_location.py rename to aiogram/methods/stop_message_live_location.py index a7677163..2565eb0d 100644 --- a/aiogram/api/methods/stop_message_live_location.py +++ b/aiogram/methods/stop_message_live_location.py @@ -11,9 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class StopMessageLiveLocation(TelegramMethod[Union[Message, bool]]): """ - Use this method to stop updating a live location message before live_period expires. On - success, if the message was sent by the bot, the sent Message is returned, otherwise True is - returned. + Use this method to stop updating a live location message before *live_period* expires. On success, if the message was sent by the bot, the sent :class:`aiogram.types.message.Message` is returned, otherwise :code:`True` is returned. Source: https://core.telegram.org/bots/api#stopmessagelivelocation """ @@ -21,15 +19,13 @@ class StopMessageLiveLocation(TelegramMethod[Union[Message, bool]]): __returning__ = Union[Message, bool] chat_id: Optional[Union[int, str]] = None - """Required if inline_message_id is not specified. Unique identifier for the target chat or - username of the target channel (in the format @channelusername)""" + """Required if *inline_message_id* is not specified. Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" message_id: Optional[int] = None - """Required if inline_message_id is not specified. Identifier of the message with live - location to stop""" + """Required if *inline_message_id* is not specified. Identifier of the message with live location to stop""" inline_message_id: Optional[str] = None - """Required if chat_id and message_id are not specified. Identifier of the inline message""" + """Required if *chat_id* and *message_id* are not specified. Identifier of the inline message""" reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for a new inline keyboard.""" + """A JSON-serialized object for a new `inline keyboard `_.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/api/methods/stop_poll.py b/aiogram/methods/stop_poll.py similarity index 73% rename from aiogram/api/methods/stop_poll.py rename to aiogram/methods/stop_poll.py index 81e8ef26..027241bf 100644 --- a/aiogram/api/methods/stop_poll.py +++ b/aiogram/methods/stop_poll.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class StopPoll(TelegramMethod[Poll]): """ - Use this method to stop a poll which was sent by the bot. On success, the stopped Poll with - the final results is returned. + Use this method to stop a poll which was sent by the bot. On success, the stopped :class:`aiogram.types.poll.Poll` with the final results is returned. Source: https://core.telegram.org/bots/api#stoppoll """ @@ -20,12 +19,11 @@ class StopPoll(TelegramMethod[Poll]): __returning__ = Poll chat_id: Union[int, str] - """Unique identifier for the target chat or username of the target channel (in the format - @channelusername)""" + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" message_id: int """Identifier of the original message with the poll""" reply_markup: Optional[InlineKeyboardMarkup] = None - """A JSON-serialized object for a new message inline keyboard.""" + """A JSON-serialized object for a new message `inline keyboard `_.""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict() diff --git a/aiogram/methods/unban_chat_member.py b/aiogram/methods/unban_chat_member.py new file mode 100644 index 00000000..cae62f8e --- /dev/null +++ b/aiogram/methods/unban_chat_member.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Optional, Union + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class UnbanChatMember(TelegramMethod[bool]): + """ + Use this method to unban a previously kicked user in a supergroup or channel. The user will **not** return to the group or channel automatically, but will be able to join via link, etc. The bot must be an administrator for this to work. By default, this method guarantees that after the call the user is not a member of the chat, but will be able to join it. So if the user is a member of the chat they will also be **removed** from the chat. If you don't want this, use the parameter *only_if_banned*. Returns :code:`True` on success. + + Source: https://core.telegram.org/bots/api#unbanchatmember + """ + + __returning__ = bool + + chat_id: Union[int, str] + """Unique identifier for the target group or username of the target supergroup or channel (in the format :code:`@username`)""" + user_id: int + """Unique identifier of the target user""" + only_if_banned: Optional[bool] = None + """Do nothing if the user is not banned""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="unbanChatMember", data=data) diff --git a/aiogram/methods/unpin_all_chat_messages.py b/aiogram/methods/unpin_all_chat_messages.py new file mode 100644 index 00000000..b37677f9 --- /dev/null +++ b/aiogram/methods/unpin_all_chat_messages.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Union + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class UnpinAllChatMessages(TelegramMethod[bool]): + """ + Use this method to clear the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns :code:`True` on success. + + Source: https://core.telegram.org/bots/api#unpinallchatmessages + """ + + __returning__ = bool + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="unpinAllChatMessages", data=data) diff --git a/aiogram/methods/unpin_chat_message.py b/aiogram/methods/unpin_chat_message.py new file mode 100644 index 00000000..736f6472 --- /dev/null +++ b/aiogram/methods/unpin_chat_message.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Dict, Optional, Union + +from .base import Request, TelegramMethod + +if TYPE_CHECKING: # pragma: no cover + from ..client.bot import Bot + + +class UnpinChatMessage(TelegramMethod[bool]): + """ + Use this method to remove a message from the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns :code:`True` on success. + + Source: https://core.telegram.org/bots/api#unpinchatmessage + """ + + __returning__ = bool + + chat_id: Union[int, str] + """Unique identifier for the target chat or username of the target channel (in the format :code:`@channelusername`)""" + message_id: Optional[int] = None + """Identifier of a message to unpin. If not specified, the most recent pinned message (by sending date) will be unpinned.""" + + def build_request(self, bot: Bot) -> Request: + data: Dict[str, Any] = self.dict() + + return Request(method="unpinChatMessage", data=data) diff --git a/aiogram/api/methods/upload_sticker_file.py b/aiogram/methods/upload_sticker_file.py similarity index 70% rename from aiogram/api/methods/upload_sticker_file.py rename to aiogram/methods/upload_sticker_file.py index a5f553e3..7d5bc86b 100644 --- a/aiogram/api/methods/upload_sticker_file.py +++ b/aiogram/methods/upload_sticker_file.py @@ -11,8 +11,7 @@ if TYPE_CHECKING: # pragma: no cover class UploadStickerFile(TelegramMethod[File]): """ - Use this method to upload a .PNG file with a sticker for later use in createNewStickerSet and - addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. + Use this method to upload a .PNG file with a sticker for later use in *createNewStickerSet* and *addStickerToSet* methods (can be used multiple times). Returns the uploaded :class:`aiogram.types.file.File` on success. Source: https://core.telegram.org/bots/api#uploadstickerfile """ @@ -22,8 +21,7 @@ class UploadStickerFile(TelegramMethod[File]): user_id: int """User identifier of sticker file owner""" png_sticker: InputFile - """PNG image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed - 512px, and either width or height must be exactly 512px.""" + """**PNG** image with the sticker, must be up to 512 kilobytes in size, dimensions must not exceed 512px, and either width or height must be exactly 512px. :ref:`More info on Sending Files » `""" def build_request(self, bot: Bot) -> Request: data: Dict[str, Any] = self.dict(exclude={"png_sticker"}) diff --git a/aiogram/api/types/__init__.py b/aiogram/types/__init__.py similarity index 97% rename from aiogram/api/types/__init__.py rename to aiogram/types/__init__.py index 3dbb0439..3a6141d9 100644 --- a/aiogram/api/types/__init__.py +++ b/aiogram/types/__init__.py @@ -5,12 +5,13 @@ from .bot_command import BotCommand from .callback_game import CallbackGame from .callback_query import CallbackQuery from .chat import Chat +from .chat_location import ChatLocation from .chat_member import ChatMember from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto from .chosen_inline_result import ChosenInlineResult from .contact import Contact -from .dice import Dice, DiceEmoji +from .dice import Dice from .document import Document from .downloadable import Downloadable from .encrypted_credentials import EncryptedCredentials @@ -64,6 +65,7 @@ from .login_url import LoginUrl from .mask_position import MaskPosition from .message import ContentType, Message from .message_entity import MessageEntity +from .message_id import MessageId from .order_info import OrderInfo from .passport_data import PassportData from .passport_element_error import PassportElementError @@ -82,6 +84,7 @@ from .poll import Poll from .poll_answer import PollAnswer from .poll_option import PollOption from .pre_checkout_query import PreCheckoutQuery +from .proximity_alert_triggered import ProximityAlertTriggered from .reply_keyboard_markup import ReplyKeyboardMarkup from .reply_keyboard_remove import ReplyKeyboardRemove from .response_parameters import ResponseParameters @@ -113,22 +116,23 @@ __all__ = ( "Chat", "Message", "ContentType", + "MessageId", "MessageEntity", "PhotoSize", + "Animation", "Audio", "Document", "Video", - "Animation", - "Voice", "VideoNote", + "Voice", "Contact", - "Location", - "Venue", + "Dice", "PollOption", "PollAnswer", "Poll", - "Dice", - "DiceEmoji", + "Location", + "Venue", + "ProximityAlertTriggered", "UserProfilePhotos", "File", "ReplyKeyboardMarkup", @@ -143,6 +147,7 @@ __all__ = ( "ChatPhoto", "ChatMember", "ChatPermissions", + "ChatLocation", "BotCommand", "ResponseParameters", "InputMedia", diff --git a/aiogram/api/types/animation.py b/aiogram/types/animation.py similarity index 74% rename from aiogram/api/types/animation.py rename to aiogram/types/animation.py index ffa3141b..c97fb548 100644 --- a/aiogram/api/types/animation.py +++ b/aiogram/types/animation.py @@ -18,8 +18,7 @@ class Animation(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" width: int """Video width as defined by sender""" height: int @@ -27,10 +26,10 @@ class Animation(TelegramObject): duration: int """Duration of the video in seconds as defined by sender""" thumb: Optional[PhotoSize] = None - """Animation thumbnail as defined by sender""" + """*Optional*. Animation thumbnail as defined by sender""" file_name: Optional[str] = None - """Original animation filename as defined by sender""" + """*Optional*. Original animation filename as defined by sender""" mime_type: Optional[str] = None - """MIME type of the file as defined by sender""" + """*Optional*. MIME type of the file as defined by sender""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" diff --git a/aiogram/api/types/audio.py b/aiogram/types/audio.py similarity index 61% rename from aiogram/api/types/audio.py rename to aiogram/types/audio.py index 4b496d06..cbb468f3 100644 --- a/aiogram/api/types/audio.py +++ b/aiogram/types/audio.py @@ -18,17 +18,18 @@ class Audio(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" duration: int """Duration of the audio in seconds as defined by sender""" performer: Optional[str] = None - """Performer of the audio as defined by sender or by audio tags""" + """*Optional*. Performer of the audio as defined by sender or by audio tags""" title: Optional[str] = None - """Title of the audio as defined by sender or by audio tags""" + """*Optional*. Title of the audio as defined by sender or by audio tags""" + file_name: Optional[str] = None + """*Optional*. Original filename as defined by sender""" mime_type: Optional[str] = None - """MIME type of the file as defined by sender""" + """*Optional*. MIME type of the file as defined by sender""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" thumb: Optional[PhotoSize] = None - """Thumbnail of the album cover to which the music file belongs""" + """*Optional*. Thumbnail of the album cover to which the music file belongs""" diff --git a/aiogram/api/types/base.py b/aiogram/types/base.py similarity index 83% rename from aiogram/api/types/base.py rename to aiogram/types/base.py index 99db1a01..0b5d08ad 100644 --- a/aiogram/api/types/base.py +++ b/aiogram/types/base.py @@ -23,4 +23,6 @@ class MutableTelegramObject(TelegramObject): allow_mutation = True -UNSET: Any = sentinel.UNSET # special sentinel object which used in sutuation when None might be a useful value +UNSET: Any = ( + sentinel.UNSET +) # special sentinel object which used in sutuation when None might be a useful value diff --git a/aiogram/api/types/bot_command.py b/aiogram/types/bot_command.py similarity index 87% rename from aiogram/api/types/bot_command.py rename to aiogram/types/bot_command.py index 96ef0a31..844abffb 100644 --- a/aiogram/api/types/bot_command.py +++ b/aiogram/types/bot_command.py @@ -11,7 +11,6 @@ class BotCommand(MutableTelegramObject): """ command: str - """Text of the command, 1-32 characters. Can contain only lowercase English letters, digits - and underscores.""" + """Text of the command, 1-32 characters. Can contain only lowercase English letters, digits and underscores.""" description: str """Description of the command, 3-256 characters.""" diff --git a/aiogram/api/types/callback_game.py b/aiogram/types/callback_game.py similarity index 61% rename from aiogram/api/types/callback_game.py rename to aiogram/types/callback_game.py index fe0676d5..6d8c556a 100644 --- a/aiogram/api/types/callback_game.py +++ b/aiogram/types/callback_game.py @@ -5,7 +5,7 @@ from .base import TelegramObject class CallbackGame(TelegramObject): """ - A placeholder, currently holds no information. Use BotFather to set up your game. + A placeholder, currently holds no information. Use `BotFather `_ to set up your game. Source: https://core.telegram.org/bots/api#callbackgame """ diff --git a/aiogram/types/callback_query.py b/aiogram/types/callback_query.py new file mode 100644 index 00000000..90786e8f --- /dev/null +++ b/aiogram/types/callback_query.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from pydantic import Field + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from ..methods import AnswerCallbackQuery + from .message import Message + from .user import User + + +class CallbackQuery(TelegramObject): + """ + This object represents an incoming callback query from a callback button in an `inline keyboard `_. If the button that originated the query was attached to a message sent by the bot, the field *message* will be present. If the button was attached to a message sent via the bot (in `inline mode `_), the field *inline_message_id* will be present. Exactly one of the fields *data* or *game_short_name* will be present. + + **NOTE:** After the user presses a callback button, Telegram clients will display a progress bar until you call :class:`aiogram.methods.answer_callback_query.AnswerCallbackQuery`. It is, therefore, necessary to react by calling :class:`aiogram.methods.answer_callback_query.AnswerCallbackQuery` even if no notification to the user is needed (e.g., without specifying any of the optional parameters). + + Source: https://core.telegram.org/bots/api#callbackquery + """ + + id: str + """Unique identifier for this query""" + from_user: User = Field(..., alias="from") + """Sender""" + chat_instance: str + """Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in :class:`aiogram.methods.games.Games`.""" + message: Optional[Message] = None + """*Optional*. Message with the callback button that originated the query. Note that message content and message date will not be available if the message is too old""" + inline_message_id: Optional[str] = None + """*Optional*. Identifier of the message sent via the bot in inline mode, that originated the query.""" + data: Optional[str] = None + """*Optional*. Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field.""" + game_short_name: Optional[str] = None + """*Optional*. Short name of a `Game `_ to be returned, serves as the unique identifier for the game""" + + def answer( + self, + text: Optional[str] = None, + show_alert: Optional[bool] = None, + url: Optional[str] = None, + cache_time: Optional[int] = None, + ) -> AnswerCallbackQuery: + """ + Answer to callback query + + :param text: + :param show_alert: + :param url: + :param cache_time: + :return: + """ + from ..methods import AnswerCallbackQuery + + return AnswerCallbackQuery( + callback_query_id=self.id, + text=text, + show_alert=show_alert, + url=url, + cache_time=cache_time, + ) diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py new file mode 100644 index 00000000..8714ceb8 --- /dev/null +++ b/aiogram/types/chat.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .chat_location import ChatLocation + from .chat_permissions import ChatPermissions + from .chat_photo import ChatPhoto + from .message import Message + + +class Chat(TelegramObject): + """ + This object represents a chat. + + Source: https://core.telegram.org/bots/api#chat + """ + + id: int + """Unique identifier for this chat. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.""" + type: str + """Type of chat, can be either 'private', 'group', 'supergroup' or 'channel'""" + title: Optional[str] = None + """*Optional*. Title, for supergroups, channels and group chats""" + username: Optional[str] = None + """*Optional*. Username, for private chats, supergroups and channels if available""" + first_name: Optional[str] = None + """*Optional*. First name of the other party in a private chat""" + last_name: Optional[str] = None + """*Optional*. Last name of the other party in a private chat""" + photo: Optional[ChatPhoto] = None + """*Optional*. Chat photo. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + bio: Optional[str] = None + """*Optional*. Bio of the other party in a private chat. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + description: Optional[str] = None + """*Optional*. Description, for groups, supergroups and channel chats. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + invite_link: Optional[str] = None + """*Optional*. Chat invite link, for groups, supergroups and channel chats. Each administrator in a chat generates their own invite links, so the bot must first generate the link using :class:`aiogram.methods.export_chat_invite_link.ExportChatInviteLink`. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + pinned_message: Optional[Message] = None + """*Optional*. The most recent pinned message (by sending date). Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + permissions: Optional[ChatPermissions] = None + """*Optional*. Default chat member permissions, for groups and supergroups. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + slow_mode_delay: Optional[int] = None + """*Optional*. For supergroups, the minimum allowed delay between consecutive messages sent by each unpriviledged user. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + sticker_set_name: Optional[str] = None + """*Optional*. For supergroups, name of group sticker set. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + can_set_sticker_set: Optional[bool] = None + """*Optional*. True, if the bot can change the group sticker set. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + linked_chat_id: Optional[int] = None + """*Optional*. Unique identifier for the linked chat, i.e. the discussion group identifier for a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" + location: Optional[ChatLocation] = None + """*Optional*. For supergroups, the location to which the supergroup is connected. Returned only in :class:`aiogram.methods.get_chat.GetChat`.""" diff --git a/aiogram/types/chat_location.py b/aiogram/types/chat_location.py new file mode 100644 index 00000000..77c5a45b --- /dev/null +++ b/aiogram/types/chat_location.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .location import Location + + +class ChatLocation(TelegramObject): + """ + Represents a location to which a chat is connected. + + Source: https://core.telegram.org/bots/api#chatlocation + """ + + location: Location + """The location to which the supergroup is connected. Can't be a live location.""" + address: str + """Location address; 1-64 characters, as defined by the chat owner""" diff --git a/aiogram/types/chat_member.py b/aiogram/types/chat_member.py new file mode 100644 index 00000000..aa1809aa --- /dev/null +++ b/aiogram/types/chat_member.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import datetime +from typing import TYPE_CHECKING, Optional, Union + +from aiogram.utils import helper + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .user import User + + +class ChatMember(TelegramObject): + """ + This object contains information about one member of a chat. + + Source: https://core.telegram.org/bots/api#chatmember + """ + + user: User + """Information about the user""" + status: str + """The member's status in the chat. Can be 'creator', 'administrator', 'member', 'restricted', 'left' or 'kicked'""" + custom_title: Optional[str] = None + """*Optional*. Owner and administrators only. Custom title for this user""" + is_anonymous: Optional[bool] = None + """*Optional*. Owner and administrators only. True, if the user's presence in the chat is hidden""" + can_be_edited: Optional[bool] = None + """*Optional*. Administrators only. True, if the bot is allowed to edit administrator privileges of that user""" + can_post_messages: Optional[bool] = None + """*Optional*. Administrators only. True, if the administrator can post in the channel; channels only""" + can_edit_messages: Optional[bool] = None + """*Optional*. Administrators only. True, if the administrator can edit messages of other users and can pin messages; channels only""" + can_delete_messages: Optional[bool] = None + """*Optional*. Administrators only. True, if the administrator can delete messages of other users""" + can_restrict_members: Optional[bool] = None + """*Optional*. Administrators only. True, if the administrator can restrict, ban or unban chat members""" + can_promote_members: Optional[bool] = None + """*Optional*. Administrators only. True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user)""" + can_change_info: Optional[bool] = None + """*Optional*. Administrators and restricted only. True, if the user is allowed to change the chat title, photo and other settings""" + can_invite_users: Optional[bool] = None + """*Optional*. Administrators and restricted only. True, if the user is allowed to invite new users to the chat""" + can_pin_messages: Optional[bool] = None + """*Optional*. Administrators and restricted only. True, if the user is allowed to pin messages; groups and supergroups only""" + is_member: Optional[bool] = None + """*Optional*. Restricted only. True, if the user is a member of the chat at the moment of the request""" + can_send_messages: Optional[bool] = None + """*Optional*. Restricted only. True, if the user is allowed to send text messages, contacts, locations and venues""" + can_send_media_messages: Optional[bool] = None + """*Optional*. Restricted only. True, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes""" + can_send_polls: Optional[bool] = None + """*Optional*. Restricted only. True, if the user is allowed to send polls""" + can_send_other_messages: Optional[bool] = None + """*Optional*. Restricted only. True, if the user is allowed to send animations, games, stickers and use inline bots""" + can_add_web_page_previews: Optional[bool] = None + """*Optional*. Restricted only. True, if the user is allowed to add web page previews to their messages""" + until_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None + """*Optional*. Restricted and kicked only. Date when restrictions will be lifted for this user; unix time""" + """Restricted and kicked only. Date when restrictions will be lifted for this user; unix time""" + + @property + def is_chat_admin(self) -> bool: + return self.status in {ChatMemberStatus.CREATOR, ChatMemberStatus.ADMINISTRATOR} + + @property + def is_chat_member(self) -> bool: + return self.status not in {ChatMemberStatus.LEFT, ChatMemberStatus.KICKED} + + +class ChatMemberStatus(helper.Helper): + """ + Chat member status + """ + + mode = helper.HelperMode.lowercase + + CREATOR = helper.Item() # creator + ADMINISTRATOR = helper.Item() # administrator + MEMBER = helper.Item() # member + RESTRICTED = helper.Item() # restricted + LEFT = helper.Item() # left + KICKED = helper.Item() # kicked diff --git a/aiogram/types/chat_permissions.py b/aiogram/types/chat_permissions.py new file mode 100644 index 00000000..348d5bbc --- /dev/null +++ b/aiogram/types/chat_permissions.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import Optional + +from .base import MutableTelegramObject + + +class ChatPermissions(MutableTelegramObject): + """ + Describes actions that a non-administrator user is allowed to take in a chat. + + Source: https://core.telegram.org/bots/api#chatpermissions + """ + + can_send_messages: Optional[bool] = None + """*Optional*. True, if the user is allowed to send text messages, contacts, locations and venues""" + can_send_media_messages: Optional[bool] = None + """*Optional*. True, if the user is allowed to send audios, documents, photos, videos, video notes and voice notes, implies can_send_messages""" + can_send_polls: Optional[bool] = None + """*Optional*. True, if the user is allowed to send polls, implies can_send_messages""" + can_send_other_messages: Optional[bool] = None + """*Optional*. True, if the user is allowed to send animations, games, stickers and use inline bots, implies can_send_media_messages""" + can_add_web_page_previews: Optional[bool] = None + """*Optional*. True, if the user is allowed to add web page previews to their messages, implies can_send_media_messages""" + can_change_info: Optional[bool] = None + """*Optional*. True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups""" + can_invite_users: Optional[bool] = None + """*Optional*. True, if the user is allowed to invite new users to the chat""" + can_pin_messages: Optional[bool] = None + """*Optional*. True, if the user is allowed to pin messages. Ignored in public supergroups""" diff --git a/aiogram/api/types/chat_photo.py b/aiogram/types/chat_photo.py similarity index 57% rename from aiogram/api/types/chat_photo.py rename to aiogram/types/chat_photo.py index 9fd53d32..a3f95f94 100644 --- a/aiogram/api/types/chat_photo.py +++ b/aiogram/types/chat_photo.py @@ -11,14 +11,10 @@ class ChatPhoto(TelegramObject): """ small_file_id: str - """File identifier of small (160x160) chat photo. This file_id can be used only for photo - download and only for as long as the photo is not changed.""" + """File identifier of small (160x160) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed.""" small_file_unique_id: str - """Unique file identifier of small (160x160) chat photo, which is supposed to be the same over - time and for different bots. Can't be used to download or reuse the file.""" + """Unique file identifier of small (160x160) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" big_file_id: str - """File identifier of big (640x640) chat photo. This file_id can be used only for photo - download and only for as long as the photo is not changed.""" + """File identifier of big (640x640) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed.""" big_file_unique_id: str - """Unique file identifier of big (640x640) chat photo, which is supposed to be the same over - time and for different bots. Can't be used to download or reuse the file.""" + """Unique file identifier of big (640x640) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" diff --git a/aiogram/types/chosen_inline_result.py b/aiogram/types/chosen_inline_result.py new file mode 100644 index 00000000..445e81fd --- /dev/null +++ b/aiogram/types/chosen_inline_result.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from pydantic import Field + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .location import Location + from .user import User + + +class ChosenInlineResult(TelegramObject): + """ + Represents a `result `_ of an inline query that was chosen by the user and sent to their chat partner. + **Note:** It is necessary to enable `inline feedback `_ via `@Botfather `_ in order to receive these objects in updates. + + Source: https://core.telegram.org/bots/api#choseninlineresult + """ + + result_id: str + """The unique identifier for the result that was chosen""" + from_user: User = Field(..., alias="from") + """The user that chose the result""" + query: str + """The query that was used to obtain the result""" + location: Optional[Location] = None + """*Optional*. Sender location, only for bots that require user location""" + inline_message_id: Optional[str] = None + """*Optional*. Identifier of the sent inline message. Available only if there is an `inline keyboard `_ attached to the message. Will be also received in `callback queries `_ and can be used to `edit `_ the message.""" diff --git a/aiogram/api/types/contact.py b/aiogram/types/contact.py similarity index 67% rename from aiogram/api/types/contact.py rename to aiogram/types/contact.py index 1c005a7d..951a5b8f 100644 --- a/aiogram/api/types/contact.py +++ b/aiogram/types/contact.py @@ -17,8 +17,8 @@ class Contact(TelegramObject): first_name: str """Contact's first name""" last_name: Optional[str] = None - """Contact's last name""" + """*Optional*. Contact's last name""" user_id: Optional[int] = None - """Contact's user identifier in Telegram""" + """*Optional*. Contact's user identifier in Telegram""" vcard: Optional[str] = None - """Additional data about the contact in the form of a vCard""" + """*Optional*. Additional data about the contact in the form of a `vCard `_""" diff --git a/aiogram/api/types/dice.py b/aiogram/types/dice.py similarity index 65% rename from aiogram/api/types/dice.py rename to aiogram/types/dice.py index d9b1da10..cc5720d8 100644 --- a/aiogram/api/types/dice.py +++ b/aiogram/types/dice.py @@ -13,9 +13,12 @@ class Dice(TelegramObject): emoji: str """Emoji on which the dice throw animation is based""" value: int - """Value of the dice, 1-6 for '' and '' base emoji, 1-5 for '' base emoji""" + """Value of the dice, 1-6 for '🎲' and '🎯' base emoji, 1-5 for '🏀' and '⚽' base emoji, 1-64 for '🎰' base emoji""" class DiceEmoji: DICE = "🎲" DART = "🎯" + BASKETBALL = "🏀" + FOOTBALL = "⚽" + SLOT_MACHINE = "🎰" diff --git a/aiogram/api/types/document.py b/aiogram/types/document.py similarity index 53% rename from aiogram/api/types/document.py rename to aiogram/types/document.py index 7939cd9a..788c47c1 100644 --- a/aiogram/api/types/document.py +++ b/aiogram/types/document.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class Document(TelegramObject): """ - This object represents a general file (as opposed to photos, voice messages and audio files). + This object represents a general file (as opposed to `photos `_, `voice messages `_ and `audio files `_). Source: https://core.telegram.org/bots/api#document """ @@ -18,13 +18,12 @@ class Document(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" thumb: Optional[PhotoSize] = None - """Document thumbnail as defined by sender""" + """*Optional*. Document thumbnail as defined by sender""" file_name: Optional[str] = None - """Original filename as defined by sender""" + """*Optional*. Original filename as defined by sender""" mime_type: Optional[str] = None - """MIME type of the file as defined by sender""" + """*Optional*. MIME type of the file as defined by sender""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" diff --git a/aiogram/types/downloadable.py b/aiogram/types/downloadable.py new file mode 100644 index 00000000..48525f65 --- /dev/null +++ b/aiogram/types/downloadable.py @@ -0,0 +1,8 @@ +try: + from typing import Protocol +except ImportError: # pragma: no cover + from typing_extensions import Protocol # type: ignore + + +class Downloadable(Protocol): + file_id: str diff --git a/aiogram/types/encrypted_credentials.py b/aiogram/types/encrypted_credentials.py new file mode 100644 index 00000000..91fd7714 --- /dev/null +++ b/aiogram/types/encrypted_credentials.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from .base import TelegramObject + + +class EncryptedCredentials(TelegramObject): + """ + Contains data required for decrypting and authenticating :class:`aiogram.types.encrypted_passport_element.EncryptedPassportElement`. See the `Telegram Passport Documentation `_ for a complete description of the data decryption and authentication processes. + + Source: https://core.telegram.org/bots/api#encryptedcredentials + """ + + data: str + """Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for :class:`aiogram.types.encrypted_passport_element.EncryptedPassportElement` decryption and authentication""" + hash: str + """Base64-encoded data hash for data authentication""" + secret: str + """Base64-encoded secret, encrypted with the bot's public RSA key, required for data decryption""" diff --git a/aiogram/types/encrypted_passport_element.py b/aiogram/types/encrypted_passport_element.py new file mode 100644 index 00000000..b17ae382 --- /dev/null +++ b/aiogram/types/encrypted_passport_element.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .passport_file import PassportFile + + +class EncryptedPassportElement(TelegramObject): + """ + Contains information about documents or other Telegram Passport elements shared with the bot by the user. + + Source: https://core.telegram.org/bots/api#encryptedpassportelement + """ + + type: str + """Element type. One of 'personal_details', 'passport', 'driver_license', 'identity_card', 'internal_passport', 'address', 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration', 'phone_number', 'email'.""" + hash: str + """Base64-encoded element hash for using in :class:`aiogram.types.passport_element_error_unspecified.PassportElementErrorUnspecified`""" + data: Optional[str] = None + """*Optional*. Base64-encoded encrypted Telegram Passport element data provided by the user, available for 'personal_details', 'passport', 'driver_license', 'identity_card', 'internal_passport' and 'address' types. Can be decrypted and verified using the accompanying :class:`aiogram.types.encrypted_credentials.EncryptedCredentials`.""" + phone_number: Optional[str] = None + """*Optional*. User's verified phone number, available only for 'phone_number' type""" + email: Optional[str] = None + """*Optional*. User's verified email address, available only for 'email' type""" + files: Optional[List[PassportFile]] = None + """*Optional*. Array of encrypted files with documents provided by the user, available for 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration' and 'temporary_registration' types. Files can be decrypted and verified using the accompanying :class:`aiogram.types.encrypted_credentials.EncryptedCredentials`.""" + front_side: Optional[PassportFile] = None + """*Optional*. Encrypted file with the front side of the document, provided by the user. Available for 'passport', 'driver_license', 'identity_card' and 'internal_passport'. The file can be decrypted and verified using the accompanying :class:`aiogram.types.encrypted_credentials.EncryptedCredentials`.""" + reverse_side: Optional[PassportFile] = None + """*Optional*. Encrypted file with the reverse side of the document, provided by the user. Available for 'driver_license' and 'identity_card'. The file can be decrypted and verified using the accompanying :class:`aiogram.types.encrypted_credentials.EncryptedCredentials`.""" + selfie: Optional[PassportFile] = None + """*Optional*. Encrypted file with the selfie of the user holding a document, provided by the user; available for 'passport', 'driver_license', 'identity_card' and 'internal_passport'. The file can be decrypted and verified using the accompanying :class:`aiogram.types.encrypted_credentials.EncryptedCredentials`.""" + translation: Optional[List[PassportFile]] = None + """*Optional*. Array of encrypted files with translated versions of documents provided by the user. Available if requested for 'passport', 'driver_license', 'identity_card', 'internal_passport', 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration' and 'temporary_registration' types. Files can be decrypted and verified using the accompanying :class:`aiogram.types.encrypted_credentials.EncryptedCredentials`.""" diff --git a/aiogram/types/file.py b/aiogram/types/file.py new file mode 100644 index 00000000..34ec4a8a --- /dev/null +++ b/aiogram/types/file.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from typing import Optional + +from .base import TelegramObject + + +class File(TelegramObject): + """ + This object represents a file ready to be downloaded. The file can be downloaded via the link :code:`https://api.telegram.org/file/bot/`. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling :class:`aiogram.methods.get_file.GetFile`. + + Maximum file size to download is 20 MB + + Source: https://core.telegram.org/bots/api#file + """ + + file_id: str + """Identifier for this file, which can be used to download or reuse the file""" + file_unique_id: str + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" + file_size: Optional[int] = None + """*Optional*. File size, if known""" + file_path: Optional[str] = None + """*Optional*. File path. Use :code:`https://api.telegram.org/file/bot/` to get the file.""" diff --git a/aiogram/types/force_reply.py b/aiogram/types/force_reply.py new file mode 100644 index 00000000..9c29d130 --- /dev/null +++ b/aiogram/types/force_reply.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import Optional + +from .base import MutableTelegramObject + + +class ForceReply(MutableTelegramObject): + """ + Upon receiving a message with this object, Telegram clients will display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply'). This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice `privacy mode `_. + + **Example:** A `poll bot `_ for groups runs in privacy mode (only receives commands, replies to its messages and mentions). There could be two ways to create a new poll: + + - Explain the user how to send a command with parameters (e.g. /newpoll question answer1 answer2). May be appealing for hardcore users but lacks modern day polish. + - Guide the user through a step-by-step process. 'Please send me your question', 'Cool, now let's add the first answer option', 'Great. Keep adding answer options, then send /done when you're ready'. + + The last option is definitely more attractive. And if you use :class:`aiogram.types.force_reply.ForceReply` in your bot's questions, it will receive the user's answers even if it only receives replies, commands and mentions — without any extra work for the user. + + Source: https://core.telegram.org/bots/api#forcereply + """ + + force_reply: bool + """Shows reply interface to the user, as if they manually selected the bot's message and tapped 'Reply'""" + selective: Optional[bool] = None + """*Optional*. Use this parameter if you want to force reply from specific users only. Targets: 1) users that are @mentioned in the *text* of the :class:`aiogram.types.message.Message` object; 2) if the bot's message is a reply (has *reply_to_message_id*), sender of the original message.""" diff --git a/aiogram/api/types/game.py b/aiogram/types/game.py similarity index 54% rename from aiogram/api/types/game.py rename to aiogram/types/game.py index 0d4d8510..e8d41cc0 100644 --- a/aiogram/api/types/game.py +++ b/aiogram/types/game.py @@ -12,8 +12,7 @@ if TYPE_CHECKING: # pragma: no cover class Game(TelegramObject): """ - This object represents a game. Use BotFather to create and edit games, their short names will - act as unique identifiers. + This object represents a game. Use BotFather to create and edit games, their short names will act as unique identifiers. Source: https://core.telegram.org/bots/api#game """ @@ -25,10 +24,8 @@ class Game(TelegramObject): photo: List[PhotoSize] """Photo that will be displayed in the game message in chats.""" text: Optional[str] = None - """Brief description of the game or high scores included in the game message. Can be - automatically edited to include current high scores for the game when the bot calls - setGameScore, or manually edited using editMessageText. 0-4096 characters.""" + """*Optional*. Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls :class:`aiogram.methods.set_game_score.SetGameScore`, or manually edited using :class:`aiogram.methods.edit_message_text.EditMessageText`. 0-4096 characters.""" text_entities: Optional[List[MessageEntity]] = None - """Special entities that appear in text, such as usernames, URLs, bot commands, etc.""" + """*Optional*. Special entities that appear in *text*, such as usernames, URLs, bot commands, etc.""" animation: Optional[Animation] = None - """Animation that will be displayed in the game message in chats. Upload via BotFather""" + """*Optional*. Animation that will be displayed in the game message in chats. Upload via `BotFather `_""" diff --git a/aiogram/api/types/game_high_score.py b/aiogram/types/game_high_score.py similarity index 78% rename from aiogram/api/types/game_high_score.py rename to aiogram/types/game_high_score.py index 7bc35de6..848306f8 100644 --- a/aiogram/api/types/game_high_score.py +++ b/aiogram/types/game_high_score.py @@ -12,7 +12,8 @@ class GameHighScore(TelegramObject): """ This object represents one row of the high scores table for a game. And that's about all we've got for now. - If you've got any questions, please check out our Bot FAQ + + If you've got any questions, please check out our `https://core.telegram.org/bots/faq `_ **Bot FAQ »** Source: https://core.telegram.org/bots/api#gamehighscore """ diff --git a/aiogram/types/inline_keyboard_button.py b/aiogram/types/inline_keyboard_button.py new file mode 100644 index 00000000..62ab33e9 --- /dev/null +++ b/aiogram/types/inline_keyboard_button.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from .base import MutableTelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .callback_game import CallbackGame + from .login_url import LoginUrl + + +class InlineKeyboardButton(MutableTelegramObject): + """ + This object represents one button of an inline keyboard. You **must** use exactly one of the optional fields. + + Source: https://core.telegram.org/bots/api#inlinekeyboardbutton + """ + + text: str + """Label text on the button""" + url: Optional[str] = None + """*Optional*. HTTP or tg:// url to be opened when button is pressed""" + login_url: Optional[LoginUrl] = None + """*Optional*. An HTTP URL used to automatically authorize the user. Can be used as a replacement for the `Telegram Login Widget `_.""" + callback_data: Optional[str] = None + """*Optional*. Data to be sent in a `callback query `_ to the bot when button is pressed, 1-64 bytes""" + switch_inline_query: Optional[str] = None + """*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. Can 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. Can be empty, in which case only the bot's username will be inserted.""" + callback_game: Optional[CallbackGame] = None + """*Optional*. Description of the game that will be launched when the user presses the button.""" + pay: Optional[bool] = None + """*Optional*. Specify True, to send a `Pay button `_.""" diff --git a/aiogram/types/inline_keyboard_markup.py b/aiogram/types/inline_keyboard_markup.py new file mode 100644 index 00000000..21fdce6f --- /dev/null +++ b/aiogram/types/inline_keyboard_markup.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List + +from .base import MutableTelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_button import InlineKeyboardButton + + +class InlineKeyboardMarkup(MutableTelegramObject): + """ + This object represents an `inline keyboard `_ that appears right next to the message it belongs to. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will display *unsupported message*. + + Source: https://core.telegram.org/bots/api#inlinekeyboardmarkup + """ + + inline_keyboard: List[List[InlineKeyboardButton]] + """Array of button rows, each represented by an Array of :class:`aiogram.types.inline_keyboard_button.InlineKeyboardButton` objects""" diff --git a/aiogram/api/types/inline_query.py b/aiogram/types/inline_query.py similarity index 91% rename from aiogram/api/types/inline_query.py rename to aiogram/types/inline_query.py index 29373d39..1389181b 100644 --- a/aiogram/api/types/inline_query.py +++ b/aiogram/types/inline_query.py @@ -7,16 +7,15 @@ from pydantic import Field from .base import TelegramObject if TYPE_CHECKING: # pragma: no cover + from ..methods import AnswerInlineQuery + from .inline_query_result import InlineQueryResult from .location import Location from .user import User - from .inline_query_result import InlineQueryResult - from ..methods import AnswerInlineQuery class InlineQuery(TelegramObject): """ - This object represents an incoming inline query. When the user sends an empty query, your bot - could return some default or trending results. + This object represents an incoming inline query. When the user sends an empty query, your bot could return some default or trending results. Source: https://core.telegram.org/bots/api#inlinequery """ @@ -30,7 +29,7 @@ class InlineQuery(TelegramObject): offset: str """Offset of the results to be returned, can be controlled by the bot""" location: Optional[Location] = None - """Sender location, only for bots that request user location""" + """*Optional*. Sender location, only for bots that request user location""" def answer( self, diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py new file mode 100644 index 00000000..a8a66403 --- /dev/null +++ b/aiogram/types/inline_query_result.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from .base import MutableTelegramObject + + +class InlineQueryResult(MutableTelegramObject): + """ + This object represents one result of an inline query. Telegram clients currently support results of the following 20 types: + + - :class:`aiogram.types.inline_query_result_cached_audio.InlineQueryResultCachedAudio` + - :class:`aiogram.types.inline_query_result_cached_document.InlineQueryResultCachedDocument` + - :class:`aiogram.types.inline_query_result_cached_gif.InlineQueryResultCachedGif` + - :class:`aiogram.types.inline_query_result_cached_mpeg4_gif.InlineQueryResultCachedMpeg4Gif` + - :class:`aiogram.types.inline_query_result_cached_photo.InlineQueryResultCachedPhoto` + - :class:`aiogram.types.inline_query_result_cached_sticker.InlineQueryResultCachedSticker` + - :class:`aiogram.types.inline_query_result_cached_video.InlineQueryResultCachedVideo` + - :class:`aiogram.types.inline_query_result_cached_voice.InlineQueryResultCachedVoice` + - :class:`aiogram.types.inline_query_result_article.InlineQueryResultArticle` + - :class:`aiogram.types.inline_query_result_audio.InlineQueryResultAudio` + - :class:`aiogram.types.inline_query_result_contact.InlineQueryResultContact` + - :class:`aiogram.types.inline_query_result_game.InlineQueryResultGame` + - :class:`aiogram.types.inline_query_result_document.InlineQueryResultDocument` + - :class:`aiogram.types.inline_query_result_gif.InlineQueryResultGif` + - :class:`aiogram.types.inline_query_result_location.InlineQueryResultLocation` + - :class:`aiogram.types.inline_query_result_mpeg4_gif.InlineQueryResultMpeg4Gif` + - :class:`aiogram.types.inline_query_result_photo.InlineQueryResultPhoto` + - :class:`aiogram.types.inline_query_result_venue.InlineQueryResultVenue` + - :class:`aiogram.types.inline_query_result_video.InlineQueryResultVideo` + - :class:`aiogram.types.inline_query_result_voice.InlineQueryResultVoice` + + Source: https://core.telegram.org/bots/api#inlinequeryresult + """ diff --git a/aiogram/api/types/inline_query_result_article.py b/aiogram/types/inline_query_result_article.py similarity index 66% rename from aiogram/api/types/inline_query_result_article.py rename to aiogram/types/inline_query_result_article.py index 0c34bdb5..a4f22bed 100644 --- a/aiogram/api/types/inline_query_result_article.py +++ b/aiogram/types/inline_query_result_article.py @@ -19,7 +19,7 @@ class InlineQueryResultArticle(InlineQueryResult): """ type: str = Field("article", const=True) - """Type of the result, must be article""" + """Type of the result, must be *article*""" id: str """Unique identifier for this result, 1-64 Bytes""" title: str @@ -27,16 +27,16 @@ class InlineQueryResultArticle(InlineQueryResult): input_message_content: InputMessageContent """Content of the message to be sent""" reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" + """*Optional*. `Inline keyboard `_ attached to the message""" url: Optional[str] = None - """URL of the result""" + """*Optional*. URL of the result""" hide_url: Optional[bool] = None - """Pass True, if you don't want the URL to be shown in the message""" + """*Optional*. Pass :code:`True`, if you don't want the URL to be shown in the message""" description: Optional[str] = None - """Short description of the result""" + """*Optional*. Short description of the result""" thumb_url: Optional[str] = None - """Url of the thumbnail for the result""" + """*Optional*. Url of the thumbnail for the result""" thumb_width: Optional[int] = None - """Thumbnail width""" + """*Optional*. Thumbnail width""" thumb_height: Optional[int] = None - """Thumbnail height""" + """*Optional*. Thumbnail height""" diff --git a/aiogram/types/inline_query_result_audio.py b/aiogram/types/inline_query_result_audio.py new file mode 100644 index 00000000..d124f5fa --- /dev/null +++ b/aiogram/types/inline_query_result_audio.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultAudio(InlineQueryResult): + """ + Represents a link to an MP3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the audio. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + + Source: https://core.telegram.org/bots/api#inlinequeryresultaudio + """ + + type: str = Field("audio", const=True) + """Type of the result, must be *audio*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + audio_url: str + """A valid URL for the audio file""" + title: str + """Title""" + caption: Optional[str] = None + """*Optional*. Caption, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the audio caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + performer: Optional[str] = None + """*Optional*. Performer""" + audio_duration: Optional[int] = None + """*Optional*. Audio duration in seconds""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the audio""" diff --git a/aiogram/types/inline_query_result_cached_audio.py b/aiogram/types/inline_query_result_cached_audio.py new file mode 100644 index 00000000..81817051 --- /dev/null +++ b/aiogram/types/inline_query_result_cached_audio.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultCachedAudio(InlineQueryResult): + """ + Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio file will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the audio. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + + Source: https://core.telegram.org/bots/api#inlinequeryresultcachedaudio + """ + + type: str = Field("audio", const=True) + """Type of the result, must be *audio*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + audio_file_id: str + """A valid file identifier for the audio file""" + caption: Optional[str] = None + """*Optional*. Caption, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the audio caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the audio""" diff --git a/aiogram/types/inline_query_result_cached_document.py b/aiogram/types/inline_query_result_cached_document.py new file mode 100644 index 00000000..ef0cfe7e --- /dev/null +++ b/aiogram/types/inline_query_result_cached_document.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultCachedDocument(InlineQueryResult): + """ + Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the file. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + + Source: https://core.telegram.org/bots/api#inlinequeryresultcacheddocument + """ + + type: str = Field("document", const=True) + """Type of the result, must be *document*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + title: str + """Title for the result""" + document_file_id: str + """A valid file identifier for the file""" + description: Optional[str] = None + """*Optional*. Short description of the result""" + caption: Optional[str] = None + """*Optional*. Caption of the document to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the document caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the file""" diff --git a/aiogram/types/inline_query_result_cached_gif.py b/aiogram/types/inline_query_result_cached_gif.py new file mode 100644 index 00000000..393f3c87 --- /dev/null +++ b/aiogram/types/inline_query_result_cached_gif.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultCachedGif(InlineQueryResult): + """ + Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use *input_message_content* to send a message with specified content instead of the animation. + + Source: https://core.telegram.org/bots/api#inlinequeryresultcachedgif + """ + + type: str = Field("gif", const=True) + """Type of the result, must be *gif*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + gif_file_id: str + """A valid file identifier for the GIF file""" + title: Optional[str] = None + """*Optional*. Title for the result""" + caption: Optional[str] = None + """*Optional*. Caption of the GIF file to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the GIF animation""" diff --git a/aiogram/types/inline_query_result_cached_mpeg4_gif.py b/aiogram/types/inline_query_result_cached_mpeg4_gif.py new file mode 100644 index 00000000..10e779b0 --- /dev/null +++ b/aiogram/types/inline_query_result_cached_mpeg4_gif.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): + """ + Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the animation. + + Source: https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif + """ + + type: str = Field("mpeg4_gif", const=True) + """Type of the result, must be *mpeg4_gif*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + mpeg4_file_id: str + """A valid file identifier for the MP4 file""" + title: Optional[str] = None + """*Optional*. Title for the result""" + caption: Optional[str] = None + """*Optional*. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the video animation""" diff --git a/aiogram/types/inline_query_result_cached_photo.py b/aiogram/types/inline_query_result_cached_photo.py new file mode 100644 index 00000000..ca9d8356 --- /dev/null +++ b/aiogram/types/inline_query_result_cached_photo.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultCachedPhoto(InlineQueryResult): + """ + Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the photo. + + Source: https://core.telegram.org/bots/api#inlinequeryresultcachedphoto + """ + + type: str = Field("photo", const=True) + """Type of the result, must be *photo*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + photo_file_id: str + """A valid file identifier of the photo""" + title: Optional[str] = None + """*Optional*. Title for the result""" + description: Optional[str] = None + """*Optional*. Short description of the result""" + caption: Optional[str] = None + """*Optional*. Caption of the photo to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the photo caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the photo""" diff --git a/aiogram/api/types/inline_query_result_cached_sticker.py b/aiogram/types/inline_query_result_cached_sticker.py similarity index 55% rename from aiogram/api/types/inline_query_result_cached_sticker.py rename to aiogram/types/inline_query_result_cached_sticker.py index 52c21c6d..2d6911df 100644 --- a/aiogram/api/types/inline_query_result_cached_sticker.py +++ b/aiogram/types/inline_query_result_cached_sticker.py @@ -13,22 +13,19 @@ if TYPE_CHECKING: # pragma: no cover class InlineQueryResultCachedSticker(InlineQueryResult): """ - Represents a link to a sticker stored on the Telegram servers. By default, this sticker will - be sent by the user. Alternatively, you can use input_message_content to send a message with - the specified content instead of the sticker. - Note: This will only work in Telegram versions released after 9 April, 2016 for static - stickers and after 06 July, 2019 for animated stickers. Older clients will ignore them. + Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the sticker. + **Note:** This will only work in Telegram versions released after 9 April, 2016 for static stickers and after 06 July, 2019 for `animated stickers `_. Older clients will ignore them. Source: https://core.telegram.org/bots/api#inlinequeryresultcachedsticker """ type: str = Field("sticker", const=True) - """Type of the result, must be sticker""" + """Type of the result, must be *sticker*""" id: str """Unique identifier for this result, 1-64 bytes""" sticker_file_id: str """A valid file identifier of the sticker""" reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" + """*Optional*. `Inline keyboard `_ attached to the message""" input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the sticker""" + """*Optional*. Content of the message to be sent instead of the sticker""" diff --git a/aiogram/types/inline_query_result_cached_video.py b/aiogram/types/inline_query_result_cached_video.py new file mode 100644 index 00000000..1e23b7ae --- /dev/null +++ b/aiogram/types/inline_query_result_cached_video.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultCachedVideo(InlineQueryResult): + """ + Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the video. + + Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvideo + """ + + type: str = Field("video", const=True) + """Type of the result, must be *video*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + video_file_id: str + """A valid file identifier for the video file""" + title: str + """Title for the result""" + description: Optional[str] = None + """*Optional*. Short description of the result""" + caption: Optional[str] = None + """*Optional*. Caption of the video to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the video caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the video""" diff --git a/aiogram/types/inline_query_result_cached_voice.py b/aiogram/types/inline_query_result_cached_voice.py new file mode 100644 index 00000000..43c55551 --- /dev/null +++ b/aiogram/types/inline_query_result_cached_voice.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultCachedVoice(InlineQueryResult): + """ + Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the voice message. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + + Source: https://core.telegram.org/bots/api#inlinequeryresultcachedvoice + """ + + type: str = Field("voice", const=True) + """Type of the result, must be *voice*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + voice_file_id: str + """A valid file identifier for the voice message""" + title: str + """Voice message title""" + caption: Optional[str] = None + """*Optional*. Caption, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the voice message caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the voice message""" diff --git a/aiogram/api/types/inline_query_result_contact.py b/aiogram/types/inline_query_result_contact.py similarity index 55% rename from aiogram/api/types/inline_query_result_contact.py rename to aiogram/types/inline_query_result_contact.py index e394fd2f..c54c0d2e 100644 --- a/aiogram/api/types/inline_query_result_contact.py +++ b/aiogram/types/inline_query_result_contact.py @@ -13,17 +13,14 @@ if TYPE_CHECKING: # pragma: no cover class InlineQueryResultContact(InlineQueryResult): """ - Represents a contact with a phone number. By default, this contact will be sent by the user. - Alternatively, you can use input_message_content to send a message with the specified content - instead of the contact. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. + Represents a contact with a phone number. By default, this contact will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the contact. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. Source: https://core.telegram.org/bots/api#inlinequeryresultcontact """ type: str = Field("contact", const=True) - """Type of the result, must be contact""" + """Type of the result, must be *contact*""" id: str """Unique identifier for this result, 1-64 Bytes""" phone_number: str @@ -31,16 +28,16 @@ class InlineQueryResultContact(InlineQueryResult): first_name: str """Contact's first name""" last_name: Optional[str] = None - """Contact's last name""" + """*Optional*. Contact's last name""" vcard: Optional[str] = None - """Additional data about the contact in the form of a vCard, 0-2048 bytes""" + """*Optional*. Additional data about the contact in the form of a `vCard `_, 0-2048 bytes""" reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" + """*Optional*. `Inline keyboard `_ attached to the message""" input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the contact""" + """*Optional*. Content of the message to be sent instead of the contact""" thumb_url: Optional[str] = None - """Url of the thumbnail for the result""" + """*Optional*. Url of the thumbnail for the result""" thumb_width: Optional[int] = None - """Thumbnail width""" + """*Optional*. Thumbnail width""" thumb_height: Optional[int] = None - """Thumbnail height""" + """*Optional*. Thumbnail height""" diff --git a/aiogram/types/inline_query_result_document.py b/aiogram/types/inline_query_result_document.py new file mode 100644 index 00000000..d817eb01 --- /dev/null +++ b/aiogram/types/inline_query_result_document.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultDocument(InlineQueryResult): + """ + Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the file. Currently, only **.PDF** and **.ZIP** files can be sent using this method. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + + Source: https://core.telegram.org/bots/api#inlinequeryresultdocument + """ + + type: str = Field("document", const=True) + """Type of the result, must be *document*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + title: str + """Title for the result""" + document_url: str + """A valid URL for the file""" + mime_type: str + """Mime type of the content of the file, either 'application/pdf' or 'application/zip'""" + caption: Optional[str] = None + """*Optional*. Caption of the document to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the document caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + description: Optional[str] = None + """*Optional*. Short description of the result""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. Inline keyboard attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the file""" + thumb_url: Optional[str] = None + """*Optional*. URL of the thumbnail (jpeg only) for the file""" + thumb_width: Optional[int] = None + """*Optional*. Thumbnail width""" + thumb_height: Optional[int] = None + """*Optional*. Thumbnail height""" diff --git a/aiogram/api/types/inline_query_result_game.py b/aiogram/types/inline_query_result_game.py similarity index 59% rename from aiogram/api/types/inline_query_result_game.py rename to aiogram/types/inline_query_result_game.py index 5ddc7222..8ed9862d 100644 --- a/aiogram/api/types/inline_query_result_game.py +++ b/aiogram/types/inline_query_result_game.py @@ -12,18 +12,17 @@ if TYPE_CHECKING: # pragma: no cover class InlineQueryResultGame(InlineQueryResult): """ - Represents a Game. - Note: This will only work in Telegram versions released after October 1, 2016. Older clients - will not display any inline results if a game result is among them. + Represents a `Game `_. + **Note:** This will only work in Telegram versions released after October 1, 2016. Older clients will not display any inline results if a game result is among them. Source: https://core.telegram.org/bots/api#inlinequeryresultgame """ type: str = Field("game", const=True) - """Type of the result, must be game""" + """Type of the result, must be *game*""" id: str """Unique identifier for this result, 1-64 bytes""" game_short_name: str """Short name of the game""" reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" + """*Optional*. `Inline keyboard `_ attached to the message""" diff --git a/aiogram/types/inline_query_result_gif.py b/aiogram/types/inline_query_result_gif.py new file mode 100644 index 00000000..2688cd30 --- /dev/null +++ b/aiogram/types/inline_query_result_gif.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultGif(InlineQueryResult): + """ + Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the animation. + + Source: https://core.telegram.org/bots/api#inlinequeryresultgif + """ + + type: str = Field("gif", const=True) + """Type of the result, must be *gif*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + gif_url: str + """A valid URL for the GIF file. File size must not exceed 1MB""" + thumb_url: str + """URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result""" + gif_width: Optional[int] = None + """*Optional*. Width of the GIF""" + gif_height: Optional[int] = None + """*Optional*. Height of the GIF""" + gif_duration: Optional[int] = None + """*Optional*. Duration of the GIF""" + thumb_mime_type: Optional[str] = None + """*Optional*. MIME type of the thumbnail, must be one of 'image/jpeg', 'image/gif', or 'video/mp4'. Defaults to 'image/jpeg'""" + title: Optional[str] = None + """*Optional*. Title for the result""" + caption: Optional[str] = None + """*Optional*. Caption of the GIF file to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the GIF animation""" diff --git a/aiogram/types/inline_query_result_location.py b/aiogram/types/inline_query_result_location.py new file mode 100644 index 00000000..01d501a2 --- /dev/null +++ b/aiogram/types/inline_query_result_location.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from pydantic import Field + +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + + +class InlineQueryResultLocation(InlineQueryResult): + """ + Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the location. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + + Source: https://core.telegram.org/bots/api#inlinequeryresultlocation + """ + + type: str = Field("location", const=True) + """Type of the result, must be *location*""" + id: str + """Unique identifier for this result, 1-64 Bytes""" + latitude: float + """Location latitude in degrees""" + longitude: float + """Location longitude in degrees""" + title: str + """Location title""" + horizontal_accuracy: Optional[float] = None + """*Optional*. The radius of uncertainty for the location, measured in meters; 0-1500""" + live_period: Optional[int] = None + """*Optional*. Period in seconds for which the location can be updated, should be between 60 and 86400.""" + heading: Optional[int] = None + """*Optional*. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.""" + proximity_alert_radius: Optional[int] = None + """*Optional*. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the location""" + thumb_url: Optional[str] = None + """*Optional*. Url of the thumbnail for the result""" + thumb_width: Optional[int] = None + """*Optional*. Thumbnail width""" + thumb_height: Optional[int] = None + """*Optional*. Thumbnail height""" diff --git a/aiogram/types/inline_query_result_mpeg4_gif.py b/aiogram/types/inline_query_result_mpeg4_gif.py new file mode 100644 index 00000000..ea6d4ab2 --- /dev/null +++ b/aiogram/types/inline_query_result_mpeg4_gif.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultMpeg4Gif(InlineQueryResult): + """ + Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the animation. + + Source: https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif + """ + + type: str = Field("mpeg4_gif", const=True) + """Type of the result, must be *mpeg4_gif*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + mpeg4_url: str + """A valid URL for the MP4 file. File size must not exceed 1MB""" + thumb_url: str + """URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result""" + mpeg4_width: Optional[int] = None + """*Optional*. Video width""" + mpeg4_height: Optional[int] = None + """*Optional*. Video height""" + mpeg4_duration: Optional[int] = None + """*Optional*. Video duration""" + thumb_mime_type: Optional[str] = None + """*Optional*. MIME type of the thumbnail, must be one of 'image/jpeg', 'image/gif', or 'video/mp4'. Defaults to 'image/jpeg'""" + title: Optional[str] = None + """*Optional*. Title for the result""" + caption: Optional[str] = None + """*Optional*. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the video animation""" diff --git a/aiogram/types/inline_query_result_photo.py b/aiogram/types/inline_query_result_photo.py new file mode 100644 index 00000000..9e2935b1 --- /dev/null +++ b/aiogram/types/inline_query_result_photo.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultPhoto(InlineQueryResult): + """ + Represents a link to a photo. By default, this photo will be sent by the user with optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the photo. + + Source: https://core.telegram.org/bots/api#inlinequeryresultphoto + """ + + type: str = Field("photo", const=True) + """Type of the result, must be *photo*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + photo_url: str + """A valid URL of the photo. Photo must be in **jpeg** format. Photo size must not exceed 5MB""" + thumb_url: str + """URL of the thumbnail for the photo""" + photo_width: Optional[int] = None + """*Optional*. Width of the photo""" + photo_height: Optional[int] = None + """*Optional*. Height of the photo""" + title: Optional[str] = None + """*Optional*. Title for the result""" + description: Optional[str] = None + """*Optional*. Short description of the result""" + caption: Optional[str] = None + """*Optional*. Caption of the photo to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the photo caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the photo""" diff --git a/aiogram/api/types/inline_query_result_venue.py b/aiogram/types/inline_query_result_venue.py similarity index 50% rename from aiogram/api/types/inline_query_result_venue.py rename to aiogram/types/inline_query_result_venue.py index 902db9a2..f5c5c3ca 100644 --- a/aiogram/api/types/inline_query_result_venue.py +++ b/aiogram/types/inline_query_result_venue.py @@ -13,16 +13,14 @@ if TYPE_CHECKING: # pragma: no cover class InlineQueryResultVenue(InlineQueryResult): """ - Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use - input_message_content to send a message with the specified content instead of the venue. - Note: This will only work in Telegram versions released after 9 April, 2016. Older clients - will ignore them. + Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the venue. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. Source: https://core.telegram.org/bots/api#inlinequeryresultvenue """ type: str = Field("venue", const=True) - """Type of the result, must be venue""" + """Type of the result, must be *venue*""" id: str """Unique identifier for this result, 1-64 Bytes""" latitude: float @@ -34,17 +32,20 @@ class InlineQueryResultVenue(InlineQueryResult): address: str """Address of the venue""" foursquare_id: Optional[str] = None - """Foursquare identifier of the venue if known""" + """*Optional*. Foursquare identifier of the venue if known""" foursquare_type: Optional[str] = None - """Foursquare type of the venue, if known. (For example, 'arts_entertainment/default', - 'arts_entertainment/aquarium' or 'food/icecream'.)""" + """*Optional*. Foursquare type of the venue, if known. (For example, 'arts_entertainment/default', 'arts_entertainment/aquarium' or 'food/icecream'.)""" + google_place_id: Optional[str] = None + """*Optional*. Google Places identifier of the venue""" + google_place_type: Optional[str] = None + """*Optional*. Google Places type of the venue. (See `supported types `_.)""" reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message""" + """*Optional*. `Inline keyboard `_ attached to the message""" input_message_content: Optional[InputMessageContent] = None - """Content of the message to be sent instead of the venue""" + """*Optional*. Content of the message to be sent instead of the venue""" thumb_url: Optional[str] = None - """Url of the thumbnail for the result""" + """*Optional*. Url of the thumbnail for the result""" thumb_width: Optional[int] = None - """Thumbnail width""" + """*Optional*. Thumbnail width""" thumb_height: Optional[int] = None - """Thumbnail height""" + """*Optional*. Thumbnail height""" diff --git a/aiogram/types/inline_query_result_video.py b/aiogram/types/inline_query_result_video.py new file mode 100644 index 00000000..c1494d69 --- /dev/null +++ b/aiogram/types/inline_query_result_video.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultVideo(InlineQueryResult): + """ + Represents a link to a page containing an embedded video player or a video file. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the video. + + If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you **must** replace its content using *input_message_content*. + + Source: https://core.telegram.org/bots/api#inlinequeryresultvideo + """ + + type: str = Field("video", const=True) + """Type of the result, must be *video*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + video_url: str + """A valid URL for the embedded video player or video file""" + mime_type: str + """Mime type of the content of video url, 'text/html' or 'video/mp4'""" + thumb_url: str + """URL of the thumbnail (jpeg only) for the video""" + title: str + """Title for the result""" + caption: Optional[str] = None + """*Optional*. Caption of the video to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the video caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + video_width: Optional[int] = None + """*Optional*. Video width""" + video_height: Optional[int] = None + """*Optional*. Video height""" + video_duration: Optional[int] = None + """*Optional*. Video duration in seconds""" + description: Optional[str] = None + """*Optional*. Short description of the result""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the video. This field is **required** if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video).""" diff --git a/aiogram/types/inline_query_result_voice.py b/aiogram/types/inline_query_result_voice.py new file mode 100644 index 00000000..eac64546 --- /dev/null +++ b/aiogram/types/inline_query_result_voice.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from pydantic import Field + +from .base import UNSET +from .inline_query_result import InlineQueryResult + +if TYPE_CHECKING: # pragma: no cover + from .inline_keyboard_markup import InlineKeyboardMarkup + from .input_message_content import InputMessageContent + from .message_entity import MessageEntity + + +class InlineQueryResultVoice(InlineQueryResult): + """ + Represents a link to a voice recording in an .OGG container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use *input_message_content* to send a message with the specified content instead of the the voice message. + **Note:** This will only work in Telegram versions released after 9 April, 2016. Older clients will ignore them. + + Source: https://core.telegram.org/bots/api#inlinequeryresultvoice + """ + + type: str = Field("voice", const=True) + """Type of the result, must be *voice*""" + id: str + """Unique identifier for this result, 1-64 bytes""" + voice_url: str + """A valid URL for the voice recording""" + title: str + """Recording title""" + caption: Optional[str] = None + """*Optional*. Caption, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the voice message caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + voice_duration: Optional[int] = None + """*Optional*. Recording duration in seconds""" + reply_markup: Optional[InlineKeyboardMarkup] = None + """*Optional*. `Inline keyboard `_ attached to the message""" + input_message_content: Optional[InputMessageContent] = None + """*Optional*. Content of the message to be sent instead of the voice recording""" diff --git a/aiogram/api/types/input_contact_message_content.py b/aiogram/types/input_contact_message_content.py similarity index 57% rename from aiogram/api/types/input_contact_message_content.py rename to aiogram/types/input_contact_message_content.py index 6630d7c7..2e5a7823 100644 --- a/aiogram/api/types/input_contact_message_content.py +++ b/aiogram/types/input_contact_message_content.py @@ -7,7 +7,7 @@ from .input_message_content import InputMessageContent class InputContactMessageContent(InputMessageContent): """ - Represents the content of a contact message to be sent as the result of an inline query. + Represents the `content `_ of a contact message to be sent as the result of an inline query. Source: https://core.telegram.org/bots/api#inputcontactmessagecontent """ @@ -17,6 +17,6 @@ class InputContactMessageContent(InputMessageContent): first_name: str """Contact's first name""" last_name: Optional[str] = None - """Contact's last name""" + """*Optional*. Contact's last name""" vcard: Optional[str] = None - """Additional data about the contact in the form of a vCard, 0-2048 bytes""" + """*Optional*. Additional data about the contact in the form of a `vCard `_, 0-2048 bytes""" diff --git a/aiogram/api/types/input_file.py b/aiogram/types/input_file.py similarity index 67% rename from aiogram/api/types/input_file.py rename to aiogram/types/input_file.py index 86d7c62a..9efa455d 100644 --- a/aiogram/api/types/input_file.py +++ b/aiogram/types/input_file.py @@ -13,13 +13,19 @@ DEFAULT_CHUNK_SIZE = 64 * 1024 # 64 kb class InputFile(ABC): """ - This object represents the contents of a file to be uploaded. Must be posted using - multipart/form-data in the usual way that files are uploaded via the browser. + This object represents the contents of a file to be uploaded. Must be posted using multipart/form-data in the usual way that files are uploaded via the browser. Source: https://core.telegram.org/bots/api#inputfile """ def __init__(self, filename: Optional[str] = None, chunk_size: int = DEFAULT_CHUNK_SIZE): + """ + Base class for input files. Should not be used directly. + Look at :class:`BufferedInputFile`, :class:`FSInputFile` :class:`URLInputFile` + + :param filename: name of the given file + :param chunk_size: reader chunks size + """ self.filename = filename self.chunk_size = chunk_size @@ -38,6 +44,13 @@ class InputFile(ABC): class BufferedInputFile(InputFile): def __init__(self, file: bytes, filename: str, chunk_size: int = DEFAULT_CHUNK_SIZE): + """ + Represents object for uploading files from filesystem + + :param file: Bytes + :param filename: Filename to be propagated to telegram. + :param chunk_size: Uploading chunk size + """ super().__init__(filename=filename, chunk_size=chunk_size) self.data = file @@ -49,6 +62,15 @@ class BufferedInputFile(InputFile): filename: Optional[str] = None, chunk_size: int = DEFAULT_CHUNK_SIZE, ) -> BufferedInputFile: + """ + Create buffer from file + + :param path: Path to file + :param filename: Filename to be propagated to telegram. + By default will be parsed from path + :param chunk_size: Uploading chunk size + :return: instance of :obj:`BufferedInputFile` + """ if filename is None: filename = os.path.basename(path) with open(path, "rb") as f: @@ -70,6 +92,14 @@ class FSInputFile(InputFile): filename: Optional[str] = None, chunk_size: int = DEFAULT_CHUNK_SIZE, ): + """ + Represents object for uploading files from filesystem + + :param path: Path to file + :param filename: Filename to be propagated to telegram. + By default will be parsed from path + :param chunk_size: Uploading chunk size + """ if filename is None: filename = os.path.basename(path) super().__init__(filename=filename, chunk_size=chunk_size) @@ -92,13 +122,20 @@ class URLInputFile(InputFile): chunk_size: int = DEFAULT_CHUNK_SIZE, timeout: int = 30, ): + """ + Represents object for streaming files from internet + + :param url: URL in internet + :param filename: Filename to be propagated to telegram. + :param chunk_size: Uploading chunk size + """ super().__init__(filename=filename, chunk_size=chunk_size) self.url = url self.timeout = timeout async def read(self, chunk_size: int) -> AsyncGenerator[bytes, None]: - from aiogram.api.client.bot import Bot + from aiogram.client.bot import Bot bot = Bot.get_current(no_error=False) stream = bot.session.stream_content( diff --git a/aiogram/types/input_location_message_content.py b/aiogram/types/input_location_message_content.py new file mode 100644 index 00000000..700532c2 --- /dev/null +++ b/aiogram/types/input_location_message_content.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import Optional + +from .input_message_content import InputMessageContent + + +class InputLocationMessageContent(InputMessageContent): + """ + Represents the `content `_ of a location message to be sent as the result of an inline query. + + Source: https://core.telegram.org/bots/api#inputlocationmessagecontent + """ + + latitude: float + """Latitude of the location in degrees""" + longitude: float + """Longitude of the location in degrees""" + horizontal_accuracy: Optional[float] = None + """*Optional*. The radius of uncertainty for the location, measured in meters; 0-1500""" + live_period: Optional[int] = None + """*Optional*. Period in seconds for which the location can be updated, should be between 60 and 86400.""" + heading: Optional[int] = None + """*Optional*. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.""" + proximity_alert_radius: Optional[int] = None + """*Optional*. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.""" diff --git a/aiogram/types/input_media.py b/aiogram/types/input_media.py new file mode 100644 index 00000000..f7523c9e --- /dev/null +++ b/aiogram/types/input_media.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from .base import MutableTelegramObject + + +class InputMedia(MutableTelegramObject): + """ + This object represents the content of a media message to be sent. It should be one of + + - :class:`aiogram.types.input_media_animation.InputMediaAnimation` + - :class:`aiogram.types.input_media_document.InputMediaDocument` + - :class:`aiogram.types.input_media_audio.InputMediaAudio` + - :class:`aiogram.types.input_media_photo.InputMediaPhoto` + - :class:`aiogram.types.input_media_video.InputMediaVideo` + + Source: https://core.telegram.org/bots/api#inputmedia + """ diff --git a/aiogram/types/input_media_animation.py b/aiogram/types/input_media_animation.py new file mode 100644 index 00000000..a18e1e88 --- /dev/null +++ b/aiogram/types/input_media_animation.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional, Union + +from pydantic import Field + +from .base import UNSET +from .input_media import InputMedia + +if TYPE_CHECKING: # pragma: no cover + from .input_file import InputFile + from .message_entity import MessageEntity + + +class InputMediaAnimation(InputMedia): + """ + Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. + + Source: https://core.telegram.org/bots/api#inputmediaanimation + """ + + type: str = Field("animation", const=True) + """Type of the result, must be *animation*""" + media: Union[str, InputFile] + """File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://' to upload a new one using multipart/form-data under name. :ref:`More info on Sending Files » `""" + thumb: Optional[Union[InputFile, str]] = None + """*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """*Optional*. Caption of the animation to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the animation caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + width: Optional[int] = None + """*Optional*. Animation width""" + height: Optional[int] = None + """*Optional*. Animation height""" + duration: Optional[int] = None + """*Optional*. Animation duration""" diff --git a/aiogram/types/input_media_audio.py b/aiogram/types/input_media_audio.py new file mode 100644 index 00000000..6252dbaf --- /dev/null +++ b/aiogram/types/input_media_audio.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional, Union + +from pydantic import Field + +from .base import UNSET +from .input_media import InputMedia + +if TYPE_CHECKING: # pragma: no cover + from .input_file import InputFile + from .message_entity import MessageEntity + + +class InputMediaAudio(InputMedia): + """ + Represents an audio file to be treated as music to be sent. + + Source: https://core.telegram.org/bots/api#inputmediaaudio + """ + + type: str = Field("audio", const=True) + """Type of the result, must be *audio*""" + media: Union[str, InputFile] + """File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://' to upload a new one using multipart/form-data under name. :ref:`More info on Sending Files » `""" + thumb: Optional[Union[InputFile, str]] = None + """*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """*Optional*. Caption of the audio to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the audio caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + duration: Optional[int] = None + """*Optional*. Duration of the audio in seconds""" + performer: Optional[str] = None + """*Optional*. Performer of the audio""" + title: Optional[str] = None + """*Optional*. Title of the audio""" diff --git a/aiogram/types/input_media_document.py b/aiogram/types/input_media_document.py new file mode 100644 index 00000000..0e3df5fe --- /dev/null +++ b/aiogram/types/input_media_document.py @@ -0,0 +1,35 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional, Union + +from pydantic import Field + +from .base import UNSET +from .input_media import InputMedia + +if TYPE_CHECKING: # pragma: no cover + from .input_file import InputFile + from .message_entity import MessageEntity + + +class InputMediaDocument(InputMedia): + """ + Represents a general file to be sent. + + Source: https://core.telegram.org/bots/api#inputmediadocument + """ + + type: str = Field("document", const=True) + """Type of the result, must be *document*""" + media: Union[str, InputFile] + """File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://' to upload a new one using multipart/form-data under name. :ref:`More info on Sending Files » `""" + thumb: Optional[Union[InputFile, str]] = None + """*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """*Optional*. Caption of the document to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the document caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + disable_content_type_detection: Optional[bool] = None + """*Optional*. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always true, if the document is sent as part of an album.""" diff --git a/aiogram/types/input_media_photo.py b/aiogram/types/input_media_photo.py new file mode 100644 index 00000000..f32be92f --- /dev/null +++ b/aiogram/types/input_media_photo.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional, Union + +from pydantic import Field + +from .base import UNSET +from .input_media import InputMedia + +if TYPE_CHECKING: # pragma: no cover + from .input_file import InputFile + from .message_entity import MessageEntity + + +class InputMediaPhoto(InputMedia): + """ + Represents a photo to be sent. + + Source: https://core.telegram.org/bots/api#inputmediaphoto + """ + + type: str = Field("photo", const=True) + """Type of the result, must be *photo*""" + media: Union[str, InputFile] + """File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://' to upload a new one using multipart/form-data under name. :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """*Optional*. Caption of the photo to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the photo caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" diff --git a/aiogram/types/input_media_video.py b/aiogram/types/input_media_video.py new file mode 100644 index 00000000..b6328736 --- /dev/null +++ b/aiogram/types/input_media_video.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional, Union + +from pydantic import Field + +from .base import UNSET +from .input_media import InputMedia + +if TYPE_CHECKING: # pragma: no cover + from .input_file import InputFile + from .message_entity import MessageEntity + + +class InputMediaVideo(InputMedia): + """ + Represents a video to be sent. + + Source: https://core.telegram.org/bots/api#inputmediavideo + """ + + type: str = Field("video", const=True) + """Type of the result, must be *video*""" + media: Union[str, InputFile] + """File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass 'attach://' to upload a new one using multipart/form-data under name. :ref:`More info on Sending Files » `""" + thumb: Optional[Union[InputFile, str]] = None + """*Optional*. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass 'attach://' if the thumbnail was uploaded using multipart/form-data under . :ref:`More info on Sending Files » `""" + caption: Optional[str] = None + """*Optional*. Caption of the video to be sent, 0-1024 characters after entities parsing""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the video caption. See `formatting options `_ for more details.""" + caption_entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in the caption, which can be specified instead of *parse_mode*""" + width: Optional[int] = None + """*Optional*. Video width""" + height: Optional[int] = None + """*Optional*. Video height""" + duration: Optional[int] = None + """*Optional*. Video duration""" + supports_streaming: Optional[bool] = None + """*Optional*. Pass :code:`True`, if the uploaded video is suitable for streaming""" diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py new file mode 100644 index 00000000..b763dd92 --- /dev/null +++ b/aiogram/types/input_message_content.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from .base import TelegramObject + + +class InputMessageContent(TelegramObject): + """ + This object represents the content of a message to be sent as a result of an inline query. Telegram clients currently support the following 4 types: + + - :class:`aiogram.types.input_text_message_content.InputTextMessageContent` + - :class:`aiogram.types.input_location_message_content.InputLocationMessageContent` + - :class:`aiogram.types.input_venue_message_content.InputVenueMessageContent` + - :class:`aiogram.types.input_contact_message_content.InputContactMessageContent` + + Source: https://core.telegram.org/bots/api#inputmessagecontent + """ diff --git a/aiogram/types/input_text_message_content.py b/aiogram/types/input_text_message_content.py new file mode 100644 index 00000000..5d0950a1 --- /dev/null +++ b/aiogram/types/input_text_message_content.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from .base import UNSET +from .input_message_content import InputMessageContent + +if TYPE_CHECKING: # pragma: no cover + from .message_entity import MessageEntity + + +class InputTextMessageContent(InputMessageContent): + """ + Represents the `content `_ of a text message to be sent as the result of an inline query. + + Source: https://core.telegram.org/bots/api#inputtextmessagecontent + """ + + message_text: str + """Text of the message to be sent, 1-4096 characters""" + parse_mode: Optional[str] = UNSET + """*Optional*. Mode for parsing entities in the message text. See `formatting options `_ for more details.""" + entities: Optional[List[MessageEntity]] = None + """*Optional*. List of special entities that appear in message text, which can be specified instead of *parse_mode*""" + disable_web_page_preview: Optional[bool] = None + """*Optional*. Disables link previews for links in the sent message""" diff --git a/aiogram/types/input_venue_message_content.py b/aiogram/types/input_venue_message_content.py new file mode 100644 index 00000000..f6ccd76f --- /dev/null +++ b/aiogram/types/input_venue_message_content.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import Optional + +from .input_message_content import InputMessageContent + + +class InputVenueMessageContent(InputMessageContent): + """ + Represents the `content `_ of a venue message to be sent as the result of an inline query. + + Source: https://core.telegram.org/bots/api#inputvenuemessagecontent + """ + + latitude: float + """Latitude of the venue in degrees""" + longitude: float + """Longitude of the venue in degrees""" + title: str + """Name of the venue""" + address: str + """Address of the venue""" + foursquare_id: Optional[str] = None + """*Optional*. Foursquare identifier of the venue, if known""" + foursquare_type: Optional[str] = None + """*Optional*. Foursquare type of the venue, if known. (For example, 'arts_entertainment/default', 'arts_entertainment/aquarium' or 'food/icecream'.)""" + google_place_id: Optional[str] = None + """*Optional*. Google Places identifier of the venue""" + google_place_type: Optional[str] = None + """*Optional*. Google Places type of the venue. (See `supported types `_.)""" diff --git a/aiogram/types/invoice.py b/aiogram/types/invoice.py new file mode 100644 index 00000000..1e734895 --- /dev/null +++ b/aiogram/types/invoice.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from .base import TelegramObject + + +class Invoice(TelegramObject): + """ + This object contains basic information about an invoice. + + Source: https://core.telegram.org/bots/api#invoice + """ + + title: str + """Product name""" + description: str + """Product description""" + start_parameter: str + """Unique bot deep-linking parameter that can be used to generate this invoice""" + currency: str + """Three-letter ISO 4217 `currency `_ code""" + total_amount: int + """Total price in the *smallest units* of the currency (integer, **not** float/double). For example, for a price of :code:`US$ 1.45` pass :code:`amount = 145`. See the *exp* parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).""" diff --git a/aiogram/types/keyboard_button.py b/aiogram/types/keyboard_button.py new file mode 100644 index 00000000..a5d83cbc --- /dev/null +++ b/aiogram/types/keyboard_button.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from .base import MutableTelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .keyboard_button_poll_type import KeyboardButtonPollType + + +class KeyboardButton(MutableTelegramObject): + """ + This object represents one button of the reply keyboard. For simple text buttons *String* can be used instead of this object to specify text of the button. Optional fields *request_contact*, *request_location*, and *request_poll* are mutually exclusive. + **Note:** *request_contact* and *request_location* options will only work in Telegram versions released after 9 April, 2016. Older clients will display *unsupported message*. + + **Note:** *request_poll* option will only work in Telegram versions released after 23 January, 2020. Older clients will display *unsupported message*. + + Source: https://core.telegram.org/bots/api#keyboardbutton + """ + + text: str + """Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed""" + request_contact: Optional[bool] = None + """*Optional*. If :code:`True`, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only""" + request_location: Optional[bool] = None + """*Optional*. If :code:`True`, the user's current location will be sent when the button is pressed. Available in private chats only""" + request_poll: Optional[KeyboardButtonPollType] = None + """*Optional*. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only""" diff --git a/aiogram/api/types/keyboard_button_poll_type.py b/aiogram/types/keyboard_button_poll_type.py similarity index 53% rename from aiogram/api/types/keyboard_button_poll_type.py rename to aiogram/types/keyboard_button_poll_type.py index fa216e45..2747adfb 100644 --- a/aiogram/api/types/keyboard_button_poll_type.py +++ b/aiogram/types/keyboard_button_poll_type.py @@ -7,13 +7,10 @@ from .base import MutableTelegramObject class KeyboardButtonPollType(MutableTelegramObject): """ - This object represents type of a poll, which is allowed to be created and sent when the - corresponding button is pressed. + This object represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed. Source: https://core.telegram.org/bots/api#keyboardbuttonpolltype """ type: Optional[str] = None - """If quiz is passed, the user will be allowed to create only polls in the quiz mode. If - regular is passed, only regular polls will be allowed. Otherwise, the user will be allowed - to create a poll of any type.""" + """*Optional*. If *quiz* is passed, the user will be allowed to create only polls in the quiz mode. If *regular* is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type.""" diff --git a/aiogram/types/labeled_price.py b/aiogram/types/labeled_price.py new file mode 100644 index 00000000..3dcffb89 --- /dev/null +++ b/aiogram/types/labeled_price.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from .base import MutableTelegramObject + + +class LabeledPrice(MutableTelegramObject): + """ + This object represents a portion of the price for goods or services. + + Source: https://core.telegram.org/bots/api#labeledprice + """ + + label: str + """Portion label""" + amount: int + """Price of the product in the *smallest units* of the `currency `_ (integer, **not** float/double). For example, for a price of :code:`US$ 1.45` pass :code:`amount = 145`. See the *exp* parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).""" diff --git a/aiogram/types/location.py b/aiogram/types/location.py new file mode 100644 index 00000000..e86c56f3 --- /dev/null +++ b/aiogram/types/location.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from typing import Optional + +from .base import TelegramObject + + +class Location(TelegramObject): + """ + This object represents a point on the map. + + Source: https://core.telegram.org/bots/api#location + """ + + longitude: float + """Longitude as defined by sender""" + latitude: float + """Latitude as defined by sender""" + horizontal_accuracy: Optional[float] = None + """*Optional*. The radius of uncertainty for the location, measured in meters; 0-1500""" + live_period: Optional[int] = None + """*Optional*. Time relative to the message sending date, during which the location can be updated, in seconds. For active live locations only.""" + heading: Optional[int] = None + """*Optional*. The direction in which user is moving, in degrees; 1-360. For active live locations only.""" + proximity_alert_radius: Optional[int] = None + """*Optional*. Maximum distance for proximity alerts about approaching another chat member, in meters. For sent live locations only.""" diff --git a/aiogram/types/login_url.py b/aiogram/types/login_url.py new file mode 100644 index 00000000..ff969643 --- /dev/null +++ b/aiogram/types/login_url.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import Optional + +from .base import TelegramObject + + +class LoginUrl(TelegramObject): + """ + This object represents a parameter of the inline keyboard button used to automatically authorize a user. Serves as a great replacement for the `Telegram Login Widget `_ when the user is coming from Telegram. All the user needs to do is tap/click a button and confirm that they want to log in: + Telegram apps support these buttons as of `version 5.7 `_. + + Sample bot: `@discussbot `_ + + Source: https://core.telegram.org/bots/api#loginurl + """ + + url: str + """An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in `Receiving authorization data `_.""" + forward_text: Optional[str] = None + """*Optional*. New text of the button in forwarded messages.""" + bot_username: Optional[str] = None + """*Optional*. Username of a bot, which will be used for user authorization. See `Setting up a bot `_ for more details. If not specified, the current bot's username will be assumed. The *url*'s domain must be the same as the domain linked with the bot. See `Linking your domain to the bot `_ for more details.""" + request_write_access: Optional[bool] = None + """*Optional*. Pass True to request the permission for your bot to send messages to the user.""" diff --git a/aiogram/api/types/mask_position.py b/aiogram/types/mask_position.py similarity index 66% rename from aiogram/api/types/mask_position.py rename to aiogram/types/mask_position.py index a9bc365d..3eea5bda 100644 --- a/aiogram/api/types/mask_position.py +++ b/aiogram/types/mask_position.py @@ -11,13 +11,10 @@ class MaskPosition(TelegramObject): """ point: str - """The part of the face relative to which the mask should be placed. One of 'forehead', - 'eyes', 'mouth', or 'chin'.""" + """The part of the face relative to which the mask should be placed. One of 'forehead', 'eyes', 'mouth', or 'chin'.""" x_shift: float - """Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. - For example, choosing -1.0 will place mask just to the left of the default mask position.""" + """Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position.""" y_shift: float - """Shift by Y-axis measured in heights of the mask scaled to the face size, from top to - bottom. For example, 1.0 will place the mask just below the default mask position.""" + """Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position.""" scale: float """Mask scaling coefficient. For example, 2.0 means double size.""" diff --git a/aiogram/api/types/message.py b/aiogram/types/message.py similarity index 87% rename from aiogram/api/types/message.py rename to aiogram/types/message.py index 67470b66..063f0521 100644 --- a/aiogram/api/types/message.py +++ b/aiogram/types/message.py @@ -5,10 +5,30 @@ from typing import TYPE_CHECKING, List, Optional, Union from pydantic import Field -from ...utils import helper +from aiogram.utils import helper + from .base import UNSET, TelegramObject if TYPE_CHECKING: # pragma: no cover + from ..methods import ( + SendAnimation, + SendAudio, + SendContact, + SendDice, + SendDocument, + SendGame, + SendInvoice, + SendLocation, + SendMediaGroup, + SendMessage, + SendPhoto, + SendPoll, + SendSticker, + SendVenue, + SendVideo, + SendVideoNote, + SendVoice, + ) from .animation import Animation from .audio import Audio from .chat import Chat @@ -18,16 +38,17 @@ if TYPE_CHECKING: # pragma: no cover from .force_reply import ForceReply from .game import Game from .inline_keyboard_markup import InlineKeyboardMarkup - from .invoice import Invoice from .input_file import InputFile from .input_media_photo import InputMediaPhoto from .input_media_video import InputMediaVideo + from .invoice import Invoice from .labeled_price import LabeledPrice from .location import Location from .message_entity import MessageEntity from .passport_data import PassportData from .photo_size import PhotoSize from .poll import Poll + from .proximity_alert_triggered import ProximityAlertTriggered from .reply_keyboard_markup import ReplyKeyboardMarkup from .reply_keyboard_remove import ReplyKeyboardRemove from .sticker import Sticker @@ -38,26 +59,6 @@ if TYPE_CHECKING: # pragma: no cover from .video_note import VideoNote from .voice import Voice - from ..methods import ( - SendAnimation, - SendAudio, - SendContact, - SendDocument, - SendGame, - SendInvoice, - SendLocation, - SendMediaGroup, - SendMessage, - SendPhoto, - SendPoll, - SendDice, - SendSticker, - SendVenue, - SendVideo, - SendVideoNote, - SendVoice, - ) - class Message(TelegramObject): """ @@ -73,119 +74,101 @@ class Message(TelegramObject): chat: Chat """Conversation the message belongs to""" from_user: Optional[User] = Field(None, alias="from") - """Sender, empty for messages sent to channels""" + """*Optional*. Sender, empty for messages sent to channels""" + sender_chat: Optional[Chat] = None + """*Optional*. Sender of the message, sent on behalf of a chat. The channel itself for channel messages. The supergroup itself for messages from anonymous group administrators. The linked channel for messages automatically forwarded to the discussion group""" forward_from: Optional[User] = None - """For forwarded messages, sender of the original message""" + """*Optional*. For forwarded messages, sender of the original message""" forward_from_chat: Optional[Chat] = None - """For messages forwarded from channels, information about the original channel""" + """*Optional*. For messages forwarded from channels or from anonymous administrators, information about the original sender chat""" forward_from_message_id: Optional[int] = None - """For messages forwarded from channels, identifier of the original message in the channel""" + """*Optional*. For messages forwarded from channels, identifier of the original message in the channel""" forward_signature: Optional[str] = None - """For messages forwarded from channels, signature of the post author if present""" + """*Optional*. For messages forwarded from channels, signature of the post author if present""" forward_sender_name: Optional[str] = None - """Sender's name for messages forwarded from users who disallow adding a link to their account - in forwarded messages""" + """*Optional*. Sender's name for messages forwarded from users who disallow adding a link to their account in forwarded messages""" forward_date: Optional[int] = None - """For forwarded messages, date the original message was sent in Unix time""" + """*Optional*. For forwarded messages, date the original message was sent in Unix time""" reply_to_message: Optional[Message] = None - """For replies, the original message. Note that the Message object in this field will not - contain further reply_to_message fields even if it itself is a reply.""" + """*Optional*. For replies, the original message. Note that the Message object in this field will not contain further *reply_to_message* fields even if it itself is a reply.""" via_bot: Optional[User] = None - """Bot through which the message was sent""" + """*Optional*. Bot through which the message was sent""" edit_date: Optional[int] = None - """Date the message was last edited in Unix time""" + """*Optional*. Date the message was last edited in Unix time""" media_group_id: Optional[str] = None - """The unique identifier of a media message group this message belongs to""" + """*Optional*. The unique identifier of a media message group this message belongs to""" author_signature: Optional[str] = None - """Signature of the post author for messages in channels""" + """*Optional*. Signature of the post author for messages in channels, or the custom title of an anonymous group administrator""" text: Optional[str] = None - """For text messages, the actual UTF-8 text of the message, 0-4096 characters""" + """*Optional*. For text messages, the actual UTF-8 text of the message, 0-4096 characters""" entities: Optional[List[MessageEntity]] = None - """For text messages, special entities like usernames, URLs, bot commands, etc. that appear in - the text""" + """*Optional*. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text""" animation: Optional[Animation] = None - """Message is an animation, information about the animation. For backward compatibility, when - this field is set, the document field will also be set""" + """*Optional*. Message is an animation, information about the animation. For backward compatibility, when this field is set, the *document* field will also be set""" audio: Optional[Audio] = None - """Message is an audio file, information about the file""" + """*Optional*. Message is an audio file, information about the file""" document: Optional[Document] = None - """Message is a general file, information about the file""" + """*Optional*. Message is a general file, information about the file""" photo: Optional[List[PhotoSize]] = None - """Message is a photo, available sizes of the photo""" + """*Optional*. Message is a photo, available sizes of the photo""" sticker: Optional[Sticker] = None - """Message is a sticker, information about the sticker""" + """*Optional*. Message is a sticker, information about the sticker""" video: Optional[Video] = None - """Message is a video, information about the video""" + """*Optional*. Message is a video, information about the video""" video_note: Optional[VideoNote] = None - """Message is a video note, information about the video message""" + """*Optional*. Message is a `video note `_, information about the video message""" voice: Optional[Voice] = None - """Message is a voice message, information about the file""" + """*Optional*. Message is a voice message, information about the file""" caption: Optional[str] = None - """Caption for the animation, audio, document, photo, video or voice, 0-1024 characters""" + """*Optional*. Caption for the animation, audio, document, photo, video or voice, 0-1024 characters""" caption_entities: Optional[List[MessageEntity]] = None - """For messages with a caption, special entities like usernames, URLs, bot commands, etc. that - appear in the caption""" + """*Optional*. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption""" contact: Optional[Contact] = None - """Message is a shared contact, information about the contact""" + """*Optional*. Message is a shared contact, information about the contact""" dice: Optional[Dice] = None - """Message is a dice with random value from 1 to 6""" + """*Optional*. Message is a dice with random value""" game: Optional[Game] = None - """Message is a game, information about the game.""" + """*Optional*. Message is a game, information about the game. `More about games » `_""" poll: Optional[Poll] = None - """Message is a native poll, information about the poll""" + """*Optional*. Message is a native poll, information about the poll""" venue: Optional[Venue] = None - """Message is a venue, information about the venue. For backward compatibility, when this - field is set, the location field will also be set""" + """*Optional*. Message is a venue, information about the venue. For backward compatibility, when this field is set, the *location* field will also be set""" location: Optional[Location] = None - """Message is a shared location, information about the location""" + """*Optional*. Message is a shared location, information about the location""" new_chat_members: Optional[List[User]] = None - """New members that were added to the group or supergroup and information about them (the bot - itself may be one of these members)""" + """*Optional*. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members)""" left_chat_member: Optional[User] = None - """A member was removed from the group, information about them (this member may be the bot - itself)""" + """*Optional*. A member was removed from the group, information about them (this member may be the bot itself)""" new_chat_title: Optional[str] = None - """A chat title was changed to this value""" + """*Optional*. A chat title was changed to this value""" new_chat_photo: Optional[List[PhotoSize]] = None - """A chat photo was change to this value""" + """*Optional*. A chat photo was change to this value""" delete_chat_photo: Optional[bool] = None - """Service message: the chat photo was deleted""" + """*Optional*. Service message: the chat photo was deleted""" group_chat_created: Optional[bool] = None - """Service message: the group has been created""" + """*Optional*. Service message: the group has been created""" supergroup_chat_created: Optional[bool] = None - """Service message: the supergroup has been created. This field can't be received in a message - coming through updates, because bot can't be a member of a supergroup when it is created. - It can only be found in reply_to_message if someone replies to a very first message in a - directly created supergroup.""" + """*Optional*. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.""" channel_chat_created: Optional[bool] = None - """Service message: the channel has been created. This field can't be received in a message - coming through updates, because bot can't be a member of a channel when it is created. It - can only be found in reply_to_message if someone replies to a very first message in a - channel.""" + """*Optional*. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can't be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel.""" migrate_to_chat_id: Optional[int] = None - """The group has been migrated to a supergroup with the specified identifier. This number may - be greater than 32 bits and some programming languages may have difficulty/silent defects - in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or - double-precision float type are safe for storing this identifier.""" + """*Optional*. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.""" migrate_from_chat_id: Optional[int] = None - """The supergroup has been migrated from a group with the specified identifier. This number - may be greater than 32 bits and some programming languages may have difficulty/silent - defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or - double-precision float type are safe for storing this identifier.""" + """*Optional*. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.""" pinned_message: Optional[Message] = None - """Specified message was pinned. Note that the Message object in this field will not contain - further reply_to_message fields even if it is itself a reply.""" + """*Optional*. Specified message was pinned. Note that the Message object in this field will not contain further *reply_to_message* fields even if it is itself a reply.""" invoice: Optional[Invoice] = None - """Message is an invoice for a payment, information about the invoice.""" + """*Optional*. Message is an invoice for a `payment `_, information about the invoice. `More about payments » `_""" successful_payment: Optional[SuccessfulPayment] = None - """Message is a service message about a successful payment, information about the payment.""" + """*Optional*. Message is a service message about a successful payment, information about the payment. `More about payments » `_""" connected_website: Optional[str] = None - """The domain name of the website on which the user has logged in.""" + """*Optional*. The domain name of the website on which the user has logged in. `More about Telegram Login » `_""" passport_data: Optional[PassportData] = None - """Telegram Passport data""" + """*Optional*. Telegram Passport data""" + proximity_alert_triggered: Optional[ProximityAlertTriggered] = None + """*Optional*. Service message. A user in the chat triggered another user's proximity alert while sharing Live Location.""" reply_markup: Optional[InlineKeyboardMarkup] = None - """Inline keyboard attached to the message. login_url buttons are represented as ordinary url - buttons.""" + """*Optional*. Inline keyboard attached to the message. :code:`login_url` buttons are represented as ordinary :code:`url` buttons.""" @property def content_type(self) -> str: diff --git a/aiogram/types/message_entity.py b/aiogram/types/message_entity.py new file mode 100644 index 00000000..c0324656 --- /dev/null +++ b/aiogram/types/message_entity.py @@ -0,0 +1,29 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from .base import MutableTelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .user import User + + +class MessageEntity(MutableTelegramObject): + """ + This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc. + + Source: https://core.telegram.org/bots/api#messageentity + """ + + type: str + """Type of the entity. Can be 'mention' (:code:`@username`), 'hashtag' (:code:`#hashtag`), 'cashtag' (:code:`$USD`), 'bot_command' (:code:`/start@jobs_bot`), 'url' (:code:`https://telegram.org`), 'email' (:code:`do-not-reply@telegram.org`), 'phone_number' (:code:`+1-212-555-0123`), 'bold' (**bold text**), 'italic' (*italic text*), 'underline' (underlined text), 'strikethrough' (strikethrough text), 'code' (monowidth string), 'pre' (monowidth block), 'text_link' (for clickable text URLs), 'text_mention' (for users `without usernames `_)""" + offset: int + """Offset in UTF-16 code units to the start of the entity""" + length: int + """Length of the entity in UTF-16 code units""" + url: Optional[str] = None + """*Optional*. For 'text_link' only, url that will be opened after user taps on the text""" + user: Optional[User] = None + """*Optional*. For 'text_mention' only, the mentioned user""" + language: Optional[str] = None + """*Optional*. For 'pre' only, the programming language of the entity text""" diff --git a/aiogram/types/message_id.py b/aiogram/types/message_id.py new file mode 100644 index 00000000..edd4784c --- /dev/null +++ b/aiogram/types/message_id.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from .base import TelegramObject + + +class MessageId(TelegramObject): + """ + This object represents a unique message identifier. + + Source: https://core.telegram.org/bots/api#messageid + """ + + message_id: int + """Unique message identifier""" diff --git a/aiogram/api/types/order_info.py b/aiogram/types/order_info.py similarity index 77% rename from aiogram/api/types/order_info.py rename to aiogram/types/order_info.py index f96bc4cc..5283a967 100644 --- a/aiogram/api/types/order_info.py +++ b/aiogram/types/order_info.py @@ -16,10 +16,10 @@ class OrderInfo(TelegramObject): """ name: Optional[str] = None - """User name""" + """*Optional*. User name""" phone_number: Optional[str] = None - """User's phone number""" + """*Optional*. User's phone number""" email: Optional[str] = None - """User email""" + """*Optional*. User email""" shipping_address: Optional[ShippingAddress] = None - """User shipping address""" + """*Optional*. User shipping address""" diff --git a/aiogram/api/types/passport_data.py b/aiogram/types/passport_data.py similarity index 92% rename from aiogram/api/types/passport_data.py rename to aiogram/types/passport_data.py index da625263..028334b2 100644 --- a/aiogram/api/types/passport_data.py +++ b/aiogram/types/passport_data.py @@ -17,7 +17,6 @@ class PassportData(TelegramObject): """ data: List[EncryptedPassportElement] - """Array with information about documents and other Telegram Passport elements that was shared - with the bot""" + """Array with information about documents and other Telegram Passport elements that was shared with the bot""" credentials: EncryptedCredentials """Encrypted credentials required to decrypt the data""" diff --git a/aiogram/types/passport_element_error.py b/aiogram/types/passport_element_error.py new file mode 100644 index 00000000..36a1db86 --- /dev/null +++ b/aiogram/types/passport_element_error.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +from .base import MutableTelegramObject + + +class PassportElementError(MutableTelegramObject): + """ + This object represents an error in the Telegram Passport element which was submitted that should be resolved by the user. It should be one of: + + - :class:`aiogram.types.passport_element_error_data_field.PassportElementErrorDataField` + - :class:`aiogram.types.passport_element_error_front_side.PassportElementErrorFrontSide` + - :class:`aiogram.types.passport_element_error_reverse_side.PassportElementErrorReverseSide` + - :class:`aiogram.types.passport_element_error_selfie.PassportElementErrorSelfie` + - :class:`aiogram.types.passport_element_error_file.PassportElementErrorFile` + - :class:`aiogram.types.passport_element_error_files.PassportElementErrorFiles` + - :class:`aiogram.types.passport_element_error_translation_file.PassportElementErrorTranslationFile` + - :class:`aiogram.types.passport_element_error_translation_files.PassportElementErrorTranslationFiles` + - :class:`aiogram.types.passport_element_error_unspecified.PassportElementErrorUnspecified` + + Source: https://core.telegram.org/bots/api#passportelementerror + """ diff --git a/aiogram/api/types/passport_element_error_data_field.py b/aiogram/types/passport_element_error_data_field.py similarity index 72% rename from aiogram/api/types/passport_element_error_data_field.py rename to aiogram/types/passport_element_error_data_field.py index 41dcc790..f5b0b67b 100644 --- a/aiogram/api/types/passport_element_error_data_field.py +++ b/aiogram/types/passport_element_error_data_field.py @@ -7,17 +7,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorDataField(PassportElementError): """ - Represents an issue in one of the data fields that was provided by the user. The error is - considered resolved when the field's value changes. + Represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes. Source: https://core.telegram.org/bots/api#passportelementerrordatafield """ source: str = Field("data", const=True) - """Error source, must be data""" + """Error source, must be *data*""" type: str - """The section of the user's Telegram Passport which has the error, one of 'personal_details', - 'passport', 'driver_license', 'identity_card', 'internal_passport', 'address'""" + """The section of the user's Telegram Passport which has the error, one of 'personal_details', 'passport', 'driver_license', 'identity_card', 'internal_passport', 'address'""" field_name: str """Name of the data field which has the error""" data_hash: str diff --git a/aiogram/api/types/passport_element_error_file.py b/aiogram/types/passport_element_error_file.py similarity index 70% rename from aiogram/api/types/passport_element_error_file.py rename to aiogram/types/passport_element_error_file.py index 90e4ffd5..217deaaa 100644 --- a/aiogram/api/types/passport_element_error_file.py +++ b/aiogram/types/passport_element_error_file.py @@ -7,17 +7,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorFile(PassportElementError): """ - Represents an issue with a document scan. The error is considered resolved when the file with - the document scan changes. + Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes. Source: https://core.telegram.org/bots/api#passportelementerrorfile """ source: str = Field("file", const=True) - """Error source, must be file""" + """Error source, must be *file*""" type: str - """The section of the user's Telegram Passport which has the issue, one of 'utility_bill', - 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'""" + """The section of the user's Telegram Passport which has the issue, one of 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'""" file_hash: str """Base64-encoded file hash""" message: str diff --git a/aiogram/api/types/passport_element_error_files.py b/aiogram/types/passport_element_error_files.py similarity index 71% rename from aiogram/api/types/passport_element_error_files.py rename to aiogram/types/passport_element_error_files.py index 83507e2a..6f42d693 100644 --- a/aiogram/api/types/passport_element_error_files.py +++ b/aiogram/types/passport_element_error_files.py @@ -9,17 +9,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorFiles(PassportElementError): """ - Represents an issue with a list of scans. The error is considered resolved when the list of - files containing the scans changes. + Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes. Source: https://core.telegram.org/bots/api#passportelementerrorfiles """ source: str = Field("files", const=True) - """Error source, must be files""" + """Error source, must be *files*""" type: str - """The section of the user's Telegram Passport which has the issue, one of 'utility_bill', - 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'""" + """The section of the user's Telegram Passport which has the issue, one of 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'""" file_hashes: List[str] """List of base64-encoded file hashes""" message: str diff --git a/aiogram/api/types/passport_element_error_front_side.py b/aiogram/types/passport_element_error_front_side.py similarity index 73% rename from aiogram/api/types/passport_element_error_front_side.py rename to aiogram/types/passport_element_error_front_side.py index 6c8d40d6..98e60acd 100644 --- a/aiogram/api/types/passport_element_error_front_side.py +++ b/aiogram/types/passport_element_error_front_side.py @@ -7,17 +7,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorFrontSide(PassportElementError): """ - Represents an issue with the front side of a document. The error is considered resolved when - the file with the front side of the document changes. + Represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes. Source: https://core.telegram.org/bots/api#passportelementerrorfrontside """ source: str = Field("front_side", const=True) - """Error source, must be front_side""" + """Error source, must be *front_side*""" type: str - """The section of the user's Telegram Passport which has the issue, one of 'passport', - 'driver_license', 'identity_card', 'internal_passport'""" + """The section of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport'""" file_hash: str """Base64-encoded hash of the file with the front side of the document""" message: str diff --git a/aiogram/api/types/passport_element_error_reverse_side.py b/aiogram/types/passport_element_error_reverse_side.py similarity index 76% rename from aiogram/api/types/passport_element_error_reverse_side.py rename to aiogram/types/passport_element_error_reverse_side.py index abcb7739..0c6073ba 100644 --- a/aiogram/api/types/passport_element_error_reverse_side.py +++ b/aiogram/types/passport_element_error_reverse_side.py @@ -7,17 +7,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorReverseSide(PassportElementError): """ - Represents an issue with the reverse side of a document. The error is considered resolved when - the file with reverse side of the document changes. + Represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes. Source: https://core.telegram.org/bots/api#passportelementerrorreverseside """ source: str = Field("reverse_side", const=True) - """Error source, must be reverse_side""" + """Error source, must be *reverse_side*""" type: str - """The section of the user's Telegram Passport which has the issue, one of 'driver_license', - 'identity_card'""" + """The section of the user's Telegram Passport which has the issue, one of 'driver_license', 'identity_card'""" file_hash: str """Base64-encoded hash of the file with the reverse side of the document""" message: str diff --git a/aiogram/api/types/passport_element_error_selfie.py b/aiogram/types/passport_element_error_selfie.py similarity index 74% rename from aiogram/api/types/passport_element_error_selfie.py rename to aiogram/types/passport_element_error_selfie.py index 772d2880..4a3b2fe1 100644 --- a/aiogram/api/types/passport_element_error_selfie.py +++ b/aiogram/types/passport_element_error_selfie.py @@ -7,17 +7,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorSelfie(PassportElementError): """ - Represents an issue with the selfie with a document. The error is considered resolved when the - file with the selfie changes. + Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes. Source: https://core.telegram.org/bots/api#passportelementerrorselfie """ source: str = Field("selfie", const=True) - """Error source, must be selfie""" + """Error source, must be *selfie*""" type: str - """The section of the user's Telegram Passport which has the issue, one of 'passport', - 'driver_license', 'identity_card', 'internal_passport'""" + """The section of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport'""" file_hash: str """Base64-encoded hash of the file with the selfie""" message: str diff --git a/aiogram/api/types/passport_element_error_translation_file.py b/aiogram/types/passport_element_error_translation_file.py similarity index 63% rename from aiogram/api/types/passport_element_error_translation_file.py rename to aiogram/types/passport_element_error_translation_file.py index a9d952e2..d4106453 100644 --- a/aiogram/api/types/passport_element_error_translation_file.py +++ b/aiogram/types/passport_element_error_translation_file.py @@ -7,18 +7,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorTranslationFile(PassportElementError): """ - Represents an issue with one of the files that constitute the translation of a document. The - error is considered resolved when the file changes. + Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes. Source: https://core.telegram.org/bots/api#passportelementerrortranslationfile """ source: str = Field("translation_file", const=True) - """Error source, must be translation_file""" + """Error source, must be *translation_file*""" type: str - """Type of element of the user's Telegram Passport which has the issue, one of 'passport', - 'driver_license', 'identity_card', 'internal_passport', 'utility_bill', 'bank_statement', - 'rental_agreement', 'passport_registration', 'temporary_registration'""" + """Type of element of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport', 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'""" file_hash: str """Base64-encoded file hash""" message: str diff --git a/aiogram/api/types/passport_element_error_translation_files.py b/aiogram/types/passport_element_error_translation_files.py similarity index 64% rename from aiogram/api/types/passport_element_error_translation_files.py rename to aiogram/types/passport_element_error_translation_files.py index 06d90f59..df0db64f 100644 --- a/aiogram/api/types/passport_element_error_translation_files.py +++ b/aiogram/types/passport_element_error_translation_files.py @@ -9,18 +9,15 @@ from .passport_element_error import PassportElementError class PassportElementErrorTranslationFiles(PassportElementError): """ - Represents an issue with the translated version of a document. The error is considered - resolved when a file with the document translation change. + Represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation change. Source: https://core.telegram.org/bots/api#passportelementerrortranslationfiles """ source: str = Field("translation_files", const=True) - """Error source, must be translation_files""" + """Error source, must be *translation_files*""" type: str - """Type of element of the user's Telegram Passport which has the issue, one of 'passport', - 'driver_license', 'identity_card', 'internal_passport', 'utility_bill', 'bank_statement', - 'rental_agreement', 'passport_registration', 'temporary_registration'""" + """Type of element of the user's Telegram Passport which has the issue, one of 'passport', 'driver_license', 'identity_card', 'internal_passport', 'utility_bill', 'bank_statement', 'rental_agreement', 'passport_registration', 'temporary_registration'""" file_hashes: List[str] """List of base64-encoded file hashes""" message: str diff --git a/aiogram/api/types/passport_element_error_unspecified.py b/aiogram/types/passport_element_error_unspecified.py similarity index 86% rename from aiogram/api/types/passport_element_error_unspecified.py rename to aiogram/types/passport_element_error_unspecified.py index cdccba7d..39d0c417 100644 --- a/aiogram/api/types/passport_element_error_unspecified.py +++ b/aiogram/types/passport_element_error_unspecified.py @@ -7,14 +7,13 @@ from .passport_element_error import PassportElementError class PassportElementErrorUnspecified(PassportElementError): """ - Represents an issue in an unspecified place. The error is considered resolved when new data is - added. + Represents an issue in an unspecified place. The error is considered resolved when new data is added. Source: https://core.telegram.org/bots/api#passportelementerrorunspecified """ source: str = Field("unspecified", const=True) - """Error source, must be unspecified""" + """Error source, must be *unspecified*""" type: str """Type of element of the user's Telegram Passport which has the issue""" element_hash: str diff --git a/aiogram/api/types/passport_file.py b/aiogram/types/passport_file.py similarity index 73% rename from aiogram/api/types/passport_file.py rename to aiogram/types/passport_file.py index 9b7185f2..4ec8b6ec 100644 --- a/aiogram/api/types/passport_file.py +++ b/aiogram/types/passport_file.py @@ -5,8 +5,7 @@ from .base import TelegramObject class PassportFile(TelegramObject): """ - This object represents a file uploaded to Telegram Passport. Currently all Telegram Passport - files are in JPEG format when decrypted and don't exceed 10MB. + This object represents a file uploaded to Telegram Passport. Currently all Telegram Passport files are in JPEG format when decrypted and don't exceed 10MB. Source: https://core.telegram.org/bots/api#passportfile """ @@ -14,8 +13,7 @@ class PassportFile(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" file_size: int """File size""" file_date: int diff --git a/aiogram/api/types/photo_size.py b/aiogram/types/photo_size.py similarity index 64% rename from aiogram/api/types/photo_size.py rename to aiogram/types/photo_size.py index 7aeb89fd..2bc2e5b1 100644 --- a/aiogram/api/types/photo_size.py +++ b/aiogram/types/photo_size.py @@ -7,7 +7,7 @@ from .base import TelegramObject class PhotoSize(TelegramObject): """ - This object represents one size of a photo or a file / sticker thumbnail. + This object represents one size of a photo or a `file `_ / :class:`aiogram.methods.sticker.Sticker` thumbnail. Source: https://core.telegram.org/bots/api#photosize """ @@ -15,11 +15,10 @@ class PhotoSize(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" width: int """Photo width""" height: int """Photo height""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" diff --git a/aiogram/api/types/poll.py b/aiogram/types/poll.py similarity index 62% rename from aiogram/api/types/poll.py rename to aiogram/types/poll.py index c19808fc..bcb80085 100644 --- a/aiogram/api/types/poll.py +++ b/aiogram/types/poll.py @@ -20,7 +20,7 @@ class Poll(TelegramObject): id: str """Unique poll identifier""" question: str - """Poll question, 1-255 characters""" + """Poll question, 1-300 characters""" options: List[PollOption] """List of poll options""" total_voter_count: int @@ -34,15 +34,12 @@ class Poll(TelegramObject): allows_multiple_answers: bool """True, if the poll allows multiple answers""" correct_option_id: Optional[int] = None - """0-based identifier of the correct answer option. Available only for polls in the quiz mode, - which are closed, or was sent (not forwarded) by the bot or to the private chat with the - bot.""" + """*Optional*. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.""" explanation: Optional[str] = None - """Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a - quiz-style poll, 0-200 characters""" + """*Optional*. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters""" explanation_entities: Optional[List[MessageEntity]] = None - """Special entities like usernames, URLs, bot commands, etc. that appear in the explanation""" + """*Optional*. Special entities like usernames, URLs, bot commands, etc. that appear in the *explanation*""" open_period: Optional[int] = None - """Amount of time in seconds the poll will be active after creation""" + """*Optional*. Amount of time in seconds the poll will be active after creation""" close_date: Optional[Union[datetime.datetime, datetime.timedelta, int]] = None - """Point in time (Unix timestamp) when the poll will be automatically closed""" + """*Optional*. Point in time (Unix timestamp) when the poll will be automatically closed""" diff --git a/aiogram/api/types/poll_answer.py b/aiogram/types/poll_answer.py similarity index 91% rename from aiogram/api/types/poll_answer.py rename to aiogram/types/poll_answer.py index a0498019..2f394451 100644 --- a/aiogram/api/types/poll_answer.py +++ b/aiogram/types/poll_answer.py @@ -20,5 +20,4 @@ class PollAnswer(TelegramObject): user: User """The user, who changed the answer to the poll""" option_ids: List[int] - """0-based identifiers of answer options, chosen by the user. May be empty if the user - retracted their vote.""" + """0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote.""" diff --git a/aiogram/api/types/poll_option.py b/aiogram/types/poll_option.py similarity index 100% rename from aiogram/api/types/poll_option.py rename to aiogram/types/poll_option.py diff --git a/aiogram/api/types/pre_checkout_query.py b/aiogram/types/pre_checkout_query.py similarity index 60% rename from aiogram/api/types/pre_checkout_query.py rename to aiogram/types/pre_checkout_query.py index e99f4378..fdac4033 100644 --- a/aiogram/api/types/pre_checkout_query.py +++ b/aiogram/types/pre_checkout_query.py @@ -7,9 +7,9 @@ from pydantic import Field from .base import TelegramObject if TYPE_CHECKING: # pragma: no cover + from ..methods import AnswerPreCheckoutQuery from .order_info import OrderInfo from .user import User - from ..methods import AnswerPreCheckoutQuery class PreCheckoutQuery(TelegramObject): @@ -24,18 +24,15 @@ class PreCheckoutQuery(TelegramObject): from_user: User = Field(..., alias="from") """User who sent the query""" currency: str - """Three-letter ISO 4217 currency code""" + """Three-letter ISO 4217 `currency `_ code""" total_amount: int - """Total price in the smallest units of the currency (integer, not float/double). For example, - for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it - shows the number of digits past the decimal point for each currency (2 for the majority of - currencies).""" + """Total price in the *smallest units* of the currency (integer, **not** float/double). For example, for a price of :code:`US$ 1.45` pass :code:`amount = 145`. See the *exp* parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).""" invoice_payload: str """Bot specified invoice payload""" shipping_option_id: Optional[str] = None - """Identifier of the shipping option chosen by the user""" + """*Optional*. Identifier of the shipping option chosen by the user""" order_info: Optional[OrderInfo] = None - """Order info provided by the user""" + """*Optional*. Order info provided by the user""" def answer(self, ok: bool, error_message: Optional[str] = None) -> AnswerPreCheckoutQuery: """ @@ -46,5 +43,7 @@ class PreCheckoutQuery(TelegramObject): from ..methods import AnswerPreCheckoutQuery return AnswerPreCheckoutQuery( - pre_checkout_query_id=self.id, ok=ok, error_message=error_message, + pre_checkout_query_id=self.id, + ok=ok, + error_message=error_message, ) diff --git a/aiogram/types/proximity_alert_triggered.py b/aiogram/types/proximity_alert_triggered.py new file mode 100644 index 00000000..95b707ec --- /dev/null +++ b/aiogram/types/proximity_alert_triggered.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .user import User + + +class ProximityAlertTriggered(TelegramObject): + """ + This object represents the content of a service message, sent whenever a user in the chat triggers a proximity alert set by another user. + + Source: https://core.telegram.org/bots/api#proximityalerttriggered + """ + + traveler: User + """User that triggered the alert""" + watcher: User + """User that set the alert""" + distance: int + """The distance between the users""" diff --git a/aiogram/types/reply_keyboard_markup.py b/aiogram/types/reply_keyboard_markup.py new file mode 100644 index 00000000..33c364d0 --- /dev/null +++ b/aiogram/types/reply_keyboard_markup.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Optional + +from .base import MutableTelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .keyboard_button import KeyboardButton + + +class ReplyKeyboardMarkup(MutableTelegramObject): + """ + This object represents a `custom keyboard `_ with reply options (see `Introduction to bots `_ for details and examples). + + Source: https://core.telegram.org/bots/api#replykeyboardmarkup + """ + + keyboard: List[List[KeyboardButton]] + """Array of button rows, each represented by an Array of :class:`aiogram.types.keyboard_button.KeyboardButton` objects""" + resize_keyboard: Optional[bool] = None + """*Optional*. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to *false*, in which case the custom keyboard is always of the same height as the app's standard keyboard.""" + one_time_keyboard: Optional[bool] = None + """*Optional*. Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again. Defaults to *false*.""" + selective: Optional[bool] = None + """*Optional*. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the *text* of the :class:`aiogram.types.message.Message` object; 2) if the bot's message is a reply (has *reply_to_message_id*), sender of the original message.""" diff --git a/aiogram/types/reply_keyboard_remove.py b/aiogram/types/reply_keyboard_remove.py new file mode 100644 index 00000000..1e2c2da9 --- /dev/null +++ b/aiogram/types/reply_keyboard_remove.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import Optional + +from pydantic import Field + +from .base import MutableTelegramObject + + +class ReplyKeyboardRemove(MutableTelegramObject): + """ + Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a button (see :class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup`). + + Source: https://core.telegram.org/bots/api#replykeyboardremove + """ + + remove_keyboard: bool = Field(True, const=True) + """Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use *one_time_keyboard* in :class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup`)""" + selective: Optional[bool] = None + """*Optional*. Use this parameter if you want to remove the keyboard for specific users only. Targets: 1) users that are @mentioned in the *text* of the :class:`aiogram.types.message.Message` object; 2) if the bot's message is a reply (has *reply_to_message_id*), sender of the original message.""" diff --git a/aiogram/types/response_parameters.py b/aiogram/types/response_parameters.py new file mode 100644 index 00000000..8bfb3cf5 --- /dev/null +++ b/aiogram/types/response_parameters.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from typing import Optional + +from .base import TelegramObject + + +class ResponseParameters(TelegramObject): + """ + Contains information about why a request was unsuccessful. + + Source: https://core.telegram.org/bots/api#responseparameters + """ + + migrate_to_chat_id: Optional[int] = None + """*Optional*. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.""" + retry_after: Optional[int] = None + """*Optional*. In case of exceeding flood control, the number of seconds left to wait before the request can be repeated""" diff --git a/aiogram/api/types/shipping_address.py b/aiogram/types/shipping_address.py similarity index 100% rename from aiogram/api/types/shipping_address.py rename to aiogram/types/shipping_address.py diff --git a/aiogram/api/types/shipping_option.py b/aiogram/types/shipping_option.py similarity index 100% rename from aiogram/api/types/shipping_option.py rename to aiogram/types/shipping_option.py diff --git a/aiogram/api/types/shipping_query.py b/aiogram/types/shipping_query.py similarity index 100% rename from aiogram/api/types/shipping_query.py rename to aiogram/types/shipping_query.py index b8228ce1..8e2d2a31 100644 --- a/aiogram/api/types/shipping_query.py +++ b/aiogram/types/shipping_query.py @@ -7,10 +7,10 @@ from pydantic import Field from .base import TelegramObject if TYPE_CHECKING: # pragma: no cover - from .shipping_address import ShippingAddress - from .user import User from ..methods import AnswerShippingQuery from ..types import ShippingOption + from .shipping_address import ShippingAddress + from .user import User class ShippingQuery(TelegramObject): diff --git a/aiogram/api/types/sticker.py b/aiogram/types/sticker.py similarity index 63% rename from aiogram/api/types/sticker.py rename to aiogram/types/sticker.py index 23d22bfc..ad5486b4 100644 --- a/aiogram/api/types/sticker.py +++ b/aiogram/types/sticker.py @@ -19,21 +19,20 @@ class Sticker(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" width: int """Sticker width""" height: int """Sticker height""" is_animated: bool - """True, if the sticker is animated""" + """:code:`True`, if the sticker is `animated `_""" thumb: Optional[PhotoSize] = None - """Sticker thumbnail in the .WEBP or .JPG format""" + """*Optional*. Sticker thumbnail in the .WEBP or .JPG format""" emoji: Optional[str] = None - """Emoji associated with the sticker""" + """*Optional*. Emoji associated with the sticker""" set_name: Optional[str] = None - """Name of the sticker set to which the sticker belongs""" + """*Optional*. Name of the sticker set to which the sticker belongs""" mask_position: Optional[MaskPosition] = None - """For mask stickers, the position where the mask should be placed""" + """*Optional*. For mask stickers, the position where the mask should be placed""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" diff --git a/aiogram/api/types/sticker_set.py b/aiogram/types/sticker_set.py similarity index 71% rename from aiogram/api/types/sticker_set.py rename to aiogram/types/sticker_set.py index 2b9e7ab1..9132daf0 100644 --- a/aiogram/api/types/sticker_set.py +++ b/aiogram/types/sticker_set.py @@ -21,10 +21,10 @@ class StickerSet(TelegramObject): title: str """Sticker set title""" is_animated: bool - """True, if the sticker set contains animated stickers""" + """:code:`True`, if the sticker set contains `animated stickers `_""" contains_masks: bool - """True, if the sticker set contains masks""" + """:code:`True`, if the sticker set contains masks""" stickers: List[Sticker] """List of all set stickers""" thumb: Optional[PhotoSize] = None - """Sticker set thumbnail in the .WEBP or .TGS format""" + """*Optional*. Sticker set thumbnail in the .WEBP or .TGS format""" diff --git a/aiogram/api/types/successful_payment.py b/aiogram/types/successful_payment.py similarity index 54% rename from aiogram/api/types/successful_payment.py rename to aiogram/types/successful_payment.py index d4105472..d87acf10 100644 --- a/aiogram/api/types/successful_payment.py +++ b/aiogram/types/successful_payment.py @@ -16,12 +16,9 @@ class SuccessfulPayment(TelegramObject): """ currency: str - """Three-letter ISO 4217 currency code""" + """Three-letter ISO 4217 `currency `_ code""" total_amount: int - """Total price in the smallest units of the currency (integer, not float/double). For example, - for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it - shows the number of digits past the decimal point for each currency (2 for the majority of - currencies).""" + """Total price in the *smallest units* of the currency (integer, **not** float/double). For example, for a price of :code:`US$ 1.45` pass :code:`amount = 145`. See the *exp* parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).""" invoice_payload: str """Bot specified invoice payload""" telegram_payment_charge_id: str @@ -29,6 +26,6 @@ class SuccessfulPayment(TelegramObject): provider_payment_charge_id: str """Provider payment identifier""" shipping_option_id: Optional[str] = None - """Identifier of the shipping option chosen by the user""" + """*Optional*. Identifier of the shipping option chosen by the user""" order_info: Optional[OrderInfo] = None - """Order info provided by the user""" + """*Optional*. Order info provided by the user""" diff --git a/aiogram/types/update.py b/aiogram/types/update.py new file mode 100644 index 00000000..625fce4a --- /dev/null +++ b/aiogram/types/update.py @@ -0,0 +1,50 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .callback_query import CallbackQuery + from .chosen_inline_result import ChosenInlineResult + from .inline_query import InlineQuery + from .message import Message + from .poll import Poll + from .poll_answer import PollAnswer + from .pre_checkout_query import PreCheckoutQuery + from .shipping_query import ShippingQuery + + +class Update(TelegramObject): + """ + This `object `_ represents an incoming update. + + At most **one** of the optional parameters can be present in any given update. + + Source: https://core.telegram.org/bots/api#update + """ + + update_id: int + """The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you're using `Webhooks `_, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially.""" + message: Optional[Message] = None + """*Optional*. New incoming message of any kind — text, photo, sticker, etc.""" + edited_message: Optional[Message] = None + """*Optional*. New version of a message that is known to the bot and was edited""" + channel_post: Optional[Message] = None + """*Optional*. New incoming channel post of any kind — text, photo, sticker, etc.""" + edited_channel_post: Optional[Message] = None + """*Optional*. New version of a channel post that is known to the bot and was edited""" + inline_query: Optional[InlineQuery] = None + """*Optional*. New incoming `inline `_ query""" + chosen_inline_result: Optional[ChosenInlineResult] = None + """*Optional*. The result of an `inline `_ query that was chosen by a user and sent to their chat partner. Please see our documentation on the `feedback collecting `_ for details on how to enable these updates for your bot.""" + callback_query: Optional[CallbackQuery] = None + """*Optional*. New incoming callback query""" + shipping_query: Optional[ShippingQuery] = None + """*Optional*. New incoming shipping query. Only for invoices with flexible price""" + pre_checkout_query: Optional[PreCheckoutQuery] = None + """*Optional*. New incoming pre-checkout query. Contains full information about checkout""" + poll: Optional[Poll] = None + """*Optional*. New poll state. Bots receive only updates about stopped polls and polls, which are sent by the bot""" + poll_answer: Optional[PollAnswer] = None + """*Optional*. A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself.""" diff --git a/aiogram/api/types/user.py b/aiogram/types/user.py similarity index 57% rename from aiogram/api/types/user.py rename to aiogram/types/user.py index 15c87c12..7f687e17 100644 --- a/aiogram/api/types/user.py +++ b/aiogram/types/user.py @@ -19,17 +19,17 @@ class User(TelegramObject): first_name: str """User's or bot's first name""" last_name: Optional[str] = None - """User's or bot's last name""" + """*Optional*. User's or bot's last name""" username: Optional[str] = None - """User's or bot's username""" + """*Optional*. User's or bot's username""" language_code: Optional[str] = None - """IETF language tag of the user's language""" + """*Optional*. `IETF language tag `_ of the user's language""" can_join_groups: Optional[bool] = None - """True, if the bot can be invited to groups. Returned only in getMe.""" + """*Optional*. True, if the bot can be invited to groups. Returned only in :class:`aiogram.methods.get_me.GetMe`.""" can_read_all_group_messages: Optional[bool] = None - """True, if privacy mode is disabled for the bot. Returned only in getMe.""" + """*Optional*. True, if `privacy mode `_ is disabled for the bot. Returned only in :class:`aiogram.methods.get_me.GetMe`.""" supports_inline_queries: Optional[bool] = None - """True, if the bot supports inline queries. Returned only in getMe.""" + """*Optional*. True, if the bot supports inline queries. Returned only in :class:`aiogram.methods.get_me.GetMe`.""" @property def full_name(self) -> str: diff --git a/aiogram/api/types/user_profile_photos.py b/aiogram/types/user_profile_photos.py similarity index 100% rename from aiogram/api/types/user_profile_photos.py rename to aiogram/types/user_profile_photos.py diff --git a/aiogram/types/venue.py b/aiogram/types/venue.py new file mode 100644 index 00000000..8641ec0d --- /dev/null +++ b/aiogram/types/venue.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from .base import TelegramObject + +if TYPE_CHECKING: # pragma: no cover + from .location import Location + + +class Venue(TelegramObject): + """ + This object represents a venue. + + Source: https://core.telegram.org/bots/api#venue + """ + + location: Location + """Venue location. Can't be a live location""" + title: str + """Name of the venue""" + address: str + """Address of the venue""" + foursquare_id: Optional[str] = None + """*Optional*. Foursquare identifier of the venue""" + foursquare_type: Optional[str] = None + """*Optional*. Foursquare type of the venue. (For example, 'arts_entertainment/default', 'arts_entertainment/aquarium' or 'food/icecream'.)""" + google_place_id: Optional[str] = None + """*Optional*. Google Places identifier of the venue""" + google_place_type: Optional[str] = None + """*Optional*. Google Places type of the venue. (See `supported types `_.)""" diff --git a/aiogram/api/types/video.py b/aiogram/types/video.py similarity index 72% rename from aiogram/api/types/video.py rename to aiogram/types/video.py index 647333b5..151abc58 100644 --- a/aiogram/api/types/video.py +++ b/aiogram/types/video.py @@ -18,8 +18,7 @@ class Video(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" width: int """Video width as defined by sender""" height: int @@ -27,8 +26,10 @@ class Video(TelegramObject): duration: int """Duration of the video in seconds as defined by sender""" thumb: Optional[PhotoSize] = None - """Video thumbnail""" + """*Optional*. Video thumbnail""" + file_name: Optional[str] = None + """*Optional*. Original filename as defined by sender""" mime_type: Optional[str] = None - """Mime type of a file as defined by sender""" + """*Optional*. Mime type of a file as defined by sender""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" diff --git a/aiogram/api/types/video_note.py b/aiogram/types/video_note.py similarity index 67% rename from aiogram/api/types/video_note.py rename to aiogram/types/video_note.py index b33baa39..99f0dd47 100644 --- a/aiogram/api/types/video_note.py +++ b/aiogram/types/video_note.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: # pragma: no cover class VideoNote(TelegramObject): """ - This object represents a video message (available in Telegram apps as of v.4.0). + This object represents a `video message `_ (available in Telegram apps as of `v.4.0 `_). Source: https://core.telegram.org/bots/api#videonote """ @@ -18,13 +18,12 @@ class VideoNote(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" length: int """Video width and height (diameter of the video message) as defined by sender""" duration: int """Duration of the video in seconds as defined by sender""" thumb: Optional[PhotoSize] = None - """Video thumbnail""" + """*Optional*. Video thumbnail""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" diff --git a/aiogram/api/types/voice.py b/aiogram/types/voice.py similarity index 75% rename from aiogram/api/types/voice.py rename to aiogram/types/voice.py index 52e5bb71..79fd8d6f 100644 --- a/aiogram/api/types/voice.py +++ b/aiogram/types/voice.py @@ -15,11 +15,10 @@ class Voice(TelegramObject): file_id: str """Identifier for this file, which can be used to download or reuse the file""" file_unique_id: str - """Unique identifier for this file, which is supposed to be the same over time and for - different bots. Can't be used to download or reuse the file.""" + """Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.""" duration: int """Duration of the audio in seconds as defined by sender""" mime_type: Optional[str] = None - """MIME type of the file as defined by sender""" + """*Optional*. MIME type of the file as defined by sender""" file_size: Optional[int] = None - """File size""" + """*Optional*. File size""" diff --git a/aiogram/api/types/webhook_info.py b/aiogram/types/webhook_info.py similarity index 56% rename from aiogram/api/types/webhook_info.py rename to aiogram/types/webhook_info.py index febcb465..24a9844e 100644 --- a/aiogram/api/types/webhook_info.py +++ b/aiogram/types/webhook_info.py @@ -18,13 +18,13 @@ class WebhookInfo(TelegramObject): """True, if a custom certificate was provided for webhook certificate checks""" pending_update_count: int """Number of updates awaiting delivery""" + ip_address: Optional[str] = None + """*Optional*. Currently used webhook IP address""" last_error_date: Optional[int] = None - """Unix time for the most recent error that happened when trying to deliver an update via - webhook""" + """*Optional*. Unix time for the most recent error that happened when trying to deliver an update via webhook""" last_error_message: Optional[str] = None - """Error message in human-readable format for the most recent error that happened when trying - to deliver an update via webhook""" + """*Optional*. Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook""" max_connections: Optional[int] = None - """Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery""" + """*Optional*. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery""" allowed_updates: Optional[List[str]] = None - """A list of update types the bot is subscribed to. Defaults to all update types""" + """*Optional*. A list of update types the bot is subscribed to. Defaults to all update types""" diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index 0b35eb99..149440b4 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -2,10 +2,10 @@ import asyncio import functools import inspect import warnings -from typing import Callable +from typing import Any, Callable, Type -def deprecated(reason, stacklevel=2) -> Callable: +def deprecated(reason: str, stacklevel: int = 2) -> Callable[..., Any]: """ This is a decorator which can be used to mark functions as deprecated. It will result in a warning being emitted @@ -24,7 +24,7 @@ def deprecated(reason, stacklevel=2) -> Callable: # def old_function(x, y): # pass - def decorator(func): + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: if inspect.isclass(func): msg = "Call to deprecated class {name} ({reason})." @@ -32,7 +32,7 @@ def deprecated(reason, stacklevel=2) -> Callable: msg = "Call to deprecated function {name} ({reason})." @functools.wraps(func) - def wrapper(*args, **kwargs): + def wrapper(*args: Any, **kwargs: Any) -> Any: warn_deprecated( msg.format(name=func.__name__, reason=reason), stacklevel=stacklevel ) @@ -70,13 +70,17 @@ def deprecated(reason, stacklevel=2) -> Callable: raise TypeError(repr(type(reason))) -def warn_deprecated(message, warning=DeprecationWarning, stacklevel=2): +def warn_deprecated( + message: str, warning: Type[Warning] = DeprecationWarning, stacklevel: int = 2 +) -> None: warnings.simplefilter("always", warning) warnings.warn(message, category=warning, stacklevel=stacklevel) warnings.simplefilter("default", warning) -def renamed_argument(old_name: str, new_name: str, until_version: str, stacklevel: int = 3): +def renamed_argument( + old_name: str, new_name: str, until_version: str, stacklevel: int = 3 +) -> Callable[..., Any]: """ A meta-decorator to mark an argument as deprecated. @@ -100,11 +104,11 @@ def renamed_argument(old_name: str, new_name: str, until_version: str, stackleve :return: decorator """ - def decorator(func): + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: if asyncio.iscoroutinefunction(func): @functools.wraps(func) - async def wrapped(*args, **kwargs): + async def wrapped(*args: Any, **kwargs: Any) -> Any: if old_name in kwargs: warn_deprecated( f"In coroutine '{func.__name__}' argument '{old_name}' " @@ -119,7 +123,7 @@ def renamed_argument(old_name: str, new_name: str, until_version: str, stackleve else: @functools.wraps(func) - def wrapped(*args, **kwargs): + def wrapped(*args: Any, **kwargs: Any) -> Any: if old_name in kwargs: warn_deprecated( f"In function `{func.__name__}` argument `{old_name}` " diff --git a/aiogram/utils/helper.py b/aiogram/utils/helper.py index 57f4e76e..3ceff6aa 100644 --- a/aiogram/utils/helper.py +++ b/aiogram/utils/helper.py @@ -263,7 +263,10 @@ class Default(Generic[T]): __slots__ = "fget", "_descriptor_instances" def __init__( - self, default: Optional[T] = None, *, fget: Optional[Callable[[Any], T]] = None, + self, + default: Optional[T] = None, + *, + fget: Optional[Callable[[Any], T]] = None, ) -> None: self.fget = fget or (lambda _: cast(T, default)) self._descriptor_instances = WeakKeyDictionary() # type: ignore diff --git a/aiogram/utils/markdown.py b/aiogram/utils/markdown.py index 5daae546..68437fa9 100644 --- a/aiogram/utils/markdown.py +++ b/aiogram/utils/markdown.py @@ -1,11 +1,13 @@ +from typing import Any + from .text_decorations import html_decoration, markdown_decoration -def _join(*content, sep=" "): +def _join(*content: Any, sep: str = " ") -> str: return sep.join(map(str, content)) -def text(*content, sep=" "): +def text(*content: Any, sep: str = " ") -> str: """ Join all elements with a separator @@ -16,7 +18,7 @@ def text(*content, sep=" "): return _join(*content, sep=sep) -def bold(*content, sep=" "): +def bold(*content: Any, sep: str = " ") -> str: """ Make bold text (Markdown) @@ -27,7 +29,7 @@ def bold(*content, sep=" "): return markdown_decoration.bold(value=html_decoration.quote(_join(*content, sep=sep))) -def hbold(*content, sep=" "): +def hbold(*content: Any, sep: str = " ") -> str: """ Make bold text (HTML) @@ -38,7 +40,7 @@ def hbold(*content, sep=" "): return html_decoration.bold(value=html_decoration.quote(_join(*content, sep=sep))) -def italic(*content, sep=" "): +def italic(*content: Any, sep: str = " ") -> str: """ Make italic text (Markdown) @@ -49,7 +51,7 @@ def italic(*content, sep=" "): return markdown_decoration.italic(value=html_decoration.quote(_join(*content, sep=sep))) -def hitalic(*content, sep=" "): +def hitalic(*content: Any, sep: str = " ") -> str: """ Make italic text (HTML) @@ -60,7 +62,7 @@ def hitalic(*content, sep=" "): return html_decoration.italic(value=html_decoration.quote(_join(*content, sep=sep))) -def code(*content, sep=" "): +def code(*content: Any, sep: str = " ") -> str: """ Make mono-width text (Markdown) @@ -71,7 +73,7 @@ def code(*content, sep=" "): return markdown_decoration.code(value=html_decoration.quote(_join(*content, sep=sep))) -def hcode(*content, sep=" "): +def hcode(*content: Any, sep: str = " ") -> str: """ Make mono-width text (HTML) @@ -82,7 +84,7 @@ def hcode(*content, sep=" "): return html_decoration.code(value=html_decoration.quote(_join(*content, sep=sep))) -def pre(*content, sep="\n"): +def pre(*content: Any, sep: str = "\n") -> str: """ Make mono-width text block (Markdown) @@ -93,7 +95,7 @@ def pre(*content, sep="\n"): return markdown_decoration.pre(value=html_decoration.quote(_join(*content, sep=sep))) -def hpre(*content, sep="\n"): +def hpre(*content: Any, sep: str = "\n") -> str: """ Make mono-width text block (HTML) @@ -104,7 +106,7 @@ def hpre(*content, sep="\n"): return html_decoration.pre(value=html_decoration.quote(_join(*content, sep=sep))) -def underline(*content, sep=" "): +def underline(*content: Any, sep: str = " ") -> str: """ Make underlined text (Markdown) @@ -115,7 +117,7 @@ def underline(*content, sep=" "): return markdown_decoration.underline(value=markdown_decoration.quote(_join(*content, sep=sep))) -def hunderline(*content, sep=" "): +def hunderline(*content: Any, sep: str = " ") -> str: """ Make underlined text (HTML) @@ -126,7 +128,7 @@ def hunderline(*content, sep=" "): return html_decoration.underline(value=html_decoration.quote(_join(*content, sep=sep))) -def strikethrough(*content, sep=" "): +def strikethrough(*content: Any, sep: str = " ") -> str: """ Make strikethrough text (Markdown) @@ -139,7 +141,7 @@ def strikethrough(*content, sep=" "): ) -def hstrikethrough(*content, sep=" "): +def hstrikethrough(*content: Any, sep: str = " ") -> str: """ Make strikethrough text (HTML) diff --git a/aiogram/utils/text_decorations.py b/aiogram/utils/text_decorations.py index ac5262c1..6377223b 100644 --- a/aiogram/utils/text_decorations.py +++ b/aiogram/utils/text_decorations.py @@ -6,12 +6,12 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Generator, List, Optional, Pattern, cast if TYPE_CHECKING: # pragma: no cover - from aiogram.api.types import MessageEntity + from aiogram.types import MessageEntity __all__ = ( - "TextDecoration", "HtmlDecoration", "MarkdownDecoration", + "TextDecoration", "html_decoration", "markdown_decoration", ) @@ -38,7 +38,7 @@ class TextDecoration(ABC): else self.pre(value=text) ) if entity.type == "text_mention": - from aiogram.api.types import User + from aiogram.types import User user = cast(User, entity.user) return self.link(value=text, link=f"tg://user?id={user.id}") @@ -57,14 +57,15 @@ class TextDecoration(ABC): """ result = "".join( self._unparse_entities( - text, sorted(entities, key=lambda item: item.offset) if entities else [] + self._add_surrogates(text), + sorted(entities, key=lambda item: item.offset) if entities else [], ) ) return result def _unparse_entities( self, - text: str, + text: bytes, entities: List[MessageEntity], offset: Optional[int] = None, length: Optional[int] = None, @@ -74,21 +75,31 @@ class TextDecoration(ABC): length = length or len(text) for index, entity in enumerate(entities): - if entity.offset < offset: + if entity.offset * 2 < offset: continue - if entity.offset > offset: - yield self.quote(text[offset : entity.offset]) - start = entity.offset - offset = entity.offset + entity.length + if entity.offset * 2 > offset: + yield self.quote(self._remove_surrogates(text[offset : entity.offset * 2])) + start = entity.offset * 2 + offset = entity.offset * 2 + entity.length * 2 - sub_entities = list(filter(lambda e: e.offset < (offset or 0), entities[index + 1 :])) + sub_entities = list( + filter(lambda e: e.offset * 2 < (offset or 0), entities[index + 1 :]) + ) yield self.apply_entity( entity, "".join(self._unparse_entities(text, sub_entities, offset=start, length=offset)), ) if offset < length: - yield self.quote(text[offset:length]) + yield self.quote(self._remove_surrogates(text[offset:length])) + + @staticmethod + def _add_surrogates(text: str) -> bytes: + return text.encode("utf-16-le") + + @staticmethod + def _remove_surrogates(text: bytes) -> str: + return text.decode("utf-16-le") @abstractmethod def link(self, value: str, link: str) -> str: # pragma: no cover @@ -153,11 +164,11 @@ class HtmlDecoration(TextDecoration): return f"{value}" def quote(self, value: str) -> str: - return html.escape(value) + return html.escape(value, quote=False) class MarkdownDecoration(TextDecoration): - MARKDOWN_QUOTE_PATTERN: Pattern[str] = re.compile(r"([_*\[\]()~`>#+\-|{}.!])") + MARKDOWN_QUOTE_PATTERN: Pattern[str] = re.compile(r"([_*\[\]()~`>#+\-=|{}.!\\])") def link(self, value: str, link: str) -> str: return f"[{value}]({link})" @@ -166,7 +177,7 @@ class MarkdownDecoration(TextDecoration): return f"*{value}*" def italic(self, value: str) -> str: - return f"_{value}_\r" + return f"_\r{value}_\r" def code(self, value: str) -> str: return f"`{value}`" @@ -178,7 +189,7 @@ class MarkdownDecoration(TextDecoration): return f"```{language}\n{value}\n```" def underline(self, value: str) -> str: - return f"__{value}__" + return f"__\r{value}__\r" def strikethrough(self, value: str) -> str: return f"~{value}~" diff --git a/codecov.yaml b/codecov.yaml index 082f0672..cdf02d42 100644 --- a/codecov.yaml +++ b/codecov.yaml @@ -21,4 +21,4 @@ comment: require_changes: no branches: - dev-3.x - after_n_builds: 6 + after_n_builds: 8 diff --git a/docs/api/client/session/aiohttp.md b/docs/api/client/session/aiohttp.md index 223ad468..dd49eef8 100644 --- a/docs/api/client/session/aiohttp.md +++ b/docs/api/client/session/aiohttp.md @@ -8,7 +8,7 @@ Currently `AiohttpSession` is a default session used in `aiogram.Bot` ```python from aiogram import Bot -from aiogram.api.client.session.aiohttp import AiohttpSession +from aiogram.client.session.aiohttp import AiohttpSession session = AiohttpSession() Bot('token', session=session) @@ -20,9 +20,10 @@ Bot('token', session=session) In order to use AiohttpSession with proxy connector you have to install [aiohttp-socks](https://pypi.org/project/aiohttp-socks/ "PyPi repository"){target=_blank} Binding session to bot: + ```python from aiogram import Bot -from aiogram.api.client.session.aiohttp import AiohttpSession +from aiogram.client.session.aiohttp import AiohttpSession session = AiohttpSession(proxy="protocol://host:port/") Bot(token="bot token", session=session) @@ -38,9 +39,10 @@ Proxy authorization credentials can be specified in proxy URL or come as an inst login and password. Consider examples: + ```python -from aiohttp import BasicAuth -from aiogram.api.client.session.aiohttp import AiohttpSession +from aiogram.client.session.aiohttp import BasicAuth +from aiogram.client.session.aiohttp import AiohttpSession auth = BasicAuth(login="user", password="password") session = AiohttpSession(proxy=("protocol://host:port", auth)) @@ -59,15 +61,16 @@ session = AiohttpSession(proxy="protocol://user:password@host:port") Since [aiohttp-socks]('https://pypi.org/project/aiohttp-socks/') supports proxy chains, you're able to use them in aiogram Example of chain proxies: + ```python -from aiohttp import BasicAuth -from aiogram.api.client.session.aiohttp import AiohttpSession +from aiogram.client.session.aiohttp import BasicAuth +from aiogram.client.session.aiohttp import AiohttpSession auth = BasicAuth(login="user", password="password") session = AiohttpSession( proxy={"protocol0://host0:port0", "protocol1://user:password@host1:port1", - ("protocol2://host2:port2", auth),} # can be any iterable if not set + ("protocol2://host2:port2", auth), } # can be any iterable if not set ) ``` diff --git a/docs2/Makefile b/docs2/Makefile new file mode 100644 index 00000000..f48952a6 --- /dev/null +++ b/docs2/Makefile @@ -0,0 +1,24 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SPHINXINTL ?= sphinx-intl +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +update-intl: gettext + $(SPHINXINTL) update -p $(BUILDDIR)/gettext $(O) + +.PHONY: help update-intl Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs2/_static/basics_middleware.png b/docs2/_static/basics_middleware.png new file mode 100644 index 00000000..b4165e2e Binary files /dev/null and b/docs2/_static/basics_middleware.png differ diff --git a/docs2/_static/logo.png b/docs2/_static/logo.png new file mode 100755 index 00000000..b363701d Binary files /dev/null and b/docs2/_static/logo.png differ diff --git a/docs2/_static/middleware_pipeline.png b/docs2/_static/middleware_pipeline.png new file mode 100644 index 00000000..dcb20d6f Binary files /dev/null and b/docs2/_static/middleware_pipeline.png differ diff --git a/docs2/_static/middleware_pipeline_nested.png b/docs2/_static/middleware_pipeline_nested.png new file mode 100644 index 00000000..f7a6195a Binary files /dev/null and b/docs2/_static/middleware_pipeline_nested.png differ diff --git a/docs2/_static/nested_routers_example.png b/docs2/_static/nested_routers_example.png new file mode 100644 index 00000000..8ccd824f Binary files /dev/null and b/docs2/_static/nested_routers_example.png differ diff --git a/docs2/_static/stylesheets/extra.css b/docs2/_static/stylesheets/extra.css new file mode 100644 index 00000000..3a9ee46a --- /dev/null +++ b/docs2/_static/stylesheets/extra.css @@ -0,0 +1,22 @@ +@font-face { + font-family: 'JetBrainsMono'; + src: url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2') format('woff2'), + url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/web/woff/JetBrainsMono-Regular.woff') format('woff'), + url('https://cdn.jsdelivr.net/gh/JetBrains/JetBrainsMono/ttf/JetBrainsMono-Regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; +} + +code, kbd, pre { + font-family: "JetBrainsMono", "Roboto Mono", "Courier New", Courier, monospace; +} + +.highlight * { + background: #f0f0f0; +} + +@media (prefers-color-scheme: dark) { + .highlight * { + background: #424242; + } +} \ No newline at end of file diff --git a/docs2/_static/update_propagation_flow.png b/docs2/_static/update_propagation_flow.png new file mode 100644 index 00000000..1bd3667f Binary files /dev/null and b/docs2/_static/update_propagation_flow.png differ diff --git a/docs2/api/bot.rst b/docs2/api/bot.rst new file mode 100644 index 00000000..15f1c0a4 --- /dev/null +++ b/docs2/api/bot.rst @@ -0,0 +1,17 @@ +### +Bot +### + +Bot instance can be created from :code:`aiogram.Bot` (:code:`from aiogram import Bot`) and +you can't use methods without instance of bot with configured token. + +This class has aliases for all methods and named in :code:`lower_camel_case`. + +For example :code:`sendMessage` named :code:`send_message` and has the same specification with all class-based methods. + +.. autoclass:: aiogram.client.bot.Bot + :members: + :show-inheritance: + :member-order: bysource + :special-members: __init__ + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/download_file.rst b/docs2/api/download_file.rst new file mode 100644 index 00000000..d60e8051 --- /dev/null +++ b/docs2/api/download_file.rst @@ -0,0 +1,98 @@ +##################### +How to download file? +##################### + +Download file manually +====================== + +First, you must get the `file_id` of the file you want to download. +Information about files sent to the bot is contained in `Message `__. + +For example, download the document that came to the bot. + +.. code-block:: + + file_id = message.document.file_id + +Then use the `getFile `__ method to get `file_path`. + +.. code-block:: + + file = await bot.get_file(file_id) + file_path = file.file_path + +After that, use the `download_file <#download-file>`__ method from the bot object. + +download_file(...) +------------------ + +Download file by `file_path` to destination. + +If you want to automatically create destination (:obj:`io.BytesIO`) use default +value of destination and handle result of this method. + +.. autoclass:: aiogram.client.bot.Bot + :members: download_file + :exclude-members: __init__ + +There are two options where you can download the file: to **disk** or to **binary I/O object**. + +Download file to disk +--------------------- + +To download file to disk, you must specify the file name or path where to download the file. +In this case, the function will return nothing. + +.. code-block:: + + await bot.download_file(file_path, "text.txt") + +Download file to binary I/O object +---------------------------------- + +To download file to binary I/O object, you must specify an object with the +:obj:`typing.BinaryIO` type or use the default (:obj:`None`) value. + +In the first case, the function will return your object: + +.. code-block:: + + my_object = MyBinaryIO() + result: MyBinaryIO = await bot.download_file(file_path, my_object) + # print(result is my_object) # True + +If you leave the default value, an :obj:`io.BytesIO` object will be created and returned. + +.. code-block:: + + result: io.BytesIO = await bot.download_file(file_path) + + +Download file in short way +========================== + +Getting `file_path` manually every time is boring, so you should use the `download <#download>`__ method. + +download(...) +------------- + +Download file by `file_id` or `Downloadable` object to destination. + +If you want to automatically create destination (:obj:`io.BytesIO`) use default +value of destination and handle result of this method. + +.. autoclass:: aiogram.client.bot.Bot + :members: download + :exclude-members: __init__ + +It differs from `download_file <#download-file>`__ **only** in that it accepts `file_id` +or an `Downloadable` object (object that contains the `file_id` attribute) instead of `file_path`. + +You can download a file to `disk <#download-file-to-disk>`__ or to a `binary I/O <#download-file-to-binary-io-object>`__ object in the same way. + +Example: + +.. code-block:: + + document = message.document + await bot.download(document) diff --git a/docs2/api/index.rst b/docs2/api/index.rst new file mode 100644 index 00000000..e82565a8 --- /dev/null +++ b/docs2/api/index.rst @@ -0,0 +1,15 @@ +####### +Bot API +####### + +**aiogram** now is fully support of `Telegram Bot API `_ + +All methods and types is fully autogenerated from Telegram Bot API docs by parser with code-generator. + +.. toctree:: + bot + session/index + types/index + methods/index + download_file + upload_file diff --git a/docs2/api/methods/add_sticker_to_set.rst b/docs2/api/methods/add_sticker_to_set.rst new file mode 100644 index 00000000..32cec35c --- /dev/null +++ b/docs2/api/methods/add_sticker_to_set.rst @@ -0,0 +1,51 @@ +############### +addStickerToSet +############### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.add_sticker_to_set + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.add_sticker_to_set(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.add_sticker_to_set import AddStickerToSet` +- alias: :code:`from aiogram.methods import AddStickerToSet` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await AddStickerToSet(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(AddStickerToSet(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return AddStickerToSet(...) \ No newline at end of file diff --git a/docs2/api/methods/answer_callback_query.rst b/docs2/api/methods/answer_callback_query.rst new file mode 100644 index 00000000..f0afd844 --- /dev/null +++ b/docs2/api/methods/answer_callback_query.rst @@ -0,0 +1,51 @@ +################### +answerCallbackQuery +################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.answer_callback_query + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.answer_callback_query(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.answer_callback_query import AnswerCallbackQuery` +- alias: :code:`from aiogram.methods import AnswerCallbackQuery` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await AnswerCallbackQuery(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(AnswerCallbackQuery(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return AnswerCallbackQuery(...) \ No newline at end of file diff --git a/docs2/api/methods/answer_inline_query.rst b/docs2/api/methods/answer_inline_query.rst new file mode 100644 index 00000000..8998eb53 --- /dev/null +++ b/docs2/api/methods/answer_inline_query.rst @@ -0,0 +1,51 @@ +################# +answerInlineQuery +################# + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.answer_inline_query + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.answer_inline_query(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.answer_inline_query import AnswerInlineQuery` +- alias: :code:`from aiogram.methods import AnswerInlineQuery` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await AnswerInlineQuery(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(AnswerInlineQuery(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return AnswerInlineQuery(...) \ No newline at end of file diff --git a/docs2/api/methods/answer_pre_checkout_query.rst b/docs2/api/methods/answer_pre_checkout_query.rst new file mode 100644 index 00000000..d3e3c6f0 --- /dev/null +++ b/docs2/api/methods/answer_pre_checkout_query.rst @@ -0,0 +1,51 @@ +###################### +answerPreCheckoutQuery +###################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.answer_pre_checkout_query + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.answer_pre_checkout_query(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.answer_pre_checkout_query import AnswerPreCheckoutQuery` +- alias: :code:`from aiogram.methods import AnswerPreCheckoutQuery` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await AnswerPreCheckoutQuery(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(AnswerPreCheckoutQuery(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return AnswerPreCheckoutQuery(...) \ No newline at end of file diff --git a/docs2/api/methods/answer_shipping_query.rst b/docs2/api/methods/answer_shipping_query.rst new file mode 100644 index 00000000..14689178 --- /dev/null +++ b/docs2/api/methods/answer_shipping_query.rst @@ -0,0 +1,51 @@ +################### +answerShippingQuery +################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.answer_shipping_query + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.answer_shipping_query(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.answer_shipping_query import AnswerShippingQuery` +- alias: :code:`from aiogram.methods import AnswerShippingQuery` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await AnswerShippingQuery(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(AnswerShippingQuery(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return AnswerShippingQuery(...) \ No newline at end of file diff --git a/docs2/api/methods/close.rst b/docs2/api/methods/close.rst new file mode 100644 index 00000000..8daec78d --- /dev/null +++ b/docs2/api/methods/close.rst @@ -0,0 +1,51 @@ +##### +close +##### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.close + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.close(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.close import Close` +- alias: :code:`from aiogram.methods import Close` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await Close(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(Close(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return Close(...) \ No newline at end of file diff --git a/docs2/api/methods/copy_message.rst b/docs2/api/methods/copy_message.rst new file mode 100644 index 00000000..44406c3a --- /dev/null +++ b/docs2/api/methods/copy_message.rst @@ -0,0 +1,51 @@ +########### +copyMessage +########### + +Returns: :obj:`MessageId` + +.. automodule:: aiogram.methods.copy_message + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: MessageId = await bot.copy_message(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.copy_message import CopyMessage` +- alias: :code:`from aiogram.methods import CopyMessage` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: MessageId = await CopyMessage(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: MessageId = await bot(CopyMessage(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return CopyMessage(...) \ No newline at end of file diff --git a/docs2/api/methods/create_new_sticker_set.rst b/docs2/api/methods/create_new_sticker_set.rst new file mode 100644 index 00000000..230f49fe --- /dev/null +++ b/docs2/api/methods/create_new_sticker_set.rst @@ -0,0 +1,51 @@ +################### +createNewStickerSet +################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.create_new_sticker_set + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.create_new_sticker_set(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.create_new_sticker_set import CreateNewStickerSet` +- alias: :code:`from aiogram.methods import CreateNewStickerSet` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await CreateNewStickerSet(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(CreateNewStickerSet(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return CreateNewStickerSet(...) \ No newline at end of file diff --git a/docs2/api/methods/delete_chat_photo.rst b/docs2/api/methods/delete_chat_photo.rst new file mode 100644 index 00000000..01280b60 --- /dev/null +++ b/docs2/api/methods/delete_chat_photo.rst @@ -0,0 +1,51 @@ +############### +deleteChatPhoto +############### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.delete_chat_photo + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.delete_chat_photo(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.delete_chat_photo import DeleteChatPhoto` +- alias: :code:`from aiogram.methods import DeleteChatPhoto` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await DeleteChatPhoto(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(DeleteChatPhoto(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return DeleteChatPhoto(...) \ No newline at end of file diff --git a/docs2/api/methods/delete_chat_sticker_set.rst b/docs2/api/methods/delete_chat_sticker_set.rst new file mode 100644 index 00000000..67a9eef0 --- /dev/null +++ b/docs2/api/methods/delete_chat_sticker_set.rst @@ -0,0 +1,51 @@ +#################### +deleteChatStickerSet +#################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.delete_chat_sticker_set + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.delete_chat_sticker_set(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.delete_chat_sticker_set import DeleteChatStickerSet` +- alias: :code:`from aiogram.methods import DeleteChatStickerSet` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await DeleteChatStickerSet(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(DeleteChatStickerSet(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return DeleteChatStickerSet(...) \ No newline at end of file diff --git a/docs2/api/methods/delete_message.rst b/docs2/api/methods/delete_message.rst new file mode 100644 index 00000000..b97f5c41 --- /dev/null +++ b/docs2/api/methods/delete_message.rst @@ -0,0 +1,51 @@ +############# +deleteMessage +############# + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.delete_message + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.delete_message(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.delete_message import DeleteMessage` +- alias: :code:`from aiogram.methods import DeleteMessage` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await DeleteMessage(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(DeleteMessage(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return DeleteMessage(...) \ No newline at end of file diff --git a/docs2/api/methods/delete_sticker_from_set.rst b/docs2/api/methods/delete_sticker_from_set.rst new file mode 100644 index 00000000..dfb57a0a --- /dev/null +++ b/docs2/api/methods/delete_sticker_from_set.rst @@ -0,0 +1,51 @@ +#################### +deleteStickerFromSet +#################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.delete_sticker_from_set + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.delete_sticker_from_set(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.delete_sticker_from_set import DeleteStickerFromSet` +- alias: :code:`from aiogram.methods import DeleteStickerFromSet` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await DeleteStickerFromSet(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(DeleteStickerFromSet(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return DeleteStickerFromSet(...) \ No newline at end of file diff --git a/docs2/api/methods/delete_webhook.rst b/docs2/api/methods/delete_webhook.rst new file mode 100644 index 00000000..1c7e7d6e --- /dev/null +++ b/docs2/api/methods/delete_webhook.rst @@ -0,0 +1,51 @@ +############# +deleteWebhook +############# + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.delete_webhook + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.delete_webhook(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.delete_webhook import DeleteWebhook` +- alias: :code:`from aiogram.methods import DeleteWebhook` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await DeleteWebhook(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(DeleteWebhook(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return DeleteWebhook(...) \ No newline at end of file diff --git a/docs2/api/methods/edit_message_caption.rst b/docs2/api/methods/edit_message_caption.rst new file mode 100644 index 00000000..d05a83b8 --- /dev/null +++ b/docs2/api/methods/edit_message_caption.rst @@ -0,0 +1,51 @@ +################## +editMessageCaption +################## + +Returns: :obj:`Union[Message, bool]` + +.. automodule:: aiogram.methods.edit_message_caption + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Union[Message, bool] = await bot.edit_message_caption(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.edit_message_caption import EditMessageCaption` +- alias: :code:`from aiogram.methods import EditMessageCaption` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Union[Message, bool] = await EditMessageCaption(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Union[Message, bool] = await bot(EditMessageCaption(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return EditMessageCaption(...) \ No newline at end of file diff --git a/docs2/api/methods/edit_message_live_location.rst b/docs2/api/methods/edit_message_live_location.rst new file mode 100644 index 00000000..5516727c --- /dev/null +++ b/docs2/api/methods/edit_message_live_location.rst @@ -0,0 +1,51 @@ +####################### +editMessageLiveLocation +####################### + +Returns: :obj:`Union[Message, bool]` + +.. automodule:: aiogram.methods.edit_message_live_location + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Union[Message, bool] = await bot.edit_message_live_location(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.edit_message_live_location import EditMessageLiveLocation` +- alias: :code:`from aiogram.methods import EditMessageLiveLocation` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Union[Message, bool] = await EditMessageLiveLocation(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Union[Message, bool] = await bot(EditMessageLiveLocation(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return EditMessageLiveLocation(...) \ No newline at end of file diff --git a/docs2/api/methods/edit_message_media.rst b/docs2/api/methods/edit_message_media.rst new file mode 100644 index 00000000..057d1d5c --- /dev/null +++ b/docs2/api/methods/edit_message_media.rst @@ -0,0 +1,51 @@ +################ +editMessageMedia +################ + +Returns: :obj:`Union[Message, bool]` + +.. automodule:: aiogram.methods.edit_message_media + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Union[Message, bool] = await bot.edit_message_media(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.edit_message_media import EditMessageMedia` +- alias: :code:`from aiogram.methods import EditMessageMedia` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Union[Message, bool] = await EditMessageMedia(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Union[Message, bool] = await bot(EditMessageMedia(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return EditMessageMedia(...) \ No newline at end of file diff --git a/docs2/api/methods/edit_message_reply_markup.rst b/docs2/api/methods/edit_message_reply_markup.rst new file mode 100644 index 00000000..d37d9400 --- /dev/null +++ b/docs2/api/methods/edit_message_reply_markup.rst @@ -0,0 +1,51 @@ +###################### +editMessageReplyMarkup +###################### + +Returns: :obj:`Union[Message, bool]` + +.. automodule:: aiogram.methods.edit_message_reply_markup + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Union[Message, bool] = await bot.edit_message_reply_markup(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.edit_message_reply_markup import EditMessageReplyMarkup` +- alias: :code:`from aiogram.methods import EditMessageReplyMarkup` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Union[Message, bool] = await EditMessageReplyMarkup(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Union[Message, bool] = await bot(EditMessageReplyMarkup(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return EditMessageReplyMarkup(...) \ No newline at end of file diff --git a/docs2/api/methods/edit_message_text.rst b/docs2/api/methods/edit_message_text.rst new file mode 100644 index 00000000..55cd428c --- /dev/null +++ b/docs2/api/methods/edit_message_text.rst @@ -0,0 +1,51 @@ +############### +editMessageText +############### + +Returns: :obj:`Union[Message, bool]` + +.. automodule:: aiogram.methods.edit_message_text + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Union[Message, bool] = await bot.edit_message_text(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.edit_message_text import EditMessageText` +- alias: :code:`from aiogram.methods import EditMessageText` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Union[Message, bool] = await EditMessageText(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Union[Message, bool] = await bot(EditMessageText(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return EditMessageText(...) \ No newline at end of file diff --git a/docs2/api/methods/export_chat_invite_link.rst b/docs2/api/methods/export_chat_invite_link.rst new file mode 100644 index 00000000..c2cb9c40 --- /dev/null +++ b/docs2/api/methods/export_chat_invite_link.rst @@ -0,0 +1,51 @@ +#################### +exportChatInviteLink +#################### + +Returns: :obj:`str` + +.. automodule:: aiogram.methods.export_chat_invite_link + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: str = await bot.export_chat_invite_link(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.export_chat_invite_link import ExportChatInviteLink` +- alias: :code:`from aiogram.methods import ExportChatInviteLink` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: str = await ExportChatInviteLink(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: str = await bot(ExportChatInviteLink(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return ExportChatInviteLink(...) \ No newline at end of file diff --git a/docs2/api/methods/forward_message.rst b/docs2/api/methods/forward_message.rst new file mode 100644 index 00000000..b8eace3c --- /dev/null +++ b/docs2/api/methods/forward_message.rst @@ -0,0 +1,51 @@ +############## +forwardMessage +############## + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.forward_message + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.forward_message(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.forward_message import ForwardMessage` +- alias: :code:`from aiogram.methods import ForwardMessage` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await ForwardMessage(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(ForwardMessage(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return ForwardMessage(...) \ No newline at end of file diff --git a/docs2/api/methods/get_chat.rst b/docs2/api/methods/get_chat.rst new file mode 100644 index 00000000..9953c616 --- /dev/null +++ b/docs2/api/methods/get_chat.rst @@ -0,0 +1,45 @@ +####### +getChat +####### + +Returns: :obj:`Chat` + +.. automodule:: aiogram.methods.get_chat + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Chat = await bot.get_chat(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_chat import GetChat` +- alias: :code:`from aiogram.methods import GetChat` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Chat = await GetChat(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Chat = await bot(GetChat(...)) + diff --git a/docs2/api/methods/get_chat_administrators.rst b/docs2/api/methods/get_chat_administrators.rst new file mode 100644 index 00000000..f5690d73 --- /dev/null +++ b/docs2/api/methods/get_chat_administrators.rst @@ -0,0 +1,45 @@ +##################### +getChatAdministrators +##################### + +Returns: :obj:`List[ChatMember]` + +.. automodule:: aiogram.methods.get_chat_administrators + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: List[ChatMember] = await bot.get_chat_administrators(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_chat_administrators import GetChatAdministrators` +- alias: :code:`from aiogram.methods import GetChatAdministrators` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: List[ChatMember] = await GetChatAdministrators(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: List[ChatMember] = await bot(GetChatAdministrators(...)) + diff --git a/docs2/api/methods/get_chat_member.rst b/docs2/api/methods/get_chat_member.rst new file mode 100644 index 00000000..53f20f64 --- /dev/null +++ b/docs2/api/methods/get_chat_member.rst @@ -0,0 +1,45 @@ +############# +getChatMember +############# + +Returns: :obj:`ChatMember` + +.. automodule:: aiogram.methods.get_chat_member + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: ChatMember = await bot.get_chat_member(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_chat_member import GetChatMember` +- alias: :code:`from aiogram.methods import GetChatMember` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: ChatMember = await GetChatMember(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: ChatMember = await bot(GetChatMember(...)) + diff --git a/docs2/api/methods/get_chat_members_count.rst b/docs2/api/methods/get_chat_members_count.rst new file mode 100644 index 00000000..95030cb7 --- /dev/null +++ b/docs2/api/methods/get_chat_members_count.rst @@ -0,0 +1,45 @@ +################### +getChatMembersCount +################### + +Returns: :obj:`int` + +.. automodule:: aiogram.methods.get_chat_members_count + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: int = await bot.get_chat_members_count(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_chat_members_count import GetChatMembersCount` +- alias: :code:`from aiogram.methods import GetChatMembersCount` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: int = await GetChatMembersCount(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: int = await bot(GetChatMembersCount(...)) + diff --git a/docs2/api/methods/get_file.rst b/docs2/api/methods/get_file.rst new file mode 100644 index 00000000..dfdf4411 --- /dev/null +++ b/docs2/api/methods/get_file.rst @@ -0,0 +1,45 @@ +####### +getFile +####### + +Returns: :obj:`File` + +.. automodule:: aiogram.methods.get_file + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: File = await bot.get_file(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_file import GetFile` +- alias: :code:`from aiogram.methods import GetFile` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: File = await GetFile(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: File = await bot(GetFile(...)) + diff --git a/docs2/api/methods/get_game_high_scores.rst b/docs2/api/methods/get_game_high_scores.rst new file mode 100644 index 00000000..cb9dc562 --- /dev/null +++ b/docs2/api/methods/get_game_high_scores.rst @@ -0,0 +1,45 @@ +################# +getGameHighScores +################# + +Returns: :obj:`List[GameHighScore]` + +.. automodule:: aiogram.methods.get_game_high_scores + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: List[GameHighScore] = await bot.get_game_high_scores(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_game_high_scores import GetGameHighScores` +- alias: :code:`from aiogram.methods import GetGameHighScores` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: List[GameHighScore] = await GetGameHighScores(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: List[GameHighScore] = await bot(GetGameHighScores(...)) + diff --git a/docs2/api/methods/get_me.rst b/docs2/api/methods/get_me.rst new file mode 100644 index 00000000..b29e64db --- /dev/null +++ b/docs2/api/methods/get_me.rst @@ -0,0 +1,45 @@ +##### +getMe +##### + +Returns: :obj:`User` + +.. automodule:: aiogram.methods.get_me + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: User = await bot.get_me(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_me import GetMe` +- alias: :code:`from aiogram.methods import GetMe` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: User = await GetMe(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: User = await bot(GetMe(...)) + diff --git a/docs2/api/methods/get_my_commands.rst b/docs2/api/methods/get_my_commands.rst new file mode 100644 index 00000000..13add8e9 --- /dev/null +++ b/docs2/api/methods/get_my_commands.rst @@ -0,0 +1,45 @@ +############# +getMyCommands +############# + +Returns: :obj:`List[BotCommand]` + +.. automodule:: aiogram.methods.get_my_commands + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: List[BotCommand] = await bot.get_my_commands(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_my_commands import GetMyCommands` +- alias: :code:`from aiogram.methods import GetMyCommands` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: List[BotCommand] = await GetMyCommands(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: List[BotCommand] = await bot(GetMyCommands(...)) + diff --git a/docs2/api/methods/get_sticker_set.rst b/docs2/api/methods/get_sticker_set.rst new file mode 100644 index 00000000..bc3faa93 --- /dev/null +++ b/docs2/api/methods/get_sticker_set.rst @@ -0,0 +1,45 @@ +############# +getStickerSet +############# + +Returns: :obj:`StickerSet` + +.. automodule:: aiogram.methods.get_sticker_set + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: StickerSet = await bot.get_sticker_set(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_sticker_set import GetStickerSet` +- alias: :code:`from aiogram.methods import GetStickerSet` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: StickerSet = await GetStickerSet(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: StickerSet = await bot(GetStickerSet(...)) + diff --git a/docs2/api/methods/get_updates.rst b/docs2/api/methods/get_updates.rst new file mode 100644 index 00000000..15cae582 --- /dev/null +++ b/docs2/api/methods/get_updates.rst @@ -0,0 +1,45 @@ +########## +getUpdates +########## + +Returns: :obj:`List[Update]` + +.. automodule:: aiogram.methods.get_updates + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: List[Update] = await bot.get_updates(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_updates import GetUpdates` +- alias: :code:`from aiogram.methods import GetUpdates` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: List[Update] = await GetUpdates(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: List[Update] = await bot(GetUpdates(...)) + diff --git a/docs2/api/methods/get_user_profile_photos.rst b/docs2/api/methods/get_user_profile_photos.rst new file mode 100644 index 00000000..3b76b9c4 --- /dev/null +++ b/docs2/api/methods/get_user_profile_photos.rst @@ -0,0 +1,45 @@ +#################### +getUserProfilePhotos +#################### + +Returns: :obj:`UserProfilePhotos` + +.. automodule:: aiogram.methods.get_user_profile_photos + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: UserProfilePhotos = await bot.get_user_profile_photos(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_user_profile_photos import GetUserProfilePhotos` +- alias: :code:`from aiogram.methods import GetUserProfilePhotos` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: UserProfilePhotos = await GetUserProfilePhotos(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: UserProfilePhotos = await bot(GetUserProfilePhotos(...)) + diff --git a/docs2/api/methods/get_webhook_info.rst b/docs2/api/methods/get_webhook_info.rst new file mode 100644 index 00000000..88bc2a46 --- /dev/null +++ b/docs2/api/methods/get_webhook_info.rst @@ -0,0 +1,45 @@ +############## +getWebhookInfo +############## + +Returns: :obj:`WebhookInfo` + +.. automodule:: aiogram.methods.get_webhook_info + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: WebhookInfo = await bot.get_webhook_info(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.get_webhook_info import GetWebhookInfo` +- alias: :code:`from aiogram.methods import GetWebhookInfo` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: WebhookInfo = await GetWebhookInfo(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: WebhookInfo = await bot(GetWebhookInfo(...)) + diff --git a/docs2/api/methods/index.rst b/docs2/api/methods/index.rst new file mode 100644 index 00000000..728008f4 --- /dev/null +++ b/docs2/api/methods/index.rst @@ -0,0 +1,139 @@ +####### +Methods +####### + +Here is list of all available API methods: + + + +Getting updates +=============== + +.. toctree:: + :maxdepth: 1 + + get_updates + set_webhook + delete_webhook + get_webhook_info + + +Available methods +================= + +.. toctree:: + :maxdepth: 1 + + get_me + log_out + close + send_message + forward_message + copy_message + send_photo + send_audio + send_document + send_video + send_animation + send_voice + send_video_note + send_media_group + send_location + edit_message_live_location + stop_message_live_location + send_venue + send_contact + send_poll + send_dice + send_chat_action + get_user_profile_photos + get_file + kick_chat_member + unban_chat_member + restrict_chat_member + promote_chat_member + set_chat_administrator_custom_title + set_chat_permissions + export_chat_invite_link + set_chat_photo + delete_chat_photo + set_chat_title + set_chat_description + pin_chat_message + unpin_chat_message + unpin_all_chat_messages + leave_chat + get_chat + get_chat_administrators + get_chat_members_count + get_chat_member + set_chat_sticker_set + delete_chat_sticker_set + answer_callback_query + set_my_commands + get_my_commands + +Updating messages +================= + +.. toctree:: + :maxdepth: 1 + + edit_message_text + edit_message_caption + edit_message_media + edit_message_reply_markup + stop_poll + delete_message + +Stickers +======== + +.. toctree:: + :maxdepth: 1 + + send_sticker + get_sticker_set + upload_sticker_file + create_new_sticker_set + add_sticker_to_set + set_sticker_position_in_set + delete_sticker_from_set + set_sticker_set_thumb + +Inline mode +=========== + +.. toctree:: + :maxdepth: 1 + + answer_inline_query + +Payments +======== + +.. toctree:: + :maxdepth: 1 + + send_invoice + answer_shipping_query + answer_pre_checkout_query + +Telegram Passport +================= + +.. toctree:: + :maxdepth: 1 + + set_passport_data_errors + +Games +===== + +.. toctree:: + :maxdepth: 1 + + send_game + set_game_score + get_game_high_scores + diff --git a/docs2/api/methods/kick_chat_member.rst b/docs2/api/methods/kick_chat_member.rst new file mode 100644 index 00000000..7cf3d70d --- /dev/null +++ b/docs2/api/methods/kick_chat_member.rst @@ -0,0 +1,51 @@ +############## +kickChatMember +############## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.kick_chat_member + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.kick_chat_member(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.kick_chat_member import KickChatMember` +- alias: :code:`from aiogram.methods import KickChatMember` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await KickChatMember(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(KickChatMember(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return KickChatMember(...) \ No newline at end of file diff --git a/docs2/api/methods/leave_chat.rst b/docs2/api/methods/leave_chat.rst new file mode 100644 index 00000000..edb603f7 --- /dev/null +++ b/docs2/api/methods/leave_chat.rst @@ -0,0 +1,51 @@ +######### +leaveChat +######### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.leave_chat + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.leave_chat(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.leave_chat import LeaveChat` +- alias: :code:`from aiogram.methods import LeaveChat` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await LeaveChat(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(LeaveChat(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return LeaveChat(...) \ No newline at end of file diff --git a/docs2/api/methods/log_out.rst b/docs2/api/methods/log_out.rst new file mode 100644 index 00000000..46cc92c9 --- /dev/null +++ b/docs2/api/methods/log_out.rst @@ -0,0 +1,51 @@ +###### +logOut +###### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.log_out + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.log_out(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.log_out import LogOut` +- alias: :code:`from aiogram.methods import LogOut` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await LogOut(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(LogOut(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return LogOut(...) \ No newline at end of file diff --git a/docs2/api/methods/pin_chat_message.rst b/docs2/api/methods/pin_chat_message.rst new file mode 100644 index 00000000..1c3dc9ee --- /dev/null +++ b/docs2/api/methods/pin_chat_message.rst @@ -0,0 +1,51 @@ +############## +pinChatMessage +############## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.pin_chat_message + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.pin_chat_message(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.pin_chat_message import PinChatMessage` +- alias: :code:`from aiogram.methods import PinChatMessage` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await PinChatMessage(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(PinChatMessage(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return PinChatMessage(...) \ No newline at end of file diff --git a/docs2/api/methods/promote_chat_member.rst b/docs2/api/methods/promote_chat_member.rst new file mode 100644 index 00000000..ec883f59 --- /dev/null +++ b/docs2/api/methods/promote_chat_member.rst @@ -0,0 +1,51 @@ +################# +promoteChatMember +################# + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.promote_chat_member + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.promote_chat_member(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.promote_chat_member import PromoteChatMember` +- alias: :code:`from aiogram.methods import PromoteChatMember` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await PromoteChatMember(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(PromoteChatMember(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return PromoteChatMember(...) \ No newline at end of file diff --git a/docs2/api/methods/restrict_chat_member.rst b/docs2/api/methods/restrict_chat_member.rst new file mode 100644 index 00000000..47cadd01 --- /dev/null +++ b/docs2/api/methods/restrict_chat_member.rst @@ -0,0 +1,51 @@ +################## +restrictChatMember +################## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.restrict_chat_member + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.restrict_chat_member(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.restrict_chat_member import RestrictChatMember` +- alias: :code:`from aiogram.methods import RestrictChatMember` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await RestrictChatMember(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(RestrictChatMember(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return RestrictChatMember(...) \ No newline at end of file diff --git a/docs2/api/methods/send_animation.rst b/docs2/api/methods/send_animation.rst new file mode 100644 index 00000000..8c2515a7 --- /dev/null +++ b/docs2/api/methods/send_animation.rst @@ -0,0 +1,51 @@ +############# +sendAnimation +############# + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_animation + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_animation(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_animation import SendAnimation` +- alias: :code:`from aiogram.methods import SendAnimation` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendAnimation(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendAnimation(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendAnimation(...) \ No newline at end of file diff --git a/docs2/api/methods/send_audio.rst b/docs2/api/methods/send_audio.rst new file mode 100644 index 00000000..9da55ded --- /dev/null +++ b/docs2/api/methods/send_audio.rst @@ -0,0 +1,51 @@ +######### +sendAudio +######### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_audio + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_audio(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_audio import SendAudio` +- alias: :code:`from aiogram.methods import SendAudio` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendAudio(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendAudio(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendAudio(...) \ No newline at end of file diff --git a/docs2/api/methods/send_chat_action.rst b/docs2/api/methods/send_chat_action.rst new file mode 100644 index 00000000..156915f8 --- /dev/null +++ b/docs2/api/methods/send_chat_action.rst @@ -0,0 +1,51 @@ +############## +sendChatAction +############## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.send_chat_action + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.send_chat_action(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_chat_action import SendChatAction` +- alias: :code:`from aiogram.methods import SendChatAction` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SendChatAction(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SendChatAction(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendChatAction(...) \ No newline at end of file diff --git a/docs2/api/methods/send_contact.rst b/docs2/api/methods/send_contact.rst new file mode 100644 index 00000000..6560a367 --- /dev/null +++ b/docs2/api/methods/send_contact.rst @@ -0,0 +1,51 @@ +########### +sendContact +########### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_contact + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_contact(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_contact import SendContact` +- alias: :code:`from aiogram.methods import SendContact` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendContact(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendContact(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendContact(...) \ No newline at end of file diff --git a/docs2/api/methods/send_dice.rst b/docs2/api/methods/send_dice.rst new file mode 100644 index 00000000..9348d2b5 --- /dev/null +++ b/docs2/api/methods/send_dice.rst @@ -0,0 +1,51 @@ +######## +sendDice +######## + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_dice + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_dice(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_dice import SendDice` +- alias: :code:`from aiogram.methods import SendDice` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendDice(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendDice(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendDice(...) \ No newline at end of file diff --git a/docs2/api/methods/send_document.rst b/docs2/api/methods/send_document.rst new file mode 100644 index 00000000..2996da0d --- /dev/null +++ b/docs2/api/methods/send_document.rst @@ -0,0 +1,51 @@ +############ +sendDocument +############ + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_document + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_document(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_document import SendDocument` +- alias: :code:`from aiogram.methods import SendDocument` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendDocument(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendDocument(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendDocument(...) \ No newline at end of file diff --git a/docs2/api/methods/send_game.rst b/docs2/api/methods/send_game.rst new file mode 100644 index 00000000..0526baaa --- /dev/null +++ b/docs2/api/methods/send_game.rst @@ -0,0 +1,51 @@ +######## +sendGame +######## + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_game + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_game(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_game import SendGame` +- alias: :code:`from aiogram.methods import SendGame` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendGame(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendGame(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendGame(...) \ No newline at end of file diff --git a/docs2/api/methods/send_invoice.rst b/docs2/api/methods/send_invoice.rst new file mode 100644 index 00000000..f7d88111 --- /dev/null +++ b/docs2/api/methods/send_invoice.rst @@ -0,0 +1,51 @@ +########### +sendInvoice +########### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_invoice + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_invoice(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_invoice import SendInvoice` +- alias: :code:`from aiogram.methods import SendInvoice` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendInvoice(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendInvoice(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendInvoice(...) \ No newline at end of file diff --git a/docs2/api/methods/send_location.rst b/docs2/api/methods/send_location.rst new file mode 100644 index 00000000..3694f8a6 --- /dev/null +++ b/docs2/api/methods/send_location.rst @@ -0,0 +1,51 @@ +############ +sendLocation +############ + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_location + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_location(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_location import SendLocation` +- alias: :code:`from aiogram.methods import SendLocation` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendLocation(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendLocation(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendLocation(...) \ No newline at end of file diff --git a/docs2/api/methods/send_media_group.rst b/docs2/api/methods/send_media_group.rst new file mode 100644 index 00000000..7f0dfe75 --- /dev/null +++ b/docs2/api/methods/send_media_group.rst @@ -0,0 +1,51 @@ +############## +sendMediaGroup +############## + +Returns: :obj:`List[Message]` + +.. automodule:: aiogram.methods.send_media_group + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: List[Message] = await bot.send_media_group(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_media_group import SendMediaGroup` +- alias: :code:`from aiogram.methods import SendMediaGroup` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: List[Message] = await SendMediaGroup(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: List[Message] = await bot(SendMediaGroup(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendMediaGroup(...) \ No newline at end of file diff --git a/docs2/api/methods/send_message.rst b/docs2/api/methods/send_message.rst new file mode 100644 index 00000000..318684bf --- /dev/null +++ b/docs2/api/methods/send_message.rst @@ -0,0 +1,51 @@ +########### +sendMessage +########### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_message + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_message(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_message import SendMessage` +- alias: :code:`from aiogram.methods import SendMessage` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendMessage(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendMessage(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendMessage(...) \ No newline at end of file diff --git a/docs2/api/methods/send_photo.rst b/docs2/api/methods/send_photo.rst new file mode 100644 index 00000000..313b815e --- /dev/null +++ b/docs2/api/methods/send_photo.rst @@ -0,0 +1,51 @@ +######### +sendPhoto +######### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_photo + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_photo(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_photo import SendPhoto` +- alias: :code:`from aiogram.methods import SendPhoto` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendPhoto(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendPhoto(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendPhoto(...) \ No newline at end of file diff --git a/docs2/api/methods/send_poll.rst b/docs2/api/methods/send_poll.rst new file mode 100644 index 00000000..e83d3240 --- /dev/null +++ b/docs2/api/methods/send_poll.rst @@ -0,0 +1,51 @@ +######## +sendPoll +######## + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_poll + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_poll(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_poll import SendPoll` +- alias: :code:`from aiogram.methods import SendPoll` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendPoll(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendPoll(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendPoll(...) \ No newline at end of file diff --git a/docs2/api/methods/send_sticker.rst b/docs2/api/methods/send_sticker.rst new file mode 100644 index 00000000..ca77bd92 --- /dev/null +++ b/docs2/api/methods/send_sticker.rst @@ -0,0 +1,51 @@ +########### +sendSticker +########### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_sticker + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_sticker(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_sticker import SendSticker` +- alias: :code:`from aiogram.methods import SendSticker` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendSticker(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendSticker(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendSticker(...) \ No newline at end of file diff --git a/docs2/api/methods/send_venue.rst b/docs2/api/methods/send_venue.rst new file mode 100644 index 00000000..6c19d76e --- /dev/null +++ b/docs2/api/methods/send_venue.rst @@ -0,0 +1,51 @@ +######### +sendVenue +######### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_venue + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_venue(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_venue import SendVenue` +- alias: :code:`from aiogram.methods import SendVenue` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendVenue(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendVenue(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendVenue(...) \ No newline at end of file diff --git a/docs2/api/methods/send_video.rst b/docs2/api/methods/send_video.rst new file mode 100644 index 00000000..4328017a --- /dev/null +++ b/docs2/api/methods/send_video.rst @@ -0,0 +1,51 @@ +######### +sendVideo +######### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_video + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_video(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_video import SendVideo` +- alias: :code:`from aiogram.methods import SendVideo` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendVideo(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendVideo(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendVideo(...) \ No newline at end of file diff --git a/docs2/api/methods/send_video_note.rst b/docs2/api/methods/send_video_note.rst new file mode 100644 index 00000000..f661a9e0 --- /dev/null +++ b/docs2/api/methods/send_video_note.rst @@ -0,0 +1,51 @@ +############# +sendVideoNote +############# + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_video_note + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_video_note(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_video_note import SendVideoNote` +- alias: :code:`from aiogram.methods import SendVideoNote` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendVideoNote(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendVideoNote(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendVideoNote(...) \ No newline at end of file diff --git a/docs2/api/methods/send_voice.rst b/docs2/api/methods/send_voice.rst new file mode 100644 index 00000000..82159641 --- /dev/null +++ b/docs2/api/methods/send_voice.rst @@ -0,0 +1,51 @@ +######### +sendVoice +######### + +Returns: :obj:`Message` + +.. automodule:: aiogram.methods.send_voice + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Message = await bot.send_voice(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.send_voice import SendVoice` +- alias: :code:`from aiogram.methods import SendVoice` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Message = await SendVoice(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Message = await bot(SendVoice(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SendVoice(...) \ No newline at end of file diff --git a/docs2/api/methods/set_chat_administrator_custom_title.rst b/docs2/api/methods/set_chat_administrator_custom_title.rst new file mode 100644 index 00000000..3e942918 --- /dev/null +++ b/docs2/api/methods/set_chat_administrator_custom_title.rst @@ -0,0 +1,51 @@ +############################### +setChatAdministratorCustomTitle +############################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_chat_administrator_custom_title + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_chat_administrator_custom_title(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_chat_administrator_custom_title import SetChatAdministratorCustomTitle` +- alias: :code:`from aiogram.methods import SetChatAdministratorCustomTitle` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetChatAdministratorCustomTitle(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetChatAdministratorCustomTitle(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetChatAdministratorCustomTitle(...) \ No newline at end of file diff --git a/docs2/api/methods/set_chat_description.rst b/docs2/api/methods/set_chat_description.rst new file mode 100644 index 00000000..af1ba8c5 --- /dev/null +++ b/docs2/api/methods/set_chat_description.rst @@ -0,0 +1,51 @@ +################## +setChatDescription +################## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_chat_description + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_chat_description(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_chat_description import SetChatDescription` +- alias: :code:`from aiogram.methods import SetChatDescription` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetChatDescription(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetChatDescription(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetChatDescription(...) \ No newline at end of file diff --git a/docs2/api/methods/set_chat_permissions.rst b/docs2/api/methods/set_chat_permissions.rst new file mode 100644 index 00000000..87f8fa84 --- /dev/null +++ b/docs2/api/methods/set_chat_permissions.rst @@ -0,0 +1,51 @@ +################## +setChatPermissions +################## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_chat_permissions + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_chat_permissions(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_chat_permissions import SetChatPermissions` +- alias: :code:`from aiogram.methods import SetChatPermissions` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetChatPermissions(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetChatPermissions(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetChatPermissions(...) \ No newline at end of file diff --git a/docs2/api/methods/set_chat_photo.rst b/docs2/api/methods/set_chat_photo.rst new file mode 100644 index 00000000..a2133a14 --- /dev/null +++ b/docs2/api/methods/set_chat_photo.rst @@ -0,0 +1,45 @@ +############ +setChatPhoto +############ + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_chat_photo + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_chat_photo(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_chat_photo import SetChatPhoto` +- alias: :code:`from aiogram.methods import SetChatPhoto` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetChatPhoto(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetChatPhoto(...)) + diff --git a/docs2/api/methods/set_chat_sticker_set.rst b/docs2/api/methods/set_chat_sticker_set.rst new file mode 100644 index 00000000..f0a76edb --- /dev/null +++ b/docs2/api/methods/set_chat_sticker_set.rst @@ -0,0 +1,51 @@ +################# +setChatStickerSet +################# + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_chat_sticker_set + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_chat_sticker_set(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_chat_sticker_set import SetChatStickerSet` +- alias: :code:`from aiogram.methods import SetChatStickerSet` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetChatStickerSet(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetChatStickerSet(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetChatStickerSet(...) \ No newline at end of file diff --git a/docs2/api/methods/set_chat_title.rst b/docs2/api/methods/set_chat_title.rst new file mode 100644 index 00000000..efba5e20 --- /dev/null +++ b/docs2/api/methods/set_chat_title.rst @@ -0,0 +1,51 @@ +############ +setChatTitle +############ + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_chat_title + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_chat_title(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_chat_title import SetChatTitle` +- alias: :code:`from aiogram.methods import SetChatTitle` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetChatTitle(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetChatTitle(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetChatTitle(...) \ No newline at end of file diff --git a/docs2/api/methods/set_game_score.rst b/docs2/api/methods/set_game_score.rst new file mode 100644 index 00000000..e631c07b --- /dev/null +++ b/docs2/api/methods/set_game_score.rst @@ -0,0 +1,51 @@ +############ +setGameScore +############ + +Returns: :obj:`Union[Message, bool]` + +.. automodule:: aiogram.methods.set_game_score + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Union[Message, bool] = await bot.set_game_score(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_game_score import SetGameScore` +- alias: :code:`from aiogram.methods import SetGameScore` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Union[Message, bool] = await SetGameScore(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Union[Message, bool] = await bot(SetGameScore(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetGameScore(...) \ No newline at end of file diff --git a/docs2/api/methods/set_my_commands.rst b/docs2/api/methods/set_my_commands.rst new file mode 100644 index 00000000..5f6db367 --- /dev/null +++ b/docs2/api/methods/set_my_commands.rst @@ -0,0 +1,51 @@ +############# +setMyCommands +############# + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_my_commands + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_my_commands(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_my_commands import SetMyCommands` +- alias: :code:`from aiogram.methods import SetMyCommands` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetMyCommands(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetMyCommands(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetMyCommands(...) \ No newline at end of file diff --git a/docs2/api/methods/set_passport_data_errors.rst b/docs2/api/methods/set_passport_data_errors.rst new file mode 100644 index 00000000..14c00b44 --- /dev/null +++ b/docs2/api/methods/set_passport_data_errors.rst @@ -0,0 +1,51 @@ +##################### +setPassportDataErrors +##################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_passport_data_errors + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_passport_data_errors(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_passport_data_errors import SetPassportDataErrors` +- alias: :code:`from aiogram.methods import SetPassportDataErrors` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetPassportDataErrors(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetPassportDataErrors(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetPassportDataErrors(...) \ No newline at end of file diff --git a/docs2/api/methods/set_sticker_position_in_set.rst b/docs2/api/methods/set_sticker_position_in_set.rst new file mode 100644 index 00000000..a9efce31 --- /dev/null +++ b/docs2/api/methods/set_sticker_position_in_set.rst @@ -0,0 +1,51 @@ +####################### +setStickerPositionInSet +####################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_sticker_position_in_set + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_sticker_position_in_set(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_sticker_position_in_set import SetStickerPositionInSet` +- alias: :code:`from aiogram.methods import SetStickerPositionInSet` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetStickerPositionInSet(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetStickerPositionInSet(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetStickerPositionInSet(...) \ No newline at end of file diff --git a/docs2/api/methods/set_sticker_set_thumb.rst b/docs2/api/methods/set_sticker_set_thumb.rst new file mode 100644 index 00000000..d2959f03 --- /dev/null +++ b/docs2/api/methods/set_sticker_set_thumb.rst @@ -0,0 +1,51 @@ +################## +setStickerSetThumb +################## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_sticker_set_thumb + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_sticker_set_thumb(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_sticker_set_thumb import SetStickerSetThumb` +- alias: :code:`from aiogram.methods import SetStickerSetThumb` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetStickerSetThumb(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetStickerSetThumb(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetStickerSetThumb(...) \ No newline at end of file diff --git a/docs2/api/methods/set_webhook.rst b/docs2/api/methods/set_webhook.rst new file mode 100644 index 00000000..c5188243 --- /dev/null +++ b/docs2/api/methods/set_webhook.rst @@ -0,0 +1,51 @@ +########## +setWebhook +########## + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.set_webhook + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.set_webhook(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.set_webhook import SetWebhook` +- alias: :code:`from aiogram.methods import SetWebhook` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await SetWebhook(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(SetWebhook(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return SetWebhook(...) \ No newline at end of file diff --git a/docs2/api/methods/stop_message_live_location.rst b/docs2/api/methods/stop_message_live_location.rst new file mode 100644 index 00000000..bc997f43 --- /dev/null +++ b/docs2/api/methods/stop_message_live_location.rst @@ -0,0 +1,51 @@ +####################### +stopMessageLiveLocation +####################### + +Returns: :obj:`Union[Message, bool]` + +.. automodule:: aiogram.methods.stop_message_live_location + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Union[Message, bool] = await bot.stop_message_live_location(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.stop_message_live_location import StopMessageLiveLocation` +- alias: :code:`from aiogram.methods import StopMessageLiveLocation` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Union[Message, bool] = await StopMessageLiveLocation(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Union[Message, bool] = await bot(StopMessageLiveLocation(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return StopMessageLiveLocation(...) \ No newline at end of file diff --git a/docs2/api/methods/stop_poll.rst b/docs2/api/methods/stop_poll.rst new file mode 100644 index 00000000..e0ad1046 --- /dev/null +++ b/docs2/api/methods/stop_poll.rst @@ -0,0 +1,51 @@ +######## +stopPoll +######## + +Returns: :obj:`Poll` + +.. automodule:: aiogram.methods.stop_poll + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: Poll = await bot.stop_poll(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.stop_poll import StopPoll` +- alias: :code:`from aiogram.methods import StopPoll` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: Poll = await StopPoll(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: Poll = await bot(StopPoll(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return StopPoll(...) \ No newline at end of file diff --git a/docs2/api/methods/unban_chat_member.rst b/docs2/api/methods/unban_chat_member.rst new file mode 100644 index 00000000..c49779e4 --- /dev/null +++ b/docs2/api/methods/unban_chat_member.rst @@ -0,0 +1,51 @@ +############### +unbanChatMember +############### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.unban_chat_member + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.unban_chat_member(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.unban_chat_member import UnbanChatMember` +- alias: :code:`from aiogram.methods import UnbanChatMember` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await UnbanChatMember(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(UnbanChatMember(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return UnbanChatMember(...) \ No newline at end of file diff --git a/docs2/api/methods/unpin_all_chat_messages.rst b/docs2/api/methods/unpin_all_chat_messages.rst new file mode 100644 index 00000000..c4c9b930 --- /dev/null +++ b/docs2/api/methods/unpin_all_chat_messages.rst @@ -0,0 +1,51 @@ +#################### +unpinAllChatMessages +#################### + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.unpin_all_chat_messages + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.unpin_all_chat_messages(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.unpin_all_chat_messages import UnpinAllChatMessages` +- alias: :code:`from aiogram.methods import UnpinAllChatMessages` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await UnpinAllChatMessages(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(UnpinAllChatMessages(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return UnpinAllChatMessages(...) \ No newline at end of file diff --git a/docs2/api/methods/unpin_chat_message.rst b/docs2/api/methods/unpin_chat_message.rst new file mode 100644 index 00000000..c1d1d68d --- /dev/null +++ b/docs2/api/methods/unpin_chat_message.rst @@ -0,0 +1,51 @@ +################ +unpinChatMessage +################ + +Returns: :obj:`bool` + +.. automodule:: aiogram.methods.unpin_chat_message + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: bool = await bot.unpin_chat_message(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.unpin_chat_message import UnpinChatMessage` +- alias: :code:`from aiogram.methods import UnpinChatMessage` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: bool = await UnpinChatMessage(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: bool = await bot(UnpinChatMessage(...)) + +As reply into Webhook in handler +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + return UnpinChatMessage(...) \ No newline at end of file diff --git a/docs2/api/methods/upload_sticker_file.rst b/docs2/api/methods/upload_sticker_file.rst new file mode 100644 index 00000000..4d763556 --- /dev/null +++ b/docs2/api/methods/upload_sticker_file.rst @@ -0,0 +1,45 @@ +################# +uploadStickerFile +################# + +Returns: :obj:`File` + +.. automodule:: aiogram.methods.upload_sticker_file + :members: + :member-order: bysource + :undoc-members: True + + +Usage +===== + +As bot method +------------- + +.. code-block:: + + result: File = await bot.upload_sticker_file(...) + + +Method as object +---------------- + +Imports: + +- :code:`from aiogram.methods.upload_sticker_file import UploadStickerFile` +- alias: :code:`from aiogram.methods import UploadStickerFile` + +In handlers with current bot +---------------------------- + +.. code-block:: python + + result: File = await UploadStickerFile(...) + +With specific bot +~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + result: File = await bot(UploadStickerFile(...)) + diff --git a/docs2/api/session/aiohttp.rst b/docs2/api/session/aiohttp.rst new file mode 100644 index 00000000..3a595d1b --- /dev/null +++ b/docs2/api/session/aiohttp.rst @@ -0,0 +1,95 @@ +####### +aiohttp +####### + +AiohttpSession represents a wrapper-class around `ClientSession` from `aiohttp `_ + +Currently `AiohttpSession` is a default session used in `aiogram.Bot` + +.. autoclass:: aiogram.client.session.aiohttp.AiohttpSession + +Usage example +============= + +.. code-block:: + + from aiogram import Bot + from aiogram.session.aiohttp import AiohttpSession + + session = AiohttpSession() + Bot('42:token', session=session) + + +Proxy requests in AiohttpSession +================================ + +In order to use AiohttpSession with proxy connector you have to install `aiohttp-socks `_ + +Binding session to bot: + +.. code-block:: + + from aiogram import Bot + from aiogram.client.session.aiohttp import AiohttpSession + + session = AiohttpSession(proxy="protocol://host:port/") + Bot(token="bot token", session=session) + + +.. note:: + + Only following protocols are supported: http(tunneling), socks4(a), socks5 + as aiohttp_socks `documentation `_ claims. + + +Authorization +------------- + +Proxy authorization credentials can be specified in proxy URL or come as an instance of :obj:`aiohttp.BasicAuth` containing +login and password. + +Consider examples: + +.. code-block:: + + from aiohttp import BasicAuth + from aiogram.client.session.aiohttp import AiohttpSession + + auth = BasicAuth(login="user", password="password") + session = AiohttpSession(proxy=("protocol://host:port", auth)) + + +or simply include your basic auth credential in URL + +.. code-block:: + + session = AiohttpSession(proxy="protocol://user:password@host:port") + + +.. note:: + + Aiogram prefers `BasicAuth` over username and password in URL, so + if proxy URL contains login and password and `BasicAuth` object is passed at the same time + aiogram will use login and password from `BasicAuth` instance. + + +Proxy chains +------------ + +Since `aiohttp-socks `_ supports proxy chains, you're able to use them in aiogram + +Example of chain proxies: + +.. code-block:: + + from aiohttp import BasicAuth + from aiogram.client.session.aiohttp import AiohttpSession + + auth = BasicAuth(login="user", password="password") + session = AiohttpSession( + proxy={ + "protocol0://host0:port0", + "protocol1://user:password@host1:port1", + ("protocol2://host2:port2", auth), + } # can be any iterable if not set + ) diff --git a/docs2/api/session/base.rst b/docs2/api/session/base.rst new file mode 100644 index 00000000..adda7e2e --- /dev/null +++ b/docs2/api/session/base.rst @@ -0,0 +1,8 @@ +#### +Base +#### + +Abstract session for all client sessions + +.. autoclass:: aiogram.client.session.base.BaseSession + :members: diff --git a/docs2/api/session/custom_server.rst b/docs2/api/session/custom_server.rst new file mode 100644 index 00000000..fe2b86f4 --- /dev/null +++ b/docs2/api/session/custom_server.rst @@ -0,0 +1,13 @@ +Use Custom API server +===================== + +.. autoclass:: aiogram.client.telegram.TelegramAPIServer + :members: + +For example, if you want to use self-hosted API server: + +.. code-block:: python3 + + session = AiohttpSession() + session.api = TelegramAPIServer.from_base('http://localhost:8082') + bot = Bot(..., session=session) diff --git a/docs2/api/session/index.rst b/docs2/api/session/index.rst new file mode 100644 index 00000000..dff24e7a --- /dev/null +++ b/docs2/api/session/index.rst @@ -0,0 +1,10 @@ +############## +Client session +############## + +Client sessions is used for interacting with API server. + +.. toctree:: + custom_server + base + aiohttp diff --git a/docs2/api/types/animation.rst b/docs2/api/types/animation.rst new file mode 100644 index 00000000..4d48b1e1 --- /dev/null +++ b/docs2/api/types/animation.rst @@ -0,0 +1,9 @@ +######### +Animation +######### + + +.. automodule:: aiogram.types.animation + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/audio.rst b/docs2/api/types/audio.rst new file mode 100644 index 00000000..9eba9027 --- /dev/null +++ b/docs2/api/types/audio.rst @@ -0,0 +1,9 @@ +##### +Audio +##### + + +.. automodule:: aiogram.types.audio + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/bot_command.rst b/docs2/api/types/bot_command.rst new file mode 100644 index 00000000..a8364c68 --- /dev/null +++ b/docs2/api/types/bot_command.rst @@ -0,0 +1,9 @@ +########## +BotCommand +########## + + +.. automodule:: aiogram.types.bot_command + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/callback_game.rst b/docs2/api/types/callback_game.rst new file mode 100644 index 00000000..89f2c91e --- /dev/null +++ b/docs2/api/types/callback_game.rst @@ -0,0 +1,9 @@ +############ +CallbackGame +############ + + +.. automodule:: aiogram.types.callback_game + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/callback_query.rst b/docs2/api/types/callback_query.rst new file mode 100644 index 00000000..f8d34ddd --- /dev/null +++ b/docs2/api/types/callback_query.rst @@ -0,0 +1,9 @@ +############# +CallbackQuery +############# + + +.. automodule:: aiogram.types.callback_query + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/chat.rst b/docs2/api/types/chat.rst new file mode 100644 index 00000000..800dead4 --- /dev/null +++ b/docs2/api/types/chat.rst @@ -0,0 +1,9 @@ +#### +Chat +#### + + +.. automodule:: aiogram.types.chat + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/chat_location.rst b/docs2/api/types/chat_location.rst new file mode 100644 index 00000000..84f4bef9 --- /dev/null +++ b/docs2/api/types/chat_location.rst @@ -0,0 +1,9 @@ +############ +ChatLocation +############ + + +.. automodule:: aiogram.types.chat_location + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/chat_member.rst b/docs2/api/types/chat_member.rst new file mode 100644 index 00000000..4fff69f9 --- /dev/null +++ b/docs2/api/types/chat_member.rst @@ -0,0 +1,9 @@ +########## +ChatMember +########## + + +.. automodule:: aiogram.types.chat_member + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/chat_permissions.rst b/docs2/api/types/chat_permissions.rst new file mode 100644 index 00000000..b3020a19 --- /dev/null +++ b/docs2/api/types/chat_permissions.rst @@ -0,0 +1,9 @@ +############### +ChatPermissions +############### + + +.. automodule:: aiogram.types.chat_permissions + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/chat_photo.rst b/docs2/api/types/chat_photo.rst new file mode 100644 index 00000000..bc7c8a5e --- /dev/null +++ b/docs2/api/types/chat_photo.rst @@ -0,0 +1,9 @@ +######### +ChatPhoto +######### + + +.. automodule:: aiogram.types.chat_photo + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/chosen_inline_result.rst b/docs2/api/types/chosen_inline_result.rst new file mode 100644 index 00000000..fe4d7c11 --- /dev/null +++ b/docs2/api/types/chosen_inline_result.rst @@ -0,0 +1,9 @@ +################## +ChosenInlineResult +################## + + +.. automodule:: aiogram.types.chosen_inline_result + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/contact.rst b/docs2/api/types/contact.rst new file mode 100644 index 00000000..aa75232e --- /dev/null +++ b/docs2/api/types/contact.rst @@ -0,0 +1,9 @@ +####### +Contact +####### + + +.. automodule:: aiogram.types.contact + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/dice.rst b/docs2/api/types/dice.rst new file mode 100644 index 00000000..15dbab13 --- /dev/null +++ b/docs2/api/types/dice.rst @@ -0,0 +1,9 @@ +#### +Dice +#### + + +.. automodule:: aiogram.types.dice + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/document.rst b/docs2/api/types/document.rst new file mode 100644 index 00000000..8138cf2c --- /dev/null +++ b/docs2/api/types/document.rst @@ -0,0 +1,9 @@ +######## +Document +######## + + +.. automodule:: aiogram.types.document + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/encrypted_credentials.rst b/docs2/api/types/encrypted_credentials.rst new file mode 100644 index 00000000..185e7881 --- /dev/null +++ b/docs2/api/types/encrypted_credentials.rst @@ -0,0 +1,9 @@ +#################### +EncryptedCredentials +#################### + + +.. automodule:: aiogram.types.encrypted_credentials + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/encrypted_passport_element.rst b/docs2/api/types/encrypted_passport_element.rst new file mode 100644 index 00000000..78a5c223 --- /dev/null +++ b/docs2/api/types/encrypted_passport_element.rst @@ -0,0 +1,9 @@ +######################## +EncryptedPassportElement +######################## + + +.. automodule:: aiogram.types.encrypted_passport_element + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/file.rst b/docs2/api/types/file.rst new file mode 100644 index 00000000..4089e6c8 --- /dev/null +++ b/docs2/api/types/file.rst @@ -0,0 +1,9 @@ +#### +File +#### + + +.. automodule:: aiogram.types.file + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/force_reply.rst b/docs2/api/types/force_reply.rst new file mode 100644 index 00000000..e15d8b18 --- /dev/null +++ b/docs2/api/types/force_reply.rst @@ -0,0 +1,9 @@ +########## +ForceReply +########## + + +.. automodule:: aiogram.types.force_reply + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/game.rst b/docs2/api/types/game.rst new file mode 100644 index 00000000..706206fc --- /dev/null +++ b/docs2/api/types/game.rst @@ -0,0 +1,9 @@ +#### +Game +#### + + +.. automodule:: aiogram.types.game + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/game_high_score.rst b/docs2/api/types/game_high_score.rst new file mode 100644 index 00000000..044c70ed --- /dev/null +++ b/docs2/api/types/game_high_score.rst @@ -0,0 +1,9 @@ +############# +GameHighScore +############# + + +.. automodule:: aiogram.types.game_high_score + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/index.rst b/docs2/api/types/index.rst new file mode 100644 index 00000000..07f8be47 --- /dev/null +++ b/docs2/api/types/index.rst @@ -0,0 +1,161 @@ +##### +Types +##### + +Here is list of all available API types: + + + +Getting updates +=============== + +.. toctree:: + :maxdepth: 1 + + update + webhook_info + +Available types +=============== + +.. toctree:: + :maxdepth: 1 + + user + chat + message + message_id + message_entity + photo_size + animation + audio + document + video + video_note + voice + contact + dice + poll_option + poll_answer + poll + location + venue + proximity_alert_triggered + user_profile_photos + file + reply_keyboard_markup + keyboard_button + keyboard_button_poll_type + reply_keyboard_remove + inline_keyboard_markup + inline_keyboard_button + login_url + callback_query + force_reply + chat_photo + chat_member + chat_permissions + chat_location + bot_command + response_parameters + input_media + input_media_photo + input_media_video + input_media_animation + input_media_audio + input_media_document + input_file + + + +Stickers +======== + +.. toctree:: + :maxdepth: 1 + + sticker + sticker_set + mask_position + +Inline mode +=========== + +.. toctree:: + :maxdepth: 1 + + inline_query + inline_query_result + inline_query_result_article + inline_query_result_photo + inline_query_result_gif + inline_query_result_mpeg4_gif + inline_query_result_video + inline_query_result_audio + inline_query_result_voice + inline_query_result_document + inline_query_result_location + inline_query_result_venue + inline_query_result_contact + inline_query_result_game + inline_query_result_cached_photo + inline_query_result_cached_gif + inline_query_result_cached_mpeg4_gif + inline_query_result_cached_sticker + inline_query_result_cached_document + inline_query_result_cached_video + inline_query_result_cached_voice + inline_query_result_cached_audio + input_message_content + input_text_message_content + input_location_message_content + input_venue_message_content + input_contact_message_content + chosen_inline_result + +Payments +======== + +.. toctree:: + :maxdepth: 1 + + labeled_price + invoice + shipping_address + order_info + shipping_option + successful_payment + shipping_query + pre_checkout_query + +Telegram Passport +================= + +.. toctree:: + :maxdepth: 1 + + passport_data + passport_file + encrypted_passport_element + encrypted_credentials + passport_element_error + passport_element_error_data_field + passport_element_error_front_side + passport_element_error_reverse_side + passport_element_error_selfie + passport_element_error_file + passport_element_error_files + passport_element_error_translation_file + passport_element_error_translation_files + passport_element_error_unspecified + +Games +===== + +.. toctree:: + :maxdepth: 1 + + game + callback_game + game_high_score + diff --git a/docs2/api/types/inline_keyboard_button.rst b/docs2/api/types/inline_keyboard_button.rst new file mode 100644 index 00000000..3689b038 --- /dev/null +++ b/docs2/api/types/inline_keyboard_button.rst @@ -0,0 +1,9 @@ +#################### +InlineKeyboardButton +#################### + + +.. automodule:: aiogram.types.inline_keyboard_button + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_keyboard_markup.rst b/docs2/api/types/inline_keyboard_markup.rst new file mode 100644 index 00000000..bdb03b8e --- /dev/null +++ b/docs2/api/types/inline_keyboard_markup.rst @@ -0,0 +1,9 @@ +#################### +InlineKeyboardMarkup +#################### + + +.. automodule:: aiogram.types.inline_keyboard_markup + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query.rst b/docs2/api/types/inline_query.rst new file mode 100644 index 00000000..ab1b08e5 --- /dev/null +++ b/docs2/api/types/inline_query.rst @@ -0,0 +1,9 @@ +########### +InlineQuery +########### + + +.. automodule:: aiogram.types.inline_query + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result.rst b/docs2/api/types/inline_query_result.rst new file mode 100644 index 00000000..8f65bd92 --- /dev/null +++ b/docs2/api/types/inline_query_result.rst @@ -0,0 +1,9 @@ +################# +InlineQueryResult +################# + + +.. automodule:: aiogram.types.inline_query_result + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_article.rst b/docs2/api/types/inline_query_result_article.rst new file mode 100644 index 00000000..a633ba41 --- /dev/null +++ b/docs2/api/types/inline_query_result_article.rst @@ -0,0 +1,9 @@ +######################## +InlineQueryResultArticle +######################## + + +.. automodule:: aiogram.types.inline_query_result_article + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_audio.rst b/docs2/api/types/inline_query_result_audio.rst new file mode 100644 index 00000000..584ee611 --- /dev/null +++ b/docs2/api/types/inline_query_result_audio.rst @@ -0,0 +1,9 @@ +###################### +InlineQueryResultAudio +###################### + + +.. automodule:: aiogram.types.inline_query_result_audio + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_audio.rst b/docs2/api/types/inline_query_result_cached_audio.rst new file mode 100644 index 00000000..af64c31a --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_audio.rst @@ -0,0 +1,9 @@ +############################ +InlineQueryResultCachedAudio +############################ + + +.. automodule:: aiogram.types.inline_query_result_cached_audio + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_document.rst b/docs2/api/types/inline_query_result_cached_document.rst new file mode 100644 index 00000000..39ebaf3c --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_document.rst @@ -0,0 +1,9 @@ +############################### +InlineQueryResultCachedDocument +############################### + + +.. automodule:: aiogram.types.inline_query_result_cached_document + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_gif.rst b/docs2/api/types/inline_query_result_cached_gif.rst new file mode 100644 index 00000000..7d88ab69 --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_gif.rst @@ -0,0 +1,9 @@ +########################## +InlineQueryResultCachedGif +########################## + + +.. automodule:: aiogram.types.inline_query_result_cached_gif + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_mpeg4_gif.rst b/docs2/api/types/inline_query_result_cached_mpeg4_gif.rst new file mode 100644 index 00000000..23e06635 --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_mpeg4_gif.rst @@ -0,0 +1,9 @@ +############################### +InlineQueryResultCachedMpeg4Gif +############################### + + +.. automodule:: aiogram.types.inline_query_result_cached_mpeg4_gif + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_photo.rst b/docs2/api/types/inline_query_result_cached_photo.rst new file mode 100644 index 00000000..3ebcc6be --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_photo.rst @@ -0,0 +1,9 @@ +############################ +InlineQueryResultCachedPhoto +############################ + + +.. automodule:: aiogram.types.inline_query_result_cached_photo + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_sticker.rst b/docs2/api/types/inline_query_result_cached_sticker.rst new file mode 100644 index 00000000..3156775a --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_sticker.rst @@ -0,0 +1,9 @@ +############################## +InlineQueryResultCachedSticker +############################## + + +.. automodule:: aiogram.types.inline_query_result_cached_sticker + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_video.rst b/docs2/api/types/inline_query_result_cached_video.rst new file mode 100644 index 00000000..51b95a05 --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_video.rst @@ -0,0 +1,9 @@ +############################ +InlineQueryResultCachedVideo +############################ + + +.. automodule:: aiogram.types.inline_query_result_cached_video + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_cached_voice.rst b/docs2/api/types/inline_query_result_cached_voice.rst new file mode 100644 index 00000000..b2c70d60 --- /dev/null +++ b/docs2/api/types/inline_query_result_cached_voice.rst @@ -0,0 +1,9 @@ +############################ +InlineQueryResultCachedVoice +############################ + + +.. automodule:: aiogram.types.inline_query_result_cached_voice + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_contact.rst b/docs2/api/types/inline_query_result_contact.rst new file mode 100644 index 00000000..e2ae2f9c --- /dev/null +++ b/docs2/api/types/inline_query_result_contact.rst @@ -0,0 +1,9 @@ +######################## +InlineQueryResultContact +######################## + + +.. automodule:: aiogram.types.inline_query_result_contact + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_document.rst b/docs2/api/types/inline_query_result_document.rst new file mode 100644 index 00000000..931b8938 --- /dev/null +++ b/docs2/api/types/inline_query_result_document.rst @@ -0,0 +1,9 @@ +######################### +InlineQueryResultDocument +######################### + + +.. automodule:: aiogram.types.inline_query_result_document + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_game.rst b/docs2/api/types/inline_query_result_game.rst new file mode 100644 index 00000000..bbe99fcd --- /dev/null +++ b/docs2/api/types/inline_query_result_game.rst @@ -0,0 +1,9 @@ +##################### +InlineQueryResultGame +##################### + + +.. automodule:: aiogram.types.inline_query_result_game + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_gif.rst b/docs2/api/types/inline_query_result_gif.rst new file mode 100644 index 00000000..fbdf91dd --- /dev/null +++ b/docs2/api/types/inline_query_result_gif.rst @@ -0,0 +1,9 @@ +#################### +InlineQueryResultGif +#################### + + +.. automodule:: aiogram.types.inline_query_result_gif + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_location.rst b/docs2/api/types/inline_query_result_location.rst new file mode 100644 index 00000000..5edd684f --- /dev/null +++ b/docs2/api/types/inline_query_result_location.rst @@ -0,0 +1,9 @@ +######################### +InlineQueryResultLocation +######################### + + +.. automodule:: aiogram.types.inline_query_result_location + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_mpeg4_gif.rst b/docs2/api/types/inline_query_result_mpeg4_gif.rst new file mode 100644 index 00000000..6fc64d7d --- /dev/null +++ b/docs2/api/types/inline_query_result_mpeg4_gif.rst @@ -0,0 +1,9 @@ +######################### +InlineQueryResultMpeg4Gif +######################### + + +.. automodule:: aiogram.types.inline_query_result_mpeg4_gif + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_photo.rst b/docs2/api/types/inline_query_result_photo.rst new file mode 100644 index 00000000..1b171e93 --- /dev/null +++ b/docs2/api/types/inline_query_result_photo.rst @@ -0,0 +1,9 @@ +###################### +InlineQueryResultPhoto +###################### + + +.. automodule:: aiogram.types.inline_query_result_photo + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_venue.rst b/docs2/api/types/inline_query_result_venue.rst new file mode 100644 index 00000000..20e9289f --- /dev/null +++ b/docs2/api/types/inline_query_result_venue.rst @@ -0,0 +1,9 @@ +###################### +InlineQueryResultVenue +###################### + + +.. automodule:: aiogram.types.inline_query_result_venue + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_video.rst b/docs2/api/types/inline_query_result_video.rst new file mode 100644 index 00000000..374dcf17 --- /dev/null +++ b/docs2/api/types/inline_query_result_video.rst @@ -0,0 +1,9 @@ +###################### +InlineQueryResultVideo +###################### + + +.. automodule:: aiogram.types.inline_query_result_video + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/inline_query_result_voice.rst b/docs2/api/types/inline_query_result_voice.rst new file mode 100644 index 00000000..23717afa --- /dev/null +++ b/docs2/api/types/inline_query_result_voice.rst @@ -0,0 +1,9 @@ +###################### +InlineQueryResultVoice +###################### + + +.. automodule:: aiogram.types.inline_query_result_voice + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_contact_message_content.rst b/docs2/api/types/input_contact_message_content.rst new file mode 100644 index 00000000..51c8cc58 --- /dev/null +++ b/docs2/api/types/input_contact_message_content.rst @@ -0,0 +1,9 @@ +########################## +InputContactMessageContent +########################## + + +.. automodule:: aiogram.types.input_contact_message_content + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_file.rst b/docs2/api/types/input_file.rst new file mode 100644 index 00000000..d295de5f --- /dev/null +++ b/docs2/api/types/input_file.rst @@ -0,0 +1,9 @@ +######### +InputFile +######### + + +.. automodule:: aiogram.types.input_file + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_location_message_content.rst b/docs2/api/types/input_location_message_content.rst new file mode 100644 index 00000000..9f33b5cc --- /dev/null +++ b/docs2/api/types/input_location_message_content.rst @@ -0,0 +1,9 @@ +########################### +InputLocationMessageContent +########################### + + +.. automodule:: aiogram.types.input_location_message_content + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_media.rst b/docs2/api/types/input_media.rst new file mode 100644 index 00000000..3c4f8e0c --- /dev/null +++ b/docs2/api/types/input_media.rst @@ -0,0 +1,9 @@ +########## +InputMedia +########## + + +.. automodule:: aiogram.types.input_media + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_media_animation.rst b/docs2/api/types/input_media_animation.rst new file mode 100644 index 00000000..9f16d9de --- /dev/null +++ b/docs2/api/types/input_media_animation.rst @@ -0,0 +1,9 @@ +################### +InputMediaAnimation +################### + + +.. automodule:: aiogram.types.input_media_animation + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_media_audio.rst b/docs2/api/types/input_media_audio.rst new file mode 100644 index 00000000..a312af6f --- /dev/null +++ b/docs2/api/types/input_media_audio.rst @@ -0,0 +1,9 @@ +############### +InputMediaAudio +############### + + +.. automodule:: aiogram.types.input_media_audio + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_media_document.rst b/docs2/api/types/input_media_document.rst new file mode 100644 index 00000000..b5f6c196 --- /dev/null +++ b/docs2/api/types/input_media_document.rst @@ -0,0 +1,9 @@ +################## +InputMediaDocument +################## + + +.. automodule:: aiogram.types.input_media_document + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_media_photo.rst b/docs2/api/types/input_media_photo.rst new file mode 100644 index 00000000..59ddb8f5 --- /dev/null +++ b/docs2/api/types/input_media_photo.rst @@ -0,0 +1,9 @@ +############### +InputMediaPhoto +############### + + +.. automodule:: aiogram.types.input_media_photo + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_media_video.rst b/docs2/api/types/input_media_video.rst new file mode 100644 index 00000000..ef2ded4d --- /dev/null +++ b/docs2/api/types/input_media_video.rst @@ -0,0 +1,9 @@ +############### +InputMediaVideo +############### + + +.. automodule:: aiogram.types.input_media_video + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_message_content.rst b/docs2/api/types/input_message_content.rst new file mode 100644 index 00000000..945f4ee1 --- /dev/null +++ b/docs2/api/types/input_message_content.rst @@ -0,0 +1,9 @@ +################### +InputMessageContent +################### + + +.. automodule:: aiogram.types.input_message_content + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_text_message_content.rst b/docs2/api/types/input_text_message_content.rst new file mode 100644 index 00000000..48ca93a1 --- /dev/null +++ b/docs2/api/types/input_text_message_content.rst @@ -0,0 +1,9 @@ +####################### +InputTextMessageContent +####################### + + +.. automodule:: aiogram.types.input_text_message_content + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/input_venue_message_content.rst b/docs2/api/types/input_venue_message_content.rst new file mode 100644 index 00000000..ed0f5792 --- /dev/null +++ b/docs2/api/types/input_venue_message_content.rst @@ -0,0 +1,9 @@ +######################## +InputVenueMessageContent +######################## + + +.. automodule:: aiogram.types.input_venue_message_content + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/invoice.rst b/docs2/api/types/invoice.rst new file mode 100644 index 00000000..b8c51922 --- /dev/null +++ b/docs2/api/types/invoice.rst @@ -0,0 +1,9 @@ +####### +Invoice +####### + + +.. automodule:: aiogram.types.invoice + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/keyboard_button.rst b/docs2/api/types/keyboard_button.rst new file mode 100644 index 00000000..7321914d --- /dev/null +++ b/docs2/api/types/keyboard_button.rst @@ -0,0 +1,9 @@ +############## +KeyboardButton +############## + + +.. automodule:: aiogram.types.keyboard_button + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/keyboard_button_poll_type.rst b/docs2/api/types/keyboard_button_poll_type.rst new file mode 100644 index 00000000..e24d92da --- /dev/null +++ b/docs2/api/types/keyboard_button_poll_type.rst @@ -0,0 +1,9 @@ +###################### +KeyboardButtonPollType +###################### + + +.. automodule:: aiogram.types.keyboard_button_poll_type + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/labeled_price.rst b/docs2/api/types/labeled_price.rst new file mode 100644 index 00000000..69512ab2 --- /dev/null +++ b/docs2/api/types/labeled_price.rst @@ -0,0 +1,9 @@ +############ +LabeledPrice +############ + + +.. automodule:: aiogram.types.labeled_price + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/location.rst b/docs2/api/types/location.rst new file mode 100644 index 00000000..244331c8 --- /dev/null +++ b/docs2/api/types/location.rst @@ -0,0 +1,9 @@ +######## +Location +######## + + +.. automodule:: aiogram.types.location + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/login_url.rst b/docs2/api/types/login_url.rst new file mode 100644 index 00000000..7122fe50 --- /dev/null +++ b/docs2/api/types/login_url.rst @@ -0,0 +1,9 @@ +######## +LoginUrl +######## + + +.. automodule:: aiogram.types.login_url + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/mask_position.rst b/docs2/api/types/mask_position.rst new file mode 100644 index 00000000..d7dd7bbb --- /dev/null +++ b/docs2/api/types/mask_position.rst @@ -0,0 +1,9 @@ +############ +MaskPosition +############ + + +.. automodule:: aiogram.types.mask_position + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/message.rst b/docs2/api/types/message.rst new file mode 100644 index 00000000..ea9ebcdd --- /dev/null +++ b/docs2/api/types/message.rst @@ -0,0 +1,9 @@ +####### +Message +####### + + +.. automodule:: aiogram.types.message + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/message_entity.rst b/docs2/api/types/message_entity.rst new file mode 100644 index 00000000..fb4ccb0c --- /dev/null +++ b/docs2/api/types/message_entity.rst @@ -0,0 +1,9 @@ +############# +MessageEntity +############# + + +.. automodule:: aiogram.types.message_entity + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/message_id.rst b/docs2/api/types/message_id.rst new file mode 100644 index 00000000..35ee56fa --- /dev/null +++ b/docs2/api/types/message_id.rst @@ -0,0 +1,9 @@ +######### +MessageId +######### + + +.. automodule:: aiogram.types.message_id + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/order_info.rst b/docs2/api/types/order_info.rst new file mode 100644 index 00000000..b32b433c --- /dev/null +++ b/docs2/api/types/order_info.rst @@ -0,0 +1,9 @@ +######### +OrderInfo +######### + + +.. automodule:: aiogram.types.order_info + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_data.rst b/docs2/api/types/passport_data.rst new file mode 100644 index 00000000..6918a40c --- /dev/null +++ b/docs2/api/types/passport_data.rst @@ -0,0 +1,9 @@ +############ +PassportData +############ + + +.. automodule:: aiogram.types.passport_data + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error.rst b/docs2/api/types/passport_element_error.rst new file mode 100644 index 00000000..eb8de72b --- /dev/null +++ b/docs2/api/types/passport_element_error.rst @@ -0,0 +1,9 @@ +#################### +PassportElementError +#################### + + +.. automodule:: aiogram.types.passport_element_error + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_data_field.rst b/docs2/api/types/passport_element_error_data_field.rst new file mode 100644 index 00000000..4887f585 --- /dev/null +++ b/docs2/api/types/passport_element_error_data_field.rst @@ -0,0 +1,9 @@ +############################# +PassportElementErrorDataField +############################# + + +.. automodule:: aiogram.types.passport_element_error_data_field + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_file.rst b/docs2/api/types/passport_element_error_file.rst new file mode 100644 index 00000000..849d2904 --- /dev/null +++ b/docs2/api/types/passport_element_error_file.rst @@ -0,0 +1,9 @@ +######################## +PassportElementErrorFile +######################## + + +.. automodule:: aiogram.types.passport_element_error_file + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_files.rst b/docs2/api/types/passport_element_error_files.rst new file mode 100644 index 00000000..36a3aa94 --- /dev/null +++ b/docs2/api/types/passport_element_error_files.rst @@ -0,0 +1,9 @@ +######################### +PassportElementErrorFiles +######################### + + +.. automodule:: aiogram.types.passport_element_error_files + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_front_side.rst b/docs2/api/types/passport_element_error_front_side.rst new file mode 100644 index 00000000..1e6cb1a4 --- /dev/null +++ b/docs2/api/types/passport_element_error_front_side.rst @@ -0,0 +1,9 @@ +############################# +PassportElementErrorFrontSide +############################# + + +.. automodule:: aiogram.types.passport_element_error_front_side + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_reverse_side.rst b/docs2/api/types/passport_element_error_reverse_side.rst new file mode 100644 index 00000000..56f2291d --- /dev/null +++ b/docs2/api/types/passport_element_error_reverse_side.rst @@ -0,0 +1,9 @@ +############################### +PassportElementErrorReverseSide +############################### + + +.. automodule:: aiogram.types.passport_element_error_reverse_side + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_selfie.rst b/docs2/api/types/passport_element_error_selfie.rst new file mode 100644 index 00000000..082ee422 --- /dev/null +++ b/docs2/api/types/passport_element_error_selfie.rst @@ -0,0 +1,9 @@ +########################## +PassportElementErrorSelfie +########################## + + +.. automodule:: aiogram.types.passport_element_error_selfie + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_translation_file.rst b/docs2/api/types/passport_element_error_translation_file.rst new file mode 100644 index 00000000..d30fa6bc --- /dev/null +++ b/docs2/api/types/passport_element_error_translation_file.rst @@ -0,0 +1,9 @@ +################################### +PassportElementErrorTranslationFile +################################### + + +.. automodule:: aiogram.types.passport_element_error_translation_file + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_translation_files.rst b/docs2/api/types/passport_element_error_translation_files.rst new file mode 100644 index 00000000..79b0ef79 --- /dev/null +++ b/docs2/api/types/passport_element_error_translation_files.rst @@ -0,0 +1,9 @@ +#################################### +PassportElementErrorTranslationFiles +#################################### + + +.. automodule:: aiogram.types.passport_element_error_translation_files + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_element_error_unspecified.rst b/docs2/api/types/passport_element_error_unspecified.rst new file mode 100644 index 00000000..d59a985e --- /dev/null +++ b/docs2/api/types/passport_element_error_unspecified.rst @@ -0,0 +1,9 @@ +############################### +PassportElementErrorUnspecified +############################### + + +.. automodule:: aiogram.types.passport_element_error_unspecified + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/passport_file.rst b/docs2/api/types/passport_file.rst new file mode 100644 index 00000000..944cc7e7 --- /dev/null +++ b/docs2/api/types/passport_file.rst @@ -0,0 +1,9 @@ +############ +PassportFile +############ + + +.. automodule:: aiogram.types.passport_file + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/photo_size.rst b/docs2/api/types/photo_size.rst new file mode 100644 index 00000000..fbd32f33 --- /dev/null +++ b/docs2/api/types/photo_size.rst @@ -0,0 +1,9 @@ +######### +PhotoSize +######### + + +.. automodule:: aiogram.types.photo_size + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/poll.rst b/docs2/api/types/poll.rst new file mode 100644 index 00000000..3b278712 --- /dev/null +++ b/docs2/api/types/poll.rst @@ -0,0 +1,9 @@ +#### +Poll +#### + + +.. automodule:: aiogram.types.poll + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/poll_answer.rst b/docs2/api/types/poll_answer.rst new file mode 100644 index 00000000..adce5f72 --- /dev/null +++ b/docs2/api/types/poll_answer.rst @@ -0,0 +1,9 @@ +########## +PollAnswer +########## + + +.. automodule:: aiogram.types.poll_answer + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/poll_option.rst b/docs2/api/types/poll_option.rst new file mode 100644 index 00000000..5b7bbefb --- /dev/null +++ b/docs2/api/types/poll_option.rst @@ -0,0 +1,9 @@ +########## +PollOption +########## + + +.. automodule:: aiogram.types.poll_option + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/pre_checkout_query.rst b/docs2/api/types/pre_checkout_query.rst new file mode 100644 index 00000000..2d9367ef --- /dev/null +++ b/docs2/api/types/pre_checkout_query.rst @@ -0,0 +1,9 @@ +################ +PreCheckoutQuery +################ + + +.. automodule:: aiogram.types.pre_checkout_query + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/proximity_alert_triggered.rst b/docs2/api/types/proximity_alert_triggered.rst new file mode 100644 index 00000000..1d0ba461 --- /dev/null +++ b/docs2/api/types/proximity_alert_triggered.rst @@ -0,0 +1,9 @@ +####################### +ProximityAlertTriggered +####################### + + +.. automodule:: aiogram.types.proximity_alert_triggered + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/reply_keyboard_markup.rst b/docs2/api/types/reply_keyboard_markup.rst new file mode 100644 index 00000000..2f32bdff --- /dev/null +++ b/docs2/api/types/reply_keyboard_markup.rst @@ -0,0 +1,9 @@ +################### +ReplyKeyboardMarkup +################### + + +.. automodule:: aiogram.types.reply_keyboard_markup + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/reply_keyboard_remove.rst b/docs2/api/types/reply_keyboard_remove.rst new file mode 100644 index 00000000..acc212ee --- /dev/null +++ b/docs2/api/types/reply_keyboard_remove.rst @@ -0,0 +1,9 @@ +################### +ReplyKeyboardRemove +################### + + +.. automodule:: aiogram.types.reply_keyboard_remove + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/response_parameters.rst b/docs2/api/types/response_parameters.rst new file mode 100644 index 00000000..42a1a4d6 --- /dev/null +++ b/docs2/api/types/response_parameters.rst @@ -0,0 +1,9 @@ +################## +ResponseParameters +################## + + +.. automodule:: aiogram.types.response_parameters + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/shipping_address.rst b/docs2/api/types/shipping_address.rst new file mode 100644 index 00000000..d2b72bde --- /dev/null +++ b/docs2/api/types/shipping_address.rst @@ -0,0 +1,9 @@ +############### +ShippingAddress +############### + + +.. automodule:: aiogram.types.shipping_address + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/shipping_option.rst b/docs2/api/types/shipping_option.rst new file mode 100644 index 00000000..e3976d5f --- /dev/null +++ b/docs2/api/types/shipping_option.rst @@ -0,0 +1,9 @@ +############## +ShippingOption +############## + + +.. automodule:: aiogram.types.shipping_option + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/shipping_query.rst b/docs2/api/types/shipping_query.rst new file mode 100644 index 00000000..9117bbd5 --- /dev/null +++ b/docs2/api/types/shipping_query.rst @@ -0,0 +1,9 @@ +############# +ShippingQuery +############# + + +.. automodule:: aiogram.types.shipping_query + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/sticker.rst b/docs2/api/types/sticker.rst new file mode 100644 index 00000000..643e4122 --- /dev/null +++ b/docs2/api/types/sticker.rst @@ -0,0 +1,9 @@ +####### +Sticker +####### + + +.. automodule:: aiogram.types.sticker + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/sticker_set.rst b/docs2/api/types/sticker_set.rst new file mode 100644 index 00000000..19e2ae99 --- /dev/null +++ b/docs2/api/types/sticker_set.rst @@ -0,0 +1,9 @@ +########## +StickerSet +########## + + +.. automodule:: aiogram.types.sticker_set + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/successful_payment.rst b/docs2/api/types/successful_payment.rst new file mode 100644 index 00000000..10ec78b1 --- /dev/null +++ b/docs2/api/types/successful_payment.rst @@ -0,0 +1,9 @@ +################# +SuccessfulPayment +################# + + +.. automodule:: aiogram.types.successful_payment + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/update.rst b/docs2/api/types/update.rst new file mode 100644 index 00000000..c8338f19 --- /dev/null +++ b/docs2/api/types/update.rst @@ -0,0 +1,9 @@ +###### +Update +###### + + +.. automodule:: aiogram.types.update + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/user.rst b/docs2/api/types/user.rst new file mode 100644 index 00000000..5c58898d --- /dev/null +++ b/docs2/api/types/user.rst @@ -0,0 +1,9 @@ +#### +User +#### + + +.. automodule:: aiogram.types.user + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/user_profile_photos.rst b/docs2/api/types/user_profile_photos.rst new file mode 100644 index 00000000..7a6876b4 --- /dev/null +++ b/docs2/api/types/user_profile_photos.rst @@ -0,0 +1,9 @@ +################# +UserProfilePhotos +################# + + +.. automodule:: aiogram.types.user_profile_photos + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/venue.rst b/docs2/api/types/venue.rst new file mode 100644 index 00000000..2621755b --- /dev/null +++ b/docs2/api/types/venue.rst @@ -0,0 +1,9 @@ +##### +Venue +##### + + +.. automodule:: aiogram.types.venue + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/video.rst b/docs2/api/types/video.rst new file mode 100644 index 00000000..30d29917 --- /dev/null +++ b/docs2/api/types/video.rst @@ -0,0 +1,9 @@ +##### +Video +##### + + +.. automodule:: aiogram.types.video + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/video_note.rst b/docs2/api/types/video_note.rst new file mode 100644 index 00000000..a436faa8 --- /dev/null +++ b/docs2/api/types/video_note.rst @@ -0,0 +1,9 @@ +######### +VideoNote +######### + + +.. automodule:: aiogram.types.video_note + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/voice.rst b/docs2/api/types/voice.rst new file mode 100644 index 00000000..29c44ea6 --- /dev/null +++ b/docs2/api/types/voice.rst @@ -0,0 +1,9 @@ +##### +Voice +##### + + +.. automodule:: aiogram.types.voice + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/types/webhook_info.rst b/docs2/api/types/webhook_info.rst new file mode 100644 index 00000000..0491c68d --- /dev/null +++ b/docs2/api/types/webhook_info.rst @@ -0,0 +1,9 @@ +########### +WebhookInfo +########### + + +.. automodule:: aiogram.types.webhook_info + :members: + :member-order: bysource + :undoc-members: True \ No newline at end of file diff --git a/docs2/api/upload_file.rst b/docs2/api/upload_file.rst new file mode 100644 index 00000000..4579700e --- /dev/null +++ b/docs2/api/upload_file.rst @@ -0,0 +1,96 @@ +.. _sending-files: + +################### +How to upload file? +################### + +As says `official Telegram Bot API documentation `_ +there are three ways to send files (photos, stickers, audio, media, etc.): + +If the file is already stored somewhere on the Telegram servers or file is available by the URL, +you don't need to reupload it. + +But if you need to upload new file just use subclasses of `InputFile `__. + +Here is available three different builtin types of input file: + +- :class:`aiogram.types.input_file.FSInputFile` - `uploading from file system <#upload-from-file-system>`__ +- :class:`aiogram.types.input_file.BufferedInputFile` - `uploading from buffer <#upload-from-buffer>`__ +- :class:`aiogram.types.input_file.URLInputFile` - `uploading from URL <#upload-from-url>`__ + +.. warning:: + + **Be respectful with Telegram** + + Instances of `InputFile` is reusable. + That's mean you can create instance of InputFile and sent this file multiple times but Telegram + is not recommend to do that and when you upload file once just save their `file_id` + and use it in next times. + +Upload from file system +======================= + +By first step you will need to import InputFile wrapper: + +.. code-block:: + + from aiogram.types import FSInputFile + +Then you can use it: + +.. code-block:: + + cat = FSInputFile("cat.png") + agenda = FSInputFile("my-document.pdf", filename="agenda-2019-11-19.pdf") + + +.. autoclass:: aiogram.types.input_file.FSInputFile + :members: + + +Upload from buffer +================== + +Files can be also passed from buffer +(For example you generate image using `Pillow `_ +and the want's to sent it to the Telegram): + +Import wrapper: + +.. code-block:: + + from aiogram.types import BufferedInputFile + +And then you can use it: + +.. code-block:: + + text_file = BufferedInputFile(b"Hello, world!", filename="file.txt") + +.. autoclass:: aiogram.types.input_file.BufferedInputFile + :members: + +Upload from url +=============== + +If you need to upload a file from another server, but the direct link is bound to your server's IP, +or you want to bypass native `upload limits `_ +by URL, you can use :obj:`aiogram.types.input_file.URLInputFile`. + +Import wrapper: + +.. code-block:: + + from aiogram.types import URLInputFile + +And then you can use it: + +.. code-block:: + + image = URLInputFile( + "https://www.python.org/static/community_logos/python-powered-h-140x182.png", + filename="python-logo.png" + ) + +.. autoclass:: aiogram.types.input_file.URLInputFile + :members: diff --git a/docs2/conf.py b/docs2/conf.py new file mode 100644 index 00000000..42ace9f6 --- /dev/null +++ b/docs2/conf.py @@ -0,0 +1,59 @@ +import datetime + +import aiogram + +project = "aiogram" +author = "aiogram Team" +copyright = f"{datetime.date.today().year}, {author}" +release = aiogram.__version__ +api_version = aiogram.__api_version__ + +templates_path = ["_templates"] +html_theme = "furo" +html_logo = "_static/logo.png" +html_static_path = ["_static"] +todo_include_todos = True +pygments_style = "sphinx" +htmlhelp_basename = project +html_theme_options = {} +html_css_files = [ + "stylesheets/extra.css", +] + +extensions = [ + "sphinx.ext.todo", + "sphinx.ext.viewcode", + "sphinx.ext.autodoc", + "sphinx.ext.ifconfig", + "sphinx.ext.intersphinx", + "sphinx-prompt", + "sphinx_substitution_extensions", + "sphinx_copybutton", +] + +rst_prolog = f""" +.. |api_version| replace:: {aiogram.__api_version__} +""" + +language = None +locale_dirs = ["locales"] + +exclude_patterns = [] +source_suffix = ".rst" +master_doc = "index" + +latex_documents = [ + (master_doc, f"{project}.tex", f"{project} Documentation", author, "manual"), +] +man_pages = [(master_doc, project, f"{project} Documentation", [author], 1)] +texinfo_documents = [ + ( + master_doc, + project, + f"{project} Documentation", + author, + project, + "Modern and fully asynchronous framework for Telegram Bot API", + "Miscellaneous", + ), +] diff --git a/docs2/dispatcher/dispatcher.rst b/docs2/dispatcher/dispatcher.rst new file mode 100644 index 00000000..798d74da --- /dev/null +++ b/docs2/dispatcher/dispatcher.rst @@ -0,0 +1,72 @@ +########## +Dispathcer +########## + +Dispatcher is root :obj:`Router` and in code Dispatcher can be used directly for routing updates or attach another routers into dispatcher. + +Here is only listed base information about Dispatcher. All about writing handlers, filters and etc. you can found in next pages: + +- `Router `__ +- `Observer `__ + + +.. autoclass:: aiogram.dispatcher.dispatcher.Dispatcher + :members: __init__, feed_update, feed_raw_update, feed_webhook_update, start_polling, run_polling + + +Simple usage +============ + +Example: + +.. code-block:: python + + dp = Dispatcher() + + @dp.message() + async def message_handler(message: types.Message) -> None: + await SendMessage(chat_id=message.from_user.id, text=message.text) + + +Including routers + +Example: + + +.. code-block:: python + + dp = Dispatcher() + router1 = Router() + dp.include_router(router1) + +Handling updates +================ + +All updates can be propagated to the dispatcher by :obj:`Dispatcher.feed_update(bot=..., update=...)` method: + +.. code-block:: python + + bot = Bot(...) + dp = Dispathcher() + + ... + + result = await dp.feed_update(bot=bot, update=incoming_update) + +Polling +======= + +.. warning:: + + not yet docummented + +... + +Webhook +======= + +.. warning:: + + not yet docummented + +... diff --git a/docs2/dispatcher/index.rst b/docs2/dispatcher/index.rst new file mode 100644 index 00000000..36b27ec8 --- /dev/null +++ b/docs2/dispatcher/index.rst @@ -0,0 +1,22 @@ +=============== +Handling events +=============== + +*aiogram* imcludes Dispatcher mechanism. +Dispatcher is needed for handling incoming updates from Telegram. + +With dispatcher you can do: + +- Handle incoming updates; +- Filter incoming events before it will be processed by specific handler; +- Modify event and related data in middlewares; +- Separate bot functionality between different handlers, modules and packages + +Dispatcher is also separated into two entities - Router and Dispatcher. +Dispatcher is subclass of router and should be always is root router. + +.. toctree:: + + observer + router + dispatcher diff --git a/docs2/dispatcher/observer.rst b/docs2/dispatcher/observer.rst new file mode 100644 index 00000000..44cc10df --- /dev/null +++ b/docs2/dispatcher/observer.rst @@ -0,0 +1,26 @@ +######## +Observer +######## + +Observer is used for filtering and handling different events. That is part of internal API with some public methods and is recommended to don't use methods is not listed here. + +In `aiogram` framework is available two variants of observer: + +- `EventObserver <#eventobserver>`__ +- `TelegramEventObserver <#telegrameventobserver>`__ + + +EventObserver +============= + +.. autoclass:: aiogram.dispatcher.event.event.EventObserver + :members: register, trigger, __call__ + :member-order: bysource + + +TelegramEventObserver +===================== + +.. autoclass:: aiogram.dispatcher.event.telegram.TelegramEventObserver + :members: register, trigger, __call__, bind_filter, middleware, outer_middleware + :member-order: bysource diff --git a/docs2/dispatcher/router.rst b/docs2/dispatcher/router.rst new file mode 100644 index 00000000..3e1a1b69 --- /dev/null +++ b/docs2/dispatcher/router.rst @@ -0,0 +1,181 @@ +###### +Router +###### + +.. autoclass:: aiogram.dispatcher.router.Router + :members: __init__, include_router + :show-inheritance: + + +Event observers +=============== + +.. warning:: + + All handlers is always should be an asynchronous. + Name of handler function is not important. Event argument name is also is not important but is recommended to don't overlap the name with contextual data in due to function can not accept two arguments with the same name. + +Here is list of available observers and examples how to register handlers (In examples used only @decorator-style): + +For examples used decorator-style registering handlers but if you dont like @decorators just use :obj:`.register(...)` method instead. + +Update +------ + +.. code-block:: python + + @router.update() + async def message_handler(update: types.Update) -> Any: pass + +.. note:: + + By default Router is already have an update handler which route all event types to another observers. + + +Message +------- + +.. code-block:: python + + @router.message() + async def message_handler(message: types.Message) -> Any: pass + + +Edited message +-------------- + +.. code-block:: python + + @router.edited_message() + async def edited_message_handler(edited_message: types.Message) -> Any: pass + +Channel post +------------ + +.. code-block:: python + + @router.channel_post() + async def channel_post_handler(channel_post: types.Message) -> Any: pass + +Edited channel post +------------------- + +.. code-block:: python + + @router.edited_channel_post() + async def edited_channel_post_handler(edited_channel_post: types.Message) -> Any: pass + + +Inline query +------------ + +.. code-block:: python + + @router.inline_query() + async def inline_query_handler(inline_query: types.Message) -> Any: pass + +Chosen inline query +------------------- + +.. code-block:: python + + @router.chosen_inline_result() + async def chosen_inline_result_handler(chosen_inline_result: types.ChosenInlineResult) -> Any: pass + +Callback query +-------------- + +.. code-block:: python + + @router.callback_query() + async def callback_query_handler(callback_query: types.CallbackQuery) -> Any: pass + +Shipping query +-------------- + +.. code-block:: python + + @router.shipping_query() + async def shipping_query_handler(shipping_query: types.ShippingQuery) -> Any: pass + +Pre checkout query +------------------ + +.. code-block:: python + + @router.pre_checkout_query() + async def pre_checkout_query_handler(pre_checkout_query: types.PreCheckoutQuery) -> Any: pass + +Poll +---- + +.. code-block:: python + + @router.poll() + async def poll_handler(poll: types.Poll) -> Any: pass + +Poll answer +----------- + +.. code-block:: python + + @router.poll_answer() + async def poll_answer_handler(poll_answer: types.PollAnswer) -> Any: pass + +Errors +------ + +.. code-block:: python + + @router.errors() + async def error_handler(exception: Exception) -> Any: pass + +Is useful for handling errors from other handlers + + +Nested routers +============== + +.. warning:: + + Routers by the way can be nested to an another routers with some limitations: + + 1. Router **CAN NOT** include itself + 1. Routers **CAN NOT** be used for circular including (router 1 include router 2, router 2 include router 3, router 3 include router 1) + + +Example: + +.. code-block:: python + :caption: module_2.py + :name: module_2 + + router2 = Router() + + @router2.message() + ... + + +.. code-block:: python + :caption: module_12.py + :name: module_1 + + from module_2 import router2 + + + router1 = Router() + router1.include_router(router2) + + +How it works? +------------- + +For example dispatcher has 2 routers, last one router is also have one nested router: + +.. image:: ../_static/nested_routers_example.png + :alt: Nested routers example + +In this case update propagation flow will have form: + +.. image:: ../_static/update_propagation_flow.png + :alt: Nested routers example diff --git a/docs2/index.rst b/docs2/index.rst new file mode 100644 index 00000000..8775c17a --- /dev/null +++ b/docs2/index.rst @@ -0,0 +1,83 @@ +####### +aiogram +####### + +.. danger:: + This version still in development! + +.. image:: https://img.shields.io/pypi/l/aiogram.svg + :target: https://opensource.org/licenses/MIT + :alt: MIT License + +.. image:: https://img.shields.io/pypi/pyversions/aiogram.svg + :target: https://pypi.python.org/pypi/aiogram + :alt: Supported python versions + +.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-4.9-blue.svg?logo=telegram + :target: https://core.telegram.org/bots/api + :alt: Telegram Bot API + +.. image:: https://github.com/aiogram/aiogram/workflows/Tests/badge.svg?branch=dev-3.x + :target: https://github.com/aiogram/aiogram/actions + :alt: Tests + +.. image:: https://img.shields.io/pypi/v/aiogram.svg + :target: https://pypi.python.org/pypi/aiogram + :alt: PyPi Package Version + +.. image:: https://img.shields.io/pypi/status/aiogram.svg + :target: https://pypi.python.org/pypi/aiogram + :alt: PyPi status + +.. image:: https://img.shields.io/pypi/dm/aiogram.svg + :target: https://pypi.python.org/pypi/aiogram + :alt: Downloads + +.. image:: https://img.shields.io/badge/telegram-aiogram-blue.svg + :target: https://t.me/aiogram_live + :alt: [Telegram] aiogram live + + +**aiogram** modern and fully asynchronous framework for +`Telegram Bot API `_ written in Python 3.7 with +`asyncio `_ and +`aiohttp `_. + +It helps you to make your bots faster and simpler. + +Features +======== + +- Asynchronous (`asyncio docs `_, :pep:`492`) +- Has type hints (:pep:`484`) and can be used with `mypy `_ +- Supports `Telegram Bot API `_ |api_version| +- Telegram Bot API integration code was `autogenerated `_ and can be easy re-generated when API was updated +- Updates router (Blueprints) +- Finite State Machine +- Middlewares +- Provides `Replies into Webhook `_ + +.. warning:: + + Before start using **aiogram** is highly recommend to know how to work + with `asyncio `_. + + Also if you has questions you can go to our community chats in Telegram: + + - `English language `_ + - `Russian language `_ + +Simple usage +------------ + +.. literalinclude:: ../examples/echo_bot.py + +Contents +======== + +.. toctree:: + :maxdepth: 3 + + install + api/index + dispatcher/index diff --git a/docs2/install.rst b/docs2/install.rst new file mode 100644 index 00000000..c9e1cfe3 --- /dev/null +++ b/docs2/install.rst @@ -0,0 +1,64 @@ +############ +Installation +############ + +Stable (2.x) +============ + +Using PIP +--------- + +.. code-block:: bash + + pip install -U aiogram + + +Using poetry +------------ + +.. code-block:: bash + + poetry add aiogram + + +Using Pipenv +------------ + +.. code-block:: bash + + pipenv install aiogram + +Using poetry +------------ + +.. code-block:: bash + + poetry add aiogram + +Using Pacman +------------ +*aiogram* is also available in Arch Linux Repository, so you can install this framework on any +Arch-based distribution like Arch Linux, Antergos, Manjaro, etc. To do this, just use pacman +to install the `python-aiogram `_ package: + +.. code-block:: bash + + pacman -S python-aiogram + +Development build (3.x) +======================= + +From private PyPi index +----------------------- + +On every push to the `dev-3.x` branch GitHub Actions build the package and publish +to the `2038.host `_ server with seems like official PyPi files structure. +That's mean you can always install latest (may be unstable) build via next command: + +.. code-block:: bash + + pip install --extra-index-url https://dev-docs.aiogram.dev/simple --pre aiogram + + +In this repository available only last success build. All previous builds is always removes +before uploading new one. Also before building this package all tests is also pass. diff --git a/docs2/make.bat b/docs2/make.bat new file mode 100644 index 00000000..922152e9 --- /dev/null +++ b/docs2/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs2/requirements.txt b/docs2/requirements.txt new file mode 100644 index 00000000..9003de3f --- /dev/null +++ b/docs2/requirements.txt @@ -0,0 +1,3 @@ +sphinx-intl +typing-extensions +Sphinx-Substitution-Extensions diff --git a/examples/echo_bot.py b/examples/echo_bot.py new file mode 100644 index 00000000..2ab06c78 --- /dev/null +++ b/examples/echo_bot.py @@ -0,0 +1,41 @@ +from typing import Any + +from aiogram import Bot, Dispatcher, types +from aiogram.dispatcher.handler import MessageHandler + +TOKEN = "42:TOKEN" +dp = Dispatcher() + + +@dp.message(commands=["start"]) +class MyHandler(MessageHandler): + """ + This handler receive messages with `/start` command + + Usage of Class-based handlers + """ + + async def handle(self) -> Any: + await self.event.answer(f"Hello, {self.from_user.full_name}!") + + +@dp.message(content_types=[types.ContentType.ANY]) +async def echo_handler(message: types.Message, bot: Bot) -> Any: + """ + Handler will forward received message back to the sender + + Usage of Function-based handlers + """ + + await bot.forward_message( + from_chat_id=message.chat.id, chat_id=message.chat.id, message_id=message.message_id + ) + + +def main() -> None: + bot = Bot(TOKEN, parse_mode="HTML") + dp.run_polling(bot) + + +if __name__ == "__main__": + main() diff --git a/poetry.lock b/poetry.lock index d81c8b2f..5f99360c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,345 +1,285 @@ [[package]] -category = "main" -description = "File support for asyncio." name = "aiofiles" +version = "0.6.0" +description = "File support for asyncio." +category = "main" optional = false python-versions = "*" -version = "0.4.0" [[package]] -category = "main" -description = "Async http client/server framework (asyncio)" name = "aiohttp" +version = "3.7.3" +description = "Async http client/server framework (asyncio)" +category = "main" optional = false -python-versions = ">=3.5.3" -version = "3.6.2" +python-versions = ">=3.6" [package.dependencies] async-timeout = ">=3.0,<4.0" attrs = ">=17.3.0" chardet = ">=2.0,<4.0" -multidict = ">=4.5,<5.0" +multidict = ">=4.5,<7.0" +typing-extensions = ">=3.6.5" yarl = ">=1.0,<2.0" [package.extras] speedups = ["aiodns", "brotlipy", "cchardet"] [[package]] -category = "main" -description = "Proxy connector for aiohttp" name = "aiohttp-socks" +version = "0.5.5" +description = "Proxy connector for aiohttp" +category = "main" optional = false python-versions = "*" -version = "0.3.9" [package.dependencies] aiohttp = ">=2.3.2" attrs = ">=19.2.0" +python-socks = {version = ">=1.0.1", extras = ["asyncio"]} + +[[package]] +name = "alabaster" +version = "0.7.12" +description = "A configurable sidebar-enabled Sphinx theme" +category = "main" +optional = false +python-versions = "*" [[package]] -category = "dev" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." name = "appdirs" -optional = false -python-versions = "*" version = "1.4.4" - -[[package]] +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" -description = "Disable App Nap on OS X 10.9" -marker = "sys_platform == \"darwin\"" -name = "appnope" optional = false python-versions = "*" -version = "0.1.0" [[package]] +name = "appnope" +version = "0.1.2" +description = "Disable App Nap on macOS >= 10.9" category = "dev" -description = "Asyncio testing server. Similar to the responses library used for 'requests'" +optional = false +python-versions = "*" + +[[package]] name = "aresponses" +version = "2.1.4" +description = "Asyncio response mocking. Similar to the responses library used for 'requests'" +category = "dev" optional = false python-versions = ">=3.6" -version = "1.1.2" [package.dependencies] aiohttp = ">=3.1.0,<4.0.0" pytest-asyncio = "*" [[package]] -category = "main" -description = "Simple lru_cache for asyncio" name = "async-lru" +version = "1.0.2" +description = "Simple lru_cache for asyncio" +category = "main" optional = false python-versions = "*" -version = "1.0.2" [[package]] -category = "main" -description = "Timeout context manager for asyncio programs" name = "async-timeout" +version = "3.0.1" +description = "Timeout context manager for asyncio programs" +category = "main" optional = false python-versions = ">=3.5.3" -version = "3.0.1" [[package]] -category = "dev" -description = "Enhance the standard unittest package with features for testing asyncio libraries" -marker = "python_version < \"3.8\"" name = "asynctest" +version = "0.13.0" +description = "Enhance the standard unittest package with features for testing asyncio libraries" +category = "dev" optional = false python-versions = ">=3.5" -version = "0.13.0" [[package]] -category = "dev" -description = "Atomic file writes." -marker = "python_version >= \"3.5\" and sys_platform == \"win32\" or sys_platform == \"win32\"" name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.4.0" [[package]] -category = "main" -description = "Classes Without Boilerplate" name = "attrs" +version = "20.3.0" +description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "19.3.0" [package.extras] -azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"] -dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"] -docs = ["sphinx", "zope.interface"] -tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] -category = "main" -description = "Internationalization utilities" name = "babel" +version = "2.9.0" +description = "Internationalization utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.8.0" [package.dependencies] pytz = ">=2015.7" [[package]] -category = "dev" -description = "Specifications for callback functions passed in to an API" name = "backcall" +version = "0.2.0" +description = "Specifications for callback functions passed in to an API" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "dev" -description = "The uncompromising code formatter." +name = "beautifulsoup4" +version = "4.9.3" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +soupsieve = {version = ">1.2", markers = "python_version >= \"3.0\""} + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] name = "black" +version = "20.8b1" +description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.6" -version = "19.10b0" [package.dependencies] appdirs = "*" -attrs = ">=18.1.0" -click = ">=6.5" +click = ">=7.1.2" +mypy-extensions = ">=0.4.3" pathspec = ">=0.6,<1" -regex = "*" -toml = ">=0.9.4" +regex = ">=2020.1.8" +toml = ">=0.10.1" typed-ast = ">=1.4.0" +typing-extensions = ">=3.7.4" [package.extras] +colorama = ["colorama (>=0.4.3)"] d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] -category = "dev" -description = "httplib2 caching for requests" -name = "cachecontrol" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.12.6" - -[package.dependencies] -msgpack = ">=0.5.2" -requests = "*" - -[package.dependencies.lockfile] -optional = true -version = ">=0.9" - -[package.extras] -filecache = ["lockfile (>=0.9)"] -redis = ["redis (>=2.10.5)"] - -[[package]] -category = "dev" -description = "Cachy provides a simple yet effective caching library." -name = "cachy" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.3.0" - -[package.extras] -memcached = ["python-memcached (>=1.59,<2.0)"] -msgpack = ["msgpack-python (>=0.5,<0.6)"] -redis = ["redis (>=3.3.6,<4.0.0)"] - -[[package]] -category = "dev" -description = "Python package for providing Mozilla's CA Bundle." name = "certifi" +version = "2020.12.5" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = "*" -version = "2020.4.5.2" [[package]] -category = "dev" -description = "Foreign Function Interface for Python calling C code." -marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platform == \"linux\"" -name = "cffi" -optional = false -python-versions = "*" -version = "1.14.0" - -[package.dependencies] -pycparser = "*" - -[[package]] -category = "dev" -description = "Validate configuration and produce human readable error messages." name = "cfgv" +version = "3.2.0" +description = "Validate configuration and produce human readable error messages." +category = "dev" optional = false python-versions = ">=3.6.1" -version = "3.1.0" [[package]] -category = "main" -description = "Universal encoding detector for Python 2 and 3" name = "chardet" +version = "3.0.4" +description = "Universal encoding detector for Python 2 and 3" +category = "main" optional = false python-versions = "*" -version = "3.0.4" [[package]] -category = "dev" -description = "Cleo allows you to create beautiful and testable command-line interfaces." -name = "cleo" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.7.6" - -[package.dependencies] -clikit = ">=0.4.0,<0.5.0" - -[[package]] -category = "dev" -description = "Composable command line interface toolkit" name = "click" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" version = "7.1.2" - -[[package]] -category = "dev" -description = "CliKit is a group of utilities to build beautiful and testable command line interfaces." -name = "clikit" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.4.3" - -[package.dependencies] -pastel = ">=0.2.0,<0.3.0" -pylev = ">=1.3,<2.0" - -[[package]] -category = "dev" -description = "Cross-platform colored terminal text." -marker = "python_version >= \"3.5\" and sys_platform == \"win32\" or sys_platform == \"win32\"" -name = "colorama" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" [[package]] -category = "dev" -description = "Code coverage measurement for Python" name = "coverage" +version = "5.3.1" +description = "Code coverage measurement for Python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "5.1" [package.extras] toml = ["toml"] [[package]] -category = "dev" -description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." -marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platform == \"linux\"" -name = "cryptography" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -version = "2.9.2" - -[package.dependencies] -cffi = ">=1.8,<1.11.3 || >1.11.3" -six = ">=1.4.1" - -[package.extras] -docs = ["sphinx (>=1.6.5,<1.8.0 || >1.8.0)", "sphinx-rtd-theme"] -docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] -idna = ["idna (>=2.1)"] -pep8test = ["flake8", "flake8-import-order", "pep8-naming"] -test = ["pytest (>=3.6.0,<3.9.0 || >3.9.0,<3.9.1 || >3.9.1,<3.9.2 || >3.9.2)", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,<3.79.2 || >3.79.2)"] - -[[package]] -category = "dev" -description = "Decorators for Humans" name = "decorator" +version = "4.4.2" +description = "Decorators for Humans" +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.4.2" [[package]] -category = "dev" -description = "Distribution utilities" name = "distlib" +version = "0.3.1" +description = "Distribution utilities" +category = "dev" optional = false python-versions = "*" -version = "0.3.0" [[package]] -category = "dev" -description = "A platform independent file lock." +name = "docutils" +version = "0.16" +description = "Docutils -- Python Documentation Utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] name = "filelock" +version = "3.0.12" +description = "A platform independent file lock." +category = "dev" optional = false python-versions = "*" -version = "3.0.12" [[package]] -category = "dev" -description = "the modular source code checker: pep8 pyflakes and co" name = "flake8" +version = "3.8.4" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "3.8.3" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" pycodestyle = ">=2.6.0a1,<2.7.0" pyflakes = ">=2.2.0,<2.3.0" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" - [[package]] -category = "dev" -description = "Generate HTML reports of flake8 violations" name = "flake8-html" +version = "0.4.1" +description = "Generate HTML reports of flake8 violations" +category = "dev" optional = false python-versions = "*" -version = "0.4.1" [package.dependencies] flake8 = ">=3.3.0" @@ -348,100 +288,98 @@ jinja2 = ">=2.9.0" pygments = ">=2.2.0" [[package]] -category = "dev" -description = "Clean single-source support for Python 3 and 2" -name = "future" +name = "furo" +version = "2020.12.30b24" +description = "A clean customisable Sphinx documentation theme." +category = "main" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "0.18.2" - -[[package]] -category = "dev" -description = "HTML parser based on the WHATWG HTML specification" -name = "html5lib" -optional = false -python-versions = "*" -version = "1.0.1" +python-versions = ">=3.5" [package.dependencies] -six = ">=1.9" -webencodings = "*" +beautifulsoup4 = "*" +sphinx = ">=3.0,<4.0" [package.extras] -all = ["genshi", "chardet (>=2.2)", "datrie", "lxml"] -chardet = ["chardet (>=2.2)"] -datrie = ["datrie"] -genshi = ["genshi"] -lxml = ["lxml"] +doc = ["myst-parser", "sphinx-copybutton", "sphinx-inline-tabs"] +test = ["pytest", "pytest-cov", "pytest-xdist"] [[package]] +name = "future" +version = "0.18.2" +description = "Clean single-source support for Python 3 and 2" category = "dev" -description = "File identification library for Python" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] name = "identify" +version = "1.5.13" +description = "File identification library for Python" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "1.4.19" [package.extras] license = ["editdistance"] [[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.9" [[package]] -category = "dev" -description = "Read metadata from Python packages" -name = "importlib-metadata" +name = "imagesize" +version = "1.2.0" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "main" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "1.1.3" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "importlib-resources"] +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] -category = "dev" -description = "Read metadata from Python packages" name = "importlib-metadata" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.6.1" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] - -[[package]] +version = "3.4.0" +description = "Read metadata from Python packages" category = "dev" -description = "IPython: Productive Interactive Computing" -name = "ipython" optional = false python-versions = ">=3.6" -version = "7.15.0" [package.dependencies] -appnope = "*" +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "ipython" +version = "7.19.0" +description = "IPython: Productive Interactive Computing" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +appnope = {version = "*", markers = "sys_platform == \"darwin\""} backcall = "*" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" jedi = ">=0.10" -pexpect = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} pickleshare = "*" prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" pygments = "*" -setuptools = ">=18.5" traitlets = ">=4.2" [package.extras] @@ -456,61 +394,48 @@ qtconsole = ["qtconsole"] test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.14)"] [[package]] -category = "dev" -description = "Vestigial utilities from IPython" name = "ipython-genutils" +version = "0.2.0" +description = "Vestigial utilities from IPython" +category = "dev" optional = false python-versions = "*" -version = "0.2.0" [[package]] -category = "dev" -description = "A Python utility / library to sort Python imports." name = "isort" +version = "5.7.0" +description = "A Python utility / library to sort Python imports." +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "4.3.21" +python-versions = ">=3.6,<4.0" [package.extras] -pipfile = ["pipreqs", "requirementslib"] -pyproject = ["toml"] -requirements = ["pipreqs", "pip-api"] -xdg_home = ["appdirs (>=1.4.0)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] [[package]] -category = "dev" -description = "An autocompletion tool for Python that can be used for text editors." name = "jedi" +version = "0.18.0" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.17.0" +python-versions = ">=3.6" [package.dependencies] -parso = ">=0.7.0" +parso = ">=0.8.0,<0.9.0" [package.extras] -qa = ["flake8 (3.7.9)"] -testing = ["colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<6.0.0)"] [[package]] -category = "dev" -description = "Low-level, pure Python DBus protocol wrapper." -marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platform == \"linux\"" -name = "jeepney" -optional = false -python-versions = ">=3.5" -version = "0.4.3" - -[package.extras] -dev = ["testpath"] - -[[package]] -category = "dev" -description = "A very fast and expressive template engine." name = "jinja2" +version = "2.11.2" +description = "A very fast and expressive template engine." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.11.2" [package.dependencies] MarkupSafe = ">=0.23" @@ -519,107 +444,48 @@ MarkupSafe = ">=0.23" i18n = ["Babel (>=0.8)"] [[package]] -category = "dev" -description = "Lightweight pipelining: using Python functions as pipeline jobs." -marker = "python_version > \"2.7\"" name = "joblib" +version = "1.0.0" +description = "Lightweight pipelining with Python functions" +category = "dev" optional = false python-versions = ">=3.6" -version = "0.15.1" [[package]] -category = "dev" -description = "An implementation of JSON Schema validation for Python" -name = "jsonschema" -optional = false -python-versions = "*" -version = "3.2.0" - -[package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0" -setuptools = "*" -six = ">=1.11.0" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" - -[package.extras] -format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"] -format_nongpl = ["idna", "jsonpointer (>1.13)", "webcolors", "rfc3986-validator (>0.1.0)", "rfc3339-validator"] - -[[package]] -category = "dev" -description = "Store and access your passwords safely." -marker = "python_version >= \"3.5\" and python_version < \"4.0\"" -name = "keyring" -optional = false -python-versions = ">=3.5" -version = "20.0.1" - -[package.dependencies] -pywin32-ctypes = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1" -secretstorage = "*" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,<3.7.3 || >3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-black-multipy", "pytest-cov"] - -[[package]] -category = "dev" -description = "Python LiveReload is an awesome tool for web developers" name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "main" optional = false python-versions = "*" -version = "2.6.2" [package.dependencies] six = "*" - -[package.dependencies.tornado] -python = ">=2.8" -version = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} [[package]] -category = "dev" -description = "Platform-independent file locking module" -name = "lockfile" -optional = false -python-versions = "*" -version = "0.12.2" - -[[package]] -category = "dev" -description = "A Python implementation of Lunr.js" name = "lunr" +version = "0.5.8" +description = "A Python implementation of Lunr.js" +category = "dev" optional = false python-versions = "*" -version = "0.5.8" [package.dependencies] future = ">=0.16.0" +nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} six = ">=1.11.0" -[package.dependencies.nltk] -optional = true -python = ">=2.8" -version = ">=3.2.5" - [package.extras] languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] [[package]] -category = "dev" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." name = "lxml" +version = "4.6.2" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" -version = "4.5.1" [package.extras] cssselect = ["cssselect (>=0.7)"] @@ -628,121 +494,120 @@ htmlsoup = ["beautifulsoup4"] source = ["Cython (>=0.29.7)"] [[package]] -category = "dev" -description = "Python implementation of Markdown." -name = "markdown" +name = "magic-filter" +version = "0.1.2" +description = "This package provides magic filter based on dynamic attribute getter" +category = "main" optional = false -python-versions = ">=3.5" -version = "3.2.2" +python-versions = ">=3.6.1,<4.0.0" + +[[package]] +name = "markdown" +version = "3.3.3" +description = "Python implementation of Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" [package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] testing = ["coverage", "pyyaml"] [[package]] -category = "dev" -description = "This is an extension to Python-Markdown which provides an \"include\" function, similar to that found in LaTeX (and also the C pre-processor and Fortran). I originally wrote it for my FORD Fortran auto-documentation generator." name = "markdown-include" +version = "0.6.0" +description = "This is an extension to Python-Markdown which provides an \"include\" function, similar to that found in LaTeX (and also the C pre-processor and Fortran). I originally wrote it for my FORD Fortran auto-documentation generator." +category = "dev" optional = false python-versions = "*" -version = "0.5.1" [package.dependencies] markdown = "*" [[package]] -category = "dev" -description = "Safely add untrusted strings to HTML/XML markup." name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.1.1" [[package]] -category = "dev" -description = "McCabe checker, plugin for flake8" name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = "*" -version = "0.6.1" [[package]] -category = "dev" -description = "AutoDoc for MarkDown" name = "mkautodoc" +version = "0.1.0" +description = "AutoDoc for MarkDown" +category = "dev" optional = false python-versions = ">=3.6" -version = "0.1.0" [[package]] -category = "dev" -description = "Project documentation with Markdown." name = "mkdocs" +version = "1.1.2" +description = "Project documentation with Markdown." +category = "dev" optional = false python-versions = ">=3.5" -version = "1.1.2" [package.dependencies] +click = ">=3.3" Jinja2 = ">=2.10.1" +livereload = ">=2.5.1" +lunr = {version = "0.5.8", extras = ["languages"]} Markdown = ">=3.2.1" PyYAML = ">=3.10" -click = ">=3.3" -livereload = ">=2.5.1" tornado = ">=5.0" -[package.dependencies.lunr] -extras = ["languages"] -version = "0.5.8" - [[package]] -category = "dev" -description = "A Material Design theme for MkDocs" name = "mkdocs-material" +version = "6.2.5" +description = "A Material Design theme for MkDocs" +category = "dev" optional = false python-versions = "*" -version = "4.6.3" [package.dependencies] -Pygments = ">=2.4" markdown = ">=3.2" -mkdocs = ">=1.0" -pymdown-extensions = ">=6.3" +mkdocs = ">=1.1" +mkdocs-material-extensions = ">=1.0" +Pygments = ">=2.4" +pymdown-extensions = ">=7.0" [[package]] +name = "mkdocs-material-extensions" +version = "1.0.1" +description = "Extension pack for Python Markdown." category = "dev" -description = "More routines for operating on iterables, beyond itertools" -name = "more-itertools" optional = false python-versions = ">=3.5" -version = "8.4.0" + +[package.dependencies] +mkdocs-material = ">=5.0.0" [[package]] -category = "dev" -description = "MessagePack (de)serializer." -name = "msgpack" -optional = false -python-versions = "*" -version = "1.0.0" - -[[package]] -category = "main" -description = "multidict implementation" name = "multidict" +version = "5.1.0" +description = "multidict implementation" +category = "main" optional = false -python-versions = ">=3.5" -version = "4.7.6" +python-versions = ">=3.6" [[package]] -category = "dev" -description = "Optional static typing for Python" name = "mypy" +version = "0.800" +description = "Optional static typing for Python" +category = "dev" optional = false python-versions = ">=3.5" -version = "0.770" [package.dependencies] mypy-extensions = ">=0.4.3,<0.5.0" @@ -753,21 +618,20 @@ typing-extensions = ">=3.7.4" dmypy = ["psutil (>=4.0)"] [[package]] -category = "dev" -description = "Experimental type system extensions for programs checked with the mypy typechecker." name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" optional = false python-versions = "*" -version = "0.4.3" [[package]] -category = "dev" -description = "Natural Language Toolkit" -marker = "python_version > \"2.7\"" name = "nltk" +version = "3.5" +description = "Natural Language Toolkit" +category = "dev" optional = false python-versions = "*" -version = "3.5" [package.dependencies] click = "*" @@ -784,204 +648,136 @@ tgrep = ["pyparsing"] twitter = ["twython"] [[package]] -category = "dev" -description = "Node.js virtual environment builder" name = "nodeenv" +version = "1.5.0" +description = "Node.js virtual environment builder" +category = "dev" optional = false python-versions = "*" -version = "1.4.0" [[package]] -category = "dev" -description = "Core utilities for Python packages" name = "packaging" +version = "20.8" +description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.4" [package.dependencies] pyparsing = ">=2.0.2" -six = "*" [[package]] -category = "dev" -description = "A Python Parser" name = "parso" +version = "0.8.1" +description = "A Python Parser" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.7.0" +python-versions = ">=3.6" [package.extras] -testing = ["docopt", "pytest (>=3.0.7)"] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] [[package]] -category = "dev" -description = "Bring colors to your terminal." -name = "pastel" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.2.0" - -[[package]] -category = "dev" -description = "Utility library for gitignore style pattern matching of file paths." name = "pathspec" +version = "0.8.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.8.0" [[package]] -category = "dev" -description = "Pexpect allows easy control of interactive console applications." name = "pexpect" +version = "4.8.0" +description = "Pexpect allows easy control of interactive console applications." +category = "dev" optional = false python-versions = "*" -version = "4.8.0" [package.dependencies] ptyprocess = ">=0.5" [[package]] -category = "dev" -description = "Tiny 'shelve'-like database with concurrency support" name = "pickleshare" -optional = false -python-versions = "*" version = "0.7.5" - -[[package]] +description = "Tiny 'shelve'-like database with concurrency support" category = "dev" -description = "Query metadatdata from sdists / bdists / installed packages." -name = "pkginfo" optional = false python-versions = "*" -version = "1.5.0.1" - -[package.extras] -testing = ["nose", "coverage"] [[package]] -category = "dev" -description = "plugin and hook calling mechanisms for python" name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.1" [package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] [[package]] -category = "dev" -description = "Python dependency management and packaging made easy." -name = "poetry" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.0.9" - -[package.dependencies] -cachy = ">=0.3.0,<0.4.0" -cleo = ">=0.7.6,<0.8.0" -clikit = ">=0.4.2,<0.5.0" -html5lib = ">=1.0,<2.0" -jsonschema = ">=3.1,<4.0" -pexpect = ">=4.7.0,<5.0.0" -pkginfo = ">=1.4,<2.0" -pyparsing = ">=2.2,<3.0" -pyrsistent = ">=0.14.2,<0.15.0" -requests = ">=2.18,<3.0" -requests-toolbelt = ">=0.8.0,<0.9.0" -shellingham = ">=1.1,<2.0" -tomlkit = ">=0.5.11,<0.6.0" - -[package.dependencies.cachecontrol] -extras = ["filecache"] -version = ">=0.12.4,<0.13.0" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=1.1.3,<1.2.0" - -[package.dependencies.keyring] -python = ">=3.5,<4.0" -version = ">=20.0.1,<21.0.0" - -[[package]] -category = "dev" -description = "A framework for managing and maintaining multi-language pre-commit hooks." name = "pre-commit" +version = "2.9.3" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" optional = false python-versions = ">=3.6.1" -version = "2.5.1" [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" toml = "*" virtualenv = ">=20.0.8" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = "*" - [[package]] -category = "dev" -description = "Library for building powerful interactive command lines in Python" name = "prompt-toolkit" +version = "3.0.14" +description = "Library for building powerful interactive command lines in Python" +category = "dev" optional = false python-versions = ">=3.6.1" -version = "3.0.5" [package.dependencies] wcwidth = "*" [[package]] -category = "dev" -description = "Run a subprocess in a pseudo terminal" name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +category = "dev" optional = false python-versions = "*" -version = "0.6.0" [[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" +version = "1.10.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.8.1" [[package]] -category = "dev" -description = "Python style guide checker" name = "pycodestyle" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.6.0" - -[[package]] +description = "Python style guide checker" category = "dev" -description = "C parser in Python" -marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platform == \"linux\"" -name = "pycparser" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.20" [[package]] -category = "main" -description = "Data validation and settings management using python 3.6 type hinting" name = "pydantic" +version = "1.7.3" +description = "Data validation and settings management using python 3.6 type hinting" +category = "main" optional = false python-versions = ">=3.6" -version = "1.5.1" [package.extras] dotenv = ["python-dotenv (>=0.10.4)"] @@ -989,457 +785,608 @@ email = ["email-validator (>=1.0.3)"] typing_extensions = ["typing-extensions (>=3.7.2)"] [[package]] -category = "dev" -description = "passive checker of Python programs" name = "pyflakes" +version = "2.2.0" +description = "passive checker of Python programs" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.2.0" [[package]] -category = "dev" -description = "Pygments is a syntax highlighting package written in Python." name = "pygments" +version = "2.7.4" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" optional = false python-versions = ">=3.5" -version = "2.6.1" [[package]] -category = "dev" -description = "A pure Python Levenshtein implementation that's not freaking GPL'd." -name = "pylev" -optional = false -python-versions = "*" -version = "1.3.0" - -[[package]] -category = "dev" -description = "Extension pack for Python Markdown." name = "pymdown-extensions" +version = "8.1" +description = "Extension pack for Python Markdown." +category = "dev" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -version = "6.3" +python-versions = ">=3.6" [package.dependencies] Markdown = ">=3.2" [[package]] -category = "dev" -description = "Python parsing module" name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.7" [[package]] -category = "dev" -description = "Persistent/Functional/Immutable data structures" -name = "pyrsistent" -optional = false -python-versions = "*" -version = "0.14.11" - -[package.dependencies] -six = "*" - -[[package]] -category = "dev" -description = "pytest: simple powerful testing with Python" name = "pytest" +version = "6.2.1" +description = "pytest: simple powerful testing with Python" +category = "dev" optional = false -python-versions = ">=3.5" -version = "5.4.3" +python-versions = ">=3.6" [package.dependencies] -atomicwrites = ">=1.0" -attrs = ">=17.4.0" -colorama = "*" -more-itertools = ">=4.0.0" +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} +iniconfig = "*" packaging = "*" -pluggy = ">=0.12,<1.0" -py = ">=1.5.0" -wcwidth = "*" - -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" +pluggy = ">=0.12,<1.0.0a1" +py = ">=1.8.2" +toml = "*" [package.extras] -checkqa-mypy = ["mypy (v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] -category = "dev" -description = "Pytest support for asyncio." name = "pytest-asyncio" +version = "0.14.0" +description = "Pytest support for asyncio." +category = "dev" optional = false python-versions = ">= 3.5" -version = "0.10.0" [package.dependencies] -pytest = ">=3.0.6" +pytest = ">=5.4.0" [package.extras] -testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=3.64)"] +testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=5.7.1)"] [[package]] -category = "dev" -description = "Pytest plugin for measuring coverage." name = "pytest-cov" +version = "2.11.1" +description = "Pytest plugin for measuring coverage." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.10.0" [package.dependencies] -coverage = ">=4.4" +coverage = ">=5.2.1" pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] [[package]] -category = "dev" -description = "pytest plugin for generating HTML reports" name = "pytest-html" +version = "3.1.1" +description = "pytest plugin for generating HTML reports" +category = "dev" optional = false python-versions = ">=3.6" -version = "2.1.1" [package.dependencies] -pytest = ">=5.0" +pytest = ">=5.0,<6.0.0 || >6.0.0" pytest-metadata = "*" [[package]] -category = "dev" -description = "pytest plugin for test session metadata" name = "pytest-metadata" +version = "1.11.0" +description = "pytest plugin for test session metadata" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -version = "1.9.0" [package.dependencies] pytest = ">=2.9.0" [[package]] -category = "dev" -description = "Thin-wrapper around the mock package for easier use with py.test" name = "pytest-mock" +version = "3.5.1" +description = "Thin-wrapper around the mock package for easier use with pytest" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.0.0" +python-versions = ">=3.5" [package.dependencies] -pytest = ">=2.7" +pytest = ">=5.0" [package.extras] -dev = ["pre-commit", "tox"] +dev = ["pre-commit", "tox", "pytest-asyncio"] [[package]] -category = "dev" -description = "Mypy static type checker plugin for Pytest" name = "pytest-mypy" +version = "0.8.0" +description = "Mypy static type checker plugin for Pytest" +category = "dev" optional = false -python-versions = "~=3.4" -version = "0.4.2" +python-versions = ">=3.5" [package.dependencies] -[[package.dependencies.mypy]] -python = ">=3.5,<3.8" -version = ">=0.500" - -[[package.dependencies.mypy]] -python = ">=3.8" -version = ">=0.700" - -[package.dependencies.pytest] -python = ">=3.5" -version = ">=2.8" +attrs = ">=19.0" +filelock = ">=3.0" +mypy = [ + {version = ">=0.500", markers = "python_version < \"3.8\""}, + {version = ">=0.700", markers = "python_version >= \"3.8\" and python_version < \"3.9\""}, + {version = ">=0.780", markers = "python_version >= \"3.9\""}, +] +pytest = ">=3.5" [[package]] +name = "python-socks" +version = "1.2.0" +description = "Core proxy (SOCKS4, SOCKS5, HTTP tunneling) functionality for Python" category = "main" -description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" + +[package.dependencies] +async-timeout = {version = ">=3.0.1", optional = true, markers = "extra == \"asyncio\""} + +[package.extras] +asyncio = ["async-timeout (>=3.0.1)"] +curio = ["curio (>=1.4)"] +trio = ["trio (>=0.16.0)"] + +[[package]] name = "pytz" +version = "2020.5" +description = "World timezone definitions, modern and historical" +category = "main" optional = false python-versions = "*" -version = "2020.1" [[package]] -category = "dev" -description = "" -marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platform == \"win32\"" -name = "pywin32-ctypes" -optional = false -python-versions = "*" -version = "0.2.0" - -[[package]] -category = "dev" -description = "YAML parser and emitter for Python" name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "5.3.1" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] -category = "dev" -description = "Alternative regular expression module, to replace re." name = "regex" +version = "2020.11.13" +description = "Alternative regular expression module, to replace re." +category = "dev" optional = false python-versions = "*" -version = "2020.6.8" [[package]] -category = "dev" -description = "Python HTTP for Humans." name = "requests" +version = "2.25.1" +description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.23.0" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" +chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] -category = "dev" -description = "A utility belt for advanced users of python-requests" -name = "requests-toolbelt" -optional = false -python-versions = "*" -version = "0.8.0" - -[package.dependencies] -requests = ">=2.0.1,<3.0.0" - -[[package]] -category = "dev" -description = "Python bindings to FreeDesktop.org Secret Service API" -marker = "python_version >= \"3.5\" and python_version < \"4.0\" and sys_platform == \"linux\"" -name = "secretstorage" -optional = false -python-versions = ">=3.5" -version = "3.1.2" - -[package.dependencies] -cryptography = "*" -jeepney = ">=0.4.2" - -[[package]] -category = "dev" -description = "Tool to Detect Surrounding Shell" -name = "shellingham" -optional = false -python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,>=2.6" -version = "1.3.2" - -[[package]] -category = "dev" -description = "Python 2 and 3 compatibility utilities" name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" [[package]] -category = "dev" -description = "Python Library for Tom's Obvious, Minimal Language" -name = "toml" +name = "snowballstemmer" +version = "2.1.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "main" optional = false python-versions = "*" -version = "0.10.1" [[package]] -category = "dev" -description = "Style preserving TOML library" -name = "tomlkit" +name = "soupsieve" +version = "2.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.5.11" +python-versions = ">=3.5" [[package]] -category = "dev" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -name = "tornado" +name = "sphinx" +version = "3.4.3" +description = "Python documentation generator" +category = "main" optional = false -python-versions = ">= 3.5" -version = "6.0.4" - -[[package]] -category = "dev" -description = "Fast, Extensible Progress Meter" -marker = "python_version > \"2.7\"" -name = "tqdm" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*" -version = "4.46.1" - -[package.extras] -dev = ["py-make (>=0.1.0)", "twine", "argopt", "pydoc-markdown"] - -[[package]] -category = "dev" -description = "Traitlets Python config system" -name = "traitlets" -optional = false -python-versions = "*" -version = "4.3.3" +python-versions = ">=3.5" [package.dependencies] -decorator = "*" -ipython-genutils = "*" -six = "*" +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.12" +imagesize = "*" +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = "*" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = "*" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.790)", "docutils-stubs"] +test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] + +[[package]] +name = "sphinx-autobuild" +version = "2020.9.1" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "sphinx-copybutton" +version = "0.3.1" +description = "Add a copy button to each of your code cells." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +sphinx = ">=1.8" + +[package.extras] +code_style = ["flake8 (>=3.7.0,<3.8.0)", "black", "pre-commit (==1.17.0)"] + +[[package]] +name = "sphinx-intl" +version = "2.0.1" +description = "Sphinx utility that make it easy to translate and to apply translation." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +babel = "*" +click = "*" +sphinx = "*" [package.extras] test = ["pytest", "mock"] +transifex = ["transifex_client (>=0.11)"] [[package]] +name = "sphinx-prompt" +version = "1.3.0" +description = "Sphinx directive to add unselectable prompt" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +pygments = "*" +Sphinx = "*" + +[[package]] +name = "sphinx-substitution-extensions" +version = "2020.9.30.0" +description = "Extensions for Sphinx which allow for substitutions." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +docutils = ">=0.15" +sphinx = ">=2.0.0" +sphinx-prompt = ">=0.1" + +[package.extras] +dev = ["autoflake (==1.4)", "black (==20.8b1)", "check-manifest (==0.43)", "doc8 (==0.8.1)", "flake8-commas (==2.0.0)", "flake8-quotes (==3.2.0)", "flake8 (==3.8.3)", "isort (==5.5.4)", "mypy (==0.782)", "pip-check-reqs (==2.1.1)", "pydocstyle (==5.1.1)", "pyenchant (==3.1.1)", "pygithub (==1.53)", "pylint (==2.6.0)", "pyroma (==2.6)", "pytest-cov (==2.10.1)", "pytest (==6.1.0)", "twine (==3.2.0)", "vulture (==2.1)"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "1.0.3" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest", "html5lib"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +test = ["pytest", "flake8", "mypy"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.4" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" category = "dev" -description = "a fork of Python 2 and 3 ast modules with type comment support" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tornado" +version = "6.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "main" +optional = false +python-versions = ">= 3.5" + +[[package]] +name = "tqdm" +version = "4.56.0" +description = "Fast, Extensible Progress Meter" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +telegram = ["requests"] + +[[package]] +name = "traitlets" +version = "5.0.5" +description = "Traitlets Python configuration system" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +ipython-genutils = "*" + +[package.extras] +test = ["pytest"] + +[[package]] name = "typed-ast" +version = "1.4.2" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" optional = false python-versions = "*" -version = "1.4.1" [[package]] -category = "dev" -description = "Backported and Experimental Type Hints for Python 3.5+" name = "typing-extensions" +version = "3.7.4.3" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" optional = false python-versions = "*" -version = "3.7.4.2" [[package]] -category = "dev" -description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" +version = "1.26.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.9" [package.extras] brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] -category = "main" -description = "Fast implementation of asyncio event loop on top of libuv" -marker = "sys_platform == \"darwin\" or sys_platform == \"linux\"" name = "uvloop" +version = "0.14.0" +description = "Fast implementation of asyncio event loop on top of libuv" +category = "main" optional = false python-versions = "*" -version = "0.14.0" [[package]] -category = "dev" -description = "Virtual Python Environment builder" name = "virtualenv" +version = "20.4.0" +description = "Virtual Python Environment builder" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "20.0.23" [package.dependencies] appdirs = ">=1.4.3,<2" -distlib = ">=0.3.0,<1" +distlib = ">=0.3.1,<1" filelock = ">=3.0.0,<4" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} six = ">=1.9.0,<2" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12,<2" - [package.extras] -docs = ["sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2)"] -testing = ["pytest (>=4)", "coverage (>=5)", "coverage-enable-subprocess (>=1)", "pytest-xdist (>=1.31.0)", "pytest-mock (>=2)", "pytest-env (>=0.6.2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "flaky (>=3)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)", "xonsh (>=0.9.16)"] [[package]] -category = "dev" -description = "Measures the displayed width of unicode strings in a terminal" name = "wcwidth" -optional = false -python-versions = "*" -version = "0.2.4" - -[[package]] +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" category = "dev" -description = "Character encoding aliases for legacy web content" -name = "webencodings" optional = false python-versions = "*" -version = "0.5.1" [[package]] -category = "main" -description = "Yet another URL library" name = "yarl" +version = "1.6.3" +description = "Yet another URL library" +category = "main" optional = false -python-versions = ">=3.5" -version = "1.4.2" +python-versions = ">=3.6" [package.dependencies] idna = ">=2.0" multidict = ">=4.0" +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [[package]] -category = "dev" -description = "Backport of pathlib-compatible object wrapper for zip files" name = "zipp" +version = "3.4.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" optional = false python-versions = ">=3.6" -version = "3.1.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] +testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] [extras] +docs = ["sphinx", "sphinx-intl", "sphinx-autobuild", "sphinx-copybutton", "furo", "sphinx-prompt", "Sphinx-Substitution-Extensions"] fast = ["uvloop"] proxy = ["aiohttp-socks"] [metadata] -content-hash = "152bb9b155a00baadd3c8b9fa21f08af719180bddccb8ad6c3dd6548c3e71e3e" +lock-version = "1.1" python-versions = "^3.7" +content-hash = "04fb2562ab4dccbd2b7125670b37a3124c7eeab55dbec4d1e05a7a8d10f97175" [metadata.files] aiofiles = [ - {file = "aiofiles-0.4.0-py3-none-any.whl", hash = "sha256:1e644c2573f953664368de28d2aa4c89dfd64550429d0c27c4680ccd3aa4985d"}, - {file = "aiofiles-0.4.0.tar.gz", hash = "sha256:021ea0ba314a86027c166ecc4b4c07f2d40fc0f4b3a950d1868a0f2571c2bbee"}, + {file = "aiofiles-0.6.0-py3-none-any.whl", hash = "sha256:bd3019af67f83b739f8e4053c6c0512a7f545b9a8d91aaeab55e6e0f9d123c27"}, + {file = "aiofiles-0.6.0.tar.gz", hash = "sha256:e0281b157d3d5d59d803e3f4557dcc9a3dff28a4dd4829a9ff478adae50ca092"}, ] aiohttp = [ - {file = "aiohttp-3.6.2-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e"}, - {file = "aiohttp-3.6.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec"}, - {file = "aiohttp-3.6.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48"}, - {file = "aiohttp-3.6.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59"}, - {file = "aiohttp-3.6.2-cp36-cp36m-win32.whl", hash = "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a"}, - {file = "aiohttp-3.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17"}, - {file = "aiohttp-3.6.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a"}, - {file = "aiohttp-3.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd"}, - {file = "aiohttp-3.6.2-cp37-cp37m-win32.whl", hash = "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965"}, - {file = "aiohttp-3.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654"}, - {file = "aiohttp-3.6.2-py3-none-any.whl", hash = "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4"}, - {file = "aiohttp-3.6.2.tar.gz", hash = "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326"}, + {file = "aiohttp-3.7.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:328b552513d4f95b0a2eea4c8573e112866107227661834652a8984766aa7656"}, + {file = "aiohttp-3.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c733ef3bdcfe52a1a75564389bad4064352274036e7e234730526d155f04d914"}, + {file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:2858b2504c8697beb9357be01dc47ef86438cc1cb36ecb6991796d19475faa3e"}, + {file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d2cfac21e31e841d60dc28c0ec7d4ec47a35c608cb8906435d47ef83ffb22150"}, + {file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:3228b7a51e3ed533f5472f54f70fd0b0a64c48dc1649a0f0e809bec312934d7a"}, + {file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:dcc119db14757b0c7bce64042158307b9b1c76471e655751a61b57f5a0e4d78e"}, + {file = "aiohttp-3.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:7d9b42127a6c0bdcc25c3dcf252bb3ddc70454fac593b1b6933ae091396deb13"}, + {file = "aiohttp-3.7.3-cp36-cp36m-win32.whl", hash = "sha256:df48a623c58180874d7407b4d9ec06a19b84ed47f60a3884345b1a5099c1818b"}, + {file = "aiohttp-3.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:0b795072bb1bf87b8620120a6373a3c61bfcb8da7e5c2377f4bb23ff4f0b62c9"}, + {file = "aiohttp-3.7.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:0d438c8ca703b1b714e82ed5b7a4412c82577040dadff479c08405e2a715564f"}, + {file = "aiohttp-3.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8389d6044ee4e2037dca83e3f6994738550f6ee8cfb746762283fad9b932868f"}, + {file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3ea8c252d8df5e9166bcf3d9edced2af132f4ead8ac422eac723c5781063709a"}, + {file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:78e2f18a82b88cbc37d22365cf8d2b879a492faedb3f2975adb4ed8dfe994d3a"}, + {file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:df3a7b258cc230a65245167a202dd07320a5af05f3d41da1488ba0fa05bc9347"}, + {file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:f326b3c1bbfda5b9308252ee0dcb30b612ee92b0e105d4abec70335fab5b1245"}, + {file = "aiohttp-3.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5e479df4b2d0f8f02133b7e4430098699450e1b2a826438af6bec9a400530957"}, + {file = "aiohttp-3.7.3-cp37-cp37m-win32.whl", hash = "sha256:6d42debaf55450643146fabe4b6817bb2a55b23698b0434107e892a43117285e"}, + {file = "aiohttp-3.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9c58b0b84055d8bc27b7df5a9d141df4ee6ff59821f922dd73155861282f6a3"}, + {file = "aiohttp-3.7.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:f411cb22115cb15452d099fec0ee636b06cf81bfb40ed9c02d30c8dc2bc2e3d1"}, + {file = "aiohttp-3.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c1e0920909d916d3375c7a1fdb0b1c78e46170e8bb42792312b6eb6676b2f87f"}, + {file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:59d11674964b74a81b149d4ceaff2b674b3b0e4d0f10f0be1533e49c4a28408b"}, + {file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:41608c0acbe0899c852281978492f9ce2c6fbfaf60aff0cefc54a7c4516b822c"}, + {file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:16a3cb5df5c56f696234ea9e65e227d1ebe9c18aa774d36ff42f532139066a5f"}, + {file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:6ccc43d68b81c424e46192a778f97da94ee0630337c9bbe5b2ecc9b0c1c59001"}, + {file = "aiohttp-3.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:d03abec50df423b026a5aa09656bd9d37f1e6a49271f123f31f9b8aed5dc3ea3"}, + {file = "aiohttp-3.7.3-cp38-cp38-win32.whl", hash = "sha256:39f4b0a6ae22a1c567cb0630c30dd082481f95c13ca528dc501a7766b9c718c0"}, + {file = "aiohttp-3.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:c68fdf21c6f3573ae19c7ee65f9ff185649a060c9a06535e9c3a0ee0bbac9235"}, + {file = "aiohttp-3.7.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:710376bf67d8ff4500a31d0c207b8941ff4fba5de6890a701d71680474fe2a60"}, + {file = "aiohttp-3.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2406dc1dda01c7f6060ab586e4601f18affb7a6b965c50a8c90ff07569cf782a"}, + {file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:2a7b7640167ab536c3cb90cfc3977c7094f1c5890d7eeede8b273c175c3910fd"}, + {file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:684850fb1e3e55c9220aad007f8386d8e3e477c4ec9211ae54d968ecdca8c6f9"}, + {file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:1edfd82a98c5161497bbb111b2b70c0813102ad7e0aa81cbeb34e64c93863005"}, + {file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:77149002d9386fae303a4a162e6bce75cc2161347ad2ba06c2f0182561875d45"}, + {file = "aiohttp-3.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:756ae7efddd68d4ea7d89c636b703e14a0c686688d42f588b90778a3c2fc0564"}, + {file = "aiohttp-3.7.3-cp39-cp39-win32.whl", hash = "sha256:3b0036c978cbcc4a4512278e98e3e6d9e6b834dc973206162eddf98b586ef1c6"}, + {file = "aiohttp-3.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:e1b95972a0ae3f248a899cdbac92ba2e01d731225f566569311043ce2226f5e7"}, + {file = "aiohttp-3.7.3.tar.gz", hash = "sha256:9c1a81af067e72261c9cbe33ea792893e83bc6aa987bfbd6fdc1e5e7b22777c4"}, ] aiohttp-socks = [ - {file = "aiohttp_socks-0.3.9-py3-none-any.whl", hash = "sha256:ccd483d7677d7ba80b7ccb738a9be27a3ad6dce4b2756509bc71c9d679d96105"}, - {file = "aiohttp_socks-0.3.9.tar.gz", hash = "sha256:5e5638d0e472baa441eab7990cf19e034960cc803f259748cc359464ccb3c2d6"}, + {file = "aiohttp_socks-0.5.5-py3-none-any.whl", hash = "sha256:faaa25ed4dc34440ca888d23e089420f3b1918dc4ecf062c3fd9474827ad6a39"}, + {file = "aiohttp_socks-0.5.5.tar.gz", hash = "sha256:2eb2059756bde34c55bb429541cbf2eba3fd53e36ac80875b461221e2858b04a"}, +] +alabaster = [ + {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, + {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, ] appnope = [ - {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, - {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, + {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"}, + {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"}, ] aresponses = [ - {file = "aresponses-1.1.2-py3-none-any.whl", hash = "sha256:639defa70730a2a0d5c7af5e42e88b7558e444ba0d43c44917efc57fb0d3b60e"}, - {file = "aresponses-1.1.2.tar.gz", hash = "sha256:20a63536d86af6f31f9b0720c561bdc595b6bfe071940e347ab58b11caff9e1b"}, + {file = "aresponses-2.1.4-py3-none-any.whl", hash = "sha256:2a5a100c9b39e559bf55c26cc837a8ce64ab160ee086afa01ee9c4ef07f245db"}, + {file = "aresponses-2.1.4.tar.gz", hash = "sha256:39674af90700f1bfe2c7c9049cd8116f5c10d34d2e2427fd744b88d9e8644c94"}, ] async-lru = [ {file = "async_lru-1.0.2.tar.gz", hash = "sha256:baa898027619f5cc31b7966f96f00e4fc0df43ba206a8940a5d1af5336a477cb"}, @@ -1457,263 +1404,227 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"}, - {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"}, + {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, + {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] babel = [ - {file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"}, - {file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"}, + {file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"}, + {file = "Babel-2.9.0.tar.gz", hash = "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"}, ] backcall = [ {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.9.3-py2-none-any.whl", hash = "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35"}, + {file = "beautifulsoup4-4.9.3-py3-none-any.whl", hash = "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666"}, + {file = "beautifulsoup4-4.9.3.tar.gz", hash = "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25"}, +] black = [ - {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, - {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, -] -cachecontrol = [ - {file = "CacheControl-0.12.6-py2.py3-none-any.whl", hash = "sha256:10d056fa27f8563a271b345207402a6dcce8efab7e5b377e270329c62471b10d"}, - {file = "CacheControl-0.12.6.tar.gz", hash = "sha256:be9aa45477a134aee56c8fac518627e1154df063e85f67d4f83ce0ccc23688e8"}, -] -cachy = [ - {file = "cachy-0.3.0-py2.py3-none-any.whl", hash = "sha256:338ca09c8860e76b275aff52374330efedc4d5a5e45dc1c5b539c1ead0786fe7"}, - {file = "cachy-0.3.0.tar.gz", hash = "sha256:186581f4ceb42a0bbe040c407da73c14092379b1e4c0e327fdb72ae4a9b269b1"}, + {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] certifi = [ - {file = "certifi-2020.4.5.2-py2.py3-none-any.whl", hash = "sha256:9cd41137dc19af6a5e03b630eefe7d1f458d964d406342dd3edf625839b944cc"}, - {file = "certifi-2020.4.5.2.tar.gz", hash = "sha256:5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1"}, -] -cffi = [ - {file = "cffi-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384"}, - {file = "cffi-1.14.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30"}, - {file = "cffi-1.14.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"}, - {file = "cffi-1.14.0-cp27-cp27m-win32.whl", hash = "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78"}, - {file = "cffi-1.14.0-cp27-cp27m-win_amd64.whl", hash = "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793"}, - {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e"}, - {file = "cffi-1.14.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a"}, - {file = "cffi-1.14.0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff"}, - {file = "cffi-1.14.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f"}, - {file = "cffi-1.14.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa"}, - {file = "cffi-1.14.0-cp35-cp35m-win32.whl", hash = "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5"}, - {file = "cffi-1.14.0-cp35-cp35m-win_amd64.whl", hash = "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4"}, - {file = "cffi-1.14.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d"}, - {file = "cffi-1.14.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc"}, - {file = "cffi-1.14.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac"}, - {file = "cffi-1.14.0-cp36-cp36m-win32.whl", hash = "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f"}, - {file = "cffi-1.14.0-cp36-cp36m-win_amd64.whl", hash = "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b"}, - {file = "cffi-1.14.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3"}, - {file = "cffi-1.14.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66"}, - {file = "cffi-1.14.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0"}, - {file = "cffi-1.14.0-cp37-cp37m-win32.whl", hash = "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f"}, - {file = "cffi-1.14.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26"}, - {file = "cffi-1.14.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd"}, - {file = "cffi-1.14.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55"}, - {file = "cffi-1.14.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2"}, - {file = "cffi-1.14.0-cp38-cp38-win32.whl", hash = "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8"}, - {file = "cffi-1.14.0-cp38-cp38-win_amd64.whl", hash = "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b"}, - {file = "cffi-1.14.0.tar.gz", hash = "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6"}, + {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, + {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, ] cfgv = [ - {file = "cfgv-3.1.0-py2.py3-none-any.whl", hash = "sha256:1ccf53320421aeeb915275a196e23b3b8ae87dea8ac6698b1638001d4a486d53"}, - {file = "cfgv-3.1.0.tar.gz", hash = "sha256:c8e8f552ffcc6194f4e18dd4f68d9aef0c0d58ae7e7be8c82bee3c5e9edfa513"}, + {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"}, + {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"}, ] chardet = [ {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, ] -cleo = [ - {file = "cleo-0.7.6-py2.py3-none-any.whl", hash = "sha256:9443d67e5b2da79b32d820ae41758dd6a25618345cb10b9a022a695e26b291b9"}, - {file = "cleo-0.7.6.tar.gz", hash = "sha256:99cf342406f3499cec43270fcfaf93c126c5164092eca201dfef0f623360b409"}, -] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] -clikit = [ - {file = "clikit-0.4.3-py2.py3-none-any.whl", hash = "sha256:71e321b7795a2a6c4888629f43365d52db071737e668ab16861121d7dd3ada09"}, - {file = "clikit-0.4.3.tar.gz", hash = "sha256:6e2d7e115e7c7b35bceb0209109935ab2f9ab50910e9ff2293f7fa0b7abf973e"}, -] colorama = [ - {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, - {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ - {file = "coverage-5.1-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65"}, - {file = "coverage-5.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2"}, - {file = "coverage-5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04"}, - {file = "coverage-5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6"}, - {file = "coverage-5.1-cp27-cp27m-win32.whl", hash = "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796"}, - {file = "coverage-5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730"}, - {file = "coverage-5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0"}, - {file = "coverage-5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a"}, - {file = "coverage-5.1-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf"}, - {file = "coverage-5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9"}, - {file = "coverage-5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768"}, - {file = "coverage-5.1-cp35-cp35m-win32.whl", hash = "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2"}, - {file = "coverage-5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7"}, - {file = "coverage-5.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0"}, - {file = "coverage-5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019"}, - {file = "coverage-5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c"}, - {file = "coverage-5.1-cp36-cp36m-win32.whl", hash = "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1"}, - {file = "coverage-5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7"}, - {file = "coverage-5.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355"}, - {file = "coverage-5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489"}, - {file = "coverage-5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd"}, - {file = "coverage-5.1-cp37-cp37m-win32.whl", hash = "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e"}, - {file = "coverage-5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a"}, - {file = "coverage-5.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55"}, - {file = "coverage-5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c"}, - {file = "coverage-5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef"}, - {file = "coverage-5.1-cp38-cp38-win32.whl", hash = "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24"}, - {file = "coverage-5.1-cp38-cp38-win_amd64.whl", hash = "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0"}, - {file = "coverage-5.1-cp39-cp39-win32.whl", hash = "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4"}, - {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"}, - {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"}, -] -cryptography = [ - {file = "cryptography-2.9.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e"}, - {file = "cryptography-2.9.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b"}, - {file = "cryptography-2.9.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365"}, - {file = "cryptography-2.9.2-cp27-cp27m-win32.whl", hash = "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0"}, - {file = "cryptography-2.9.2-cp27-cp27m-win_amd64.whl", hash = "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55"}, - {file = "cryptography-2.9.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270"}, - {file = "cryptography-2.9.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf"}, - {file = "cryptography-2.9.2-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d"}, - {file = "cryptography-2.9.2-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785"}, - {file = "cryptography-2.9.2-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b"}, - {file = "cryptography-2.9.2-cp35-cp35m-win32.whl", hash = "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae"}, - {file = "cryptography-2.9.2-cp35-cp35m-win_amd64.whl", hash = "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b"}, - {file = "cryptography-2.9.2-cp36-cp36m-win32.whl", hash = "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6"}, - {file = "cryptography-2.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3"}, - {file = "cryptography-2.9.2-cp37-cp37m-win32.whl", hash = "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b"}, - {file = "cryptography-2.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e"}, - {file = "cryptography-2.9.2-cp38-cp38-win32.whl", hash = "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0"}, - {file = "cryptography-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5"}, - {file = "cryptography-2.9.2.tar.gz", hash = "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229"}, + {file = "coverage-5.3.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fabeeb121735d47d8eab8671b6b031ce08514c86b7ad8f7d5490a7b6dcd6267d"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:7e4d159021c2029b958b2363abec4a11db0ce8cd43abb0d9ce44284cb97217e7"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:378ac77af41350a8c6b8801a66021b52da8a05fd77e578b7380e876c0ce4f528"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e448f56cfeae7b1b3b5bcd99bb377cde7c4eb1970a525c770720a352bc4c8044"}, + {file = "coverage-5.3.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:cc44e3545d908ecf3e5773266c487ad1877be718d9dc65fc7eb6e7d14960985b"}, + {file = "coverage-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:08b3ba72bd981531fd557f67beee376d6700fba183b167857038997ba30dd297"}, + {file = "coverage-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8dacc4073c359f40fcf73aede8428c35f84639baad7e1b46fce5ab7a8a7be4bb"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ee2f1d1c223c3d2c24e3afbb2dd38be3f03b1a8d6a83ee3d9eb8c36a52bee899"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a9d4ff06804920388aab69c5ea8a77525cf165356db70131616acd269e19b36"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:782a5c7df9f91979a7a21792e09b34a658058896628217ae6362088b123c8500"}, + {file = "coverage-5.3.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:fda29412a66099af6d6de0baa6bd7c52674de177ec2ad2630ca264142d69c6c7"}, + {file = "coverage-5.3.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:f2c6888eada180814b8583c3e793f3f343a692fc802546eed45f40a001b1169f"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8f33d1156241c43755137288dea619105477961cfa7e47f48dbf96bc2c30720b"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b239711e774c8eb910e9b1ac719f02f5ae4bf35fa0420f438cdc3a7e4e7dd6ec"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:f54de00baf200b4539a5a092a759f000b5f45fd226d6d25a76b0dff71177a714"}, + {file = "coverage-5.3.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:be0416074d7f253865bb67630cf7210cbc14eb05f4099cc0f82430135aaa7a3b"}, + {file = "coverage-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:c46643970dff9f5c976c6512fd35768c4a3819f01f61169d8cdac3f9290903b7"}, + {file = "coverage-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9a4f66259bdd6964d8cf26142733c81fb562252db74ea367d9beb4f815478e72"}, + {file = "coverage-5.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c6e5174f8ca585755988bc278c8bb5d02d9dc2e971591ef4a1baabdf2d99589b"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3911c2ef96e5ddc748a3c8b4702c61986628bb719b8378bf1e4a6184bbd48fe4"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c5ec71fd4a43b6d84ddb88c1df94572479d9a26ef3f150cef3dacefecf888105"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f51dbba78d68a44e99d484ca8c8f604f17e957c1ca09c3ebc2c7e3bbd9ba0448"}, + {file = "coverage-5.3.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:a2070c5affdb3a5e751f24208c5c4f3d5f008fa04d28731416e023c93b275277"}, + {file = "coverage-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:535dc1e6e68fad5355f9984d5637c33badbdc987b0c0d303ee95a6c979c9516f"}, + {file = "coverage-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:a4857f7e2bc6921dbd487c5c88b84f5633de3e7d416c4dc0bb70256775551a6c"}, + {file = "coverage-5.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fac3c432851038b3e6afe086f777732bcf7f6ebbfd90951fa04ee53db6d0bcdd"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:cd556c79ad665faeae28020a0ab3bda6cd47d94bec48e36970719b0b86e4dcf4"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a66ca3bdf21c653e47f726ca57f46ba7fc1f260ad99ba783acc3e58e3ebdb9ff"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ab110c48bc3d97b4d19af41865e14531f300b482da21783fdaacd159251890e8"}, + {file = "coverage-5.3.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e52d3d95df81c8f6b2a1685aabffadf2d2d9ad97203a40f8d61e51b70f191e4e"}, + {file = "coverage-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:fa10fee7e32213f5c7b0d6428ea92e3a3fdd6d725590238a3f92c0de1c78b9d2"}, + {file = "coverage-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ce6f3a147b4b1a8b09aae48517ae91139b1b010c5f36423fa2b866a8b23df879"}, + {file = "coverage-5.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:93a280c9eb736a0dcca19296f3c30c720cb41a71b1f9e617f341f0a8e791a69b"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:3102bb2c206700a7d28181dbe04d66b30780cde1d1c02c5f3c165cf3d2489497"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8ffd4b204d7de77b5dd558cdff986a8274796a1e57813ed005b33fd97e29f059"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a607ae05b6c96057ba86c811d9c43423f35e03874ffb03fbdcd45e0637e8b631"}, + {file = "coverage-5.3.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:3a3c3f8863255f3c31db3889f8055989527173ef6192a283eb6f4db3c579d830"}, + {file = "coverage-5.3.1-cp38-cp38-win32.whl", hash = "sha256:ff1330e8bc996570221b450e2d539134baa9465f5cb98aff0e0f73f34172e0ae"}, + {file = "coverage-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:3498b27d8236057def41de3585f317abae235dd3a11d33e01736ffedb2ef8606"}, + {file = "coverage-5.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ceb499d2b3d1d7b7ba23abe8bf26df5f06ba8c71127f188333dddcf356b4b63f"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:3b14b1da110ea50c8bcbadc3b82c3933974dbeea1832e814aab93ca1163cd4c1"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:76b2775dda7e78680d688daabcb485dc87cf5e3184a0b3e012e1d40e38527cc8"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:cef06fb382557f66d81d804230c11ab292d94b840b3cb7bf4450778377b592f4"}, + {file = "coverage-5.3.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f61319e33222591f885c598e3e24f6a4be3533c1d70c19e0dc59e83a71ce27d"}, + {file = "coverage-5.3.1-cp39-cp39-win32.whl", hash = "sha256:cc6f8246e74dd210d7e2b56c76ceaba1cc52b025cd75dbe96eb48791e0250e98"}, + {file = "coverage-5.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2757fa64e11ec12220968f65d086b7a29b6583d16e9a544c889b22ba98555ef1"}, + {file = "coverage-5.3.1-pp36-none-any.whl", hash = "sha256:723d22d324e7997a651478e9c5a3120a0ecbc9a7e94071f7e1954562a8806cf3"}, + {file = "coverage-5.3.1-pp37-none-any.whl", hash = "sha256:c89b558f8a9a5a6f2cfc923c304d49f0ce629c3bd85cb442ca258ec20366394c"}, + {file = "coverage-5.3.1.tar.gz", hash = "sha256:38f16b1317b8dd82df67ed5daa5f5e7c959e46579840d77a67a4ceb9cef0a50b"}, ] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, ] distlib = [ - {file = "distlib-0.3.0.zip", hash = "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"}, + {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"}, + {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"}, +] +docutils = [ + {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, + {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, ] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, ] flake8 = [ - {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"}, - {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"}, + {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, + {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, ] flake8-html = [ {file = "flake8-html-0.4.1.tar.gz", hash = "sha256:2fb436cbfe1e109275bc8fb7fdd0cb00e67b3b48cfeb397309b6b2c61eeb4cb4"}, {file = "flake8_html-0.4.1-py2.py3-none-any.whl", hash = "sha256:17324eb947e7006807e4184ee26953e67baf421b3cf9e646a38bfec34eec5a94"}, ] +furo = [ + {file = "furo-2020.12.30b24-py3-none-any.whl", hash = "sha256:251dadee4dee96dddf2dc9b5243b88212e16923f53397bf12bc98574918bda41"}, + {file = "furo-2020.12.30b24.tar.gz", hash = "sha256:30171899c9c06d692a778e6daf6cb2e5cbb05efc6006e1692e5e776007dc8a8c"}, +] future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] -html5lib = [ - {file = "html5lib-1.0.1-py2.py3-none-any.whl", hash = "sha256:20b159aa3badc9d5ee8f5c647e5efd02ed2a66ab8d354930bd9ff139fc1dc0a3"}, - {file = "html5lib-1.0.1.tar.gz", hash = "sha256:66cb0dcfdbbc4f9c3ba1a63fdb511ffdbd4f513b2b6d81b80cd26ce6b3fb3736"}, -] identify = [ - {file = "identify-1.4.19-py2.py3-none-any.whl", hash = "sha256:781fd3401f5d2b17b22a8b18b493a48d5d948e3330634e82742e23f9c20234ef"}, - {file = "identify-1.4.19.tar.gz", hash = "sha256:249ebc7e2066d6393d27c1b1be3b70433f824a120b1d8274d362f1eb419e3b52"}, + {file = "identify-1.5.13-py2.py3-none-any.whl", hash = "sha256:9dfb63a2e871b807e3ba62f029813552a24b5289504f5b071dea9b041aee9fe4"}, + {file = "identify-1.5.13.tar.gz", hash = "sha256:70b638cf4743f33042bebb3b51e25261a0a10e80f978739f17e7fd4837664a66"}, ] idna = [ - {file = "idna-2.9-py2.py3-none-any.whl", hash = "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"}, - {file = "idna-2.9.tar.gz", hash = "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"}, + {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, + {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, +] +imagesize = [ + {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, + {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] importlib-metadata = [ - {file = "importlib_metadata-1.1.3-py2.py3-none-any.whl", hash = "sha256:7c7f8ac40673f507f349bef2eed21a0e5f01ddf5b2a7356a6c65eb2099b53764"}, - {file = "importlib_metadata-1.1.3.tar.gz", hash = "sha256:7a99fb4084ffe6dae374961ba7a6521b79c1d07c658ab3a28aa264ee1d1b14e3"}, - {file = "importlib_metadata-1.6.1-py2.py3-none-any.whl", hash = "sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"}, - {file = "importlib_metadata-1.6.1.tar.gz", hash = "sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545"}, + {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, + {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] ipython = [ - {file = "ipython-7.15.0-py3-none-any.whl", hash = "sha256:1b85d65632211bf5d3e6f1406f3393c8c429a47d7b947b9a87812aa5bce6595c"}, - {file = "ipython-7.15.0.tar.gz", hash = "sha256:0ef1433879816a960cd3ae1ae1dc82c64732ca75cec8dab5a4e29783fb571d0e"}, + {file = "ipython-7.19.0-py3-none-any.whl", hash = "sha256:c987e8178ced651532b3b1ff9965925bfd445c279239697052561a9ab806d28f"}, + {file = "ipython-7.19.0.tar.gz", hash = "sha256:cbb2ef3d5961d44e6a963b9817d4ea4e1fa2eb589c371a470fed14d8d40cbd6a"}, ] ipython-genutils = [ {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, ] isort = [ - {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, - {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, + {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"}, + {file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"}, ] jedi = [ - {file = "jedi-0.17.0-py2.py3-none-any.whl", hash = "sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798"}, - {file = "jedi-0.17.0.tar.gz", hash = "sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030"}, -] -jeepney = [ - {file = "jeepney-0.4.3-py3-none-any.whl", hash = "sha256:d6c6b49683446d2407d2fe3acb7a368a77ff063f9182fe427da15d622adc24cf"}, - {file = "jeepney-0.4.3.tar.gz", hash = "sha256:3479b861cc2b6407de5188695fa1a8d57e5072d7059322469b62628869b8e36e"}, + {file = "jedi-0.18.0-py2.py3-none-any.whl", hash = "sha256:18456d83f65f400ab0c2d3319e48520420ef43b23a086fdc05dff34132f0fb93"}, + {file = "jedi-0.18.0.tar.gz", hash = "sha256:92550a404bad8afed881a137ec9a461fed49eca661414be45059329614ed0707"}, ] jinja2 = [ {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"}, {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"}, ] joblib = [ - {file = "joblib-0.15.1-py3-none-any.whl", hash = "sha256:6825784ffda353cc8a1be573118085789e5b5d29401856b35b756645ab5aecb5"}, - {file = "joblib-0.15.1.tar.gz", hash = "sha256:61e49189c84b3c5d99a969d314853f4d1d263316cc694bec17548ebaa9c47b6e"}, -] -jsonschema = [ - {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, - {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, -] -keyring = [ - {file = "keyring-20.0.1-py2.py3-none-any.whl", hash = "sha256:c674f032424b4bffc62abeac5523ec49cc84aed07a480c3233e0baf618efc15c"}, - {file = "keyring-20.0.1.tar.gz", hash = "sha256:963bfa7f090269d30bdc5e25589e5fd9dad2cf2a7c6f176a7f2386910e5d0d8d"}, + {file = "joblib-1.0.0-py3-none-any.whl", hash = "sha256:75ead23f13484a2a414874779d69ade40d4fa1abe62b222a23cd50d4bc822f6f"}, + {file = "joblib-1.0.0.tar.gz", hash = "sha256:7ad866067ac1fdec27d51c8678ea760601b70e32ff1881d4dc8e1171f2b64b24"}, ] livereload = [ - {file = "livereload-2.6.2.tar.gz", hash = "sha256:d1eddcb5c5eb8d2ca1fa1f750e580da624c0f7fcb734aa5780dc81b7dcbd89be"}, -] -lockfile = [ - {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, - {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, ] lunr = [ {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, ] lxml = [ - {file = "lxml-4.5.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ee2be8b8f72a2772e72ab926a3bccebf47bb727bda41ae070dc91d1fb759b726"}, - {file = "lxml-4.5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:fadd2a63a2bfd7fb604508e553d1cf68eca250b2fbdbd81213b5f6f2fbf23529"}, - {file = "lxml-4.5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4f282737d187ae723b2633856085c31ae5d4d432968b7f3f478a48a54835f5c4"}, - {file = "lxml-4.5.1-cp27-cp27m-win32.whl", hash = "sha256:7fd88cb91a470b383aafad554c3fe1ccf6dfb2456ff0e84b95335d582a799804"}, - {file = "lxml-4.5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:0790ddca3f825dd914978c94c2545dbea5f56f008b050e835403714babe62a5f"}, - {file = "lxml-4.5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9144ce36ca0824b29ebc2e02ca186e54040ebb224292072250467190fb613b96"}, - {file = "lxml-4.5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:a636346c6c0e1092ffc202d97ec1843a75937d8c98aaf6771348ad6422e44bb0"}, - {file = "lxml-4.5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:f95d28193c3863132b1f55c1056036bf580b5a488d908f7d22a04ace8935a3a9"}, - {file = "lxml-4.5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b26719890c79a1dae7d53acac5f089d66fd8cc68a81f4e4bd355e45470dc25e1"}, - {file = "lxml-4.5.1-cp35-cp35m-win32.whl", hash = "sha256:a9e3b8011388e7e373565daa5e92f6c9cb844790dc18e43073212bb3e76f7007"}, - {file = "lxml-4.5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:2754d4406438c83144f9ffd3628bbe2dcc6d62b20dbc5c1ec4bc4385e5d44b42"}, - {file = "lxml-4.5.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:925baf6ff1ef2c45169f548cc85204433e061360bfa7d01e1be7ae38bef73194"}, - {file = "lxml-4.5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a87dbee7ad9dce3aaefada2081843caf08a44a8f52e03e0a4cc5819f8398f2f4"}, - {file = "lxml-4.5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:51bb4edeb36d24ec97eb3e6a6007be128b720114f9a875d6b370317d62ac80b9"}, - {file = "lxml-4.5.1-cp36-cp36m-win32.whl", hash = "sha256:c79e5debbe092e3c93ca4aee44c9a7631bdd407b2871cb541b979fd350bbbc29"}, - {file = "lxml-4.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b7462cdab6fffcda853338e1741ce99706cdf880d921b5a769202ea7b94e8528"}, - {file = "lxml-4.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06748c7192eab0f48e3d35a7adae609a329c6257495d5e53878003660dc0fec6"}, - {file = "lxml-4.5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1aa7a6197c1cdd65d974f3e4953764eee3d9c7b67e3966616b41fab7f8f516b7"}, - {file = "lxml-4.5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:afb53edf1046599991fb4a7d03e601ab5f5422a5435c47ee6ba91ec3b61416a6"}, - {file = "lxml-4.5.1-cp37-cp37m-win32.whl", hash = "sha256:2d1ddce96cf15f1254a68dba6935e6e0f1fe39247de631c115e84dd404a6f031"}, - {file = "lxml-4.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:22c6d34fdb0e65d5f782a4d1a1edb52e0a8365858dafb1c08cb1d16546cf0786"}, - {file = "lxml-4.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c47a8a5d00060122ca5908909478abce7bbf62d812e3fc35c6c802df8fb01fe7"}, - {file = "lxml-4.5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b77975465234ff49fdad871c08aa747aae06f5e5be62866595057c43f8d2f62c"}, - {file = "lxml-4.5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2b02c106709466a93ed424454ce4c970791c486d5fcdf52b0d822a7e29789626"}, - {file = "lxml-4.5.1-cp38-cp38-win32.whl", hash = "sha256:7eee37c1b9815e6505847aa5e68f192e8a1b730c5c7ead39ff317fde9ce29448"}, - {file = "lxml-4.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:d8d40e0121ca1606aa9e78c28a3a7d88a05c06b3ca61630242cded87d8ce55fa"}, - {file = "lxml-4.5.1.tar.gz", hash = "sha256:27ee0faf8077c7c1a589573b1450743011117f1aa1a91d5ae776bbc5ca6070f2"}, + {file = "lxml-4.6.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f"}, + {file = "lxml-4.6.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d"}, + {file = "lxml-4.6.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2"}, + {file = "lxml-4.6.2-cp27-cp27m-win32.whl", hash = "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"}, + {file = "lxml-4.6.2-cp27-cp27m-win_amd64.whl", hash = "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e"}, + {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2"}, + {file = "lxml-4.6.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe"}, + {file = "lxml-4.6.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388"}, + {file = "lxml-4.6.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80"}, + {file = "lxml-4.6.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b"}, + {file = "lxml-4.6.2-cp35-cp35m-win32.whl", hash = "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8"}, + {file = "lxml-4.6.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780"}, + {file = "lxml-4.6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af"}, + {file = "lxml-4.6.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37"}, + {file = "lxml-4.6.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98"}, + {file = "lxml-4.6.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d"}, + {file = "lxml-4.6.2-cp36-cp36m-win32.whl", hash = "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf"}, + {file = "lxml-4.6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939"}, + {file = "lxml-4.6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e"}, + {file = "lxml-4.6.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711"}, + {file = "lxml-4.6.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089"}, + {file = "lxml-4.6.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01"}, + {file = "lxml-4.6.2-cp37-cp37m-win32.whl", hash = "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2"}, + {file = "lxml-4.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc"}, + {file = "lxml-4.6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d"}, + {file = "lxml-4.6.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3"}, + {file = "lxml-4.6.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644"}, + {file = "lxml-4.6.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308"}, + {file = "lxml-4.6.2-cp38-cp38-win32.whl", hash = "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505"}, + {file = "lxml-4.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a"}, + {file = "lxml-4.6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931"}, + {file = "lxml-4.6.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03"}, + {file = "lxml-4.6.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5"}, + {file = "lxml-4.6.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a"}, + {file = "lxml-4.6.2-cp39-cp39-win32.whl", hash = "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75"}, + {file = "lxml-4.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf"}, + {file = "lxml-4.6.2.tar.gz", hash = "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc"}, +] +magic-filter = [ + {file = "magic-filter-0.1.2.tar.gz", hash = "sha256:dfd1a778493083ac1355791d1716c3beb6629598739f2c2ec078815952282c1d"}, + {file = "magic_filter-0.1.2-py3-none-any.whl", hash = "sha256:16d0c96584f0660fd7fa94b6cd16f92383616208a32568bf8f95a57fc1a69e9d"}, ] markdown = [ - {file = "Markdown-3.2.2-py3-none-any.whl", hash = "sha256:c467cd6233885534bf0fe96e62e3cf46cfc1605112356c4f9981512b8174de59"}, - {file = "Markdown-3.2.2.tar.gz", hash = "sha256:1fafe3f1ecabfb514a5285fca634a53c1b32a81cb0feb154264d55bf2ff22c17"}, + {file = "Markdown-3.3.3-py3-none-any.whl", hash = "sha256:c109c15b7dc20a9ac454c9e6025927d44460b85bd039da028d85e2b6d0bcc328"}, + {file = "Markdown-3.3.3.tar.gz", hash = "sha256:5d9f2b5ca24bc4c7a390d22323ca4bad200368612b5aaa7796babf971d2b2f18"}, ] markdown-include = [ - {file = "markdown-include-0.5.1.tar.gz", hash = "sha256:72a45461b589489a088753893bc95c5fa5909936186485f4ed55caa57d10250f"}, + {file = "markdown-include-0.6.0.tar.gz", hash = "sha256:6f5d680e36f7780c7f0f61dca53ca581bd50d1b56137ddcd6353efafa0c3e4a2"}, ] markupsafe = [ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, @@ -1743,6 +1654,11 @@ markupsafe = [ {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] mccabe = [ @@ -1757,67 +1673,75 @@ mkdocs = [ {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, ] mkdocs-material = [ - {file = "mkdocs-material-4.6.3.tar.gz", hash = "sha256:1d486635b03f5a2ec87325842f7b10c7ae7daa0eef76b185572eece6a6ea212c"}, - {file = "mkdocs_material-4.6.3-py2.py3-none-any.whl", hash = "sha256:7f3afa0a09c07d0b89a6a9755fdb00513aee8f0cec3538bb903325c80f66f444"}, + {file = "mkdocs-material-6.2.5.tar.gz", hash = "sha256:26900f51e177eb7dcfc8140ffe33c71b22ffa2920271e093679f0670b78e7e8b"}, + {file = "mkdocs_material-6.2.5-py2.py3-none-any.whl", hash = "sha256:04574cbaeb12671da66cd58904d6066dd269239f4a1bdb819c2c6e1ac9d9947a"}, ] -more-itertools = [ - {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, - {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, -] -msgpack = [ - {file = "msgpack-1.0.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:cec8bf10981ed70998d98431cd814db0ecf3384e6b113366e7f36af71a0fca08"}, - {file = "msgpack-1.0.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aa5c057eab4f40ec47ea6f5a9825846be2ff6bf34102c560bad5cad5a677c5be"}, - {file = "msgpack-1.0.0-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:4233b7f86c1208190c78a525cd3828ca1623359ef48f78a6fea4b91bb995775a"}, - {file = "msgpack-1.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:b3758dfd3423e358bbb18a7cccd1c74228dffa7a697e5be6cb9535de625c0dbf"}, - {file = "msgpack-1.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:25b3bc3190f3d9d965b818123b7752c5dfb953f0d774b454fd206c18fe384fb8"}, - {file = "msgpack-1.0.0-cp36-cp36m-win32.whl", hash = "sha256:e7bbdd8e2b277b77782f3ce34734b0dfde6cbe94ddb74de8d733d603c7f9e2b1"}, - {file = "msgpack-1.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5dba6d074fac9b24f29aaf1d2d032306c27f04187651511257e7831733293ec2"}, - {file = "msgpack-1.0.0-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:908944e3f038bca67fcfedb7845c4a257c7749bf9818632586b53bcf06ba4b97"}, - {file = "msgpack-1.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:db685187a415f51d6b937257474ca72199f393dad89534ebbdd7d7a3b000080e"}, - {file = "msgpack-1.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ea41c9219c597f1d2bf6b374d951d310d58684b5de9dc4bd2976db9e1e22c140"}, - {file = "msgpack-1.0.0-cp37-cp37m-win32.whl", hash = "sha256:e35b051077fc2f3ce12e7c6a34cf309680c63a842db3a0616ea6ed25ad20d272"}, - {file = "msgpack-1.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5bea44181fc8e18eed1d0cd76e355073f00ce232ff9653a0ae88cb7d9e643322"}, - {file = "msgpack-1.0.0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c901e8058dd6653307906c5f157f26ed09eb94a850dddd989621098d347926ab"}, - {file = "msgpack-1.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:271b489499a43af001a2e42f42d876bb98ccaa7e20512ff37ca78c8e12e68f84"}, - {file = "msgpack-1.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7a22c965588baeb07242cb561b63f309db27a07382825fc98aecaf0827c1538e"}, - {file = "msgpack-1.0.0-cp38-cp38-win32.whl", hash = "sha256:002a0d813e1f7b60da599bdf969e632074f9eec1b96cbed8fb0973a63160a408"}, - {file = "msgpack-1.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:39c54fdebf5fa4dda733369012c59e7d085ebdfe35b6cf648f09d16708f1be5d"}, - {file = "msgpack-1.0.0.tar.gz", hash = "sha256:9534d5cc480d4aff720233411a1f765be90885750b07df772380b34c10ecb5c0"}, +mkdocs-material-extensions = [ + {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"}, + {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"}, ] multidict = [ - {file = "multidict-4.7.6-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000"}, - {file = "multidict-4.7.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a"}, - {file = "multidict-4.7.6-cp35-cp35m-win32.whl", hash = "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5"}, - {file = "multidict-4.7.6-cp35-cp35m-win_amd64.whl", hash = "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3"}, - {file = "multidict-4.7.6-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87"}, - {file = "multidict-4.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2"}, - {file = "multidict-4.7.6-cp36-cp36m-win32.whl", hash = "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7"}, - {file = "multidict-4.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463"}, - {file = "multidict-4.7.6-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d"}, - {file = "multidict-4.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255"}, - {file = "multidict-4.7.6-cp37-cp37m-win32.whl", hash = "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507"}, - {file = "multidict-4.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c"}, - {file = "multidict-4.7.6-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b"}, - {file = "multidict-4.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7"}, - {file = "multidict-4.7.6-cp38-cp38-win32.whl", hash = "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d"}, - {file = "multidict-4.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19"}, - {file = "multidict-4.7.6.tar.gz", hash = "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430"}, + {file = "multidict-5.1.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:b7993704f1a4b204e71debe6095150d43b2ee6150fa4f44d6d966ec356a8d61f"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:9dd6e9b1a913d096ac95d0399bd737e00f2af1e1594a787e00f7975778c8b2bf"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f21756997ad8ef815d8ef3d34edd98804ab5ea337feedcd62fb52d22bf531281"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab820665e67373de5802acae069a6a05567ae234ddb129f31d290fc3d1aa56d"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:9436dc58c123f07b230383083855593550c4d301d2532045a17ccf6eca505f6d"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:830f57206cc96ed0ccf68304141fec9481a096c4d2e2831f311bde1c404401da"}, + {file = "multidict-5.1.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:2e68965192c4ea61fff1b81c14ff712fc7dc15d2bd120602e4a3494ea6584224"}, + {file = "multidict-5.1.0-cp36-cp36m-win32.whl", hash = "sha256:2f1a132f1c88724674271d636e6b7351477c27722f2ed789f719f9e3545a3d26"}, + {file = "multidict-5.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3a4f32116f8f72ecf2a29dabfb27b23ab7cdc0ba807e8459e59a93a9be9506f6"}, + {file = "multidict-5.1.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:46c73e09ad374a6d876c599f2328161bcd95e280f84d2060cf57991dec5cfe76"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:018132dbd8688c7a69ad89c4a3f39ea2f9f33302ebe567a879da8f4ca73f0d0a"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4b186eb7d6ae7c06eb4392411189469e6a820da81447f46c0072a41c748ab73f"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3a041b76d13706b7fff23b9fc83117c7b8fe8d5fe9e6be45eee72b9baa75f348"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:051012ccee979b2b06be928a6150d237aec75dd6bf2d1eeeb190baf2b05abc93"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:6a4d5ce640e37b0efcc8441caeea8f43a06addace2335bd11151bc02d2ee31f9"}, + {file = "multidict-5.1.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:5cf3443199b83ed9e955f511b5b241fd3ae004e3cb81c58ec10f4fe47c7dce37"}, + {file = "multidict-5.1.0-cp37-cp37m-win32.whl", hash = "sha256:f200755768dc19c6f4e2b672421e0ebb3dd54c38d5a4f262b872d8cfcc9e93b5"}, + {file = "multidict-5.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:05c20b68e512166fddba59a918773ba002fdd77800cad9f55b59790030bab632"}, + {file = "multidict-5.1.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:54fd1e83a184e19c598d5e70ba508196fd0bbdd676ce159feb412a4a6664f952"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:0e3c84e6c67eba89c2dbcee08504ba8644ab4284863452450520dad8f1e89b79"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:dc862056f76443a0db4509116c5cd480fe1b6a2d45512a653f9a855cc0517456"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0e929169f9c090dae0646a011c8b058e5e5fb391466016b39d21745b48817fd7"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:d81eddcb12d608cc08081fa88d046c78afb1bf8107e6feab5d43503fea74a635"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:585fd452dd7782130d112f7ddf3473ffdd521414674c33876187e101b588738a"}, + {file = "multidict-5.1.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:37e5438e1c78931df5d3c0c78ae049092877e5e9c02dd1ff5abb9cf27a5914ea"}, + {file = "multidict-5.1.0-cp38-cp38-win32.whl", hash = "sha256:07b42215124aedecc6083f1ce6b7e5ec5b50047afa701f3442054373a6deb656"}, + {file = "multidict-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:929006d3c2d923788ba153ad0de8ed2e5ed39fdbe8e7be21e2f22ed06c6783d3"}, + {file = "multidict-5.1.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b797515be8743b771aa868f83563f789bbd4b236659ba52243b735d80b29ed93"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d5c65bdf4484872c4af3150aeebe101ba560dcfb34488d9a8ff8dbcd21079647"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b47a43177a5e65b771b80db71e7be76c0ba23cc8aa73eeeb089ed5219cdbe27d"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:806068d4f86cb06af37cd65821554f98240a19ce646d3cd24e1c33587f313eb8"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:46dd362c2f045095c920162e9307de5ffd0a1bfbba0a6e990b344366f55a30c1"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:ace010325c787c378afd7f7c1ac66b26313b3344628652eacd149bdd23c68841"}, + {file = "multidict-5.1.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ecc771ab628ea281517e24fd2c52e8f31c41e66652d07599ad8818abaad38cda"}, + {file = "multidict-5.1.0-cp39-cp39-win32.whl", hash = "sha256:fc13a9524bc18b6fb6e0dbec3533ba0496bbed167c56d0aabefd965584557d80"}, + {file = "multidict-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:7df80d07818b385f3129180369079bd6934cf70469f99daaebfac89dca288359"}, + {file = "multidict-5.1.0.tar.gz", hash = "sha256:25b4e5f22d3a37ddf3effc0710ba692cfc792c2b9edfb9c05aefe823256e84d5"}, ] mypy = [ - {file = "mypy-0.770-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:a34b577cdf6313bf24755f7a0e3f3c326d5c1f4fe7422d1d06498eb25ad0c600"}, - {file = "mypy-0.770-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:86c857510a9b7c3104cf4cde1568f4921762c8f9842e987bc03ed4f160925754"}, - {file = "mypy-0.770-cp35-cp35m-win_amd64.whl", hash = "sha256:a8ffcd53cb5dfc131850851cc09f1c44689c2812d0beb954d8138d4f5fc17f65"}, - {file = "mypy-0.770-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:7687f6455ec3ed7649d1ae574136835a4272b65b3ddcf01ab8704ac65616c5ce"}, - {file = "mypy-0.770-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:3beff56b453b6ef94ecb2996bea101a08f1f8a9771d3cbf4988a61e4d9973761"}, - {file = "mypy-0.770-cp36-cp36m-win_amd64.whl", hash = "sha256:15b948e1302682e3682f11f50208b726a246ab4e6c1b39f9264a8796bb416aa2"}, - {file = "mypy-0.770-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:b90928f2d9eb2f33162405f32dde9f6dcead63a0971ca8a1b50eb4ca3e35ceb8"}, - {file = "mypy-0.770-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c56ffe22faa2e51054c5f7a3bc70a370939c2ed4de308c690e7949230c995913"}, - {file = "mypy-0.770-cp37-cp37m-win_amd64.whl", hash = "sha256:8dfb69fbf9f3aeed18afffb15e319ca7f8da9642336348ddd6cab2713ddcf8f9"}, - {file = "mypy-0.770-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:219a3116ecd015f8dca7b5d2c366c973509dfb9a8fc97ef044a36e3da66144a1"}, - {file = "mypy-0.770-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7ec45a70d40ede1ec7ad7f95b3c94c9cf4c186a32f6bacb1795b60abd2f9ef27"}, - {file = "mypy-0.770-cp38-cp38-win_amd64.whl", hash = "sha256:f91c7ae919bbc3f96cd5e5b2e786b2b108343d1d7972ea130f7de27fdd547cf3"}, - {file = "mypy-0.770-py3-none-any.whl", hash = "sha256:3b1fc683fb204c6b4403a1ef23f0b1fac8e4477091585e0c8c54cbdf7d7bb164"}, - {file = "mypy-0.770.tar.gz", hash = "sha256:8a627507ef9b307b46a1fea9513d5c98680ba09591253082b4c48697ba05a4ae"}, + {file = "mypy-0.800-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:e1c84c65ff6d69fb42958ece5b1255394714e0aac4df5ffe151bc4fe19c7600a"}, + {file = "mypy-0.800-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:947126195bfe4709c360e89b40114c6746ae248f04d379dca6f6ab677aa07641"}, + {file = "mypy-0.800-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:b95068a3ce3b50332c40e31a955653be245666a4bc7819d3c8898aa9fb9ea496"}, + {file = "mypy-0.800-cp35-cp35m-win_amd64.whl", hash = "sha256:ca7ad5aed210841f1e77f5f2f7d725b62c78fa77519312042c719ed2ab937876"}, + {file = "mypy-0.800-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e32b7b282c4ed4e378bba8b8dfa08e1cfa6f6574067ef22f86bee5b1039de0c9"}, + {file = "mypy-0.800-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e497a544391f733eca922fdcb326d19e894789cd4ff61d48b4b195776476c5cf"}, + {file = "mypy-0.800-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:5615785d3e2f4f03ab7697983d82c4b98af5c321614f51b8f1034eb9ebe48363"}, + {file = "mypy-0.800-cp36-cp36m-win_amd64.whl", hash = "sha256:2b216eacca0ec0ee124af9429bfd858d5619a0725ee5f88057e6e076f9eb1a7b"}, + {file = "mypy-0.800-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e3b8432f8df19e3c11235c4563a7250666dc9aa7cdda58d21b4177b20256ca9f"}, + {file = "mypy-0.800-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d16c54b0dffb861dc6318a8730952265876d90c5101085a4bc56913e8521ba19"}, + {file = "mypy-0.800-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0d2fc8beb99cd88f2d7e20d69131353053fbecea17904ee6f0348759302c52fa"}, + {file = "mypy-0.800-cp37-cp37m-win_amd64.whl", hash = "sha256:aa9d4901f3ee1a986a3a79fe079ffbf7f999478c281376f48faa31daaa814e86"}, + {file = "mypy-0.800-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:319ee5c248a7c3f94477f92a729b7ab06bf8a6d04447ef3aa8c9ba2aa47c6dcf"}, + {file = "mypy-0.800-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:74f5aa50d0866bc6fb8e213441c41e466c86678c800700b87b012ed11c0a13e0"}, + {file = "mypy-0.800-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:a301da58d566aca05f8f449403c710c50a9860782148332322decf73a603280b"}, + {file = "mypy-0.800-cp38-cp38-win_amd64.whl", hash = "sha256:b9150db14a48a8fa114189bfe49baccdff89da8c6639c2717750c7ae62316738"}, + {file = "mypy-0.800-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5fdf935a46aa20aa937f2478480ebf4be9186e98e49cc3843af9a5795a49a25"}, + {file = "mypy-0.800-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6f8425fecd2ba6007e526209bb985ce7f49ed0d2ac1cc1a44f243380a06a84fb"}, + {file = "mypy-0.800-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:5ff616787122774f510caeb7b980542a7cc2222be3f00837a304ea85cd56e488"}, + {file = "mypy-0.800-cp39-cp39-win_amd64.whl", hash = "sha256:90b6f46dc2181d74f80617deca611925d7e63007cf416397358aa42efb593e07"}, + {file = "mypy-0.800-py3-none-any.whl", hash = "sha256:3e0c159a7853e3521e3f582adb1f3eac66d0b0639d434278e2867af3a8c62653"}, + {file = "mypy-0.800.tar.gz", hash = "sha256:e0202e37756ed09daf4b0ba64ad2c245d357659e014c3f51d8cd0681ba66940a"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -1827,23 +1751,20 @@ nltk = [ {file = "nltk-3.5.zip", hash = "sha256:845365449cd8c5f9731f7cb9f8bd6fd0767553b9d53af9eb1b3abf7700936b35"}, ] nodeenv = [ - {file = "nodeenv-1.4.0-py2.py3-none-any.whl", hash = "sha256:4b0b77afa3ba9b54f4b6396e60b0c83f59eaeb2d63dc3cc7a70f7f4af96c82bc"}, + {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"}, + {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"}, ] packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, + {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"}, + {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"}, ] parso = [ - {file = "parso-0.7.0-py2.py3-none-any.whl", hash = "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0"}, - {file = "parso-0.7.0.tar.gz", hash = "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c"}, -] -pastel = [ - {file = "pastel-0.2.0-py2.py3-none-any.whl", hash = "sha256:18b559dc3ad4ba9b8bd5baebe6503f25f36d21460f021cf27a8d889cb5d17840"}, - {file = "pastel-0.2.0.tar.gz", hash = "sha256:46155fc523bdd4efcd450bbcb3f2b94a6e3b25edc0eb493e081104ad09e1ca36"}, + {file = "parso-0.8.1-py2.py3-none-any.whl", hash = "sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410"}, + {file = "parso-0.8.1.tar.gz", hash = "sha256:8519430ad07087d4c997fda3a7918f7cfa27cb58972a8c89c2a0295a1c940e9e"}, ] pathspec = [ - {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"}, - {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, + {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, + {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, ] pexpect = [ {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, @@ -1853,234 +1774,331 @@ pickleshare = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] -pkginfo = [ - {file = "pkginfo-1.5.0.1-py2.py3-none-any.whl", hash = "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32"}, - {file = "pkginfo-1.5.0.1.tar.gz", hash = "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb"}, -] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] -poetry = [ - {file = "poetry-1.0.9-py2.py3-none-any.whl", hash = "sha256:27e0c9c16f785156a8d8d0e98a73e239c8d2083306c180718825d11d5664aea0"}, - {file = "poetry-1.0.9.tar.gz", hash = "sha256:0a4c56983546964b47cbbe0e1b49fef5662277bbf0673e3e350e1215560377ab"}, -] pre-commit = [ - {file = "pre_commit-2.5.1-py2.py3-none-any.whl", hash = "sha256:c5c8fd4d0e1c363723aaf0a8f9cba0f434c160b48c4028f4bae6d219177945b3"}, - {file = "pre_commit-2.5.1.tar.gz", hash = "sha256:da463cf8f0e257f9af49047ba514f6b90dbd9b4f92f4c8847a3ccd36834874c7"}, + {file = "pre_commit-2.9.3-py2.py3-none-any.whl", hash = "sha256:6c86d977d00ddc8a60d68eec19f51ef212d9462937acf3ea37c7adec32284ac0"}, + {file = "pre_commit-2.9.3.tar.gz", hash = "sha256:ee784c11953e6d8badb97d19bc46b997a3a9eded849881ec587accd8608d74a4"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.5-py3-none-any.whl", hash = "sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04"}, - {file = "prompt_toolkit-3.0.5.tar.gz", hash = "sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8"}, + {file = "prompt_toolkit-3.0.14-py3-none-any.whl", hash = "sha256:c96b30925025a7635471dc083ffb6af0cc67482a00611bd81aeaeeeb7e5a5e12"}, + {file = "prompt_toolkit-3.0.14.tar.gz", hash = "sha256:7e966747c18ececaec785699626b771c1ba8344c8d31759a1915d6b12fad6525"}, ] ptyprocess = [ - {file = "ptyprocess-0.6.0-py2.py3-none-any.whl", hash = "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"}, - {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, ] py = [ - {file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"}, - {file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"}, + {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, + {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pycodestyle = [ {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, ] -pycparser = [ - {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, - {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, -] pydantic = [ - {file = "pydantic-1.5.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2a6904e9f18dea58f76f16b95cba6a2f20b72d787abd84ecd67ebc526e61dce6"}, - {file = "pydantic-1.5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:da8099fca5ee339d5572cfa8af12cf0856ae993406f0b1eb9bb38c8a660e7416"}, - {file = "pydantic-1.5.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:68dece67bff2b3a5cc188258e46b49f676a722304f1c6148ae08e9291e284d98"}, - {file = "pydantic-1.5.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:ab863853cb502480b118187d670f753be65ec144e1654924bec33d63bc8b3ce2"}, - {file = "pydantic-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:2007eb062ed0e57875ce8ead12760a6e44bf5836e6a1a7ea81d71eeecf3ede0f"}, - {file = "pydantic-1.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:20a15a303ce1e4d831b4e79c17a4a29cb6740b12524f5bba3ea363bff65732bc"}, - {file = "pydantic-1.5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:473101121b1bd454c8effc9fe66d54812fdc128184d9015c5aaa0d4e58a6d338"}, - {file = "pydantic-1.5.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:9be755919258d5d168aeffbe913ed6e8bd562e018df7724b68cabdee3371e331"}, - {file = "pydantic-1.5.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:b96ce81c4b5ca62ab81181212edfd057beaa41411cd9700fbcb48a6ba6564b4e"}, - {file = "pydantic-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:93b9f265329d9827f39f0fca68f5d72cc8321881cdc519a1304fa73b9f8a75bd"}, - {file = "pydantic-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2c753d355126ddd1eefeb167fa61c7037ecd30b98e7ebecdc0d1da463b4ea09"}, - {file = "pydantic-1.5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8433dbb87246c0f562af75d00fa80155b74e4f6924b0db6a2078a3cd2f11c6c4"}, - {file = "pydantic-1.5.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:0a1cdf24e567d42dc762d3fed399bd211a13db2e8462af9dfa93b34c41648efb"}, - {file = "pydantic-1.5.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:8be325fc9da897029ee48d1b5e40df817d97fe969f3ac3fd2434ba7e198c55d5"}, - {file = "pydantic-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:3714a4056f5bdbecf3a41e0706ec9b228c9513eee2ad884dc2c568c4dfa540e9"}, - {file = "pydantic-1.5.1-py36.py37.py38-none-any.whl", hash = "sha256:70f27d2f0268f490fe3de0a9b6fca7b7492b8fd6623f9fecd25b221ebee385e3"}, - {file = "pydantic-1.5.1.tar.gz", hash = "sha256:f0018613c7a0d19df3240c2a913849786f21b6539b9f23d85ce4067489dfacfa"}, + {file = "pydantic-1.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c59ea046aea25be14dc22d69c97bee629e6d48d2b2ecb724d7fe8806bf5f61cd"}, + {file = "pydantic-1.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a4143c8d0c456a093387b96e0f5ee941a950992904d88bc816b4f0e72c9a0009"}, + {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:d8df4b9090b595511906fa48deda47af04e7d092318bfb291f4d45dfb6bb2127"}, + {file = "pydantic-1.7.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:514b473d264671a5c672dfb28bdfe1bf1afd390f6b206aa2ec9fed7fc592c48e"}, + {file = "pydantic-1.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:dba5c1f0a3aeea5083e75db9660935da90216f8a81b6d68e67f54e135ed5eb23"}, + {file = "pydantic-1.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59e45f3b694b05a69032a0d603c32d453a23f0de80844fb14d55ab0c6c78ff2f"}, + {file = "pydantic-1.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b24e8a572e4b4c18f614004dda8c9f2c07328cb5b6e314d6e1bbd536cb1a6c1"}, + {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:b2b054d095b6431cdda2f852a6d2f0fdec77686b305c57961b4c5dd6d863bf3c"}, + {file = "pydantic-1.7.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:025bf13ce27990acc059d0c5be46f416fc9b293f45363b3d19855165fee1874f"}, + {file = "pydantic-1.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6e3874aa7e8babd37b40c4504e3a94cc2023696ced5a0500949f3347664ff8e2"}, + {file = "pydantic-1.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e682f6442ebe4e50cb5e1cfde7dda6766fb586631c3e5569f6aa1951fd1a76ef"}, + {file = "pydantic-1.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:185e18134bec5ef43351149fe34fda4758e53d05bb8ea4d5928f0720997b79ef"}, + {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:f5b06f5099e163295b8ff5b1b71132ecf5866cc6e7f586d78d7d3fd6e8084608"}, + {file = "pydantic-1.7.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:24ca47365be2a5a3cc3f4a26dcc755bcdc9f0036f55dcedbd55663662ba145ec"}, + {file = "pydantic-1.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:d1fe3f0df8ac0f3a9792666c69a7cd70530f329036426d06b4f899c025aca74e"}, + {file = "pydantic-1.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f6864844b039805add62ebe8a8c676286340ba0c6d043ae5dea24114b82a319e"}, + {file = "pydantic-1.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:ecb54491f98544c12c66ff3d15e701612fc388161fd455242447083350904730"}, + {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ffd180ebd5dd2a9ac0da4e8b995c9c99e7c74c31f985ba090ee01d681b1c4b95"}, + {file = "pydantic-1.7.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8d72e814c7821125b16f1553124d12faba88e85405b0864328899aceaad7282b"}, + {file = "pydantic-1.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:475f2fa134cf272d6631072554f845d0630907fce053926ff634cc6bc45bf1af"}, + {file = "pydantic-1.7.3-py3-none-any.whl", hash = "sha256:38be427ea01a78206bcaf9a56f835784afcba9e5b88fbdce33bbbfbcd7841229"}, + {file = "pydantic-1.7.3.tar.gz", hash = "sha256:213125b7e9e64713d16d988d10997dabc6a1f73f3991e1ff8e35ebb1409c7dc9"}, ] pyflakes = [ {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, ] pygments = [ - {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"}, - {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"}, -] -pylev = [ - {file = "pylev-1.3.0-py2.py3-none-any.whl", hash = "sha256:1d29a87beb45ebe1e821e7a3b10da2b6b2f4c79b43f482c2df1a1f748a6e114e"}, - {file = "pylev-1.3.0.tar.gz", hash = "sha256:063910098161199b81e453025653ec53556c1be7165a9b7c50be2f4d57eae1c3"}, + {file = "Pygments-2.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"}, + {file = "Pygments-2.7.4.tar.gz", hash = "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"}, ] pymdown-extensions = [ - {file = "pymdown-extensions-6.3.tar.gz", hash = "sha256:cb879686a586b22292899771f5e5bc3382808e92aa938f71b550ecdea709419f"}, - {file = "pymdown_extensions-6.3-py2.py3-none-any.whl", hash = "sha256:66fae2683c7a1dac53184f7de57f51f8dad73f9ead2f453e94e85096cb811335"}, + {file = "pymdown-extensions-8.1.tar.gz", hash = "sha256:565583c5964ac8253896ef4a7f14023075503ca6514d7d470b343290b96fc6da"}, + {file = "pymdown_extensions-8.1-py2.py3-none-any.whl", hash = "sha256:c0b285fdd6e8438895b0c4422847af012f32a487ae083ffafa4c21a151b4983b"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] -pyrsistent = [ - {file = "pyrsistent-0.14.11.tar.gz", hash = "sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"}, -] pytest = [ - {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, - {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, + {file = "pytest-6.2.1-py3-none-any.whl", hash = "sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8"}, + {file = "pytest-6.2.1.tar.gz", hash = "sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"}, ] pytest-asyncio = [ - {file = "pytest-asyncio-0.10.0.tar.gz", hash = "sha256:9fac5100fd716cbecf6ef89233e8590a4ad61d729d1732e0a96b84182df1daaf"}, - {file = "pytest_asyncio-0.10.0-py3-none-any.whl", hash = "sha256:d734718e25cfc32d2bf78d346e99d33724deeba774cc4afdf491530c6184b63b"}, + {file = "pytest-asyncio-0.14.0.tar.gz", hash = "sha256:9882c0c6b24429449f5f969a5158b528f39bde47dc32e85b9f0403965017e700"}, + {file = "pytest_asyncio-0.14.0-py3-none-any.whl", hash = "sha256:2eae1e34f6c68fc0a9dc12d4bea190483843ff4708d24277c41568d6b6044f1d"}, ] pytest-cov = [ - {file = "pytest-cov-2.10.0.tar.gz", hash = "sha256:1a629dc9f48e53512fcbfda6b07de490c374b0c83c55ff7a1720b3fccff0ac87"}, - {file = "pytest_cov-2.10.0-py2.py3-none-any.whl", hash = "sha256:6e6d18092dce6fad667cd7020deed816f858ad3b49d5b5e2b1cc1c97a4dba65c"}, + {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, + {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, ] pytest-html = [ - {file = "pytest-html-2.1.1.tar.gz", hash = "sha256:6a4ac391e105e391208e3eb9bd294a60dd336447fd8e1acddff3a6de7f4e57c5"}, - {file = "pytest_html-2.1.1-py2.py3-none-any.whl", hash = "sha256:9e4817e8be8ddde62e8653c8934d0f296b605da3d2277a052f762c56a8b32df2"}, + {file = "pytest-html-3.1.1.tar.gz", hash = "sha256:3ee1cf319c913d19fe53aeb0bc400e7b0bc2dbeb477553733db1dad12eb75ee3"}, + {file = "pytest_html-3.1.1-py3-none-any.whl", hash = "sha256:b7f82f123936a3f4d2950bc993c2c1ca09ce262c9ae12f9ac763a2401380b455"}, ] pytest-metadata = [ - {file = "pytest-metadata-1.9.0.tar.gz", hash = "sha256:168d203abba8cabb65cf1b5fa675b0ba60dccbf1825d147960876a7e6f7c219c"}, - {file = "pytest_metadata-1.9.0-py2.py3-none-any.whl", hash = "sha256:91d09c0e367e93c63c98461e9960833f465bff53d00ed2f8ccf680205e5053a4"}, + {file = "pytest-metadata-1.11.0.tar.gz", hash = "sha256:71b506d49d34e539cc3cfdb7ce2c5f072bea5c953320002c95968e0238f8ecf1"}, + {file = "pytest_metadata-1.11.0-py2.py3-none-any.whl", hash = "sha256:576055b8336dd4a9006dd2a47615f76f2f8c30ab12b1b1c039d99e834583523f"}, ] pytest-mock = [ - {file = "pytest-mock-2.0.0.tar.gz", hash = "sha256:b35eb281e93aafed138db25c8772b95d3756108b601947f89af503f8c629413f"}, - {file = "pytest_mock-2.0.0-py2.py3-none-any.whl", hash = "sha256:cb67402d87d5f53c579263d37971a164743dc33c159dfb4fb4a86f37c5552307"}, + {file = "pytest-mock-3.5.1.tar.gz", hash = "sha256:a1e2aba6af9560d313c642dae7e00a2a12b022b80301d9d7fc8ec6858e1dd9fc"}, + {file = "pytest_mock-3.5.1-py3-none-any.whl", hash = "sha256:379b391cfad22422ea2e252bdfc008edd08509029bcde3c25b2c0bd741e0424e"}, ] pytest-mypy = [ - {file = "pytest-mypy-0.4.2.tar.gz", hash = "sha256:5a5338cecff17f005b181546a13e282761754b481225df37f33d37f86ac5b304"}, - {file = "pytest_mypy-0.4.2-py3-none-any.whl", hash = "sha256:3b7b56912d55439d5f447cc609f91caac7f74f0f1c89f1379d04f06bac777c32"}, + {file = "pytest-mypy-0.8.0.tar.gz", hash = "sha256:63d418a4fea7d598ac40b659723c00804d16a251d90a5cfbca213eeba5aaf01c"}, + {file = "pytest_mypy-0.8.0-py3-none-any.whl", hash = "sha256:8d2112972c1debf087943f48963a0daf04f3424840aea0cf437cc97053b1b0ef"}, +] +python-socks = [ + {file = "python-socks-1.2.0.tar.gz", hash = "sha256:3054a8afa984a35144198e00fed1144eeae3287cc231ac7db3908d32ab642cd4"}, + {file = "python_socks-1.2.0-py3-none-any.whl", hash = "sha256:26e45b29e18ab7a28ad646e82d3e47a32fe13942b0b1c75ae3f6fe9e5c03efcb"}, ] pytz = [ - {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"}, - {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"}, -] -pywin32-ctypes = [ - {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, - {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, + {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, + {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, ] pyyaml = [ - {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"}, - {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"}, - {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"}, - {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"}, - {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"}, - {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"}, - {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"}, - {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"}, + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] regex = [ - {file = "regex-2020.6.8-cp27-cp27m-win32.whl", hash = "sha256:fbff901c54c22425a5b809b914a3bfaf4b9570eee0e5ce8186ac71eb2025191c"}, - {file = "regex-2020.6.8-cp27-cp27m-win_amd64.whl", hash = "sha256:112e34adf95e45158c597feea65d06a8124898bdeac975c9087fe71b572bd938"}, - {file = "regex-2020.6.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:92d8a043a4241a710c1cf7593f5577fbb832cf6c3a00ff3fc1ff2052aff5dd89"}, - {file = "regex-2020.6.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bae83f2a56ab30d5353b47f9b2a33e4aac4de9401fb582b55c42b132a8ac3868"}, - {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b2ba0f78b3ef375114856cbdaa30559914d081c416b431f2437f83ce4f8b7f2f"}, - {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:95fa7726d073c87141f7bbfb04c284901f8328e2d430eeb71b8ffdd5742a5ded"}, - {file = "regex-2020.6.8-cp36-cp36m-win32.whl", hash = "sha256:e3cdc9423808f7e1bb9c2e0bdb1c9dc37b0607b30d646ff6faf0d4e41ee8fee3"}, - {file = "regex-2020.6.8-cp36-cp36m-win_amd64.whl", hash = "sha256:c78e66a922de1c95a208e4ec02e2e5cf0bb83a36ceececc10a72841e53fbf2bd"}, - {file = "regex-2020.6.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:08997a37b221a3e27d68ffb601e45abfb0093d39ee770e4257bd2f5115e8cb0a"}, - {file = "regex-2020.6.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2f6f211633ee8d3f7706953e9d3edc7ce63a1d6aad0be5dcee1ece127eea13ae"}, - {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:55b4c25cbb3b29f8d5e63aeed27b49fa0f8476b0d4e1b3171d85db891938cc3a"}, - {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89cda1a5d3e33ec9e231ece7307afc101b5217523d55ef4dc7fb2abd6de71ba3"}, - {file = "regex-2020.6.8-cp37-cp37m-win32.whl", hash = "sha256:690f858d9a94d903cf5cada62ce069b5d93b313d7d05456dbcd99420856562d9"}, - {file = "regex-2020.6.8-cp37-cp37m-win_amd64.whl", hash = "sha256:1700419d8a18c26ff396b3b06ace315b5f2a6e780dad387e4c48717a12a22c29"}, - {file = "regex-2020.6.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:654cb773b2792e50151f0e22be0f2b6e1c3a04c5328ff1d9d59c0398d37ef610"}, - {file = "regex-2020.6.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:52e1b4bef02f4040b2fd547357a170fc1146e60ab310cdbdd098db86e929b387"}, - {file = "regex-2020.6.8-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:cf59bbf282b627130f5ba68b7fa3abdb96372b24b66bdf72a4920e8153fc7910"}, - {file = "regex-2020.6.8-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5aaa5928b039ae440d775acea11d01e42ff26e1561c0ffcd3d805750973c6baf"}, - {file = "regex-2020.6.8-cp38-cp38-win32.whl", hash = "sha256:97712e0d0af05febd8ab63d2ef0ab2d0cd9deddf4476f7aa153f76feef4b2754"}, - {file = "regex-2020.6.8-cp38-cp38-win_amd64.whl", hash = "sha256:6ad8663c17db4c5ef438141f99e291c4d4edfeaacc0ce28b5bba2b0bf273d9b5"}, - {file = "regex-2020.6.8.tar.gz", hash = "sha256:e9b64e609d37438f7d6e68c2546d2cb8062f3adb27e6336bc129b51be20773ac"}, + {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"}, + {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"}, + {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"}, + {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"}, + {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"}, + {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"}, + {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"}, + {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"}, + {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"}, + {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"}, + {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"}, + {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"}, + {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"}, + {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"}, + {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"}, + {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"}, + {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"}, ] requests = [ - {file = "requests-2.23.0-py2.py3-none-any.whl", hash = "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee"}, - {file = "requests-2.23.0.tar.gz", hash = "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"}, -] -requests-toolbelt = [ - {file = "requests-toolbelt-0.8.0.tar.gz", hash = "sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5"}, - {file = "requests_toolbelt-0.8.0-py2.py3-none-any.whl", hash = "sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237"}, -] -secretstorage = [ - {file = "SecretStorage-3.1.2-py3-none-any.whl", hash = "sha256:b5ec909dde94d4ae2fa26af7c089036997030f0cf0a5cb372b4cccabd81c143b"}, - {file = "SecretStorage-3.1.2.tar.gz", hash = "sha256:15da8a989b65498e29be338b3b279965f1b8f09b9668bd8010da183024c8bff6"}, -] -shellingham = [ - {file = "shellingham-1.3.2-py2.py3-none-any.whl", hash = "sha256:7f6206ae169dc1a03af8a138681b3f962ae61cc93ade84d0585cca3aaf770044"}, - {file = "shellingham-1.3.2.tar.gz", hash = "sha256:576c1982bea0ba82fb46c36feb951319d7f42214a82634233f58b40d858a751e"}, + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] -toml = [ - {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, - {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, +snowballstemmer = [ + {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, + {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, ] -tomlkit = [ - {file = "tomlkit-0.5.11-py2.py3-none-any.whl", hash = "sha256:4e1bd6c9197d984528f9ff0cc9db667c317d8881288db50db20eeeb0f6b0380b"}, - {file = "tomlkit-0.5.11.tar.gz", hash = "sha256:f044eda25647882e5ef22b43a1688fb6ab12af2fc50e8456cdfc751c873101cf"}, +soupsieve = [ + {file = "soupsieve-2.1-py3-none-any.whl", hash = "sha256:4bb21a6ee4707bf43b61230e80740e71bfe56e55d1f1f50924b087bb2975c851"}, + {file = "soupsieve-2.1.tar.gz", hash = "sha256:6dc52924dc0bc710a5d16794e6b3480b2c7c08b07729505feab2b2c16661ff6e"}, +] +sphinx = [ + {file = "Sphinx-3.4.3-py3-none-any.whl", hash = "sha256:c314c857e7cd47c856d2c5adff514ac2e6495f8b8e0f886a8a37e9305dfea0d8"}, + {file = "Sphinx-3.4.3.tar.gz", hash = "sha256:41cad293f954f7d37f803d97eb184158cfd90f51195131e94875bc07cd08b93c"}, +] +sphinx-autobuild = [ + {file = "sphinx-autobuild-2020.9.1.tar.gz", hash = "sha256:4b184a7db893f2100bbd831991ae54ca89167a2b9ce68faea71eaa9e37716aed"}, + {file = "sphinx_autobuild-2020.9.1-py3-none-any.whl", hash = "sha256:df5c72cb8b8fc9b31279c4619780c4e95029be6de569ff60a8bb2e99d20f63dd"}, +] +sphinx-copybutton = [ + {file = "sphinx-copybutton-0.3.1.tar.gz", hash = "sha256:0e0461df394515284e3907e3f418a0c60ef6ab6c9a27a800c8552772d0a402a2"}, + {file = "sphinx_copybutton-0.3.1-py3-none-any.whl", hash = "sha256:5125c718e763596e6e52d92e15ee0d6f4800ad3817939be6dee51218870b3e3d"}, +] +sphinx-intl = [ + {file = "sphinx-intl-2.0.1.tar.gz", hash = "sha256:b25a6ec169347909e8d983eefe2d8adecb3edc2f27760db79b965c69950638b4"}, + {file = "sphinx_intl-2.0.1-py3.8.egg", hash = "sha256:2ff97cba0e4e43249e339a3c29dd2f5b63c25ce794050aabca320ad95f5c5b55"}, +] +sphinx-prompt = [ + {file = "sphinx-prompt-1.3.0.tar.gz", hash = "sha256:859f49774865ec06d0e24fa6d80dbe2cbc2922cf82c1a6869ff39ea1f14da9d1"}, + {file = "sphinx_prompt-1.3.0-py3-none-any.whl", hash = "sha256:f3d2389c55dadc790e10a936bf889706671538af442078d3f9dea634adf81d0b"}, +] +sphinx-substitution-extensions = [ + {file = "Sphinx Substitution Extensions-2020.9.30.0.tar.gz", hash = "sha256:578afc04eb4f701d9a922f8b75f678d3a1a897fa7a172a9226b92f17553f177a"}, + {file = "Sphinx_Substitution_Extensions-2020.9.30.0-py3-none-any.whl", hash = "sha256:02e543fb64c82a168031d6f64af5d11bdf91d3f06480ffe399c11be87453729f"}, +] +sphinxcontrib-applehelp = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] +sphinxcontrib-devhelp = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] +sphinxcontrib-htmlhelp = [ + {file = "sphinxcontrib-htmlhelp-1.0.3.tar.gz", hash = "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"}, + {file = "sphinxcontrib_htmlhelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f"}, +] +sphinxcontrib-jsmath = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] +sphinxcontrib-qthelp = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] +sphinxcontrib-serializinghtml = [ + {file = "sphinxcontrib-serializinghtml-1.1.4.tar.gz", hash = "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc"}, + {file = "sphinxcontrib_serializinghtml-1.1.4-py2.py3-none-any.whl", hash = "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"}, +] +toml = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tornado = [ - {file = "tornado-6.0.4-cp35-cp35m-win32.whl", hash = "sha256:5217e601700f24e966ddab689f90b7ea4bd91ff3357c3600fa1045e26d68e55d"}, - {file = "tornado-6.0.4-cp35-cp35m-win_amd64.whl", hash = "sha256:c98232a3ac391f5faea6821b53db8db461157baa788f5d6222a193e9456e1740"}, - {file = "tornado-6.0.4-cp36-cp36m-win32.whl", hash = "sha256:5f6a07e62e799be5d2330e68d808c8ac41d4a259b9cea61da4101b83cb5dc673"}, - {file = "tornado-6.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c952975c8ba74f546ae6de2e226ab3cc3cc11ae47baf607459a6728585bb542a"}, - {file = "tornado-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:2c027eb2a393d964b22b5c154d1a23a5f8727db6fda837118a776b29e2b8ebc6"}, - {file = "tornado-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:5618f72e947533832cbc3dec54e1dffc1747a5cb17d1fd91577ed14fa0dc081b"}, - {file = "tornado-6.0.4-cp38-cp38-win32.whl", hash = "sha256:22aed82c2ea340c3771e3babc5ef220272f6fd06b5108a53b4976d0d722bcd52"}, - {file = "tornado-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:c58d56003daf1b616336781b26d184023ea4af13ae143d9dda65e31e534940b9"}, - {file = "tornado-6.0.4.tar.gz", hash = "sha256:0fe2d45ba43b00a41cd73f8be321a44936dc1aba233dee979f17a042b83eb6dc"}, + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, ] tqdm = [ - {file = "tqdm-4.46.1-py2.py3-none-any.whl", hash = "sha256:07c06493f1403c1380b630ae3dcbe5ae62abcf369a93bbc052502279f189ab8c"}, - {file = "tqdm-4.46.1.tar.gz", hash = "sha256:cd140979c2bebd2311dfb14781d8f19bd5a9debb92dcab9f6ef899c987fcf71f"}, + {file = "tqdm-4.56.0-py2.py3-none-any.whl", hash = "sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a"}, + {file = "tqdm-4.56.0.tar.gz", hash = "sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65"}, ] traitlets = [ - {file = "traitlets-4.3.3-py2.py3-none-any.whl", hash = "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44"}, - {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, + {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"}, + {file = "traitlets-5.0.5.tar.gz", hash = "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396"}, ] typed-ast = [ - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, - {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, - {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, - {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, - {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, - {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, - {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487"}, + {file = "typed_ast-1.4.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400"}, + {file = "typed_ast-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606"}, + {file = "typed_ast-1.4.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc"}, + {file = "typed_ast-1.4.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151"}, + {file = "typed_ast-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3"}, + {file = "typed_ast-1.4.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581"}, + {file = "typed_ast-1.4.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd"}, + {file = "typed_ast-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496"}, + {file = "typed_ast-1.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea"}, + {file = "typed_ast-1.4.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787"}, + {file = "typed_ast-1.4.2-cp38-cp38-win32.whl", hash = "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2"}, + {file = "typed_ast-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937"}, + {file = "typed_ast-1.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166"}, + {file = "typed_ast-1.4.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d"}, + {file = "typed_ast-1.4.2-cp39-cp39-win32.whl", hash = "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b"}, + {file = "typed_ast-1.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440"}, + {file = "typed_ast-1.4.2.tar.gz", hash = "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, - {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, - {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, + {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, + {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, + {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, ] urllib3 = [ - {file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"}, - {file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"}, + {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"}, + {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"}, ] uvloop = [ {file = "uvloop-0.14.0-cp35-cp35m-macosx_10_11_x86_64.whl", hash = "sha256:08b109f0213af392150e2fe6f81d33261bb5ce968a288eb698aad4f46eb711bd"}, @@ -2094,37 +2112,53 @@ uvloop = [ {file = "uvloop-0.14.0.tar.gz", hash = "sha256:123ac9c0c7dd71464f58f1b4ee0bbd81285d96cdda8bc3519281b8973e3a461e"}, ] virtualenv = [ - {file = "virtualenv-20.0.23-py2.py3-none-any.whl", hash = "sha256:ccfb8e1e05a1174f7bd4c163700277ba730496094fe1a58bea9d4ac140a207c8"}, - {file = "virtualenv-20.0.23.tar.gz", hash = "sha256:5102fbf1ec57e80671ef40ed98a84e980a71194cedf30c87c2b25c3a9e0b0107"}, + {file = "virtualenv-20.4.0-py2.py3-none-any.whl", hash = "sha256:227a8fed626f2f20a6cdb0870054989f82dd27b2560a911935ba905a2a5e0034"}, + {file = "virtualenv-20.4.0.tar.gz", hash = "sha256:219ee956e38b08e32d5639289aaa5bd190cfbe7dafcb8fa65407fca08e808f9c"}, ] wcwidth = [ - {file = "wcwidth-0.2.4-py2.py3-none-any.whl", hash = "sha256:79375666b9954d4a1a10739315816324c3e73110af9d0e102d906fdb0aec009f"}, - {file = "wcwidth-0.2.4.tar.gz", hash = "sha256:8c6b5b6ee1360b842645f336d9e5d68c55817c26d3050f46b235ef2bc650e48f"}, -] -webencodings = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, ] yarl = [ - {file = "yarl-1.4.2-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:3ce3d4f7c6b69c4e4f0704b32eca8123b9c58ae91af740481aa57d7857b5e41b"}, - {file = "yarl-1.4.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:a4844ebb2be14768f7994f2017f70aca39d658a96c786211be5ddbe1c68794c1"}, - {file = "yarl-1.4.2-cp35-cp35m-win32.whl", hash = "sha256:d8cdee92bc930d8b09d8bd2043cedd544d9c8bd7436a77678dd602467a993080"}, - {file = "yarl-1.4.2-cp35-cp35m-win_amd64.whl", hash = "sha256:c2b509ac3d4b988ae8769901c66345425e361d518aecbe4acbfc2567e416626a"}, - {file = "yarl-1.4.2-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:308b98b0c8cd1dfef1a0311dc5e38ae8f9b58349226aa0533f15a16717ad702f"}, - {file = "yarl-1.4.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:944494be42fa630134bf907714d40207e646fd5a94423c90d5b514f7b0713fea"}, - {file = "yarl-1.4.2-cp36-cp36m-win32.whl", hash = "sha256:5b10eb0e7f044cf0b035112446b26a3a2946bca9d7d7edb5e54a2ad2f6652abb"}, - {file = "yarl-1.4.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a161de7e50224e8e3de6e184707476b5a989037dcb24292b391a3d66ff158e70"}, - {file = "yarl-1.4.2-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:26d7c90cb04dee1665282a5d1a998defc1a9e012fdca0f33396f81508f49696d"}, - {file = "yarl-1.4.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0c2ab325d33f1b824734b3ef51d4d54a54e0e7a23d13b86974507602334c2cce"}, - {file = "yarl-1.4.2-cp37-cp37m-win32.whl", hash = "sha256:e15199cdb423316e15f108f51249e44eb156ae5dba232cb73be555324a1d49c2"}, - {file = "yarl-1.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:2098a4b4b9d75ee352807a95cdf5f10180db903bc5b7270715c6bbe2551f64ce"}, - {file = "yarl-1.4.2-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c9959d49a77b0e07559e579f38b2f3711c2b8716b8410b320bf9713013215a1b"}, - {file = "yarl-1.4.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:25e66e5e2007c7a39541ca13b559cd8ebc2ad8fe00ea94a2aad28a9b1e44e5ae"}, - {file = "yarl-1.4.2-cp38-cp38-win32.whl", hash = "sha256:6faa19d3824c21bcbfdfce5171e193c8b4ddafdf0ac3f129ccf0cdfcb083e462"}, - {file = "yarl-1.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:0ca2f395591bbd85ddd50a82eb1fde9c1066fafe888c5c7cc1d810cf03fd3cc6"}, - {file = "yarl-1.4.2.tar.gz", hash = "sha256:58cd9c469eced558cd81aa3f484b2924e8897049e06889e8ff2510435b7ef74b"}, + {file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:bafb450deef6861815ed579c7a6113a879a6ef58aed4c3a4be54400ae8871478"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:547f7665ad50fa8563150ed079f8e805e63dd85def6674c97efd78eed6c224a6"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:63f90b20ca654b3ecc7a8d62c03ffa46999595f0167d6450fa8383bab252987e"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_ppc64le.whl", hash = "sha256:97b5bdc450d63c3ba30a127d018b866ea94e65655efaf889ebeabc20f7d12406"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:d8d07d102f17b68966e2de0e07bfd6e139c7c02ef06d3a0f8d2f0f055e13bb76"}, + {file = "yarl-1.6.3-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:15263c3b0b47968c1d90daa89f21fcc889bb4b1aac5555580d74565de6836366"}, + {file = "yarl-1.6.3-cp36-cp36m-win32.whl", hash = "sha256:b5dfc9a40c198334f4f3f55880ecf910adebdcb2a0b9a9c23c9345faa9185721"}, + {file = "yarl-1.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:b2e9a456c121e26d13c29251f8267541bd75e6a1ccf9e859179701c36a078643"}, + {file = "yarl-1.6.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ce3beb46a72d9f2190f9e1027886bfc513702d748047b548b05dab7dfb584d2e"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2ce4c621d21326a4a5500c25031e102af589edb50c09b321049e388b3934eec3"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d26608cf178efb8faa5ff0f2d2e77c208f471c5a3709e577a7b3fd0445703ac8"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:4c5bcfc3ed226bf6419f7a33982fb4b8ec2e45785a0561eb99274ebbf09fdd6a"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:4736eaee5626db8d9cda9eb5282028cc834e2aeb194e0d8b50217d707e98bb5c"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:68dc568889b1c13f1e4745c96b931cc94fdd0defe92a72c2b8ce01091b22e35f"}, + {file = "yarl-1.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:7356644cbed76119d0b6bd32ffba704d30d747e0c217109d7979a7bc36c4d970"}, + {file = "yarl-1.6.3-cp37-cp37m-win32.whl", hash = "sha256:00d7ad91b6583602eb9c1d085a2cf281ada267e9a197e8b7cae487dadbfa293e"}, + {file = "yarl-1.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:69ee97c71fee1f63d04c945f56d5d726483c4762845400a6795a3b75d56b6c50"}, + {file = "yarl-1.6.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e46fba844f4895b36f4c398c5af062a9808d1f26b2999c58909517384d5deda2"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:31ede6e8c4329fb81c86706ba8f6bf661a924b53ba191b27aa5fcee5714d18ec"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fcbb48a93e8699eae920f8d92f7160c03567b421bc17362a9ffbbd706a816f71"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:72a660bdd24497e3e84f5519e57a9ee9220b6f3ac4d45056961bf22838ce20cc"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:324ba3d3c6fee56e2e0b0d09bf5c73824b9f08234339d2b788af65e60040c959"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:e6b5460dc5ad42ad2b36cca524491dfcaffbfd9c8df50508bddc354e787b8dc2"}, + {file = "yarl-1.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:6d6283d8e0631b617edf0fd726353cb76630b83a089a40933043894e7f6721e2"}, + {file = "yarl-1.6.3-cp38-cp38-win32.whl", hash = "sha256:9ede61b0854e267fd565e7527e2f2eb3ef8858b301319be0604177690e1a3896"}, + {file = "yarl-1.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:f0b059678fd549c66b89bed03efcabb009075bd131c248ecdf087bdb6faba24a"}, + {file = "yarl-1.6.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:329412812ecfc94a57cd37c9d547579510a9e83c516bc069470db5f75684629e"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c49ff66d479d38ab863c50f7bb27dee97c6627c5fe60697de15529da9c3de724"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f040bcc6725c821a4c0665f3aa96a4d0805a7aaf2caf266d256b8ed71b9f041c"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:d5c32c82990e4ac4d8150fd7652b972216b204de4e83a122546dce571c1bdf25"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:d597767fcd2c3dc49d6eea360c458b65643d1e4dbed91361cf5e36e53c1f8c96"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:8aa3decd5e0e852dc68335abf5478a518b41bf2ab2f330fe44916399efedfae0"}, + {file = "yarl-1.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:73494d5b71099ae8cb8754f1df131c11d433b387efab7b51849e7e1e851f07a4"}, + {file = "yarl-1.6.3-cp39-cp39-win32.whl", hash = "sha256:5b883e458058f8d6099e4420f0cc2567989032b5f34b271c0827de9f1079a424"}, + {file = "yarl-1.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:4953fb0b4fdb7e08b2f3b3be80a00d28c5c8a2056bb066169de00e6501b986b6"}, + {file = "yarl-1.6.3.tar.gz", hash = "sha256:8a9066529240171b68893d60dca86a763eae2139dd42f42106b03cf4b426bf10"}, ] zipp = [ - {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"}, - {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"}, + {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"}, + {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"}, ] diff --git a/pyproject.toml b/pyproject.toml index 22efdce3..7dfa0d1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "aiogram" -version = "3.0.0-alpha.5" +version = "3.0.0-alpha.6" description = "Modern and fully asynchronous framework for Telegram Bot API" authors = ["Alex Root Junior "] license = "MIT" @@ -9,26 +9,26 @@ homepage = "https://aiogram.dev/" documentation = "https://docs.aiogram.dev/" repository = "https://github.com/aiogram/aiogram/" keywords = [ - "telegram", - "bot", - "api", - "framework", - "wrapper", - "asyncio", + "telegram", + "bot", + "api", + "framework", + "wrapper", + "asyncio", ] classifiers = [ - "Development Status :: 3 - Alpha", - "Environment :: Console", - "Framework :: AsyncIO", - "Intended Audience :: Developers", - "Intended Audience :: System Administrators", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Topic :: Software Development :: Libraries :: Application Frameworks", - "Framework :: AsyncIO", - "Typing :: Typed", + "Development Status :: 3 - Alpha", + "Environment :: Console", + "Framework :: AsyncIO", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Topic :: Software Development :: Libraries :: Application Frameworks", + "Framework :: AsyncIO", + "Typing :: Typed", ] [tool.poetry.dependencies] @@ -36,43 +36,68 @@ python = "^3.7" aiohttp = "^3.6" pydantic = "^1.5" Babel = "^2.7" -aiofiles = "^0.4.0" -uvloop = {version = "^0.14.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'", optional = true} +aiofiles = "^0.6.0" +uvloop = { version = "^0.14.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'", optional = true } async_lru = "^1.0" -aiohttp-socks = {version = "^0.3.8", optional = true} +aiohttp-socks = { version = "^0.5.5", optional = true } +typing-extensions = { version = "^3.7.4", python = "<3.8" } +magic-filter = "^0.1.2" +sphinx = { version = "^3.1.0", optional = true } +sphinx-intl = { version = "^2.0.1", optional = true } +sphinx-autobuild = { version = "^2020.9.1", optional = true } +sphinx-copybutton = { version = "^0.3.1", optional = true } +furo = { version = "^2020.11.15-beta.17", optional = true } +sphinx-prompt = { version = "^1.3.0", optional = true } +Sphinx-Substitution-Extensions = { version = "^2020.9.30", optional = true } [tool.poetry.dev-dependencies] -uvloop = {version = "^0.14.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'"} -pytest = "^5.3" -pytest-html = "^2.0" -pytest-asyncio = "^0.10.0" -pytest-mypy = "^0.4.2" -pytest-mock = "^2.0" +uvloop = { version = "^0.14.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'" } +pytest = "^6.1" +pytest-html = "^3.1" +pytest-asyncio = "^0.14.0" +pytest-mypy = "^0.8" +pytest-mock = "^3.3" pytest-cov = "^2.8" -aresponses = "^1.1" -asynctest = {version = "^0.13.0", python = "<3.8"} -black = {version = "^19.0", allow-prereleases = true} -isort = "^4.3" +aresponses = "^2.0" +asynctest = { version = "^0.13.0", python = "<3.8" } +isort = "^5.6" flake8 = "^3.7" flake8-html = "^0.4.0" -mypy = "^0.770" +mypy = "^0.800" mkdocs = "^1.0" -mkdocs-material = "^4.6" +mkdocs-material = "^6.1" mkautodoc = "^0.1.0" pygments = "^2.4" -pymdown-extensions = "^6.1" +pymdown-extensions = "^8.0" lxml = "^4.4" ipython = "^7.10" -markdown-include = "^0.5.1" -aiohttp-socks = "^0.3.4" +markdown-include = "^0.6" +aiohttp-socks = "^0.5" pre-commit = "^2.3.0" packaging = "^20.3" typing-extensions = "^3.7.4" -poetry = "^1.0.5" +sphinx = "^3.1.0" +sphinx-intl = "^2.0.1" +sphinx-autobuild = "^2020.9.1" +sphinx-copybutton = "^0.3.1" +furo = "^2020.11.15-beta.17" +sphinx-prompt = "^1.3.0" +Sphinx-Substitution-Extensions = "^2020.9.30" +black = "^20.8b1" [tool.poetry.extras] fast = ["uvloop"] proxy = ["aiohttp-socks"] +docs = [ + "sphinx", + "sphinx-intl", + "sphinx-autobuild", + "sphinx-copybutton", + "furo", + "black", + "sphinx-prompt", + "Sphinx-Substitution-Extensions", +] [tool.black] line-length = 99 @@ -95,8 +120,18 @@ include_trailing_comma = true force_grid_wrap = 0 use_parentheses = true line_length = 99 -known_third_party = ["aiofiles", "aiohttp", "aiohttp_socks", "aresponses", "async_lru", "packaging", "pkg_resources", "pydantic", "pytest"] +known_third_party = [ + "aiofiles", + "aiohttp", + "aiohttp_socks", + "aresponses", + "async_lru", + "packaging", + "pkg_resources", + "pydantic", + "pytest" +] [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/deprecated.py b/tests/deprecated.py index 53d65cf7..f030e515 100644 --- a/tests/deprecated.py +++ b/tests/deprecated.py @@ -9,7 +9,9 @@ import aiogram @contextmanager def check_deprecated( - max_version: str, exception: Type[Exception], warning: Type[Warning] = DeprecationWarning, + max_version: str, + exception: Type[Exception], + warning: Type[Warning] = DeprecationWarning, ) -> None: """ Should be used for modules that are being deprecated or already removed from aiogram diff --git a/tests/mocked_bot.py b/tests/mocked_bot.py index f0b1ead6..aee31398 100644 --- a/tests/mocked_bot.py +++ b/tests/mocked_bot.py @@ -2,10 +2,10 @@ from collections import deque from typing import TYPE_CHECKING, AsyncGenerator, Deque, Optional, Type from aiogram import Bot -from aiogram.api.client.session.base import BaseSession -from aiogram.api.methods import TelegramMethod -from aiogram.api.methods.base import Request, Response, T -from aiogram.api.types import UNSET +from aiogram.client.session.base import BaseSession +from aiogram.methods import TelegramMethod +from aiogram.methods.base import Request, Response, T +from aiogram.types import UNSET class MockedSession(BaseSession): diff --git a/tests/test_api/test_client/test_api_server.py b/tests/test_api/test_client/test_api_server.py index 74b6a785..6faf0c0f 100644 --- a/tests/test_api/test_client/test_api_server.py +++ b/tests/test_api/test_client/test_api_server.py @@ -1,4 +1,4 @@ -from aiogram.api.client.telegram import PRODUCTION +from aiogram.client.telegram import PRODUCTION, TelegramAPIServer class TestAPIServer: @@ -9,3 +9,13 @@ class TestAPIServer: def test_file_url(self): file_url = PRODUCTION.file_url(token="42:TEST", path="path") assert file_url == "https://api.telegram.org/file/bot42:TEST/path" + + def test_from_base(self): + local_server = TelegramAPIServer.from_base("http://localhost:8081", is_local=True) + + method_url = local_server.api_url("42:TEST", method="apiMethod") + file_url = local_server.file_url(token="42:TEST", path="path") + + assert method_url == "http://localhost:8081/bot42:TEST/apiMethod" + assert file_url == "http://localhost:8081/file/bot42:TEST/path" + assert local_server.is_local diff --git a/tests/test_api/test_client/test_bot.py b/tests/test_api/test_client/test_bot.py index b9dff9e7..a3c11bd9 100644 --- a/tests/test_api/test_client/test_bot.py +++ b/tests/test_api/test_client/test_bot.py @@ -5,15 +5,16 @@ import pytest from aresponses import ResponsesMockServer from aiogram import Bot -from aiogram.api.client.session.aiohttp import AiohttpSession -from aiogram.api.methods import GetFile, GetMe -from aiogram.api.types import File, PhotoSize +from aiogram.client.session.aiohttp import AiohttpSession +from aiogram.methods import GetFile, GetMe +from aiogram.types import File, PhotoSize from tests.mocked_bot import MockedBot try: from asynctest import CoroutineMock, patch except ImportError: - from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore + from unittest.mock import AsyncMock as CoroutineMock # type: ignore + from unittest.mock import patch class TestBot: @@ -38,7 +39,7 @@ class TestBot: method = GetMe() with patch( - "aiogram.api.client.session.aiohttp.AiohttpSession.make_request", + "aiogram.client.session.aiohttp.AiohttpSession.make_request", new_callable=CoroutineMock, ) as mocked_make_request: await bot(method) @@ -51,16 +52,16 @@ class TestBot: await session.create_session() with patch( - "aiogram.api.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock + "aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock ) as mocked_close: - await bot.close() + await bot.session.close() mocked_close.assert_awaited() @pytest.mark.asyncio @pytest.mark.parametrize("close", [True, False]) async def test_context_manager(self, close: bool): with patch( - "aiogram.api.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock + "aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock ) as mocked_close: async with Bot("42:TEST", session=AiohttpSession()).context(auto_close=close) as bot: assert isinstance(bot, Bot) diff --git a/tests/test_api/test_client/test_session/test_aiohttp_session.py b/tests/test_api/test_client/test_session/test_aiohttp_session.py index 4e982c1b..5649628b 100644 --- a/tests/test_api/test_client/test_session/test_aiohttp_session.py +++ b/tests/test_api/test_client/test_session/test_aiohttp_session.py @@ -1,20 +1,21 @@ from typing import AsyncContextManager, AsyncGenerator -import aiohttp import aiohttp_socks import pytest from aresponses import ResponsesMockServer from aiogram import Bot -from aiogram.api.client.session.aiohttp import AiohttpSession -from aiogram.api.methods import Request, TelegramMethod -from aiogram.api.types import InputFile, UNSET +from aiogram.client.session import aiohttp +from aiogram.client.session.aiohttp import AiohttpSession +from aiogram.methods import Request, TelegramMethod +from aiogram.types import InputFile, UNSET from tests.mocked_bot import MockedBot try: from asynctest import CoroutineMock, patch except ImportError: - from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore + from unittest.mock import AsyncMock as CoroutineMock # type: ignore + from unittest.mock import patch class BareInputFile(InputFile): @@ -172,7 +173,7 @@ class TestAiohttpSession: call = TestMethod() with patch( - "aiogram.api.client.session.base.BaseSession.raise_for_status" + "aiogram.client.session.base.BaseSession.raise_for_status" ) as patched_raise_for_status: result = await session.make_request(bot, call) assert isinstance(result, int) @@ -206,12 +207,12 @@ class TestAiohttpSession: assert isinstance(session, AsyncContextManager) with patch( - "aiogram.api.client.session.aiohttp.AiohttpSession.create_session", + "aiogram.client.session.aiohttp.AiohttpSession.create_session", new_callable=CoroutineMock, ) as mocked_create_session, patch( - "aiogram.api.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock + "aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock ) as mocked_close: async with session as ctx: assert session == ctx - mocked_close.awaited_once() - mocked_create_session.awaited_once() + mocked_close.assert_awaited_once() + mocked_create_session.assert_awaited_once() diff --git a/tests/test_api/test_client/test_session/test_base_session.py b/tests/test_api/test_client/test_session/test_base_session.py index 42dfedf7..3ed8a126 100644 --- a/tests/test_api/test_client/test_session/test_base_session.py +++ b/tests/test_api/test_client/test_session/test_base_session.py @@ -4,15 +4,16 @@ from typing import AsyncContextManager, AsyncGenerator, Optional import pytest -from aiogram.api.client.session.base import BaseSession, T -from aiogram.api.client.telegram import PRODUCTION, TelegramAPIServer -from aiogram.api.methods import GetMe, Response, TelegramMethod -from aiogram.api.types import UNSET +from aiogram.client.session.base import BaseSession, T +from aiogram.client.telegram import PRODUCTION, TelegramAPIServer +from aiogram.methods import GetMe, Response, TelegramMethod +from aiogram.types import UNSET try: from asynctest import CoroutineMock, patch except ImportError: - from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore + from unittest.mock import AsyncMock as CoroutineMock # type: ignore + from unittest.mock import patch class CustomSession(BaseSession): @@ -169,4 +170,4 @@ class TestBaseSession: ) as mocked_close: async with session as ctx: assert session == ctx - mocked_close.awaited_once() + mocked_close.assert_awaited_once() diff --git a/tests/test_api/test_methods/test_add_sticker_to_set.py b/tests/test_api/test_methods/test_add_sticker_to_set.py index 8c91423e..dae220cc 100644 --- a/tests/test_api/test_methods/test_add_sticker_to_set.py +++ b/tests/test_api/test_methods/test_add_sticker_to_set.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import AddStickerToSet, Request +from aiogram.methods import AddStickerToSet, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_answer_callback_query.py b/tests/test_api/test_methods/test_answer_callback_query.py index d2617b38..c3749455 100644 --- a/tests/test_api/test_methods/test_answer_callback_query.py +++ b/tests/test_api/test_methods/test_answer_callback_query.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import AnswerCallbackQuery, Request +from aiogram.methods import AnswerCallbackQuery, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_answer_inline_query.py b/tests/test_api/test_methods/test_answer_inline_query.py index 950502c9..a843a59d 100644 --- a/tests/test_api/test_methods/test_answer_inline_query.py +++ b/tests/test_api/test_methods/test_answer_inline_query.py @@ -1,8 +1,8 @@ import pytest from aiogram import Bot -from aiogram.api.methods import AnswerInlineQuery, Request -from aiogram.api.types import InlineQueryResult, InlineQueryResultPhoto +from aiogram.methods import AnswerInlineQuery, Request +from aiogram.types import InlineQueryResult, InlineQueryResultPhoto from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_answer_pre_checkout_query.py b/tests/test_api/test_methods/test_answer_pre_checkout_query.py index 0d49e8e5..b1afa384 100644 --- a/tests/test_api/test_methods/test_answer_pre_checkout_query.py +++ b/tests/test_api/test_methods/test_answer_pre_checkout_query.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import AnswerPreCheckoutQuery, Request +from aiogram.methods import AnswerPreCheckoutQuery, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_answer_shipping_query.py b/tests/test_api/test_methods/test_answer_shipping_query.py index 9a0f1a9e..bba639a3 100644 --- a/tests/test_api/test_methods/test_answer_shipping_query.py +++ b/tests/test_api/test_methods/test_answer_shipping_query.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import AnswerShippingQuery, Request +from aiogram.methods import AnswerShippingQuery, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_base.py b/tests/test_api/test_methods/test_base.py index 3fd46078..079721a3 100644 --- a/tests/test_api/test_methods/test_base.py +++ b/tests/test_api/test_methods/test_base.py @@ -3,7 +3,7 @@ from typing import Dict, Optional import pytest from aiogram import Bot -from aiogram.api.methods.base import prepare_parse_mode +from aiogram.methods.base import prepare_parse_mode from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_close.py b/tests/test_api/test_methods/test_close.py new file mode 100644 index 00000000..c497520e --- /dev/null +++ b/tests/test_api/test_methods/test_close.py @@ -0,0 +1,26 @@ +import pytest + +from aiogram.methods import Close, Request +from tests.mocked_bot import MockedBot + + +class TestClose: + @pytest.mark.asyncio + async def test_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(Close, ok=True, result=True) + + response: bool = await Close() + request: Request = bot.get_request() + assert request.method == "close" + # assert request.data == {} + assert response == prepare_result.result + + @pytest.mark.asyncio + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(Close, ok=True, result=True) + + response: bool = await bot.close() + request: Request = bot.get_request() + assert request.method == "close" + # assert request.data == {} + assert response == prepare_result.result diff --git a/tests/test_api/test_methods/test_copy_message.py b/tests/test_api/test_methods/test_copy_message.py new file mode 100644 index 00000000..d380f24a --- /dev/null +++ b/tests/test_api/test_methods/test_copy_message.py @@ -0,0 +1,35 @@ +import pytest + +from aiogram.methods import CopyMessage, Request +from aiogram.types import MessageId +from tests.mocked_bot import MockedBot + + +class TestCopyMessage: + @pytest.mark.asyncio + async def test_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(CopyMessage, ok=True, result=MessageId(message_id=42)) + + response: MessageId = await CopyMessage( + chat_id=42, + from_chat_id=42, + message_id=42, + ) + request: Request = bot.get_request() + assert request.method == "copyMessage" + # assert request.data == {} + assert response == prepare_result.result + + @pytest.mark.asyncio + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(CopyMessage, ok=True, result=MessageId(message_id=42)) + + response: MessageId = await bot.copy_message( + chat_id=42, + from_chat_id=42, + message_id=42, + ) + request: Request = bot.get_request() + assert request.method == "copyMessage" + # assert request.data == {} + assert response == prepare_result.result diff --git a/tests/test_api/test_methods/test_create_new_sticker_set.py b/tests/test_api/test_methods/test_create_new_sticker_set.py index 17b8b049..218bff0d 100644 --- a/tests/test_api/test_methods/test_create_new_sticker_set.py +++ b/tests/test_api/test_methods/test_create_new_sticker_set.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import CreateNewStickerSet, Request +from aiogram.methods import CreateNewStickerSet, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_delete_chat_photo.py b/tests/test_api/test_methods/test_delete_chat_photo.py index 293a7b0e..c807bd71 100644 --- a/tests/test_api/test_methods/test_delete_chat_photo.py +++ b/tests/test_api/test_methods/test_delete_chat_photo.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import DeleteChatPhoto, Request +from aiogram.methods import DeleteChatPhoto, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_delete_chat_sticker_set.py b/tests/test_api/test_methods/test_delete_chat_sticker_set.py index 618903ec..80d0967d 100644 --- a/tests/test_api/test_methods/test_delete_chat_sticker_set.py +++ b/tests/test_api/test_methods/test_delete_chat_sticker_set.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import DeleteChatStickerSet, Request +from aiogram.methods import DeleteChatStickerSet, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_delete_message.py b/tests/test_api/test_methods/test_delete_message.py index 0b510abd..87b8efad 100644 --- a/tests/test_api/test_methods/test_delete_message.py +++ b/tests/test_api/test_methods/test_delete_message.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import DeleteMessage, Request +from aiogram.methods import DeleteMessage, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_delete_sticker_from_set.py b/tests/test_api/test_methods/test_delete_sticker_from_set.py index 8b344225..350e0b3e 100644 --- a/tests/test_api/test_methods/test_delete_sticker_from_set.py +++ b/tests/test_api/test_methods/test_delete_sticker_from_set.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import DeleteStickerFromSet, Request +from aiogram.methods import DeleteStickerFromSet, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_delete_webhook.py b/tests/test_api/test_methods/test_delete_webhook.py index 22c4d5c9..91ecb809 100644 --- a/tests/test_api/test_methods/test_delete_webhook.py +++ b/tests/test_api/test_methods/test_delete_webhook.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import DeleteWebhook, Request +from aiogram.methods import DeleteWebhook, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_edit_message_caption.py b/tests/test_api/test_methods/test_edit_message_caption.py index 50fcf838..62803d43 100644 --- a/tests/test_api/test_methods/test_edit_message_caption.py +++ b/tests/test_api/test_methods/test_edit_message_caption.py @@ -3,8 +3,8 @@ from typing import Union import pytest -from aiogram.api.methods import EditMessageCaption, Request -from aiogram.api.types import Chat, Message +from aiogram.methods import EditMessageCaption, Request +from aiogram.types import Chat, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_edit_message_live_location.py b/tests/test_api/test_methods/test_edit_message_live_location.py index 96e566ac..db04fb19 100644 --- a/tests/test_api/test_methods/test_edit_message_live_location.py +++ b/tests/test_api/test_methods/test_edit_message_live_location.py @@ -2,8 +2,8 @@ from typing import Union import pytest -from aiogram.api.methods import EditMessageLiveLocation, Request -from aiogram.api.types import Message +from aiogram.methods import EditMessageLiveLocation, Request +from aiogram.types import Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_edit_message_media.py b/tests/test_api/test_methods/test_edit_message_media.py index 882ae98a..c6715163 100644 --- a/tests/test_api/test_methods/test_edit_message_media.py +++ b/tests/test_api/test_methods/test_edit_message_media.py @@ -2,8 +2,8 @@ from typing import Union import pytest -from aiogram.api.methods import EditMessageMedia, Request -from aiogram.api.types import BufferedInputFile, InputMedia, InputMediaPhoto, Message +from aiogram.methods import EditMessageMedia, Request +from aiogram.types import BufferedInputFile, InputMedia, InputMediaPhoto, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_edit_message_reply_markup.py b/tests/test_api/test_methods/test_edit_message_reply_markup.py index 48f69e2a..474f052a 100644 --- a/tests/test_api/test_methods/test_edit_message_reply_markup.py +++ b/tests/test_api/test_methods/test_edit_message_reply_markup.py @@ -2,8 +2,8 @@ from typing import Union import pytest -from aiogram.api.methods import EditMessageReplyMarkup, Request -from aiogram.api.types import InlineKeyboardButton, InlineKeyboardMarkup, Message +from aiogram.methods import EditMessageReplyMarkup, Request +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_edit_message_text.py b/tests/test_api/test_methods/test_edit_message_text.py index 0e349a03..d889e7ec 100644 --- a/tests/test_api/test_methods/test_edit_message_text.py +++ b/tests/test_api/test_methods/test_edit_message_text.py @@ -2,8 +2,8 @@ from typing import Union import pytest -from aiogram.api.methods import EditMessageText, Request -from aiogram.api.types import Message +from aiogram.methods import EditMessageText, Request +from aiogram.types import Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_export_chat_invite_link.py b/tests/test_api/test_methods/test_export_chat_invite_link.py index 8e76e907..2d5f6d05 100644 --- a/tests/test_api/test_methods/test_export_chat_invite_link.py +++ b/tests/test_api/test_methods/test_export_chat_invite_link.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import ExportChatInviteLink, Request +from aiogram.methods import ExportChatInviteLink, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_forward_message.py b/tests/test_api/test_methods/test_forward_message.py index 3e2ae0d9..77adb37d 100644 --- a/tests/test_api/test_methods/test_forward_message.py +++ b/tests/test_api/test_methods/test_forward_message.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import ForwardMessage, Request -from aiogram.api.types import Chat, Message +from aiogram.methods import ForwardMessage, Request +from aiogram.types import Chat, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_chat.py b/tests/test_api/test_methods/test_get_chat.py index d88b5261..3d9510b9 100644 --- a/tests/test_api/test_methods/test_get_chat.py +++ b/tests/test_api/test_methods/test_get_chat.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import GetChat, Request -from aiogram.api.types import Chat +from aiogram.methods import GetChat, Request +from aiogram.types import Chat from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_chat_administrators.py b/tests/test_api/test_methods/test_get_chat_administrators.py index f5e76c2e..9309460b 100644 --- a/tests/test_api/test_methods/test_get_chat_administrators.py +++ b/tests/test_api/test_methods/test_get_chat_administrators.py @@ -2,8 +2,8 @@ from typing import List import pytest -from aiogram.api.methods import GetChatAdministrators, Request -from aiogram.api.types import ChatMember, User +from aiogram.methods import GetChatAdministrators, Request +from aiogram.types import ChatMember, User from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_chat_member.py b/tests/test_api/test_methods/test_get_chat_member.py index d63bdbd0..111b06cd 100644 --- a/tests/test_api/test_methods/test_get_chat_member.py +++ b/tests/test_api/test_methods/test_get_chat_member.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import GetChatMember, Request -from aiogram.api.types import ChatMember, User +from aiogram.methods import GetChatMember, Request +from aiogram.types import ChatMember, User from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_chat_members_count.py b/tests/test_api/test_methods/test_get_chat_members_count.py index 1ae1a6e7..fd88f925 100644 --- a/tests/test_api/test_methods/test_get_chat_members_count.py +++ b/tests/test_api/test_methods/test_get_chat_members_count.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import GetChatMembersCount, Request +from aiogram.methods import GetChatMembersCount, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_file.py b/tests/test_api/test_methods/test_get_file.py index 1b8fe22a..f466ef04 100644 --- a/tests/test_api/test_methods/test_get_file.py +++ b/tests/test_api/test_methods/test_get_file.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import GetFile, Request -from aiogram.api.types import File +from aiogram.methods import GetFile, Request +from aiogram.types import File from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_game_high_scores.py b/tests/test_api/test_methods/test_get_game_high_scores.py index efeac5cb..397b450d 100644 --- a/tests/test_api/test_methods/test_get_game_high_scores.py +++ b/tests/test_api/test_methods/test_get_game_high_scores.py @@ -2,8 +2,8 @@ from typing import List import pytest -from aiogram.api.methods import GetGameHighScores, Request -from aiogram.api.types import GameHighScore, User +from aiogram.methods import GetGameHighScores, Request +from aiogram.types import GameHighScore, User from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_me.py b/tests/test_api/test_methods/test_get_me.py index bd1bb860..9e6454e3 100644 --- a/tests/test_api/test_methods/test_get_me.py +++ b/tests/test_api/test_methods/test_get_me.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import GetMe, Request -from aiogram.api.types import User +from aiogram.methods import GetMe, Request +from aiogram.types import User from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_my_commands.py b/tests/test_api/test_methods/test_get_my_commands.py index 0cca2287..569a13a3 100644 --- a/tests/test_api/test_methods/test_get_my_commands.py +++ b/tests/test_api/test_methods/test_get_my_commands.py @@ -2,8 +2,8 @@ from typing import List import pytest -from aiogram.api.methods import GetMyCommands, Request -from aiogram.api.types import BotCommand +from aiogram.methods import GetMyCommands, Request +from aiogram.types import BotCommand from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_sticker_set.py b/tests/test_api/test_methods/test_get_sticker_set.py index 130de89a..8b331982 100644 --- a/tests/test_api/test_methods/test_get_sticker_set.py +++ b/tests/test_api/test_methods/test_get_sticker_set.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import GetStickerSet, Request -from aiogram.api.types import Sticker, StickerSet +from aiogram.methods import GetStickerSet, Request +from aiogram.types import Sticker, StickerSet from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_updates.py b/tests/test_api/test_methods/test_get_updates.py index 92081e7b..c400b1df 100644 --- a/tests/test_api/test_methods/test_get_updates.py +++ b/tests/test_api/test_methods/test_get_updates.py @@ -2,8 +2,8 @@ from typing import List import pytest -from aiogram.api.methods import GetUpdates, Request -from aiogram.api.types import Update +from aiogram.methods import GetUpdates, Request +from aiogram.types import Update from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_user_profile_photos.py b/tests/test_api/test_methods/test_get_user_profile_photos.py index 093dea82..3e24a115 100644 --- a/tests/test_api/test_methods/test_get_user_profile_photos.py +++ b/tests/test_api/test_methods/test_get_user_profile_photos.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import GetUserProfilePhotos, Request -from aiogram.api.types import PhotoSize, UserProfilePhotos +from aiogram.methods import GetUserProfilePhotos, Request +from aiogram.types import PhotoSize, UserProfilePhotos from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_get_webhook_info.py b/tests/test_api/test_methods/test_get_webhook_info.py index e3259125..6dc28928 100644 --- a/tests/test_api/test_methods/test_get_webhook_info.py +++ b/tests/test_api/test_methods/test_get_webhook_info.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import GetWebhookInfo, Request -from aiogram.api.types import WebhookInfo +from aiogram.methods import GetWebhookInfo, Request +from aiogram.types import WebhookInfo from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_kick_chat_member.py b/tests/test_api/test_methods/test_kick_chat_member.py index f142f649..d60133c5 100644 --- a/tests/test_api/test_methods/test_kick_chat_member.py +++ b/tests/test_api/test_methods/test_kick_chat_member.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import KickChatMember, Request +from aiogram.methods import KickChatMember, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_leave_chat.py b/tests/test_api/test_methods/test_leave_chat.py index f9f876e6..d4788002 100644 --- a/tests/test_api/test_methods/test_leave_chat.py +++ b/tests/test_api/test_methods/test_leave_chat.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import LeaveChat, Request +from aiogram.methods import LeaveChat, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_log_out.py b/tests/test_api/test_methods/test_log_out.py new file mode 100644 index 00000000..e000540f --- /dev/null +++ b/tests/test_api/test_methods/test_log_out.py @@ -0,0 +1,26 @@ +import pytest + +from aiogram.methods import LogOut, Request +from tests.mocked_bot import MockedBot + + +class TestLogOut: + @pytest.mark.asyncio + async def test_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(LogOut, ok=True, result=True) + + response: bool = await LogOut() + request: Request = bot.get_request() + assert request.method == "logOut" + # assert request.data == {} + assert response == prepare_result.result + + @pytest.mark.asyncio + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(LogOut, ok=True, result=True) + + response: bool = await bot.log_out() + request: Request = bot.get_request() + assert request.method == "logOut" + # assert request.data == {} + assert response == prepare_result.result diff --git a/tests/test_api/test_methods/test_pin_chat_message.py b/tests/test_api/test_methods/test_pin_chat_message.py index fa87d796..190fefcc 100644 --- a/tests/test_api/test_methods/test_pin_chat_message.py +++ b/tests/test_api/test_methods/test_pin_chat_message.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import PinChatMessage, Request +from aiogram.methods import PinChatMessage, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_promote_chat_member.py b/tests/test_api/test_methods/test_promote_chat_member.py index 3b8a7797..11528160 100644 --- a/tests/test_api/test_methods/test_promote_chat_member.py +++ b/tests/test_api/test_methods/test_promote_chat_member.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import PromoteChatMember, Request +from aiogram.methods import PromoteChatMember, Request from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_restrict_chat_member.py b/tests/test_api/test_methods/test_restrict_chat_member.py index a48caeff..fe3ce74d 100644 --- a/tests/test_api/test_methods/test_restrict_chat_member.py +++ b/tests/test_api/test_methods/test_restrict_chat_member.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, RestrictChatMember -from aiogram.api.types import ChatPermissions +from aiogram.methods import Request, RestrictChatMember +from aiogram.types import ChatPermissions from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_animation.py b/tests/test_api/test_methods/test_send_animation.py index b02bad5f..95e85cc1 100644 --- a/tests/test_api/test_methods/test_send_animation.py +++ b/tests/test_api/test_methods/test_send_animation.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendAnimation -from aiogram.api.types import Animation, Chat, Message +from aiogram.methods import Request, SendAnimation +from aiogram.types import Animation, Chat, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_audio.py b/tests/test_api/test_methods/test_send_audio.py index fdc06bdb..4a33bbdc 100644 --- a/tests/test_api/test_methods/test_send_audio.py +++ b/tests/test_api/test_methods/test_send_audio.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendAudio -from aiogram.api.types import Audio, Chat, File, Message +from aiogram.methods import Request, SendAudio +from aiogram.types import Audio, Chat, File, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_chat_action.py b/tests/test_api/test_methods/test_send_chat_action.py index 1d98b482..1478b160 100644 --- a/tests/test_api/test_methods/test_send_chat_action.py +++ b/tests/test_api/test_methods/test_send_chat_action.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SendChatAction +from aiogram.methods import Request, SendChatAction from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_contact.py b/tests/test_api/test_methods/test_send_contact.py index 44435d2d..801968ed 100644 --- a/tests/test_api/test_methods/test_send_contact.py +++ b/tests/test_api/test_methods/test_send_contact.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendContact -from aiogram.api.types import Chat, Contact, Message +from aiogram.methods import Request, SendContact +from aiogram.types import Chat, Contact, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_dice.py b/tests/test_api/test_methods/test_send_dice.py index 1594cda4..981c242b 100644 --- a/tests/test_api/test_methods/test_send_dice.py +++ b/tests/test_api/test_methods/test_send_dice.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, SendDice -from aiogram.api.types import Message +from aiogram.methods import Request, SendDice +from aiogram.types import Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_document.py b/tests/test_api/test_methods/test_send_document.py index 5ddd2e6a..d7d5b32d 100644 --- a/tests/test_api/test_methods/test_send_document.py +++ b/tests/test_api/test_methods/test_send_document.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendDocument -from aiogram.api.types import Chat, Document, Message +from aiogram.methods import Request, SendDocument +from aiogram.types import Chat, Document, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_game.py b/tests/test_api/test_methods/test_send_game.py index 7728c079..35373f2e 100644 --- a/tests/test_api/test_methods/test_send_game.py +++ b/tests/test_api/test_methods/test_send_game.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendGame -from aiogram.api.types import Chat, Game, Message, PhotoSize +from aiogram.methods import Request, SendGame +from aiogram.types import Chat, Game, Message, PhotoSize from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_invoice.py b/tests/test_api/test_methods/test_send_invoice.py index eb6c83b6..d033e621 100644 --- a/tests/test_api/test_methods/test_send_invoice.py +++ b/tests/test_api/test_methods/test_send_invoice.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendInvoice -from aiogram.api.types import Chat, Invoice, LabeledPrice, Message +from aiogram.methods import Request, SendInvoice +from aiogram.types import Chat, Invoice, LabeledPrice, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_location.py b/tests/test_api/test_methods/test_send_location.py index d833733c..cbadceaa 100644 --- a/tests/test_api/test_methods/test_send_location.py +++ b/tests/test_api/test_methods/test_send_location.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendLocation -from aiogram.api.types import Chat, Location, Message +from aiogram.methods import Request, SendLocation +from aiogram.types import Chat, Location, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_media_group.py b/tests/test_api/test_methods/test_send_media_group.py index 14a30ccb..884d362c 100644 --- a/tests/test_api/test_methods/test_send_media_group.py +++ b/tests/test_api/test_methods/test_send_media_group.py @@ -3,8 +3,8 @@ from typing import List import pytest -from aiogram.api.methods import Request, SendMediaGroup -from aiogram.api.types import ( +from aiogram.methods import Request, SendMediaGroup +from aiogram.types import ( BufferedInputFile, Chat, InputMediaPhoto, diff --git a/tests/test_api/test_methods/test_send_message.py b/tests/test_api/test_methods/test_send_message.py index 9c09284e..26d85613 100644 --- a/tests/test_api/test_methods/test_send_message.py +++ b/tests/test_api/test_methods/test_send_message.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendMessage -from aiogram.api.types import Chat, Message +from aiogram.methods import Request, SendMessage +from aiogram.types import Chat, Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_photo.py b/tests/test_api/test_methods/test_send_photo.py index 2ff2b7c6..e3c2065e 100644 --- a/tests/test_api/test_methods/test_send_photo.py +++ b/tests/test_api/test_methods/test_send_photo.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendPhoto -from aiogram.api.types import Chat, Message, PhotoSize +from aiogram.methods import Request, SendPhoto +from aiogram.types import Chat, Message, PhotoSize from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_poll.py b/tests/test_api/test_methods/test_send_poll.py index 5e2b6c28..2f963506 100644 --- a/tests/test_api/test_methods/test_send_poll.py +++ b/tests/test_api/test_methods/test_send_poll.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendPoll -from aiogram.api.types import Chat, Message, Poll, PollOption +from aiogram.methods import Request, SendPoll +from aiogram.types import Chat, Message, Poll, PollOption from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_sticker.py b/tests/test_api/test_methods/test_send_sticker.py index 8475581e..12c3b28e 100644 --- a/tests/test_api/test_methods/test_send_sticker.py +++ b/tests/test_api/test_methods/test_send_sticker.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendSticker -from aiogram.api.types import Chat, Message, Sticker +from aiogram.methods import Request, SendSticker +from aiogram.types import Chat, Message, Sticker from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_venue.py b/tests/test_api/test_methods/test_send_venue.py index bad987af..9246dd90 100644 --- a/tests/test_api/test_methods/test_send_venue.py +++ b/tests/test_api/test_methods/test_send_venue.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendVenue -from aiogram.api.types import Chat, Location, Message, Venue +from aiogram.methods import Request, SendVenue +from aiogram.types import Chat, Location, Message, Venue from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_video.py b/tests/test_api/test_methods/test_send_video.py index 6d52f109..0729dda5 100644 --- a/tests/test_api/test_methods/test_send_video.py +++ b/tests/test_api/test_methods/test_send_video.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendVideo -from aiogram.api.types import Chat, Message, Video +from aiogram.methods import Request, SendVideo +from aiogram.types import Chat, Message, Video from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_video_note.py b/tests/test_api/test_methods/test_send_video_note.py index bb235bed..8a31209e 100644 --- a/tests/test_api/test_methods/test_send_video_note.py +++ b/tests/test_api/test_methods/test_send_video_note.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendVideoNote -from aiogram.api.types import BufferedInputFile, Chat, Message, VideoNote +from aiogram.methods import Request, SendVideoNote +from aiogram.types import BufferedInputFile, Chat, Message, VideoNote from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_send_voice.py b/tests/test_api/test_methods/test_send_voice.py index 6dcab09d..bd19921a 100644 --- a/tests/test_api/test_methods/test_send_voice.py +++ b/tests/test_api/test_methods/test_send_voice.py @@ -2,8 +2,8 @@ import datetime import pytest -from aiogram.api.methods import Request, SendVoice -from aiogram.api.types import Chat, Message, Voice +from aiogram.methods import Request, SendVoice +from aiogram.types import Chat, Message, Voice from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py b/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py index e4372c3d..2f4752c7 100644 --- a/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py +++ b/tests/test_api/test_methods/test_set_chat_administrator_custom_title.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SetChatAdministratorCustomTitle, SetChatTitle +from aiogram.methods import Request, SetChatAdministratorCustomTitle, SetChatTitle from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_chat_description.py b/tests/test_api/test_methods/test_set_chat_description.py index 244ec570..3679d1c5 100644 --- a/tests/test_api/test_methods/test_set_chat_description.py +++ b/tests/test_api/test_methods/test_set_chat_description.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SetChatDescription +from aiogram.methods import Request, SetChatDescription from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_chat_permissions.py b/tests/test_api/test_methods/test_set_chat_permissions.py index 8239cc44..83c90883 100644 --- a/tests/test_api/test_methods/test_set_chat_permissions.py +++ b/tests/test_api/test_methods/test_set_chat_permissions.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, SetChatPermissions -from aiogram.api.types import ChatPermissions +from aiogram.methods import Request, SetChatPermissions +from aiogram.types import ChatPermissions from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_chat_photo.py b/tests/test_api/test_methods/test_set_chat_photo.py index 5517c66b..02e00670 100644 --- a/tests/test_api/test_methods/test_set_chat_photo.py +++ b/tests/test_api/test_methods/test_set_chat_photo.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, SetChatPhoto -from aiogram.api.types import BufferedInputFile, InputFile +from aiogram.methods import Request, SetChatPhoto +from aiogram.types import BufferedInputFile, InputFile from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_chat_sticker_set.py b/tests/test_api/test_methods/test_set_chat_sticker_set.py index 45bb666e..50a87ca7 100644 --- a/tests/test_api/test_methods/test_set_chat_sticker_set.py +++ b/tests/test_api/test_methods/test_set_chat_sticker_set.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SetChatStickerSet +from aiogram.methods import Request, SetChatStickerSet from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_chat_title.py b/tests/test_api/test_methods/test_set_chat_title.py index 88bb1795..40473bc1 100644 --- a/tests/test_api/test_methods/test_set_chat_title.py +++ b/tests/test_api/test_methods/test_set_chat_title.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SetChatTitle +from aiogram.methods import Request, SetChatTitle from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_game_score.py b/tests/test_api/test_methods/test_set_game_score.py index 8f9eaf6d..5b6cbb84 100644 --- a/tests/test_api/test_methods/test_set_game_score.py +++ b/tests/test_api/test_methods/test_set_game_score.py @@ -2,8 +2,8 @@ from typing import Union import pytest -from aiogram.api.methods import Request, SetGameScore -from aiogram.api.types import Message +from aiogram.methods import Request, SetGameScore +from aiogram.types import Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_my_commands.py b/tests/test_api/test_methods/test_set_my_commands.py index ccf9f36b..23b9476e 100644 --- a/tests/test_api/test_methods/test_set_my_commands.py +++ b/tests/test_api/test_methods/test_set_my_commands.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, SetMyCommands -from aiogram.api.types import BotCommand +from aiogram.methods import Request, SetMyCommands +from aiogram.types import BotCommand from tests.mocked_bot import MockedBot @@ -21,7 +21,9 @@ class TestSetMyCommands: async def test_bot_method(self, bot: MockedBot): prepare_result = bot.add_result_for(SetMyCommands, ok=True, result=None) - response: bool = await bot.set_my_commands(commands=[],) + response: bool = await bot.set_my_commands( + commands=[], + ) request: Request = bot.get_request() assert request.method == "setMyCommands" # assert request.data == {} diff --git a/tests/test_api/test_methods/test_set_passport_data_errors.py b/tests/test_api/test_methods/test_set_passport_data_errors.py index 5a78577a..e5ad0a8d 100644 --- a/tests/test_api/test_methods/test_set_passport_data_errors.py +++ b/tests/test_api/test_methods/test_set_passport_data_errors.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, SetPassportDataErrors -from aiogram.api.types import PassportElementError +from aiogram.methods import Request, SetPassportDataErrors +from aiogram.types import PassportElementError from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_sticker_position_in_set.py b/tests/test_api/test_methods/test_set_sticker_position_in_set.py index 9cfb01bc..b1e72507 100644 --- a/tests/test_api/test_methods/test_set_sticker_position_in_set.py +++ b/tests/test_api/test_methods/test_set_sticker_position_in_set.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SetStickerPositionInSet +from aiogram.methods import Request, SetStickerPositionInSet from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_sticker_set_thumb.py b/tests/test_api/test_methods/test_set_sticker_set_thumb.py index affb49ee..b31526d5 100644 --- a/tests/test_api/test_methods/test_set_sticker_set_thumb.py +++ b/tests/test_api/test_methods/test_set_sticker_set_thumb.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SetStickerSetThumb +from aiogram.methods import Request, SetStickerSetThumb from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_set_webhook.py b/tests/test_api/test_methods/test_set_webhook.py index fc26b54a..08ddae7d 100644 --- a/tests/test_api/test_methods/test_set_webhook.py +++ b/tests/test_api/test_methods/test_set_webhook.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, SetWebhook +from aiogram.methods import Request, SetWebhook from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_stop_message_live_location.py b/tests/test_api/test_methods/test_stop_message_live_location.py index da9c5910..8ea1ed62 100644 --- a/tests/test_api/test_methods/test_stop_message_live_location.py +++ b/tests/test_api/test_methods/test_stop_message_live_location.py @@ -2,8 +2,8 @@ from typing import Union import pytest -from aiogram.api.methods import Request, StopMessageLiveLocation -from aiogram.api.types import Message +from aiogram.methods import Request, StopMessageLiveLocation +from aiogram.types import Message from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_stop_poll.py b/tests/test_api/test_methods/test_stop_poll.py index b02c8ad3..03ea9b75 100644 --- a/tests/test_api/test_methods/test_stop_poll.py +++ b/tests/test_api/test_methods/test_stop_poll.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, StopPoll -from aiogram.api.types import Poll, PollOption +from aiogram.methods import Request, StopPoll +from aiogram.types import Poll, PollOption from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_unban_chat_member.py b/tests/test_api/test_methods/test_unban_chat_member.py index 64abe810..2d0fffbd 100644 --- a/tests/test_api/test_methods/test_unban_chat_member.py +++ b/tests/test_api/test_methods/test_unban_chat_member.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, UnbanChatMember +from aiogram.methods import Request, UnbanChatMember from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_unpin_all_chat_messages.py b/tests/test_api/test_methods/test_unpin_all_chat_messages.py new file mode 100644 index 00000000..48348dfd --- /dev/null +++ b/tests/test_api/test_methods/test_unpin_all_chat_messages.py @@ -0,0 +1,30 @@ +import pytest + +from aiogram.methods import Request, UnpinAllChatMessages +from tests.mocked_bot import MockedBot + + +class TestUnpinAllChatMessages: + @pytest.mark.asyncio + async def test_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(UnpinAllChatMessages, ok=True, result=True) + + response: bool = await UnpinAllChatMessages( + chat_id=42, + ) + request: Request = bot.get_request() + assert request.method == "unpinAllChatMessages" + # assert request.data == {} + assert response == prepare_result.result + + @pytest.mark.asyncio + async def test_bot_method(self, bot: MockedBot): + prepare_result = bot.add_result_for(UnpinAllChatMessages, ok=True, result=True) + + response: bool = await bot.unpin_all_chat_messages( + chat_id=42, + ) + request: Request = bot.get_request() + assert request.method == "unpinAllChatMessages" + # assert request.data == {} + assert response == prepare_result.result diff --git a/tests/test_api/test_methods/test_unpin_chat_message.py b/tests/test_api/test_methods/test_unpin_chat_message.py index 315b3165..1ebe5ccb 100644 --- a/tests/test_api/test_methods/test_unpin_chat_message.py +++ b/tests/test_api/test_methods/test_unpin_chat_message.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.methods import Request, UnpinChatMessage +from aiogram.methods import Request, UnpinChatMessage from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_methods/test_upload_sticker_file.py b/tests/test_api/test_methods/test_upload_sticker_file.py index a90b395b..22b85a33 100644 --- a/tests/test_api/test_methods/test_upload_sticker_file.py +++ b/tests/test_api/test_methods/test_upload_sticker_file.py @@ -1,7 +1,7 @@ import pytest -from aiogram.api.methods import Request, UploadStickerFile -from aiogram.api.types import BufferedInputFile, File +from aiogram.methods import Request, UploadStickerFile +from aiogram.types import BufferedInputFile, File from tests.mocked_bot import MockedBot diff --git a/tests/test_api/test_types/test_callback_query.py b/tests/test_api/test_types/test_callback_query.py index 792cca68..3eae5a07 100644 --- a/tests/test_api/test_types/test_callback_query.py +++ b/tests/test_api/test_types/test_callback_query.py @@ -1,5 +1,5 @@ -from aiogram.api.methods import AnswerCallbackQuery -from aiogram.api.types import CallbackQuery, User +from aiogram.methods import AnswerCallbackQuery +from aiogram.types import CallbackQuery, User class TestCallbackQuery: diff --git a/tests/test_api/test_types/test_chat_member.py b/tests/test_api/test_types/test_chat_member.py index bb88e690..e92c7203 100644 --- a/tests/test_api/test_types/test_chat_member.py +++ b/tests/test_api/test_types/test_chat_member.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.types import ChatMember, User +from aiogram.types import ChatMember, User user = User(id=42, is_bot=False, first_name="User", last_name=None) diff --git a/tests/test_api/test_types/test_inline_query.py b/tests/test_api/test_types/test_inline_query.py index c828c17a..c822649d 100644 --- a/tests/test_api/test_types/test_inline_query.py +++ b/tests/test_api/test_types/test_inline_query.py @@ -1,5 +1,5 @@ -from aiogram.api.methods import AnswerInlineQuery -from aiogram.api.types import InlineQuery, User +from aiogram.methods import AnswerInlineQuery +from aiogram.types import InlineQuery, User class TestInlineQuery: diff --git a/tests/test_api/test_types/test_input_file.py b/tests/test_api/test_types/test_input_file.py index 0b63e64a..e59dc461 100644 --- a/tests/test_api/test_types/test_input_file.py +++ b/tests/test_api/test_types/test_input_file.py @@ -4,7 +4,7 @@ import pytest from aresponses import ResponsesMockServer from aiogram import Bot -from aiogram.api.types import BufferedInputFile, FSInputFile, InputFile, URLInputFile +from aiogram.types import BufferedInputFile, FSInputFile, InputFile, URLInputFile class TestInputFile: diff --git a/tests/test_api/test_types/test_message.py b/tests/test_api/test_types/test_message.py index f386e191..a6cf235e 100644 --- a/tests/test_api/test_types/test_message.py +++ b/tests/test_api/test_types/test_message.py @@ -3,7 +3,7 @@ from typing import Any, Dict, Type, Union import pytest -from aiogram.api.methods import ( +from aiogram.methods import ( SendAnimation, SendAudio, SendContact, @@ -22,7 +22,7 @@ from aiogram.api.methods import ( SendVideoNote, SendVoice, ) -from aiogram.api.types import ( +from aiogram.types import ( Animation, Audio, Chat, @@ -45,7 +45,7 @@ from aiogram.api.types import ( VideoNote, Voice, ) -from aiogram.api.types.message import ContentType, Message +from aiogram.types.message import ContentType, Message class TestMessage: @@ -448,7 +448,12 @@ class TestMessage: ["sticker", dict(sticker="sticker"), SendSticker], [ "venue", - dict(latitude=0.42, longitude=0.42, title="title", address="address",), + dict( + latitude=0.42, + longitude=0.42, + title="title", + address="address", + ), SendVenue, ], ["video", dict(video="video"), SendVideo], diff --git a/tests/test_api/test_types/test_pre_checkout_query.py b/tests/test_api/test_types/test_pre_checkout_query.py index 1bef6cff..ef6c9865 100644 --- a/tests/test_api/test_types/test_pre_checkout_query.py +++ b/tests/test_api/test_types/test_pre_checkout_query.py @@ -1,5 +1,5 @@ -from aiogram.api.methods import AnswerPreCheckoutQuery -from aiogram.api.types import PreCheckoutQuery, User +from aiogram.methods import AnswerPreCheckoutQuery +from aiogram.types import PreCheckoutQuery, User class TestPreCheckoutQuery: diff --git a/tests/test_api/test_types/test_reply_keyboard_remove.py b/tests/test_api/test_types/test_reply_keyboard_remove.py index 03eb5e49..677c36e6 100644 --- a/tests/test_api/test_types/test_reply_keyboard_remove.py +++ b/tests/test_api/test_types/test_reply_keyboard_remove.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.types import ReplyKeyboardRemove +from aiogram.types import ReplyKeyboardRemove class TestReplyKeyboardRemove: @@ -14,8 +14,7 @@ class TestReplyKeyboardRemove: ), "Remove keyboard has incorrect default value!" @pytest.mark.parametrize( - "kwargs,expected", - [[{}, True], [{"remove_keyboard": True}, True], [{"remove_keyboard": False}, False]], + "kwargs,expected", [[{}, True], [{"remove_keyboard": True}, True]], ) def test_remove_keyboard_values(self, kwargs, expected): assert ReplyKeyboardRemove(**kwargs).remove_keyboard is expected diff --git a/tests/test_api/test_types/test_shipping_query.py b/tests/test_api/test_types/test_shipping_query.py index 939bb6c5..82db8880 100644 --- a/tests/test_api/test_types/test_shipping_query.py +++ b/tests/test_api/test_types/test_shipping_query.py @@ -1,5 +1,5 @@ -from aiogram.api.methods import AnswerShippingQuery -from aiogram.api.types import LabeledPrice, ShippingAddress, ShippingOption, ShippingQuery, User +from aiogram.methods import AnswerShippingQuery +from aiogram.types import LabeledPrice, ShippingAddress, ShippingOption, ShippingQuery, User class TestInlineQuery: diff --git a/tests/test_api/test_types/test_user.py b/tests/test_api/test_types/test_user.py index ed09b97c..bda3f94e 100644 --- a/tests/test_api/test_types/test_user.py +++ b/tests/test_api/test_types/test_user.py @@ -1,6 +1,6 @@ import pytest -from aiogram.api.types import User +from aiogram.types import User class TestUser: diff --git a/tests/test_dispatcher/test_deprecated.py b/tests/test_dispatcher/test_deprecated.py index edea4aac..d2609079 100644 --- a/tests/test_dispatcher/test_deprecated.py +++ b/tests/test_dispatcher/test_deprecated.py @@ -17,7 +17,6 @@ OBSERVERS = { "poll_answer", "pre_checkout_query", "shipping_query", - "update", } DEPRECATED_OBSERVERS = {observer + "_handler" for observer in OBSERVERS} diff --git a/tests/test_dispatcher/test_dispatcher.py b/tests/test_dispatcher/test_dispatcher.py index e5b9b50f..ef32c712 100644 --- a/tests/test_dispatcher/test_dispatcher.py +++ b/tests/test_dispatcher/test_dispatcher.py @@ -2,30 +2,47 @@ import asyncio import datetime import time import warnings +from typing import Any import pytest from aiogram import Bot -from aiogram.api.methods import GetMe, GetUpdates, SendMessage -from aiogram.api.types import Chat, Message, Update, User from aiogram.dispatcher.dispatcher import Dispatcher -from aiogram.dispatcher.event.bases import NOT_HANDLED +from aiogram.dispatcher.event.bases import UNHANDLED, SkipHandler +from aiogram.dispatcher.middlewares.user_context import UserContextMiddleware from aiogram.dispatcher.router import Router +from aiogram.methods import GetMe, GetUpdates, SendMessage +from aiogram.types import ( + CallbackQuery, + Chat, + ChosenInlineResult, + InlineQuery, + Message, + Poll, + PollAnswer, + PollOption, + PreCheckoutQuery, + ShippingAddress, + ShippingQuery, + Update, + User, +) from tests.mocked_bot import MockedBot try: from asynctest import CoroutineMock, patch except ImportError: - from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore + from unittest.mock import AsyncMock as CoroutineMock # type: ignore + from unittest.mock import patch async def simple_message_handler(message: Message): - await asyncio.sleep(1.5) + await asyncio.sleep(0.2) return message.answer("ok") async def invalid_message_handler(message: Message): - await asyncio.sleep(1.5) + await asyncio.sleep(0.2) raise Exception(42) @@ -43,6 +60,13 @@ UPDATE = Update(**RAW_UPDATE) class TestDispatcher: + def test_init(self): + dp = Dispatcher() + + assert dp.update.handlers + assert dp.update.handlers[0].callback == dp._listen_update + assert dp.update.outer_middlewares + def test_parent_router(self): dp = Dispatcher() with pytest.raises(RuntimeError): @@ -145,6 +169,290 @@ class TestDispatcher: assert await dispatcher._process_update(bot=bot, update=Update(update_id=42)) + @pytest.mark.asyncio + @pytest.mark.parametrize( + "event_type,update,has_chat,has_user", + [ + pytest.param( + "message", + Update( + update_id=42, + message=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, + True, + ), + pytest.param( + "edited_message", + Update( + update_id=42, + edited_message=Message( + message_id=42, + date=datetime.datetime.now(), + text="edited test", + chat=Chat(id=42, type="private"), + from_user=User(id=42, is_bot=False, first_name="Test"), + ), + ), + True, + True, + ), + pytest.param( + "channel_post", + Update( + update_id=42, + channel_post=Message( + message_id=42, + date=datetime.datetime.now(), + text="test", + chat=Chat(id=-42, type="private"), + ), + ), + True, + False, + ), + pytest.param( + "edited_channel_post", + Update( + update_id=42, + edited_channel_post=Message( + message_id=42, + date=datetime.datetime.now(), + text="test", + chat=Chat(id=-42, type="private"), + ), + ), + True, + False, + ), + pytest.param( + "inline_query", + Update( + update_id=42, + inline_query=InlineQuery( + id="query id", + from_user=User(id=42, is_bot=False, first_name="Test"), + query="query", + offset="offser", + ), + ), + False, + True, + ), + pytest.param( + "chosen_inline_result", + Update( + update_id=42, + chosen_inline_result=ChosenInlineResult( + result_id="result id", + from_user=User(id=42, is_bot=False, first_name="Test"), + query="query", + ), + ), + False, + True, + ), + pytest.param( + "callback_query", + Update( + update_id=42, + callback_query=CallbackQuery( + id="query id", + from_user=User(id=42, is_bot=False, first_name="Test"), + chat_instance="instance", + data="placeholder", + ), + ), + False, + True, + ), + pytest.param( + "callback_query", + Update( + update_id=42, + callback_query=CallbackQuery( + id="query id", + from_user=User(id=42, is_bot=False, first_name="Test"), + chat_instance="instance", + data="placeholder", + message=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, + True, + ), + pytest.param( + "shipping_query", + Update( + update_id=42, + shipping_query=ShippingQuery( + id="id", + from_user=User(id=42, is_bot=False, first_name="Test"), + invoice_payload="payload", + shipping_address=ShippingAddress( + country_code="placeholder", + state="placeholder", + city="placeholder", + street_line1="placeholder", + street_line2="placeholder", + post_code="placeholder", + ), + ), + ), + False, + True, + ), + pytest.param( + "pre_checkout_query", + Update( + update_id=42, + pre_checkout_query=PreCheckoutQuery( + id="query id", + from_user=User(id=42, is_bot=False, first_name="Test"), + currency="BTC", + total_amount=1, + invoice_payload="payload", + ), + ), + False, + True, + ), + pytest.param( + "poll", + Update( + update_id=42, + poll=Poll( + id="poll id", + question="Q?", + options=[ + PollOption(text="A1", voter_count=2), + PollOption(text="A2", voter_count=3), + ], + is_closed=False, + is_anonymous=False, + type="quiz", + allows_multiple_answers=False, + total_voter_count=0, + correct_option_id=1, + ), + ), + False, + False, + ), + pytest.param( + "poll_answer", + Update( + update_id=42, + poll_answer=PollAnswer( + poll_id="poll id", + user=User(id=42, is_bot=False, first_name="Test"), + option_ids=[42], + ), + ), + False, + True, + ), + ], + ) + async def test_listen_update( + self, event_type: str, update: Update, has_chat: bool, has_user: bool + ): + router = Dispatcher() + observer = router.observers[event_type] + + @observer() + async def my_handler(event: Any, **kwargs: Any): + assert event == getattr(update, event_type) + if has_chat: + assert Chat.get_current(False) + if has_user: + assert User.get_current(False) + return kwargs + + result = await router.update.trigger(update, test="PASS") + assert isinstance(result, dict) + assert result["event_update"] == update + assert result["event_router"] == router + assert result["test"] == "PASS" + + @pytest.mark.asyncio + async def test_listen_unknown_update(self): + dp = Dispatcher() + + with pytest.raises(SkipHandler): + await dp._listen_update(Update(update_id=42)) + + @pytest.mark.asyncio + async def test_listen_unhandled_update(self): + dp = Dispatcher() + observer = dp.observers["message"] + + @observer(lambda event: False) + async def handler(event: Any): + pass + + response = await dp._listen_update( + Update( + update_id=42, + poll=Poll( + id="poll id", + question="Q?", + options=[ + PollOption(text="A1", voter_count=2), + PollOption(text="A2", voter_count=3), + ], + is_closed=False, + is_anonymous=False, + type="quiz", + allows_multiple_answers=False, + total_voter_count=0, + correct_option_id=0, + ), + ) + ) + assert response is UNHANDLED + + @pytest.mark.asyncio + async def test_nested_router_listen_update(self): + dp = Dispatcher() + router0 = Router() + router1 = Router() + dp.include_router(router0) + router0.include_router(router1) + observer = router1.message + + @observer() + async def my_handler(event: Message, **kwargs: Any): + return kwargs + + update = Update( + update_id=42, + message=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"), + ), + ) + result = await dp._listen_update(update, test="PASS") + assert isinstance(result, dict) + assert result["event_update"] == update + assert result["event_router"] == router1 + assert result["test"] == "PASS" + @pytest.mark.asyncio async def test_process_update_call_request(self, bot: MockedBot): dispatcher = Dispatcher() @@ -192,6 +500,45 @@ class TestDispatcher: await dispatcher._polling(bot=bot) mocked_process_update.assert_awaited() + @pytest.mark.asyncio + async def test_exception_handler_catch_exceptions(self): + dp = Dispatcher() + router = Router() + dp.include_router(router) + + @router.message() + async def message_handler(message: Message): + raise Exception("KABOOM") + + update = Update( + update_id=42, + message=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"), + ), + ) + with pytest.raises(Exception, match="KABOOM"): + await dp.update.trigger(update) + + @router.errors() + async def error_handler(event: Update, exception: Exception): + return "KABOOM" + + response = await dp.update.trigger(update) + assert response == "KABOOM" + + @dp.errors() + async def root_error_handler(event: Update, exception: Exception): + return exception + + response = await dp.update.trigger(update) + + assert isinstance(response, Exception) + assert str(response) == "KABOOM" + @pytest.mark.asyncio async def test_start_polling(self, bot: MockedBot): dispatcher = Dispatcher() @@ -231,21 +578,11 @@ class TestDispatcher: dispatcher = Dispatcher() dispatcher.message.register(simple_message_handler) - response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=2) + response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.3) assert isinstance(response, dict) assert response["method"] == "sendMessage" assert response["text"] == "ok" - # @pytest.mark.asyncio - # async def test_feed_webhook_update_fast_process_error(self, bot: MockedBot): - # dispatcher = Dispatcher() - # dispatcher.message_handler.register(invalid_message_handler) - # - # response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=2) - # assert isinstance(response, dict) - # assert response["method"] == "sendMessage" - # assert response["text"] == "ok" - @pytest.mark.asyncio async def test_feed_webhook_update_slow_process(self, bot: MockedBot, recwarn): warnings.simplefilter("always") @@ -257,9 +594,9 @@ class TestDispatcher: "aiogram.dispatcher.dispatcher.Dispatcher._silent_call_request", new_callable=CoroutineMock, ) as mocked_silent_call_request: - response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=1) + response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.1) assert response is None - await asyncio.sleep(1) + await asyncio.sleep(0.2) mocked_silent_call_request.assert_awaited() @pytest.mark.asyncio @@ -269,9 +606,9 @@ class TestDispatcher: dispatcher = Dispatcher() dispatcher.message.register(invalid_message_handler) - response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=1) + response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.1) assert response is None - await asyncio.sleep(1) + await asyncio.sleep(0.2) log_records = [rec.message for rec in caplog.records] assert "Cause exception while process update" in log_records[0] diff --git a/tests/test_dispatcher/test_event/test_event.py b/tests/test_dispatcher/test_event/test_event.py index 335b864c..be733ebb 100644 --- a/tests/test_dispatcher/test_event/test_event.py +++ b/tests/test_dispatcher/test_event/test_event.py @@ -9,7 +9,8 @@ from aiogram.dispatcher.event.handler import HandlerObject try: from asynctest import CoroutineMock, patch except ImportError: - from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore + from unittest.mock import AsyncMock as CoroutineMock # type: ignore + from unittest.mock import patch async def my_handler(value: str, index: int = 0) -> Any: @@ -51,7 +52,8 @@ class TestEventObserver: assert observer.handlers[2].awaitable with patch( - "aiogram.dispatcher.event.handler.CallableMixin.call", new_callable=CoroutineMock, + "aiogram.dispatcher.event.handler.CallableMixin.call", + new_callable=CoroutineMock, ) as mocked_my_handler: results = await observer.trigger("test") assert results is None diff --git a/tests/test_dispatcher/test_event/test_handler.py b/tests/test_dispatcher/test_event/test_handler.py index 3db676db..f67b848e 100644 --- a/tests/test_dispatcher/test_event/test_handler.py +++ b/tests/test_dispatcher/test_event/test_handler.py @@ -3,11 +3,11 @@ from typing import Any, Dict, Union import pytest -from aiogram.api.types import Update from aiogram.dispatcher.event.handler import CallableMixin, FilterObject, HandlerObject from aiogram.dispatcher.filters import Text from aiogram.dispatcher.filters.base import BaseFilter from aiogram.dispatcher.handler.base import BaseHandler +from aiogram.types import Update def callback1(foo: int, bar: int, baz: int): diff --git a/tests/test_dispatcher/test_event/test_telegram.py b/tests/test_dispatcher/test_event/test_telegram.py index 5d4c6607..d4306b1a 100644 --- a/tests/test_dispatcher/test_event/test_telegram.py +++ b/tests/test_dispatcher/test_event/test_telegram.py @@ -4,12 +4,12 @@ from typing import Any, Awaitable, Callable, Dict, NoReturn, Union import pytest -from aiogram.api.types import Chat, Message, User from aiogram.dispatcher.event.bases import SkipHandler from aiogram.dispatcher.event.handler import HandlerObject from aiogram.dispatcher.event.telegram import TelegramEventObserver from aiogram.dispatcher.filters.base import BaseFilter from aiogram.dispatcher.router import Router +from aiogram.types import Chat, Message, User # TODO: Test middlewares in routers tree diff --git a/tests/test_dispatcher/test_filters/test_base.py b/tests/test_dispatcher/test_filters/test_base.py index 688b13ab..27a1f349 100644 --- a/tests/test_dispatcher/test_filters/test_base.py +++ b/tests/test_dispatcher/test_filters/test_base.py @@ -7,7 +7,8 @@ from aiogram.dispatcher.filters.base import BaseFilter try: from asynctest import CoroutineMock, patch except ImportError: - from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore + from unittest.mock import AsyncMock as CoroutineMock # type: ignore + from unittest.mock import patch class MyFilter(BaseFilter): diff --git a/tests/test_dispatcher/test_filters/test_command.py b/tests/test_dispatcher/test_filters/test_command.py index 319ae2fa..6eb24097 100644 --- a/tests/test_dispatcher/test_filters/test_command.py +++ b/tests/test_dispatcher/test_filters/test_command.py @@ -4,9 +4,9 @@ from typing import Match import pytest -from aiogram.api.methods import GetMe -from aiogram.api.types import Chat, Message, User from aiogram.dispatcher.filters import Command, CommandObject +from aiogram.methods import GetMe +from aiogram.types import Chat, Message, User from tests.mocked_bot import MockedBot diff --git a/tests/test_dispatcher/test_filters/test_content_types.py b/tests/test_dispatcher/test_filters/test_content_types.py index f5252323..a009acfa 100644 --- a/tests/test_dispatcher/test_filters/test_content_types.py +++ b/tests/test_dispatcher/test_filters/test_content_types.py @@ -4,8 +4,8 @@ from typing import cast import pytest from pydantic import ValidationError -from aiogram.api.types import ContentType, Message from aiogram.dispatcher.filters import ContentTypesFilter +from aiogram.types import ContentType, Message @dataclass diff --git a/tests/test_dispatcher/test_filters/test_text.py b/tests/test_dispatcher/test_filters/test_text.py index 3e0bc479..72f95e9d 100644 --- a/tests/test_dispatcher/test_filters/test_text.py +++ b/tests/test_dispatcher/test_filters/test_text.py @@ -5,9 +5,9 @@ from typing import Sequence, Type import pytest from pydantic import ValidationError -from aiogram.api.types import CallbackQuery, Chat, InlineQuery, Message, Poll, PollOption, User from aiogram.dispatcher.filters import BUILTIN_FILTERS from aiogram.dispatcher.filters.text import Text +from aiogram.types import CallbackQuery, Chat, InlineQuery, Message, Poll, PollOption, User class TestText: diff --git a/tests/test_dispatcher/test_handler/test_base.py b/tests/test_dispatcher/test_handler/test_base.py index 48550702..88982907 100644 --- a/tests/test_dispatcher/test_handler/test_base.py +++ b/tests/test_dispatcher/test_handler/test_base.py @@ -6,9 +6,9 @@ from typing import Any import pytest from aiogram import Bot -from aiogram.api.types import Chat, Message, Update from aiogram.dispatcher.event.handler import HandlerObject from aiogram.dispatcher.handler.base import BaseHandler +from aiogram.types import Chat, Message, Update class MyHandler(BaseHandler): diff --git a/tests/test_dispatcher/test_handler/test_callback_query.py b/tests/test_dispatcher/test_handler/test_callback_query.py index c63381de..e47534f4 100644 --- a/tests/test_dispatcher/test_handler/test_callback_query.py +++ b/tests/test_dispatcher/test_handler/test_callback_query.py @@ -2,8 +2,8 @@ from typing import Any import pytest -from aiogram.api.types import CallbackQuery, User from aiogram.dispatcher.handler import CallbackQueryHandler +from aiogram.types import CallbackQuery, User class TestCallbackQueryHandler: diff --git a/tests/test_dispatcher/test_handler/test_chosen_inline_result.py b/tests/test_dispatcher/test_handler/test_chosen_inline_result.py index 66ccde90..2e1f4045 100644 --- a/tests/test_dispatcher/test_handler/test_chosen_inline_result.py +++ b/tests/test_dispatcher/test_handler/test_chosen_inline_result.py @@ -2,8 +2,8 @@ from typing import Any import pytest -from aiogram.api.types import CallbackQuery, ChosenInlineResult, User from aiogram.dispatcher.handler import ChosenInlineResultHandler +from aiogram.types import CallbackQuery, ChosenInlineResult, User class TestChosenInlineResultHandler: diff --git a/tests/test_dispatcher/test_handler/test_error.py b/tests/test_dispatcher/test_handler/test_error.py index 093d9b0a..f6e6b090 100644 --- a/tests/test_dispatcher/test_handler/test_error.py +++ b/tests/test_dispatcher/test_handler/test_error.py @@ -2,7 +2,8 @@ from typing import Any import pytest -from aiogram.api.types import ( +from aiogram.dispatcher.handler import ErrorHandler, PollHandler +from aiogram.types import ( CallbackQuery, InlineQuery, Poll, @@ -11,7 +12,6 @@ from aiogram.api.types import ( ShippingQuery, User, ) -from aiogram.dispatcher.handler import ErrorHandler, PollHandler class TestErrorHandler: diff --git a/tests/test_dispatcher/test_handler/test_inline_query.py b/tests/test_dispatcher/test_handler/test_inline_query.py index 19c454da..100fccdd 100644 --- a/tests/test_dispatcher/test_handler/test_inline_query.py +++ b/tests/test_dispatcher/test_handler/test_inline_query.py @@ -2,8 +2,8 @@ from typing import Any import pytest -from aiogram.api.types import CallbackQuery, InlineQuery, User from aiogram.dispatcher.handler import InlineQueryHandler +from aiogram.types import CallbackQuery, InlineQuery, User class TestCallbackQueryHandler: diff --git a/tests/test_dispatcher/test_handler/test_message.py b/tests/test_dispatcher/test_handler/test_message.py index d4cc4818..5f95d2bd 100644 --- a/tests/test_dispatcher/test_handler/test_message.py +++ b/tests/test_dispatcher/test_handler/test_message.py @@ -3,9 +3,9 @@ from typing import Any import pytest -from aiogram.api.types import Chat, Message, User from aiogram.dispatcher.filters import CommandObject from aiogram.dispatcher.handler.message import MessageHandler, MessageHandlerCommandMixin +from aiogram.types import Chat, Message, User class MyHandler(MessageHandler): diff --git a/tests/test_dispatcher/test_handler/test_poll.py b/tests/test_dispatcher/test_handler/test_poll.py index 5908b2fc..172012d6 100644 --- a/tests/test_dispatcher/test_handler/test_poll.py +++ b/tests/test_dispatcher/test_handler/test_poll.py @@ -2,7 +2,8 @@ from typing import Any import pytest -from aiogram.api.types import ( +from aiogram.dispatcher.handler import PollHandler +from aiogram.types import ( CallbackQuery, InlineQuery, Poll, @@ -11,7 +12,6 @@ from aiogram.api.types import ( ShippingQuery, User, ) -from aiogram.dispatcher.handler import PollHandler class TestShippingQueryHandler: diff --git a/tests/test_dispatcher/test_handler/test_pre_checkout_query.py b/tests/test_dispatcher/test_handler/test_pre_checkout_query.py index 159d541e..828bd57d 100644 --- a/tests/test_dispatcher/test_handler/test_pre_checkout_query.py +++ b/tests/test_dispatcher/test_handler/test_pre_checkout_query.py @@ -2,8 +2,8 @@ from typing import Any import pytest -from aiogram.api.types import PreCheckoutQuery, User from aiogram.dispatcher.handler import PreCheckoutQueryHandler +from aiogram.types import PreCheckoutQuery, User class TestPreCheckoutQueryHandler: diff --git a/tests/test_dispatcher/test_handler/test_shipping_query.py b/tests/test_dispatcher/test_handler/test_shipping_query.py index 0df94218..0d5aa578 100644 --- a/tests/test_dispatcher/test_handler/test_shipping_query.py +++ b/tests/test_dispatcher/test_handler/test_shipping_query.py @@ -2,8 +2,8 @@ from typing import Any import pytest -from aiogram.api.types import CallbackQuery, InlineQuery, ShippingAddress, ShippingQuery, User from aiogram.dispatcher.handler import ShippingQueryHandler +from aiogram.types import CallbackQuery, InlineQuery, ShippingAddress, ShippingQuery, User class TestShippingQueryHandler: diff --git a/tests/test_dispatcher/test_router.py b/tests/test_dispatcher/test_router.py index 9d425388..093715b1 100644 --- a/tests/test_dispatcher/test_router.py +++ b/tests/test_dispatcher/test_router.py @@ -3,7 +3,10 @@ from typing import Any import pytest -from aiogram.api.types import ( +from aiogram.dispatcher.event.bases import UNHANDLED, SkipHandler, skip +from aiogram.dispatcher.middlewares.user_context import UserContextMiddleware +from aiogram.dispatcher.router import Router +from aiogram.types import ( CallbackQuery, Chat, ChosenInlineResult, @@ -18,9 +21,6 @@ from aiogram.api.types import ( Update, User, ) -from aiogram.dispatcher.event.bases import NOT_HANDLED, SkipHandler, skip -from aiogram.dispatcher.middlewares.user_context import UserContextMiddleware -from aiogram.dispatcher.router import Router from aiogram.utils.warnings import CodeHasNoEffect importable_router = Router() @@ -80,8 +80,7 @@ class TestRouter: def test_observers_config(self): router = Router() - assert router.update.handlers - assert router.update.handlers[0].callback == router._listen_update + assert router.observers["message"] == router.message assert router.observers["edited_message"] == router.edited_message assert router.observers["channel_post"] == router.channel_post @@ -93,291 +92,6 @@ class TestRouter: assert router.observers["pre_checkout_query"] == router.pre_checkout_query assert router.observers["poll"] == router.poll - @pytest.mark.asyncio - @pytest.mark.parametrize( - "event_type,update,has_chat,has_user", - [ - pytest.param( - "message", - Update( - update_id=42, - message=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, - True, - ), - pytest.param( - "edited_message", - Update( - update_id=42, - edited_message=Message( - message_id=42, - date=datetime.datetime.now(), - text="edited test", - chat=Chat(id=42, type="private"), - from_user=User(id=42, is_bot=False, first_name="Test"), - ), - ), - True, - True, - ), - pytest.param( - "channel_post", - Update( - update_id=42, - channel_post=Message( - message_id=42, - date=datetime.datetime.now(), - text="test", - chat=Chat(id=-42, type="private"), - ), - ), - True, - False, - ), - pytest.param( - "edited_channel_post", - Update( - update_id=42, - edited_channel_post=Message( - message_id=42, - date=datetime.datetime.now(), - text="test", - chat=Chat(id=-42, type="private"), - ), - ), - True, - False, - ), - pytest.param( - "inline_query", - Update( - update_id=42, - inline_query=InlineQuery( - id="query id", - from_user=User(id=42, is_bot=False, first_name="Test"), - query="query", - offset="offser", - ), - ), - False, - True, - ), - pytest.param( - "chosen_inline_result", - Update( - update_id=42, - chosen_inline_result=ChosenInlineResult( - result_id="result id", - from_user=User(id=42, is_bot=False, first_name="Test"), - query="query", - ), - ), - False, - True, - ), - pytest.param( - "callback_query", - Update( - update_id=42, - callback_query=CallbackQuery( - id="query id", - from_user=User(id=42, is_bot=False, first_name="Test"), - chat_instance="instance", - data="placeholder", - ), - ), - False, - True, - ), - pytest.param( - "callback_query", - Update( - update_id=42, - callback_query=CallbackQuery( - id="query id", - from_user=User(id=42, is_bot=False, first_name="Test"), - chat_instance="instance", - data="placeholder", - message=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, - True, - ), - pytest.param( - "shipping_query", - Update( - update_id=42, - shipping_query=ShippingQuery( - id="id", - from_user=User(id=42, is_bot=False, first_name="Test"), - invoice_payload="payload", - shipping_address=ShippingAddress( - country_code="placeholder", - state="placeholder", - city="placeholder", - street_line1="placeholder", - street_line2="placeholder", - post_code="placeholder", - ), - ), - ), - False, - True, - ), - pytest.param( - "pre_checkout_query", - Update( - update_id=42, - pre_checkout_query=PreCheckoutQuery( - id="query id", - from_user=User(id=42, is_bot=False, first_name="Test"), - currency="BTC", - total_amount=1, - invoice_payload="payload", - ), - ), - False, - True, - ), - pytest.param( - "poll", - Update( - update_id=42, - poll=Poll( - id="poll id", - question="Q?", - options=[ - PollOption(text="A1", voter_count=2), - PollOption(text="A2", voter_count=3), - ], - is_closed=False, - is_anonymous=False, - type="quiz", - allows_multiple_answers=False, - total_voter_count=0, - correct_option_id=1, - ), - ), - False, - False, - ), - pytest.param( - "poll_answer", - Update( - update_id=42, - poll_answer=PollAnswer( - poll_id="poll id", - user=User(id=42, is_bot=False, first_name="Test"), - option_ids=[42], - ), - ), - False, - True, - ), - ], - ) - async def test_listen_update( - self, event_type: str, update: Update, has_chat: bool, has_user: bool - ): - router = Router() - router.update.outer_middleware(UserContextMiddleware()) - observer = router.observers[event_type] - - @observer() - async def my_handler(event: Any, **kwargs: Any): - assert event == getattr(update, event_type) - if has_chat: - assert Chat.get_current(False) - if has_user: - assert User.get_current(False) - return kwargs - - result = await router.update.trigger(update, test="PASS") - assert isinstance(result, dict) - assert result["event_update"] == update - assert result["event_router"] == router - assert result["test"] == "PASS" - - @pytest.mark.asyncio - async def test_listen_unknown_update(self): - router = Router() - - with pytest.raises(SkipHandler): - await router._listen_update(Update(update_id=42)) - - @pytest.mark.asyncio - async def test_listen_unhandled_update(self): - router = Router() - observer = router.observers["message"] - - @observer(lambda event: False) - async def handler(event: Any): - pass - - response = await router._listen_update( - Update( - update_id=42, - poll=Poll( - id="poll id", - question="Q?", - options=[ - PollOption(text="A1", voter_count=2), - PollOption(text="A2", voter_count=3), - ], - is_closed=False, - is_anonymous=False, - type="quiz", - allows_multiple_answers=False, - total_voter_count=0, - correct_option_id=0, - ), - ) - ) - assert response is NOT_HANDLED - - @pytest.mark.asyncio - async def test_nested_router_listen_update(self): - router1 = Router() - router2 = Router() - router3 = Router() - router1.include_router(router2) - router1.include_router(router3) - observer = router3.message - - @observer() - async def my_handler(event: Message, **kwargs: Any): - return kwargs - - update = Update( - update_id=42, - message=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"), - ), - ) - result = await router1._listen_update(update, test="PASS") - assert isinstance(result, dict) - assert result["event_update"] == update - assert result["event_router"] == router3 - assert result["test"] == "PASS" - @pytest.mark.asyncio async def test_emit_startup(self): router1 = Router() @@ -427,42 +141,3 @@ class TestRouter: skip() with pytest.raises(SkipHandler, match="KABOOM"): skip("KABOOM") - - @pytest.mark.asyncio - async def test_exception_handler_catch_exceptions(self): - root_router = Router() - router = Router() - root_router.include_router(router) - - @router.message() - async def message_handler(message: Message): - raise Exception("KABOOM") - - update = Update( - update_id=42, - message=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"), - ), - ) - with pytest.raises(Exception, match="KABOOM"): - await root_router.update.trigger(update) - - @root_router.errors() - async def root_error_handler(event: Update, exception: Exception): - return exception - - response = await root_router.update.trigger(update) - - assert isinstance(response, Exception) - assert str(response) == "KABOOM" - - @router.errors() - async def error_handler(event: Update, exception: Exception): - return "KABOOM" - - response = await root_router.update.trigger(update) - assert response == "KABOOM" diff --git a/tests/test_utils/test_markdown.py b/tests/test_utils/test_markdown.py index 792c1bb4..12e44ccf 100644 --- a/tests/test_utils/test_markdown.py +++ b/tests/test_utils/test_markdown.py @@ -31,13 +31,13 @@ class TestMarkdown: [text, ("test", "test"), None, "test test"], [bold, ("test", "test"), " ", "*test test*"], [hbold, ("test", "test"), " ", "test test"], - [italic, ("test", "test"), " ", "_test test_\r"], + [italic, ("test", "test"), " ", "_\rtest test_\r"], [hitalic, ("test", "test"), " ", "test test"], [code, ("test", "test"), " ", "`test test`"], [hcode, ("test", "test"), " ", "test test"], [pre, ("test", "test"), " ", "```test test```"], [hpre, ("test", "test"), " ", "
test test
"], - [underline, ("test", "test"), " ", "__test test__"], + [underline, ("test", "test"), " ", "__\rtest test__\r"], [hunderline, ("test", "test"), " ", "test test"], [strikethrough, ("test", "test"), " ", "~test test~"], [hstrikethrough, ("test", "test"), " ", "test test"], diff --git a/tests/test_utils/test_text_decorations.py b/tests/test_utils/test_text_decorations.py index 790d327c..6cb5105d 100644 --- a/tests/test_utils/test_text_decorations.py +++ b/tests/test_utils/test_text_decorations.py @@ -2,7 +2,7 @@ from typing import List, Optional import pytest -from aiogram.api.types import MessageEntity, User +from aiogram.types import MessageEntity, User from aiogram.utils.text_decorations import TextDecoration, html_decoration, markdown_decoration @@ -53,7 +53,7 @@ class TestTextDecoration: 'test', ], [markdown_decoration, MessageEntity(type="bold", offset=0, length=5), "*test*"], - [markdown_decoration, MessageEntity(type="italic", offset=0, length=5), "_test_\r"], + [markdown_decoration, MessageEntity(type="italic", offset=0, length=5), "_\rtest_\r"], [markdown_decoration, MessageEntity(type="code", offset=0, length=5), "`test`"], [markdown_decoration, MessageEntity(type="pre", offset=0, length=5), "```test```"], [ @@ -61,7 +61,11 @@ class TestTextDecoration: MessageEntity(type="pre", offset=0, length=5, language="python"), "```python\ntest\n```", ], - [markdown_decoration, MessageEntity(type="underline", offset=0, length=5), "__test__"], + [ + markdown_decoration, + MessageEntity(type="underline", offset=0, length=5), + "__\rtest__\r", + ], [ markdown_decoration, MessageEntity(type="strikethrough", offset=0, length=5), @@ -210,7 +214,7 @@ class TestTextDecoration: [ html_decoration, "test te👍🏿st test", - [MessageEntity(type="bold", offset=5, length=6, url=None, user=None)], + [MessageEntity(type="bold", offset=5, length=8, url=None, user=None)], "test te👍🏿st test", ], [