mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Merge branch 'dev-3.x' into scenes
This commit is contained in:
commit
6a63be516f
23 changed files with 1016 additions and 505 deletions
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -30,6 +30,5 @@ Please describe the tests that you ran to verify your changes. Provide instructi
|
|||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
||||
|
|
|
|||
10
CHANGES.rst
10
CHANGES.rst
|
|
@ -16,6 +16,16 @@ Changelog
|
|||
|
||||
.. towncrier release notes start
|
||||
|
||||
3.1.1 (2023-09-25)
|
||||
===================
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- Fixed `pydantic` version <2.4, since 2.4 has breaking changes.
|
||||
`#1322 <https://github.com/aiogram/aiogram/issues/1322>`_
|
||||
|
||||
|
||||
3.1.0 (2023-09-22)
|
||||
===================
|
||||
|
||||
|
|
|
|||
1
CHANGES/1302.doc
Normal file
1
CHANGES/1302.doc
Normal file
|
|
@ -0,0 +1 @@
|
|||
Corrected grammatical errors, improved sentence structures, translation for migration 2.x-3.x
|
||||
6
CHANGES/1327.misc.rst
Normal file
6
CHANGES/1327.misc.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
Updated dependencies, bumped minimum required version:
|
||||
|
||||
- :code:`magic-filter` - fixed `.resolve` operation
|
||||
- :code:`pydantic` - fixed compatibility (broken in 2.4)
|
||||
- :code:`aiodns` - added new dependency to the :code:`fast` extras (:code:`pip install aiogram[fast]`)
|
||||
- *others...*
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
__version__ = "3.1.0"
|
||||
__version__ = "3.1.1"
|
||||
__api_version__ = "6.9"
|
||||
|
|
|
|||
|
|
@ -139,6 +139,10 @@ class AiohttpSession(BaseSession):
|
|||
if self._session is not None and not self._session.closed:
|
||||
await self._session.close()
|
||||
|
||||
# Wait 250 ms for the underlying SSL connections to close
|
||||
# https://docs.aiohttp.org/en/stable/client_advanced.html#graceful-shutdown
|
||||
await asyncio.sleep(0.25)
|
||||
|
||||
def build_form_data(self, bot: Bot, method: TelegramMethod[TelegramType]) -> FormData:
|
||||
form = FormData(quote_fields=False)
|
||||
files: Dict[str, InputFile] = {}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class RedisStorage(BaseStorage):
|
|||
return RedisEventIsolation(redis=self.redis, key_builder=self.key_builder, **kwargs)
|
||||
|
||||
async def close(self) -> None:
|
||||
await self.redis.close()
|
||||
await self.redis.aclose(close_connection_pool=True)
|
||||
|
||||
async def set_state(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Simple usage
|
|||
Extension
|
||||
=========
|
||||
|
||||
This base handler is subclass of [BaseHandler](basics.md#basehandler) with some extensions:
|
||||
This base handler is subclass of :ref:`BaseHandler <cbh-base-handler>` with some extensions:
|
||||
|
||||
- :code:`self.chat` is alias for :code:`self.event.chat`
|
||||
- :code:`self.from_user` is alias for :code:`self.event.from_user`
|
||||
|
|
|
|||
|
|
@ -3,19 +3,20 @@
|
|||
# This file is distributed under the same license as the aiogram package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: aiogram \n"
|
||||
"Project-Id-Version: aiogram\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-03-11 01:52+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"POT-Creation-Date: 2023-06-01 20:49+0300\n"
|
||||
"PO-Revision-Date: 2023-09-14 17:37+0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: uk\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.11.0\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
"X-Generator: Poedit 3.3.2\n"
|
||||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:3
|
||||
msgid "setStickerSetThumb"
|
||||
|
|
@ -23,15 +24,38 @@ msgstr ""
|
|||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:5
|
||||
msgid "Returns: :obj:`bool`"
|
||||
msgstr ""
|
||||
msgstr "Повертає: :obj:`bool`"
|
||||
|
||||
#: aiogram.methods.set_sticker_set_thumb.SetStickerSetThumbnail:1 of
|
||||
msgid "Use this method to set the thumbnail of a regular or mask sticker set. The format of the thumbnail file must match the format of the stickers in the set. Returns :code:`True` on success."
|
||||
msgstr "Використовуйте цей метод, щоб встановити мініатюру звичайного або маскового набору стікерів. Формат файлу мініатюри повинен відповідати формату стікерів у наборі. Повертає :code:`True` при успіху."
|
||||
|
||||
#: aiogram.methods.set_sticker_set_thumb.SetStickerSetThumbnail:3 of
|
||||
msgid "Source: https://core.telegram.org/bots/api#setstickersetthumbnail"
|
||||
msgstr "Джерело: https://core.telegram.org/bots/api#setstickersetthumbnail"
|
||||
|
||||
#: ../../docstring
|
||||
#: aiogram.methods.set_sticker_set_thumb.SetStickerSetThumbnail.name:1 of
|
||||
msgid "Sticker set name"
|
||||
msgstr "Назва набору стікерів"
|
||||
|
||||
#: ../../docstring
|
||||
#: aiogram.methods.set_sticker_set_thumb.SetStickerSetThumbnail.user_id:1 of
|
||||
msgid "User identifier of the sticker set owner"
|
||||
msgstr "Ідентифікатор користувача власника набору стікерів "
|
||||
|
||||
#: ../../docstring
|
||||
#: aiogram.methods.set_sticker_set_thumb.SetStickerSetThumbnail.thumbnail:1 of
|
||||
msgid "A **.WEBP** or **.PNG** image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a **.TGS** animation with a thumbnail up to 32 kilobytes in size (see `https://core.telegram.org/stickers#animated-sticker-requirements <https://core.telegram.org/stickers#animated-sticker-requirements>`_`https://core.telegram.org/stickers#animated-sticker-requirements <https://core.telegram.org/stickers#animated-sticker-requirements>`_ for animated sticker technical requirements), or a **WEBM** video with the thumbnail up to 32 kilobytes in size; see `https://core.telegram.org/stickers#video-sticker-requirements <https://core.telegram.org/stickers#video-sticker-requirements>`_`https://core.telegram.org/stickers#video-sticker-requirements <https://core.telegram.org/stickers#video-sticker-requirements>`_ for video 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 information on Sending Files » <sending-files>`. Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail."
|
||||
msgstr "Зображення у форматі **.WEBP** або **.PNG** з мініатюрою має бути розміром до 128 кілобайт і мати ширину та висоту рівно 100 пікселів, або анімацією у форматі **.TGS** з мініатюрою розміром до 32 кілобайт (див. `https://core.telegram.org/stickers#animated-sticker-requirements <https://core.telegram.org/stickers#animated-sticker-requirements>`_`https://core.telegram.org/stickers#animated-sticker-requirements <https://core.telegram. org/stickers#animated-sticker-requirements>`_ технічні вимоги до анімованих наліпок), або **WEBM** відео з мініатюрою розміром до 32 кілобайт; дивіться `https://core.telegram.org/stickers#video-sticker-requirements <https://core.telegram.org/stickers#video-sticker-requirements>`_`https://core.telegram.org/stickers#video-sticker-requirements <https://core.telegram.org/stickers#video-sticker-requirements>`_ технічні вимоги до відео наліпок. Передайте *file_id* як рядок, щоб надіслати файл, який вже існує на серверах Telegram, передайте HTTP URL як рядок, щоб Telegram отримав файл з Інтернету, або завантажте новий файл за допомогою мультичастини/форма-даних. :ref:`Додаткова інформація про надсилання файлів \" <sending-files>`. Ескізи анімованих і відео-наборів наклейок не можуть бути завантажені через HTTP URL. Якщо цей параметр не вказано, то мініатюру буде вилучено, а замість неї буде використано першу наліпку."
|
||||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:14
|
||||
msgid "Usage"
|
||||
msgstr ""
|
||||
msgstr "Використання"
|
||||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:17
|
||||
msgid "As bot method"
|
||||
msgstr ""
|
||||
msgstr "Як метод в bot"
|
||||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:25
|
||||
msgid "Method as object"
|
||||
|
|
@ -39,12 +63,10 @@ msgstr ""
|
|||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:27
|
||||
msgid "Imports:"
|
||||
msgstr ""
|
||||
msgstr "Імпорти:"
|
||||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:29
|
||||
msgid ""
|
||||
":code:`from aiogram.methods.set_sticker_set_thumb import "
|
||||
"SetStickerSetThumb`"
|
||||
msgid ":code:`from aiogram.methods.set_sticker_set_thumb import SetStickerSetThumb`"
|
||||
msgstr ""
|
||||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:30
|
||||
|
|
@ -53,11 +75,11 @@ msgstr ""
|
|||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:33
|
||||
msgid "With specific bot"
|
||||
msgstr ""
|
||||
msgstr "З конкретним bot"
|
||||
|
||||
#: ../../api/methods/set_sticker_set_thumb.rst:40
|
||||
msgid "As reply into Webhook in handler"
|
||||
msgstr ""
|
||||
msgstr "Як відповідь у Webhook в обробнику"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Use this method to set the "
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: aiogram\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-08-26 23:17+0300\n"
|
||||
"POT-Creation-Date: 2023-09-14 17:21+0300\n"
|
||||
"PO-Revision-Date: 2022-12-10 19:44+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
|
|
@ -30,9 +30,10 @@ msgstr ""
|
|||
"інших маршрутизаторів до диспетчера."
|
||||
|
||||
#: ../../dispatcher/dispatcher.rst:7
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Here is only listed base information about Dispatcher. All about writing "
|
||||
"handlers, filters and etc. you can found in next pages:"
|
||||
"handlers, filters and etc. you can find in next pages:"
|
||||
msgstr ""
|
||||
"Тут наведена лише базова інформація про диспетчер. Усе про написання "
|
||||
"обробників, фільтрів і т.п. ви можете знайти на наступних сторінках:"
|
||||
|
|
@ -142,7 +143,8 @@ msgid "contextual data"
|
|||
msgstr "контекстні дані"
|
||||
|
||||
#: aiogram.dispatcher.dispatcher.Dispatcher.run_polling
|
||||
#: aiogram.dispatcher.dispatcher.Dispatcher.start_polling of
|
||||
#: aiogram.dispatcher.dispatcher.Dispatcher.start_polling
|
||||
#: aiogram.dispatcher.dispatcher.Dispatcher.stop_polling of
|
||||
msgid "Returns"
|
||||
msgstr "Повертає"
|
||||
|
||||
|
|
@ -161,6 +163,10 @@ msgid ""
|
|||
"used update types are enabled (resolved from handlers)"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.dispatcher.dispatcher.Dispatcher.stop_polling:1 of
|
||||
msgid "Execute this method if you want to stop polling programmatically"
|
||||
msgstr ""
|
||||
|
||||
#: ../../dispatcher/dispatcher.rst:18
|
||||
msgid "Simple usage"
|
||||
msgstr "Просте застосування"
|
||||
|
|
@ -190,3 +196,4 @@ msgstr ""
|
|||
|
||||
#~ msgid "`Observer <observer.html>`__"
|
||||
#~ msgstr "`Observer <observer.html>`__"
|
||||
|
||||
|
|
|
|||
|
|
@ -5,100 +5,103 @@
|
|||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: aiogram\n"
|
||||
"Project-Id-Version: aiogram\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-10-18 01:50+0300\n"
|
||||
"POT-Creation-Date: 2023-09-14 17:21+0300\n"
|
||||
"PO-Revision-Date: 2022-10-25 15:53+0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.10.3\n"
|
||||
"X-Generator: Poedit 3.1.1\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:3
|
||||
#: ../../dispatcher/filters/index.rst:5
|
||||
msgid "Filtering events"
|
||||
msgstr "Фільтрування подій"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:5
|
||||
#: ../../dispatcher/filters/index.rst:7
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Filters is needed for routing updates to the specific handler. Searching of "
|
||||
"handler is always stops on first match set of filters are pass."
|
||||
"Filters is needed for routing updates to the specific handler. Searching "
|
||||
"of handler is always stops on first match set of filters are pass. By "
|
||||
"default, all handlers has empty set of filters, so all updates will be "
|
||||
"passed to first handler that has empty set of filters."
|
||||
msgstr ""
|
||||
"Фільтри потрібні для маршрутизації оновлень до конкретного обробника "
|
||||
"(handler) . Пошук обробника (handler) завжди зупиняється після першого збігу "
|
||||
"набору фільтрів."
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:8
|
||||
msgid "*aiogram* has some builtin useful filters."
|
||||
msgstr "*aiogram* має декілька вбудованих корисних фільтрів."
|
||||
"(handler) . Пошук обробника (handler) завжди зупиняється після першого "
|
||||
"збігу набору фільтрів."
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:11
|
||||
#, fuzzy
|
||||
msgid "*aiogram* has some builtin useful filters or you can write own filters."
|
||||
msgstr "*aiogram* має декілька вбудованих корисних фільтрів."
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:14
|
||||
msgid "Builtin filters"
|
||||
msgstr "Вбудовані фільтри"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:13
|
||||
#: ../../dispatcher/filters/index.rst:16
|
||||
msgid "Here is list of builtin filters:"
|
||||
msgstr "Ось список вбудованих фільтрів:"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:27
|
||||
#: ../../dispatcher/filters/index.rst:29
|
||||
msgid "Writing own filters"
|
||||
msgstr "Написання власних фільтрів"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:29
|
||||
#: ../../dispatcher/filters/index.rst:31
|
||||
msgid "Filters can be:"
|
||||
msgstr "Фільтри бувають:"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:31
|
||||
#: ../../dispatcher/filters/index.rst:33
|
||||
msgid "Asynchronous function (:code:`async def my_filter(*args, **kwargs): pass`)"
|
||||
msgstr "Асинхронною функцією (:code:`async def my_filter(*args, **kwargs): pass`)"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:32
|
||||
#: ../../dispatcher/filters/index.rst:34
|
||||
msgid "Synchronous function (:code:`def my_filter(*args, **kwargs): pass`)"
|
||||
msgstr "Синхронною функцією (:code:`def my_filter(*args, **kwargs): pass`)"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:33
|
||||
#: ../../dispatcher/filters/index.rst:35
|
||||
msgid "Anonymous function (:code:`lambda event: True`)"
|
||||
msgstr "Анонімною функцією (:code:`lambda event: True`)"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:34
|
||||
#: ../../dispatcher/filters/index.rst:36
|
||||
msgid "Any awaitable object"
|
||||
msgstr ""
|
||||
"Будь-яким очікуваним об'єктом (awaitable object, об'єкт, який може бути "
|
||||
"використаний в :code:`await` виразі)"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:35
|
||||
#: ../../dispatcher/filters/index.rst:37
|
||||
msgid "Subclass of :class:`aiogram.filters.base.Filter`"
|
||||
msgstr "Підкласом :class:`aiogram.filters.base.Filter`"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:36
|
||||
#: ../../dispatcher/filters/index.rst:38
|
||||
msgid "Instances of :ref:`MagicFilter <magic-filters>`"
|
||||
msgstr "Екземпляром :ref:`MagicFilter <magic-filters>`"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:38
|
||||
#: ../../dispatcher/filters/index.rst:40
|
||||
msgid ""
|
||||
"and should return bool or dict. If the dictionary is passed as result of filter "
|
||||
"- resulted data will be propagated to the next filters and handler as keywords "
|
||||
"arguments."
|
||||
"and should return bool or dict. If the dictionary is passed as result of "
|
||||
"filter - resulted data will be propagated to the next filters and handler"
|
||||
" as keywords arguments."
|
||||
msgstr ""
|
||||
"і має повертати bool або dict. Якщо словник передається як результат фільтра, "
|
||||
"отримані дані будуть передані до наступних фільтрів і обробника як аргументи "
|
||||
"ключових слів."
|
||||
"і має повертати bool або dict. Якщо словник передається як результат "
|
||||
"фільтра, отримані дані будуть передані до наступних фільтрів і обробника "
|
||||
"як аргументи ключових слів."
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:43
|
||||
#: ../../dispatcher/filters/index.rst:45
|
||||
msgid "Base class for own filters"
|
||||
msgstr "Базовий клас для власних фільтрів"
|
||||
|
||||
#: aiogram.filters.base.Filter:1 of
|
||||
msgid ""
|
||||
"If you want to register own filters like builtin filters you will need to write "
|
||||
"subclass of this class with overriding the :code:`__call__` method and adding "
|
||||
"filter attributes."
|
||||
"If you want to register own filters like builtin filters you will need to"
|
||||
" write subclass of this class with overriding the :code:`__call__` method"
|
||||
" and adding filter attributes."
|
||||
msgstr ""
|
||||
"Якщо Ви хочете зареєструвати власні фільтри, як вбудовані фільтри, Вам потрібно "
|
||||
"буде написати підклас цього класу з заміною методу :code:`__call__` і "
|
||||
"додаванням атрибутів фільтра."
|
||||
"Якщо Ви хочете зареєструвати власні фільтри, як вбудовані фільтри, Вам "
|
||||
"потрібно буде написати підклас цього класу з заміною методу "
|
||||
":code:`__call__` і додаванням атрибутів фільтра."
|
||||
|
||||
#: aiogram.filters.base.Filter.__call__:1 of
|
||||
msgid "This method should be overridden."
|
||||
|
|
@ -118,72 +121,78 @@ msgstr ":class:`bool` or :class:`Dict[str, Any]`"
|
|||
|
||||
#: aiogram.filters.base.Filter.update_handler_flags:1 of
|
||||
msgid ""
|
||||
"Also if you want to extend handler flags with using this filter you should "
|
||||
"implement this method"
|
||||
"Also if you want to extend handler flags with using this filter you "
|
||||
"should implement this method"
|
||||
msgstr ""
|
||||
"Крім того, якщо ви хочете розширити маркери обробника (handler) за допомогою "
|
||||
"цього фільтра, вам слід реалізувати цей метод"
|
||||
"Крім того, якщо ви хочете розширити маркери обробника (handler) за "
|
||||
"допомогою цього фільтра, вам слід реалізувати цей метод"
|
||||
|
||||
#: aiogram.filters.base.Filter.update_handler_flags of
|
||||
msgid "Parameters"
|
||||
msgstr "Параметри"
|
||||
|
||||
#: aiogram.filters.base.Filter.update_handler_flags:3 of
|
||||
#: aiogram.filters.base.Filter.update_handler_flags:4 of
|
||||
msgid "existing flags, can be updated directly"
|
||||
msgstr "існуючі маркери, можна оновити безпосередньо"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:51
|
||||
#: ../../dispatcher/filters/index.rst:53
|
||||
msgid "Own filter example"
|
||||
msgstr "Приклад власного фільтра"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:53
|
||||
#: ../../dispatcher/filters/index.rst:55
|
||||
msgid "For example if you need to make simple text filter:"
|
||||
msgstr "Наприклад, якщо Вам потрібно створити простий текстовий фільтр:"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:60
|
||||
#: ../../dispatcher/filters/index.rst:62
|
||||
msgid "Combining Filters"
|
||||
msgstr "Комбінування фільтрів"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:62
|
||||
#: ../../dispatcher/filters/index.rst:64
|
||||
msgid "In general, all filters can be combined in two ways"
|
||||
msgstr "Взагалом, усі фільтри можна комбінувати двома способами"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:66
|
||||
#: ../../dispatcher/filters/index.rst:68
|
||||
msgid "Recommended way"
|
||||
msgstr "Рекомендований спосіб"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:68
|
||||
#: ../../dispatcher/filters/index.rst:70
|
||||
msgid ""
|
||||
"If you specify multiple filters in a row, it will be checked with an \"and\" "
|
||||
"condition:"
|
||||
"If you specify multiple filters in a row, it will be checked with an "
|
||||
"\"and\" condition:"
|
||||
msgstr ""
|
||||
"Якщо Ви вкажете кілька фільтрів поспіль, це буде перевірено умовою \"and\" :"
|
||||
"Якщо Ви вкажете кілька фільтрів поспіль, це буде перевірено умовою "
|
||||
"\"and\" :"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:75
|
||||
#: ../../dispatcher/filters/index.rst:77
|
||||
msgid ""
|
||||
"Also, if you want to use two alternative ways to run the same handler (\"or\" "
|
||||
"condition) you can register the handler twice or more times as you like"
|
||||
"Also, if you want to use two alternative ways to run the same handler "
|
||||
"(\"or\" condition) you can register the handler twice or more times as "
|
||||
"you like"
|
||||
msgstr ""
|
||||
"Крім того, якщо ви хочете використовувати два альтернативні способи запуску "
|
||||
"одного обробника (умова \"or\"), ви можете зареєструвати обробник двічі або "
|
||||
"більше разів, як вам подобається"
|
||||
"Крім того, якщо ви хочете використовувати два альтернативні способи "
|
||||
"запуску одного обробника (умова \"or\"), ви можете зареєструвати обробник"
|
||||
" двічі або більше разів, як вам подобається"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:84
|
||||
#: ../../dispatcher/filters/index.rst:86
|
||||
msgid ""
|
||||
"Also sometimes you will need to invert the filter result, for example you have "
|
||||
"an *IsAdmin* filter and you want to check if the user is not an admin"
|
||||
"Also sometimes you will need to invert the filter result, for example you"
|
||||
" have an *IsAdmin* filter and you want to check if the user is not an "
|
||||
"admin"
|
||||
msgstr ""
|
||||
"Також іноді Вам потрібно буде інвертувати результат фільтра, наприклад, у вас є "
|
||||
"фільтр *IsAdmin* і ви хочете перевірити, чи користувач не є адміністратором"
|
||||
"Також іноді Вам потрібно буде інвертувати результат фільтра, наприклад, у"
|
||||
" вас є фільтр *IsAdmin* і ви хочете перевірити, чи користувач не є "
|
||||
"адміністратором"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:93
|
||||
#: ../../dispatcher/filters/index.rst:95
|
||||
msgid "Another possible way"
|
||||
msgstr "Інший можливий спосіб"
|
||||
|
||||
#: ../../dispatcher/filters/index.rst:95
|
||||
#: ../../dispatcher/filters/index.rst:97
|
||||
msgid ""
|
||||
"An alternative way is to combine using special functions (:func:`and_f`, :func:"
|
||||
"`or_f`, :func:`invert_f` from :code:`aiogram.filters` module):"
|
||||
"An alternative way is to combine using special functions (:func:`and_f`, "
|
||||
":func:`or_f`, :func:`invert_f` from :code:`aiogram.filters` module):"
|
||||
msgstr ""
|
||||
"Альтернативним способом є об’єднання за допомогою спеціальних функцій (:func:"
|
||||
"`and_f`, :func:`or_f`, :func:`invert_f` з модуля :code:`aiogram.filters`):"
|
||||
"Альтернативним способом є об’єднання за допомогою спеціальних функцій "
|
||||
"(:func:`and_f`, :func:`or_f`, :func:`invert_f` з модуля "
|
||||
":code:`aiogram.filters`):"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,303 +3,422 @@
|
|||
# This file is distributed under the same license as the aiogram package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: aiogram \n"
|
||||
"Project-Id-Version: aiogram\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-08-26 23:17+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"POT-Creation-Date: 2023-09-14 18:12+0300\n"
|
||||
"PO-Revision-Date: 2023-09-14 18:34+0300\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: uk_UA\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 "
|
||||
"&& (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
"X-Generator: Poedit 3.3.2\n"
|
||||
|
||||
#: ../../migration_2_to_3.rst:3
|
||||
msgid "Migration FAQ (2.x -> 3.0)"
|
||||
msgstr ""
|
||||
msgstr "FAQ по переходу з версії 2.x на 3.0"
|
||||
|
||||
#: ../../migration_2_to_3.rst:7
|
||||
msgid "This guide is still in progress."
|
||||
msgstr ""
|
||||
msgstr "Цей посібник все ще в розробці."
|
||||
|
||||
#: ../../migration_2_to_3.rst:9
|
||||
msgid ""
|
||||
"This version introduces much many breaking changes and architectural "
|
||||
"improvements, helping to reduce global variables count in your code, "
|
||||
"provides useful mechanisms to separate your code to modules or just make "
|
||||
"sharable modules via packages on the PyPi, makes middlewares and filters "
|
||||
"more controllable and others."
|
||||
"This version introduces numerous breaking changes and architectural "
|
||||
"improvements. It helps reduce the count of global variables in your code, "
|
||||
"provides useful mechanisms to modularize your code, and enables the creation of "
|
||||
"shareable modules via packages on PyPI. It also makes middlewares and filters "
|
||||
"more controllable, among other improvements."
|
||||
msgstr ""
|
||||
"Ця версія містить численні суттєві зміни та архітектурні покращення. Вона "
|
||||
"допомагає зменшити кількість глобальних змінних у вашому коді, надає корисні "
|
||||
"механізми для модуляризації вашого коду та дозволяє створювати спільні модулі "
|
||||
"за допомогою пакетів на PyPI. Крім того, серед інших покращень, він робить "
|
||||
"проміжне програмне забезпечення (мідлварі) та фільтри більш контрольованими."
|
||||
|
||||
#: ../../migration_2_to_3.rst:14
|
||||
#: ../../migration_2_to_3.rst:15
|
||||
msgid ""
|
||||
"On this page you can read about points that changed corresponding to last"
|
||||
" stable 2.x version."
|
||||
"On this page, you can read about the changes made in relation to the last "
|
||||
"stable 2.x version."
|
||||
msgstr ""
|
||||
"На цій сторінці ви можете прочитати про зміни, внесені в останню стабільну "
|
||||
"версію 2.x."
|
||||
|
||||
#: ../../migration_2_to_3.rst:18
|
||||
#: ../../migration_2_to_3.rst:19
|
||||
msgid ""
|
||||
"This page is most like a detailed changelog than a migration guide, but "
|
||||
"it will be updated in the future."
|
||||
"This page more closely resembles a detailed changelog than a migration guide, "
|
||||
"but it will be updated in the future."
|
||||
msgstr ""
|
||||
"Ця сторінка більше нагадує детальний список змін, ніж посібник з міграції, але "
|
||||
"вона буде оновлюватися в майбутньому."
|
||||
|
||||
#: ../../migration_2_to_3.rst:21
|
||||
#: ../../migration_2_to_3.rst:22
|
||||
msgid ""
|
||||
"Feel free to contribute to this page, if you find something that is not "
|
||||
"mentioned here."
|
||||
msgstr ""
|
||||
"Не соромтеся зробити свій внесок у цю сторінку, якщо ви знайшли щось, про що "
|
||||
"тут не згадано."
|
||||
|
||||
#: ../../migration_2_to_3.rst:25
|
||||
#: ../../migration_2_to_3.rst:26
|
||||
msgid "Dispatcher"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:27
|
||||
#: ../../migration_2_to_3.rst:28
|
||||
msgid ""
|
||||
":class:`Dispatcher` class no longer accepts the `Bot` instance into the "
|
||||
"initializer, it should be passed to dispatcher only for starting polling "
|
||||
"or handling event from webhook. Also this way adds possibility to use "
|
||||
"multiple bot instances at the same time (\"multibot\")"
|
||||
"The :class:`Dispatcher` class no longer accepts a `Bot` instance in its "
|
||||
"initializer. Instead, the `Bot` instance should be passed to the dispatcher "
|
||||
"only for starting polling or handling events from webhooks. This approach also "
|
||||
"allows for the use of multiple bot instances simultaneously (\"multibot\")."
|
||||
msgstr ""
|
||||
"Клас :class:`Dispatcher` більше не приймає екземпляр `Bot` у своєму "
|
||||
"ініціалізаторі. Замість цього екземпляр `Bot` слід передавати диспетчеру тільки "
|
||||
"для запуску полінгу або обробки подій з вебхуків. Такий підхід також дозволяє "
|
||||
"використовувати декілька екземплярів бота одночасно (\"мультибот\")."
|
||||
|
||||
#: ../../migration_2_to_3.rst:30
|
||||
#: ../../migration_2_to_3.rst:32
|
||||
msgid ""
|
||||
":class:`Dispatcher` now can be extended with another Dispatcher-like "
|
||||
"thing named :class:`Router` (:ref:`Read more » <Nested routers>`). With "
|
||||
"routes you can easily separate your code to multiple modules and may be "
|
||||
"share this modules between projects."
|
||||
":class:`Dispatcher` now can be extended with another Dispatcher-like thing "
|
||||
"named :class:`Router` (:ref:`Read more » <Nested routers>`)."
|
||||
msgstr ""
|
||||
"Клас :class:`Dispatcher` тепер можна розширити ще одним об'єктом на кшталт "
|
||||
"диспетчера з назвою :class:`Router` (:ref:`Детальніше » <Nested routers>`)."
|
||||
|
||||
#: ../../migration_2_to_3.rst:34
|
||||
msgid ""
|
||||
"With routes, you can easily modularize your code and potentially share these "
|
||||
"modules between projects."
|
||||
msgstr ""
|
||||
"За допомогою роутерів ви можете легко модулювати свій код і потенційно "
|
||||
"перевикористовувати ці модулі між проектами."
|
||||
|
||||
#: ../../migration_2_to_3.rst:35
|
||||
msgid ""
|
||||
"Removed the **_handler** suffix from all event handler decorators and "
|
||||
"registering methods. (:ref:`Read more » <Event observers>`)"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:36
|
||||
msgid ""
|
||||
"Executor entirely removed, now you can use Dispatcher directly to start "
|
||||
"polling or webhook."
|
||||
msgstr ""
|
||||
"Видалено суфікс **_handler** з усіх декораторів обробників подій та методів "
|
||||
"реєстрації. (:ref:`Детальніше » <Event observers>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:37
|
||||
msgid ""
|
||||
"Throttling method is completely removed, now you can use middlewares to "
|
||||
"control the execution context and use any throttling mechanism you want."
|
||||
"The Executor has been entirely removed; you can now use the Dispatcher directly "
|
||||
"to start polling or handle webhooks."
|
||||
msgstr ""
|
||||
"Executor було повністю вилучено; тепер ви можете використовувати Dispatcher "
|
||||
"безпосередньо для запуску полінгу або обробки вебхуків."
|
||||
|
||||
#: ../../migration_2_to_3.rst:39
|
||||
#: ../../migration_2_to_3.rst:38
|
||||
msgid ""
|
||||
"Removed global context variables from the API types, Bot and Dispatcher "
|
||||
"object, from now if you want to get current bot instance inside handlers "
|
||||
"or filters you should accept the argument :code:`bot: Bot` and use it "
|
||||
"instead of :code:`Bot.get_current()` Inside middlewares it can be "
|
||||
"accessed via :code:`data[\"bot\"]`."
|
||||
"The throttling method has been completely removed; you can now use middlewares "
|
||||
"to control the execution context and implement any throttling mechanism you "
|
||||
"desire."
|
||||
msgstr ""
|
||||
"Метод дроселювання (Throttling) повністю вилучено; тепер ви можете "
|
||||
"використовувати проміжне програмне забезпечення (middleware) для керування "
|
||||
"контекстом виконання та реалізовувати будь-який механізм дроселювання за вашим "
|
||||
"бажанням."
|
||||
|
||||
#: ../../migration_2_to_3.rst:43
|
||||
#: ../../migration_2_to_3.rst:40
|
||||
msgid ""
|
||||
"Now to skip pending updates, you should call the "
|
||||
":class:`aiogram.methods.delete_webhook.DeleteWebhook` method directly "
|
||||
"instead of passing :code:`skip_updates=True` to start polling method."
|
||||
"Removed global context variables from the API types, Bot and Dispatcher object, "
|
||||
"From now on, if you want to access the current bot instance within handlers or "
|
||||
"filters, you should accept the argument :code:`bot: Bot` and use it instead of :"
|
||||
"code:`Bot.get_current()`. In middlewares, it can be accessed via :code:"
|
||||
"`data[\"bot\"]`."
|
||||
msgstr ""
|
||||
"Вилучено глобальні контекстні змінні з типів API, об'єктів Bot та Dispatcher, "
|
||||
"Відтепер, якщо ви хочете отримати доступ до поточного екземпляру бота в "
|
||||
"обробниках або фільтрах, ви повинні приймати аргумент :code:`bot: Bot` і "
|
||||
"використовувати його замість :code:`Bot.get_current()`. У проміжному "
|
||||
"програмному забезпеченні (middleware) доступ до нього можна отримати через :"
|
||||
"code:`data[\"bot\"]`."
|
||||
|
||||
#: ../../migration_2_to_3.rst:47
|
||||
msgid "Filtering events"
|
||||
#: ../../migration_2_to_3.rst:44
|
||||
msgid ""
|
||||
"To skip pending updates, you should now call the :class:`aiogram.methods."
|
||||
"delete_webhook.DeleteWebhook` method directly, rather than passing :code:"
|
||||
"`skip_updates=True` to the start polling method."
|
||||
msgstr ""
|
||||
"Щоб пропустити очікувані оновлення, тепер вам слід викликати метод :class:"
|
||||
"`aiogram.methods.delete_webhook.DeleteWebhook` безпосередньо, а не передавати :"
|
||||
"code:`skip_updates=True` до методу запуску полінгу."
|
||||
|
||||
#: ../../migration_2_to_3.rst:49
|
||||
msgid ""
|
||||
"Keyword filters can no more be used, use filters explicitly. (`Read more "
|
||||
"» <https://github.com/aiogram/aiogram/issues/942>`_)"
|
||||
msgstr ""
|
||||
msgid "Filtering events"
|
||||
msgstr "Фільтрація подій"
|
||||
|
||||
#: ../../migration_2_to_3.rst:50
|
||||
#: ../../migration_2_to_3.rst:51
|
||||
msgid ""
|
||||
"In due to keyword filters was removed all enabled by default filters "
|
||||
"(state and content_type now is not enabled), so you should specify them "
|
||||
"explicitly if you want to use. For example instead of using "
|
||||
":code:`@dp.message_handler(content_types=ContentType.PHOTO)` you should "
|
||||
"use :code:`@router.message(F.photo)`"
|
||||
"Keyword filters can no longer be used; use filters explicitly. (`Read more » "
|
||||
"<https://github.com/aiogram/aiogram/issues/942>`_)"
|
||||
msgstr ""
|
||||
"Фільтри за ключовими словами більше не можна використовувати; використовуйте "
|
||||
"фільтри явно. (`Детальніше » <https://github.com/aiogram/aiogram/issues/942>`_)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:54
|
||||
#: ../../migration_2_to_3.rst:52
|
||||
msgid ""
|
||||
"Most of common filters is replaced by \"magic filter\". (:ref:`Read more "
|
||||
"» <magic-filters>`)"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:55
|
||||
msgid ""
|
||||
"Now by default message handler receives any content type, if you want "
|
||||
"specific one just add the filters (Magic or any other)"
|
||||
"Due to the removal of keyword filters, all previously enabled-by-default "
|
||||
"filters (such as state and content_type) are now disabled. You must specify "
|
||||
"them explicitly if you wish to use them. For example instead of using :code:"
|
||||
"`@dp.message_handler(content_types=ContentType.PHOTO)` you should use :code:"
|
||||
"`@router.message(F.photo)`"
|
||||
msgstr ""
|
||||
"У зв'язку з вилученням keyword фільтрів, всі раніше ввімкнені за замовчуванням "
|
||||
"фільтри (такі як state і content_type) тепер вимкнено. Якщо ви бажаєте їх "
|
||||
"використовувати, ви повинні вказати їх явно. Наприклад, замість :code:`@dp."
|
||||
"message_handler(content_types=ContentType.PHOTO)` слід використовувати :code:"
|
||||
"`@router.message(F.photo)`."
|
||||
|
||||
#: ../../migration_2_to_3.rst:57
|
||||
msgid ""
|
||||
"State filter now is not enabled by default, that's mean if you using "
|
||||
":code:`state=\"*\"` in v2 then you should not pass any state filter in "
|
||||
"v3, and vice versa, if the state in v2 is not specified now you should "
|
||||
"specify the state."
|
||||
"Most common filters have been replaced by the \"magic filter.\" (:ref:`Read "
|
||||
"more » <magic-filters>`)"
|
||||
msgstr ""
|
||||
"Більшість звичайних фільтрів було замінено на \"магічний фільтр\". (:ref:`Детальніше "
|
||||
"далі » <magic-filters>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:58
|
||||
msgid ""
|
||||
"By default, the message handler now receives any content type. If you want a "
|
||||
"specific one, simply add the appropriate filters (Magic or any other)."
|
||||
msgstr ""
|
||||
"За замовчуванням обробник повідомлень тепер отримує будь-який тип вмісту. Якщо "
|
||||
"вам потрібен певний тип, просто додайте відповідні фільтри (Magic або будь-який "
|
||||
"інший)."
|
||||
|
||||
#: ../../migration_2_to_3.rst:60
|
||||
msgid ""
|
||||
"Added possibility to register per-router global filters, that helps to "
|
||||
"reduces the number of repetitions in the code and makes easily way to "
|
||||
"control for what each router will be used."
|
||||
"The state filter is no longer enabled by default. This means that if you used :"
|
||||
"code:`state=\"*\"` in v2, you should not pass any state filter in v3. "
|
||||
"Conversely, if the state was not specified in v2, you will now need to specify "
|
||||
"it in v3."
|
||||
msgstr ""
|
||||
"Фільтр стану більше не вмикається за замовчуванням. Це означає, що якщо ви "
|
||||
"використовували :code:`state=\"*\"` у v2, вам не слід передавати фільтр стану у "
|
||||
"v3. І навпаки, якщо стан не було вказано у v2, вам потрібно буде вказати його у "
|
||||
"v3."
|
||||
|
||||
#: ../../migration_2_to_3.rst:66
|
||||
#: ../../migration_2_to_3.rst:63
|
||||
msgid ""
|
||||
"Added the possibility to register global filters for each router, which helps "
|
||||
"to reduce code repetition and provides an easier way to control the purpose of "
|
||||
"each router."
|
||||
msgstr ""
|
||||
"Додано можливість реєстрації глобальних фільтрів для кожного роутера, що "
|
||||
"допомагає зменшити повторення коду і полегшує контроль призначення кожного "
|
||||
"роутера."
|
||||
|
||||
#: ../../migration_2_to_3.rst:69
|
||||
msgid "Bot API"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:68
|
||||
#: ../../migration_2_to_3.rst:71
|
||||
msgid ""
|
||||
"Now all API methods is classes with validation (via `pydantic "
|
||||
"<https://docs.pydantic.dev/>`_) (all API calls is also available as "
|
||||
"methods in the Bot class)."
|
||||
"All API methods are now classes with validation, implemented via `pydantic "
|
||||
"<https://docs.pydantic.dev/>`. These API calls are also available as methods in "
|
||||
"the Bot class."
|
||||
msgstr ""
|
||||
"Всі методи API тепер є класами з валідацією, реалізованими через `pydantic "
|
||||
"<https://docs.pydantic.dev/>`. Ці виклики API також доступні як методи в класі "
|
||||
"Bot."
|
||||
|
||||
#: ../../migration_2_to_3.rst:70
|
||||
#: ../../migration_2_to_3.rst:74
|
||||
msgid ""
|
||||
"Added more pre-defined Enums and moved into `aiogram.enums` sub-package. "
|
||||
"For example chat type enum now is :class:`aiogram.enums.ChatType` instead"
|
||||
" of :class:`aiogram.types.chat.ChatType`. (:ref:`Read more » <enums>`)"
|
||||
"More pre-defined Enums have been added and moved to the `aiogram.enums` sub-"
|
||||
"package. For example, the chat type enum is now :class:`aiogram.enums.ChatType` "
|
||||
"instead of :class:`aiogram.types.chat.ChatType`."
|
||||
msgstr ""
|
||||
"Додано більше попередньо визначених enums та переміщено їх до підпакету "
|
||||
"`aiogram.enums`. Наприклад, enum типу чату тепер має вигляд :class:`aiogram."
|
||||
"enums.ChatType` замість :class:`aiogram.types.chat.ChatType`."
|
||||
|
||||
#: ../../migration_2_to_3.rst:73
|
||||
#: ../../migration_2_to_3.rst:76
|
||||
msgid ""
|
||||
"Separated HTTP client session into container that can be reused between "
|
||||
"different Bot instances in the application."
|
||||
"The HTTP client session has been separated into a container that can be reused "
|
||||
"across different Bot instances within the application."
|
||||
msgstr ""
|
||||
"Клієнтська сесія HTTP була відокремлена в контейнер, який можна повторно "
|
||||
"використовувати для різних екземплярів бота в додатку."
|
||||
|
||||
#: ../../migration_2_to_3.rst:75
|
||||
#: ../../migration_2_to_3.rst:78
|
||||
msgid ""
|
||||
"API Exceptions is no more classified by specific message in due to "
|
||||
"Telegram has no documented error codes. But all errors is classified by "
|
||||
"HTTP status code and for each method only one case can be caused with the"
|
||||
" same code, so in most cases you should check that only error type (by "
|
||||
"status-code) without checking error message. (:ref:`Read more » <error-"
|
||||
"types>`)"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:82
|
||||
msgid "Middlewares"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:84
|
||||
msgid ""
|
||||
"Middlewares can now control a execution context, e.g. using context "
|
||||
"managers (:ref:`Read more » <middlewares>`)"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:85
|
||||
msgid ""
|
||||
"All contextual data now is shared between middlewares, filters and "
|
||||
"handlers to end-to-end use. For example now you can easily pass some data"
|
||||
" into context inside middleware and get it in the filters layer as the "
|
||||
"same way as in the handlers via keyword arguments."
|
||||
"API Exceptions are no longer classified by specific messages, as Telegram has "
|
||||
"no documented error codes. However, all errors are classified by HTTP status "
|
||||
"codes, and for each method, only one type of error can be associated with a "
|
||||
"given code. Therefore, in most cases, you should check only the error type (by "
|
||||
"status code) without inspecting the error message."
|
||||
msgstr ""
|
||||
"Виключення API більше не класифікуються за конкретними повідомленнями, оскільки "
|
||||
"Telegram не має задокументованих кодів помилок. Проте всі помилки "
|
||||
"класифікуються за кодами статусу HTTP, і для кожного методу з певним кодом може "
|
||||
"бути пов'язаний лише один тип помилки. Тому в більшості випадків слід "
|
||||
"перевіряти лише тип помилки (за кодом статусу), не перевіряючи повідомлення про "
|
||||
"помилку."
|
||||
|
||||
#: ../../migration_2_to_3.rst:88
|
||||
msgid ""
|
||||
"Added mechanism named **flags**, that helps to customize handler behavior"
|
||||
" in conjunction with middlewares. (:ref:`Read more » <flags>`)"
|
||||
msgstr ""
|
||||
msgid "Middlewares"
|
||||
msgstr "Проміжне ПО (Middlewares)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:93
|
||||
msgid "Keyboard Markup"
|
||||
#: ../../migration_2_to_3.rst:90
|
||||
msgid ""
|
||||
"Middlewares can now control an execution context, e.g., using context managers. "
|
||||
"(:ref:`Read more » <middlewares>`)"
|
||||
msgstr ""
|
||||
"Проміжне програмне забезпечення тепер може керувати контекстом виконання, "
|
||||
"наприклад, за допомогою менеджерів контексту. (:ref:`Детальніше » "
|
||||
"<middlewares>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:92
|
||||
msgid ""
|
||||
"All contextual data is now shared end-to-end between middlewares, filters, and "
|
||||
"handlers. For example now you can easily pass some data into context inside "
|
||||
"middleware and get it in the filters layer as the same way as in the handlers "
|
||||
"via keyword arguments."
|
||||
msgstr ""
|
||||
"Всі контекстні дані тепер наскрізно використовуються між проміжним програмним "
|
||||
"забезпеченням, фільтрами та обробниками. Наприклад, тепер ви можете легко "
|
||||
"передати деякі дані в контекст у проміжному програмному забезпеченні і отримати "
|
||||
"їх у шарі фільтрів так само, як і в обробниках через аргументи ключових слів."
|
||||
|
||||
#: ../../migration_2_to_3.rst:95
|
||||
msgid ""
|
||||
"Now :class:`aiogram.types.inline_keyboard_markup.InlineKeyboardMarkup` "
|
||||
"and :class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup` has "
|
||||
"no methods to extend it, instead you have to use markup builders "
|
||||
":class:`aiogram.utils.keyboard.ReplyKeyboardBuilder` and "
|
||||
":class:`aiogram.utils.keyboard.KeyboardBuilder` respectively (:ref:`Read "
|
||||
"more » <Keyboard builder>`)"
|
||||
"Added a mechanism named **flags** that helps customize handler behavior in "
|
||||
"conjunction with middlewares. (:ref:`Read more » <flags>`)"
|
||||
msgstr ""
|
||||
"Додано механізм з назвою **flags**, який допомагає налаштовувати поведінку "
|
||||
"обробника у поєднанні з проміжним програмним забезпеченням. (:ref:`Детальніше "
|
||||
"про » <flags>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:103
|
||||
msgid "Callbacks data"
|
||||
msgstr ""
|
||||
#: ../../migration_2_to_3.rst:100
|
||||
msgid "Keyboard Markup"
|
||||
msgstr "Розмітка клавіатури"
|
||||
|
||||
#: ../../migration_2_to_3.rst:105
|
||||
#: ../../migration_2_to_3.rst:102
|
||||
msgid ""
|
||||
"Callback data factory now is strictly typed via `pydantic "
|
||||
"<https://docs.pydantic.dev/>`_ models (:ref:`Read more » <Callback data "
|
||||
"factory>`)"
|
||||
"Now :class:`aiogram.types.inline_keyboard_markup.InlineKeyboardMarkup` and :"
|
||||
"class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup` no longer have "
|
||||
"methods for extension, instead you have to use markup builders :class:`aiogram."
|
||||
"utils.keyboard.ReplyKeyboardBuilder` and :class:`aiogram.utils.keyboard."
|
||||
"KeyboardBuilder` respectively (:ref:`Read more » <Keyboard builder>`)"
|
||||
msgstr ""
|
||||
"Тепер :class:`aiogram.types.inline_keyboard_markup.InlineKeyboardMarkup` та :"
|
||||
"class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup` більше не мають "
|
||||
"методів для розширення, натомість вам слід використовувати будівники розмітки :"
|
||||
"class:`aiogram.utils.keyboard.ReplyKeyboardBuilder` та :class:`aiogram.utils.keyboard.InlineKeyboardBuilder` "
|
||||
"відповідно (:ref:`Детальніше » <Keyboard builder>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:110
|
||||
msgid "Finite State machine"
|
||||
msgid "Callbacks data"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:112
|
||||
msgid ""
|
||||
"State filter will no more added to all handlers, you will need to specify"
|
||||
" state if you want"
|
||||
"The callback data factory is now strictly typed using `pydantic <https://docs."
|
||||
"pydantic.dev/>`_ models. (:ref:`Read more » <Callback data factory>`)"
|
||||
msgstr ""
|
||||
"Фабрику даних зворотного виклику тепер строго типізовано за допомогою моделей "
|
||||
"`pydantic <https://docs.pydantic.dev/>`_. (:ref:`Детальніше » <Callback data "
|
||||
"factory>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:113
|
||||
#: ../../migration_2_to_3.rst:117
|
||||
msgid "Finite State machine"
|
||||
msgstr "Скінченний автомат"
|
||||
|
||||
#: ../../migration_2_to_3.rst:119
|
||||
msgid ""
|
||||
"Added possibility to change FSM strategy, for example if you want to "
|
||||
"control state for each user in chat topics instead of user in chat you "
|
||||
"can specify it in the Dispatcher."
|
||||
"State filters will no longer be automatically added to all handlers; you will "
|
||||
"need to specify the state if you want to use it."
|
||||
msgstr ""
|
||||
"Фільтри станів більше не будуть автоматично додаватися до всіх обробників; вам "
|
||||
"потрібно буде вказати стан, якщо ви хочете його використати."
|
||||
|
||||
#: ../../migration_2_to_3.rst:115
|
||||
#: ../../migration_2_to_3.rst:121
|
||||
msgid ""
|
||||
"Now :class:`aiogram.fsm.state.State` and "
|
||||
":class:`aiogram.fsm.state.StateGroup` don't have helper methods like "
|
||||
":code:`.set()`, :code:`.next()`, etc."
|
||||
"Added the possibility to change the FSM strategy. For example, if you want to "
|
||||
"control the state for each user based on chat topics rather than the user in a "
|
||||
"chat, you can specify this in the Dispatcher."
|
||||
msgstr ""
|
||||
"Додано можливість змінювати стратегію FSM. Наприклад, якщо ви хочете "
|
||||
"контролювати стан для кожного користувача на основі топіків чату, а не "
|
||||
"користувача в чаті, ви можете вказати це в Диспетчері."
|
||||
|
||||
#: ../../migration_2_to_3.rst:118
|
||||
#: ../../migration_2_to_3.rst:124
|
||||
msgid ""
|
||||
"Instead of this you should set states by passing them directly to "
|
||||
":class:`aiogram.fsm.context.FSMContext` (:ref:`Read more » <Finite State "
|
||||
"Machine>`)"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:120
|
||||
msgid ""
|
||||
"State proxy is deprecated, you should update the state data by calling "
|
||||
":code:`state.set_data(...)` and :code:`state.get_data()` respectively."
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:125
|
||||
msgid "Sending Files"
|
||||
"Now :class:`aiogram.fsm.state.State` and :class:`aiogram.fsm.state.StateGroup` "
|
||||
"don't have helper methods like :code:`.set()`, :code:`.next()`, etc."
|
||||
msgstr ""
|
||||
"Тепер :class:`aiogram.fsm.state.State` та :class:`aiogram.fsm.state.StateGroup` "
|
||||
"не мають допоміжних методів, таких як :code:`.set()`, :code:`.next()` тощо."
|
||||
|
||||
#: ../../migration_2_to_3.rst:127
|
||||
msgid ""
|
||||
"From now you should wrap sending files into InputFile object before send "
|
||||
"instead of passing IO object directly to the API method. (:ref:`Read more"
|
||||
" » <sending-files>`)"
|
||||
"Instead, you should set states by passing them directly to :class:`aiogram.fsm."
|
||||
"context.FSMContext` (:ref:`Read more » <Finite State Machine>`)"
|
||||
msgstr ""
|
||||
"Замість цього вам слід встановлювати стани, передаючи їх безпосередньо до :"
|
||||
"class:`aiogram.fsm.context.FSMContext` (:ref:`Детальніше » <Finite State "
|
||||
"Machine>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:132
|
||||
#: ../../migration_2_to_3.rst:129
|
||||
msgid ""
|
||||
"The state proxy is deprecated; you should update the state data by calling :"
|
||||
"code:`state.set_data(...)` and :code:`state.get_data()` respectively."
|
||||
msgstr ""
|
||||
"Проксі стану є застарілим; вам слід оновити дані стану, викликавши :code:`state."
|
||||
"set_data(...)` та :code:`state.get_data()` відповідно."
|
||||
|
||||
#: ../../migration_2_to_3.rst:134
|
||||
msgid "Sending Files"
|
||||
msgstr "Надсилання файлів"
|
||||
|
||||
#: ../../migration_2_to_3.rst:136
|
||||
msgid ""
|
||||
"From now on, you should wrap files in an InputFile object before sending them, "
|
||||
"instead of passing the IO object directly to the API method. (:ref:`Read more » "
|
||||
"<sending-files>`)"
|
||||
msgstr ""
|
||||
"Відтепер перед відправкою файлів слід обертати їх в об'єкт InputFile замість "
|
||||
"того, щоб передавати об'єкт вводу-виводу безпосередньо до методу API. (:ref:"
|
||||
"`Детальніше » <sending-files>`)"
|
||||
|
||||
#: ../../migration_2_to_3.rst:141
|
||||
msgid "Webhook"
|
||||
msgstr ""
|
||||
|
||||
#: ../../migration_2_to_3.rst:134
|
||||
msgid "Simplified aiohttp web app configuration"
|
||||
msgstr ""
|
||||
#: ../../migration_2_to_3.rst:143
|
||||
msgid "The aiohttp web app configuration has been simplified."
|
||||
msgstr "Спрощено налаштування веб-застосунку aiohttp."
|
||||
|
||||
#: ../../migration_2_to_3.rst:135
|
||||
#: ../../migration_2_to_3.rst:144
|
||||
msgid ""
|
||||
"By default added possibility to upload files when you use reply into "
|
||||
"webhook"
|
||||
"By default, the ability to upload files has been added when you use the reply "
|
||||
"function in a webhook."
|
||||
msgstr ""
|
||||
"За замовчуванням додана можливість завантажувати файли, коли ви використовуєте "
|
||||
"функцію відповіді у вебхук."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Callback data factory now is strictly"
|
||||
#~ " typed via `pydantic "
|
||||
#~ "<https://docs.pydantic.dev/>`_ models (:ref:`Read "
|
||||
#~ "more » <callback-data-factory>`)"
|
||||
#~ msgstr ""
|
||||
#: ../../migration_2_to_3.rst:148
|
||||
msgid "Telegram API Server"
|
||||
msgstr "Сервер Telegram API"
|
||||
|
||||
#: ../../migration_2_to_3.rst:150
|
||||
msgid ""
|
||||
"The `server` parameter has been moved from the `Bot` instance to `api` in "
|
||||
"`BaseSession`."
|
||||
msgstr ""
|
||||
"Параметр `server` було перенесено з екземпляра `Bot` до `api` в `BaseSession`."
|
||||
|
||||
#: ../../migration_2_to_3.rst:151
|
||||
msgid ""
|
||||
"The constant `aiogram.bot.api.TELEGRAM_PRODUCTION` has been moved to `aiogram."
|
||||
"client.telegram.PRODUCTION`."
|
||||
msgstr ""
|
||||
"Константа `aiogram.bot.api.TELEGRAM_PRODUCTION` була переміщена на `aiogram."
|
||||
"client.telegram.PRODUCTION`."
|
||||
|
|
|
|||
291
docs/locale/uk_UA/LC_MESSAGES/utils/media_group.po
Normal file
291
docs/locale/uk_UA/LC_MESSAGES/utils/media_group.po
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) 2023, aiogram Team
|
||||
# This file is distributed under the same license as the aiogram package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2023.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: aiogram \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-14 17:21+0300\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.12.1\n"
|
||||
|
||||
#: ../../utils/media_group.rst:3
|
||||
msgid "Media group builder"
|
||||
msgstr ""
|
||||
|
||||
#: ../../utils/media_group.rst:5
|
||||
msgid ""
|
||||
"This module provides a builder for media groups, it can be used to build "
|
||||
"media groups for "
|
||||
":class:`aiogram.types.input_media_photo.InputMediaPhoto`, "
|
||||
":class:`aiogram.types.input_media_video.InputMediaVideo`, "
|
||||
":class:`aiogram.types.input_media_document.InputMediaDocument` and "
|
||||
":class:`aiogram.types.input_media_audio.InputMediaAudio`."
|
||||
msgstr ""
|
||||
|
||||
#: ../../utils/media_group.rst:11
|
||||
msgid ""
|
||||
":class:`aiogram.types.input_media_animation.InputMediaAnimation` is not "
|
||||
"supported yet in the Bot API to send as media group."
|
||||
msgstr ""
|
||||
|
||||
#: ../../utils/media_group.rst:16
|
||||
msgid "Usage"
|
||||
msgstr ""
|
||||
|
||||
#: ../../utils/media_group.rst:30
|
||||
msgid ""
|
||||
"To send media group use "
|
||||
":meth:`aiogram.methods.send_media_group.SendMediaGroup` method, but when "
|
||||
"you use :class:`aiogram.utils.media_group.MediaGroupBuilder` you should "
|
||||
"pass ``media`` argument as ``media_group.build()``."
|
||||
msgstr ""
|
||||
|
||||
#: ../../utils/media_group.rst:34
|
||||
msgid ""
|
||||
"If you specify ``caption`` in "
|
||||
":class:`aiogram.utils.media_group.MediaGroupBuilder` it will be used as "
|
||||
"``caption`` for first media in group."
|
||||
msgstr ""
|
||||
|
||||
#: ../../utils/media_group.rst:43
|
||||
msgid "References"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add:1 of
|
||||
msgid "Add a media object to the media group."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video of
|
||||
msgid "Parameters"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add:3 of
|
||||
msgid ""
|
||||
"Keyword arguments for the media object. The available keyword arguments "
|
||||
"depend on the media type."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.build of
|
||||
msgid "Returns"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add:5
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:22
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:27
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:17
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:30 of
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:1 of
|
||||
msgid "Add an audio file to the media group."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:3
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:3 of
|
||||
msgid ""
|
||||
"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://<file_attach_name>' to upload a new one "
|
||||
"using multipart/form-data under <file_attach_name> name. :ref:`More "
|
||||
"information on Sending Files » <sending-files>`"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:3
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:3 of
|
||||
msgid ""
|
||||
"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://<file_attach_name>' to upload a new one "
|
||||
"using multipart/form-data under <file_attach_name> name."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:7
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:7 of
|
||||
msgid ":ref:`More information on Sending Files » <sending-files>`"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:8 of
|
||||
msgid ""
|
||||
"*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."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:12 of
|
||||
msgid ""
|
||||
"*Optional*. Caption of the audio to be sent, 0-1024 characters after "
|
||||
"entities parsing"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:14 of
|
||||
msgid ""
|
||||
"*Optional*. Mode for parsing entities in the audio caption. See "
|
||||
"`formatting options <https://core.telegram.org/bots/api#formatting-"
|
||||
"options>`_ for more details."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:17
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:22
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:13
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:21 of
|
||||
msgid ""
|
||||
"*Optional*. List of special entities that appear in the caption, which "
|
||||
"can be specified instead of *parse_mode*"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:19 of
|
||||
msgid "*Optional*. Duration of the audio in seconds"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:20 of
|
||||
msgid "*Optional*. Performer of the audio"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_audio:21 of
|
||||
msgid "*Optional*. Title of the audio"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:1 of
|
||||
msgid "Add a document to the media group."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:3
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:3 of
|
||||
msgid ""
|
||||
"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://<file_attach_name>' to upload a new one "
|
||||
"using multipart/form-data under <file_attach_name> name. :ref:`More "
|
||||
"information on Sending Files » <sending-files>`"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:8
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:8 of
|
||||
msgid ""
|
||||
"*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://<file_attach_name>' if the "
|
||||
"thumbnail was uploaded using multipart/form-data under "
|
||||
"<file_attach_name>. :ref:`More information on Sending Files » <sending-"
|
||||
"files>`"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:17 of
|
||||
msgid ""
|
||||
"*Optional*. Caption of the document to be sent, 0-1024 characters after "
|
||||
"entities parsing"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:19 of
|
||||
msgid ""
|
||||
"*Optional*. Mode for parsing entities in the document caption. See "
|
||||
"`formatting options <https://core.telegram.org/bots/api#formatting-"
|
||||
"options>`_ for more details."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_document:24 of
|
||||
msgid ""
|
||||
"*Optional*. Disables automatic server-side content type detection for "
|
||||
"files uploaded using multipart/form-data. Always :code:`True`, if the "
|
||||
"document is sent as part of an album."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:1 of
|
||||
msgid "Add a photo to the media group."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:8 of
|
||||
msgid ""
|
||||
"*Optional*. Caption of the photo to be sent, 0-1024 characters after "
|
||||
"entities parsing"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:10 of
|
||||
msgid ""
|
||||
"*Optional*. Mode for parsing entities in the photo caption. See "
|
||||
"`formatting options <https://core.telegram.org/bots/api#formatting-"
|
||||
"options>`_ for more details."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_photo:15 of
|
||||
msgid ""
|
||||
"*Optional*. Pass :code:`True` if the photo needs to be covered with a "
|
||||
"spoiler animation"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:1 of
|
||||
msgid "Add a video to the media group."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:16 of
|
||||
msgid ""
|
||||
"*Optional*. Caption of the video to be sent, 0-1024 characters after "
|
||||
"entities parsing"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:18 of
|
||||
msgid ""
|
||||
"*Optional*. Mode for parsing entities in the video caption. See "
|
||||
"`formatting options <https://core.telegram.org/bots/api#formatting-"
|
||||
"options>`_ for more details."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:23 of
|
||||
msgid "*Optional*. Video width"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:24 of
|
||||
msgid "*Optional*. Video height"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:25 of
|
||||
msgid "*Optional*. Video duration in seconds"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:26 of
|
||||
msgid ""
|
||||
"*Optional*. Pass :code:`True` if the uploaded video is suitable for "
|
||||
"streaming"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.add_video:28 of
|
||||
msgid ""
|
||||
"*Optional*. Pass :code:`True` if the video needs to be covered with a "
|
||||
"spoiler animation"
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.build:1 of
|
||||
msgid "Builds a list of media objects for a media group."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.build:3 of
|
||||
msgid "Adds the caption to the first media object if it is present."
|
||||
msgstr ""
|
||||
|
||||
#: aiogram.utils.media_group.MediaGroupBuilder.build:5 of
|
||||
msgid "List of media objects."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -6,16 +6,17 @@ Migration FAQ (2.x -> 3.0)
|
|||
|
||||
This guide is still in progress.
|
||||
|
||||
This version introduces much many breaking changes and architectural improvements,
|
||||
helping to reduce global variables count in your code, provides useful mechanisms
|
||||
to separate your code to modules or just make sharable modules via packages on the PyPi,
|
||||
makes middlewares and filters more controllable and others.
|
||||
This version introduces numerous breaking changes and architectural improvements.
|
||||
It helps reduce the count of global variables in your code, provides useful mechanisms
|
||||
to modularize your code, and enables the creation of shareable modules via packages on PyPI.
|
||||
It also makes middlewares and filters more controllable, among other improvements.
|
||||
|
||||
On this page you can read about points that changed corresponding to last stable 2.x version.
|
||||
|
||||
On this page, you can read about the changes made in relation to the last stable 2.x version.
|
||||
|
||||
.. note::
|
||||
|
||||
This page is most like a detailed changelog than a migration guide,
|
||||
This page more closely resembles a detailed changelog than a migration guide,
|
||||
but it will be updated in the future.
|
||||
|
||||
Feel free to contribute to this page, if you find something that is not mentioned here.
|
||||
|
|
@ -24,68 +25,74 @@ On this page you can read about points that changed corresponding to last stable
|
|||
Dispatcher
|
||||
==========
|
||||
|
||||
- :class:`Dispatcher` class no longer accepts the `Bot` instance into the initializer,
|
||||
it should be passed to dispatcher only for starting polling or handling event from webhook.
|
||||
Also this way adds possibility to use multiple bot instances at the same time ("multibot")
|
||||
- The :class:`Dispatcher` class no longer accepts a `Bot` instance in its initializer.
|
||||
Instead, the `Bot` instance should be passed to the dispatcher only for starting polling
|
||||
or handling events from webhooks. This approach also allows for the use of multiple bot
|
||||
instances simultaneously ("multibot").
|
||||
- :class:`Dispatcher` now can be extended with another Dispatcher-like
|
||||
thing named :class:`Router` (:ref:`Read more » <Nested routers>`).
|
||||
With routes you can easily separate your code to multiple modules
|
||||
and may be share this modules between projects.
|
||||
- With routes, you can easily modularize your code and potentially share these modules between projects.
|
||||
- Removed the **_handler** suffix from all event handler decorators and registering methods.
|
||||
(:ref:`Read more » <Event observers>`)
|
||||
- Executor entirely removed, now you can use Dispatcher directly to start polling or webhook.
|
||||
- Throttling method is completely removed, now you can use middlewares to control
|
||||
the execution context and use any throttling mechanism you want.
|
||||
- The Executor has been entirely removed; you can now use the Dispatcher directly to start poll the API or handle webhooks from it.
|
||||
- The throttling method has been completely removed; you can now use middlewares to control
|
||||
the execution context and implement any throttling mechanism you desire.
|
||||
- Removed global context variables from the API types, Bot and Dispatcher object,
|
||||
from now if you want to get current bot instance inside handlers or filters you should
|
||||
accept the argument :code:`bot: Bot` and use it instead of :code:`Bot.get_current()`
|
||||
Inside middlewares it can be accessed via :code:`data["bot"]`.
|
||||
- Now to skip pending updates, you should call the :class:`aiogram.methods.delete_webhook.DeleteWebhook` method directly instead of passing :code:`skip_updates=True` to start polling method.
|
||||
From now on, if you want to access the current bot instance within handlers or filters,
|
||||
you should accept the argument :code:`bot: Bot` and use it instead of :code:`Bot.get_current()`.
|
||||
In middlewares, it can be accessed via :code:`data["bot"]`.
|
||||
- To skip pending updates, you should now call the :class:`aiogram.methods.delete_webhook.DeleteWebhook` method directly, rather than passing :code:`skip_updates=True` to the start polling method.
|
||||
|
||||
|
||||
|
||||
Filtering events
|
||||
================
|
||||
|
||||
- Keyword filters can no more be used, use filters explicitly. (`Read more » <https://github.com/aiogram/aiogram/issues/942>`_)
|
||||
- In due to keyword filters was removed all enabled by default filters (state and content_type now is not enabled),
|
||||
so you should specify them explicitly if you want to use.
|
||||
- Keyword filters can no longer be used; use filters explicitly. (`Read more » <https://github.com/aiogram/aiogram/issues/942>`_)
|
||||
- Due to the removal of keyword filters, all previously enabled-by-default filters
|
||||
(such as state and content_type) are now disabled.
|
||||
You must specify them explicitly if you wish to use them.
|
||||
For example instead of using :code:`@dp.message_handler(content_types=ContentType.PHOTO)`
|
||||
you should use :code:`@router.message(F.photo)`
|
||||
- Most of common filters is replaced by "magic filter". (:ref:`Read more » <magic-filters>`)
|
||||
- Now by default message handler receives any content type,
|
||||
if you want specific one just add the filters (Magic or any other)
|
||||
- State filter now is not enabled by default, that's mean if you using :code:`state="*"` in v2
|
||||
then you should not pass any state filter in v3, and vice versa,
|
||||
if the state in v2 is not specified now you should specify the state.
|
||||
- Added possibility to register per-router global filters, that helps to reduces
|
||||
the number of repetitions in the code and makes easily way to control
|
||||
for what each router will be used.
|
||||
- Most common filters have been replaced with the "magic filter." (:ref:`Read more » <magic-filters>`)
|
||||
- By default, the message handler now receives any content type.
|
||||
If you want a specific one, simply add the appropriate filters (Magic or any other).
|
||||
- The state filter is no longer enabled by default. This means that if you used :code:`state="*"`
|
||||
in v2, you should not pass any state filter in v3.
|
||||
Conversely, if the state was not specified in v2, you will now need to specify it in v3.
|
||||
- Added the possibility to register global filters for each router, which helps to reduce code
|
||||
repetition and provides an easier way to control the purpose of each router.
|
||||
|
||||
|
||||
|
||||
Bot API
|
||||
=======
|
||||
|
||||
- Now all API methods is classes with validation (via `pydantic <https://docs.pydantic.dev/>`_)
|
||||
(all API calls is also available as methods in the Bot class).
|
||||
- Added more pre-defined Enums and moved into `aiogram.enums` sub-package. For example chat type enum now is
|
||||
:class:`aiogram.enums.ChatType` instead of :class:`aiogram.types.chat.ChatType`.
|
||||
(:ref:`Read more » <enums>`)
|
||||
- Separated HTTP client session into container that can be reused between different
|
||||
Bot instances in the application.
|
||||
- API Exceptions is no more classified by specific message in due to Telegram has no documented error codes.
|
||||
But all errors is classified by HTTP status code and for each method only one case can be caused with the same code,
|
||||
so in most cases you should check that only error type (by status-code) without checking error message.
|
||||
(:ref:`Read more » <error-types>`)
|
||||
- All API methods are now classes with validation, implemented via
|
||||
`pydantic <https://docs.pydantic.dev/>`.
|
||||
These API calls are also available as methods in the Bot class.
|
||||
- More pre-defined Enums have been added and moved to the `aiogram.enums` sub-package.
|
||||
For example, the chat type enum is now :class:`aiogram.enums.ChatType` instead of :class:`aiogram.types.chat.ChatType`.
|
||||
- The HTTP client session has been separated into a container that can be reused
|
||||
across different Bot instances within the application.
|
||||
- API Exceptions are no longer classified by specific messages,
|
||||
as Telegram has no documented error codes.
|
||||
However, all errors are classified by HTTP status codes, and for each method,
|
||||
only one type of error can be associated with a given code.
|
||||
Therefore, in most cases, you should check only the error type (by status code)
|
||||
without inspecting the error message.
|
||||
|
||||
|
||||
|
||||
Middlewares
|
||||
===========
|
||||
|
||||
- Middlewares can now control a execution context, e.g. using context managers (:ref:`Read more » <middlewares>`)
|
||||
- All contextual data now is shared between middlewares, filters and handlers to end-to-end use.
|
||||
- Middlewares can now control an execution context, e.g., using context managers.
|
||||
(:ref:`Read more » <middlewares>`)
|
||||
- All contextual data is now shared end-to-end between middlewares, filters, and handlers.
|
||||
For example now you can easily pass some data into context inside middleware and
|
||||
get it in the filters layer as the same way as in the handlers via keyword arguments.
|
||||
- Added mechanism named **flags**, that helps to customize handler behavior
|
||||
- Added a mechanism named **flags** that helps customize handler behavior
|
||||
in conjunction with middlewares. (:ref:`Read more » <flags>`)
|
||||
|
||||
|
||||
|
|
@ -93,7 +100,7 @@ Keyboard Markup
|
|||
===============
|
||||
|
||||
- Now :class:`aiogram.types.inline_keyboard_markup.InlineKeyboardMarkup`
|
||||
and :class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup` has no methods to extend it,
|
||||
and :class:`aiogram.types.reply_keyboard_markup.ReplyKeyboardMarkup` no longer have methods for extension,
|
||||
instead you have to use markup builders :class:`aiogram.utils.keyboard.ReplyKeyboardBuilder`
|
||||
and :class:`aiogram.utils.keyboard.KeyboardBuilder` respectively
|
||||
(:ref:`Read more » <Keyboard builder>`)
|
||||
|
|
@ -102,41 +109,43 @@ Keyboard Markup
|
|||
Callbacks data
|
||||
==============
|
||||
|
||||
- Callback data factory now is strictly typed via `pydantic <https://docs.pydantic.dev/>`_ models
|
||||
- The callback data factory is now strictly typed using `pydantic <https://docs.pydantic.dev/>`_ models.
|
||||
(:ref:`Read more » <Callback data factory>`)
|
||||
|
||||
|
||||
Finite State machine
|
||||
====================
|
||||
|
||||
- State filter will no more added to all handlers, you will need to specify state if you want
|
||||
- Added possibility to change FSM strategy, for example if you want to control state
|
||||
for each user in chat topics instead of user in chat you can specify it in the Dispatcher.
|
||||
- State filters will no longer be automatically added to all handlers;
|
||||
you will need to specify the state if you want to use it.
|
||||
- Added the possibility to change the FSM strategy. For example,
|
||||
if you want to control the state for each user based on chat topics rather than
|
||||
the user in a chat, you can specify this in the Dispatcher.
|
||||
- Now :class:`aiogram.fsm.state.State` and :class:`aiogram.fsm.state.StateGroup` don't have helper
|
||||
methods like :code:`.set()`, :code:`.next()`, etc.
|
||||
|
||||
Instead of this you should set states by passing them directly to
|
||||
- Instead, you should set states by passing them directly to
|
||||
:class:`aiogram.fsm.context.FSMContext` (:ref:`Read more » <Finite State Machine>`)
|
||||
- State proxy is deprecated, you should update the state data by calling
|
||||
- The state proxy is deprecated; you should update the state data by calling
|
||||
:code:`state.set_data(...)` and :code:`state.get_data()` respectively.
|
||||
|
||||
|
||||
Sending Files
|
||||
=============
|
||||
|
||||
- From now you should wrap sending files into InputFile object before send instead of passing
|
||||
IO object directly to the API method. (:ref:`Read more » <sending-files>`)
|
||||
- From now on, you should wrap files in an InputFile object before sending them,
|
||||
instead of passing the IO object directly to the API method. (:ref:`Read more » <sending-files>`)
|
||||
|
||||
|
||||
Webhook
|
||||
=======
|
||||
|
||||
- Simplified aiohttp web app configuration
|
||||
- By default added possibility to upload files when you use reply into webhook
|
||||
- The aiohttp web app configuration has been simplified.
|
||||
- By default, the ability to upload files has been added when you `make requests in response to updates <https://core.telegram.org/bots/faq#how-can-i-make-requests-in-response-to-updates>`_ (available for webhook only).
|
||||
|
||||
|
||||
Telegram API Server
|
||||
===================
|
||||
|
||||
- `server` param was moved from `Bot` instance to `api` in `BaseSession`.
|
||||
- `aiogram.bot.api.TELEGRAM_PRODUCTION` was moved to `aiogram.client.telegram.PRODUCTION`.
|
||||
- The `server` parameter has been moved from the `Bot` instance to `api` in `BaseSession`.
|
||||
- The constant `aiogram.bot.api.TELEGRAM_PRODUCTION` has been moved to `aiogram.client.telegram.PRODUCTION`.
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ classifiers = [
|
|||
"Topic :: Communications :: Chat",
|
||||
]
|
||||
dependencies = [
|
||||
"magic-filter~=1.0.11",
|
||||
"magic-filter>=1.0.12,<1.1",
|
||||
"aiohttp~=3.8.5",
|
||||
"pydantic>=2.1.1,<3",
|
||||
"aiofiles~=23.1.0",
|
||||
"pydantic>=2.4.1,<2.5",
|
||||
"aiofiles~=23.2.1",
|
||||
"certifi>=2023.7.22",
|
||||
"typing-extensions~=4.7.1",
|
||||
"typing-extensions~=4.8.0",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
|
|
@ -55,55 +55,55 @@ path = "aiogram/__meta__.py"
|
|||
[project.optional-dependencies]
|
||||
fast = [
|
||||
"uvloop>=0.17.0; (sys_platform == 'darwin' or sys_platform == 'linux') and platform_python_implementation != 'PyPy'",
|
||||
"aiodns>=3.0.0",
|
||||
]
|
||||
redis = [
|
||||
"redis~=4.6.0",
|
||||
"redis[hiredis]~=5.0.1",
|
||||
]
|
||||
proxy = [
|
||||
"aiohttp-socks~=0.8.0",
|
||||
"aiohttp-socks~=0.8.3",
|
||||
]
|
||||
i18n = [
|
||||
"Babel~=2.12.1",
|
||||
]
|
||||
cli = [
|
||||
"aiogram-cli~=1.0",
|
||||
"aiogram-cli~=1.0.3",
|
||||
]
|
||||
test = [
|
||||
"pytest~=7.4.0",
|
||||
"pytest-html~=3.2.0",
|
||||
"pytest-asyncio~=0.21.0",
|
||||
"pytest~=7.4.2",
|
||||
"pytest-html~=4.0.2",
|
||||
"pytest-asyncio~=0.21.1",
|
||||
"pytest-lazy-fixture~=0.6.3",
|
||||
"pytest-mock~=3.10.0",
|
||||
"pytest-mypy~=0.10.0",
|
||||
"pytest-cov~=4.0.0",
|
||||
"pytest-aiohttp~=1.0.4",
|
||||
"pytest-mock~=3.11.0",
|
||||
"pytest-mypy~=0.10.3",
|
||||
"pytest-cov~=4.1.0",
|
||||
"pytest-aiohttp~=1.0.5",
|
||||
"aresponses~=2.1.6",
|
||||
"pytz~=2022.7.1",
|
||||
"pycryptodomex~=3.18",
|
||||
"pytz~=2023.3",
|
||||
"pycryptodomex~=3.19.0",
|
||||
]
|
||||
docs = [
|
||||
"Sphinx~=7.1.1",
|
||||
"sphinx-intl~=2.0.1",
|
||||
"Sphinx~=7.2.6",
|
||||
"sphinx-intl~=2.1.0",
|
||||
"sphinx-autobuild~=2021.3.14",
|
||||
"sphinx-copybutton~=0.5.2",
|
||||
"furo~=2023.7.26",
|
||||
"furo~=2023.9.10",
|
||||
"Sphinx-Substitution-Extensions~=2022.2.16",
|
||||
"towncrier~=23.6.0",
|
||||
"pygments~=2.15.1",
|
||||
"pymdown-extensions~=10.1",
|
||||
"pygments~=2.16.1",
|
||||
"pymdown-extensions~=10.3",
|
||||
"markdown-include~=0.8.1",
|
||||
"Pygments~=2.15.1",
|
||||
"Pygments~=2.16.1",
|
||||
"sphinxcontrib-towncrier~=0.3.2a0",
|
||||
]
|
||||
dev = [
|
||||
"black~=23.7.0",
|
||||
"isort~=5.11",
|
||||
"ruff~=0.0.280",
|
||||
"mypy~=1.4.1",
|
||||
"black~=23.9.1",
|
||||
"isort~=5.12.0",
|
||||
"ruff~=0.0.291",
|
||||
"mypy~=1.5.1",
|
||||
"toml~=0.10.2",
|
||||
"pre-commit~=3.3.3",
|
||||
"towncrier~=23.6.0",
|
||||
"packaging~=23.0",
|
||||
"pre-commit~=3.4.0",
|
||||
"packaging~=23.1",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
|
@ -154,9 +154,6 @@ features = [
|
|||
"test",
|
||||
"cli",
|
||||
]
|
||||
#extra-dependencies = [
|
||||
# "butcher @ git+https://github.com/aiogram/butcher.git@v0.1.22",
|
||||
#]
|
||||
|
||||
[tool.hatch.envs.dev.scripts]
|
||||
update = [
|
||||
|
|
@ -240,6 +237,10 @@ asyncio_mode = "auto"
|
|||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
filterwarnings = [
|
||||
"error",
|
||||
"ignore::pytest.PytestUnraisableExceptionWarning",
|
||||
]
|
||||
|
||||
[tool.coverage.run]
|
||||
branch = false
|
||||
|
|
|
|||
|
|
@ -15,6 +15,26 @@ from aiogram.types import File, PhotoSize
|
|||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def bot():
|
||||
"""Override mocked bot fixture with real bot."""
|
||||
async with Bot("42:TEST").context() as bot:
|
||||
yield bot
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def mocked_bot():
|
||||
"""Mocked bot fixture."""
|
||||
return MockedBot()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def session():
|
||||
"""Override session fixture."""
|
||||
async with AiohttpSession() as session:
|
||||
yield session
|
||||
|
||||
|
||||
class TestBot:
|
||||
def test_init(self):
|
||||
bot = Bot("42:TEST")
|
||||
|
|
@ -30,9 +50,7 @@ class TestBot:
|
|||
assert bot == Bot("42:TEST")
|
||||
assert bot != "42:TEST"
|
||||
|
||||
async def test_emit(self):
|
||||
bot = Bot("42:TEST")
|
||||
|
||||
async def test_emit(self, bot: Bot):
|
||||
method = GetMe()
|
||||
|
||||
with patch(
|
||||
|
|
@ -42,8 +60,7 @@ class TestBot:
|
|||
await bot(method)
|
||||
mocked_make_request.assert_awaited_with(bot, method, timeout=None)
|
||||
|
||||
async def test_close(self):
|
||||
session = AiohttpSession()
|
||||
async def test_close(self, session: AiohttpSession):
|
||||
bot = Bot("42:TEST", session=session)
|
||||
await session.create_session()
|
||||
|
||||
|
|
@ -56,18 +73,23 @@ class TestBot:
|
|||
@pytest.mark.parametrize("close", [True, False])
|
||||
async def test_context_manager(self, close: bool):
|
||||
with patch(
|
||||
"aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=AsyncMock
|
||||
target="aiogram.client.session.aiohttp.AiohttpSession.close",
|
||||
new_callable=AsyncMock,
|
||||
) as mocked_close:
|
||||
async with Bot("42:TEST", session=AiohttpSession()).context(auto_close=close) as bot:
|
||||
session = AiohttpSession()
|
||||
async with Bot("42:TEST", session=session).context(auto_close=close) as bot:
|
||||
assert isinstance(bot, Bot)
|
||||
|
||||
if close:
|
||||
mocked_close.assert_awaited()
|
||||
else:
|
||||
mocked_close.assert_not_awaited()
|
||||
await session.close()
|
||||
|
||||
async def test_download_file(self, aresponses: ResponsesMockServer):
|
||||
aresponses.add(
|
||||
aresponses.ANY, aresponses.ANY, "get", aresponses.Response(status=200, body=b"\f" * 10)
|
||||
method_pattern="get",
|
||||
response=aresponses.Response(status=200, body=b"\f" * 10),
|
||||
)
|
||||
|
||||
# https://github.com/Tinche/aiofiles#writing-tests-for-aiofiles
|
||||
|
|
@ -77,30 +99,34 @@ class TestBot:
|
|||
|
||||
mock_file = MagicMock()
|
||||
|
||||
bot = Bot("42:TEST")
|
||||
with patch("aiofiles.threadpool.sync_open", return_value=mock_file):
|
||||
await bot.download_file("TEST", "file.png")
|
||||
mock_file.write.assert_called_once_with(b"\f" * 10)
|
||||
|
||||
async def test_download_file_default_destination(self, aresponses: ResponsesMockServer):
|
||||
bot = Bot("42:TEST")
|
||||
async with Bot("42:TEST").context() as bot:
|
||||
with patch("aiofiles.threadpool.sync_open", return_value=mock_file):
|
||||
await bot.download_file("TEST", "file.png")
|
||||
mock_file.write.assert_called_once_with(b"\f" * 10)
|
||||
|
||||
async def test_download_file_default_destination(
|
||||
self,
|
||||
bot: Bot,
|
||||
aresponses: ResponsesMockServer,
|
||||
):
|
||||
aresponses.add(
|
||||
aresponses.ANY, aresponses.ANY, "get", aresponses.Response(status=200, body=b"\f" * 10)
|
||||
method_pattern="get",
|
||||
response=aresponses.Response(status=200, body=b"\f" * 10),
|
||||
)
|
||||
|
||||
result = await bot.download_file("TEST")
|
||||
|
||||
assert isinstance(result, io.BytesIO)
|
||||
assert result.read() == b"\f" * 10
|
||||
|
||||
async def test_download_file_custom_destination(self, aresponses: ResponsesMockServer):
|
||||
bot = Bot("42:TEST")
|
||||
|
||||
async def test_download_file_custom_destination(
|
||||
self,
|
||||
bot: Bot,
|
||||
aresponses: ResponsesMockServer,
|
||||
):
|
||||
aresponses.add(
|
||||
aresponses.ANY, aresponses.ANY, "get", aresponses.Response(status=200, body=b"\f" * 10)
|
||||
method_pattern="get",
|
||||
response=aresponses.Response(status=200, body=b"\f" * 10),
|
||||
)
|
||||
|
||||
custom = io.BytesIO()
|
||||
|
||||
result = await bot.download_file("TEST", custom)
|
||||
|
|
@ -109,19 +135,19 @@ class TestBot:
|
|||
assert result is custom
|
||||
assert result.read() == b"\f" * 10
|
||||
|
||||
async def test_download(self, bot: MockedBot, aresponses: ResponsesMockServer):
|
||||
bot.add_result_for(
|
||||
async def test_download(self, mocked_bot: MockedBot):
|
||||
mocked_bot.add_result_for(
|
||||
GetFile, ok=True, result=File(file_id="file id", file_unique_id="file id")
|
||||
)
|
||||
bot.add_result_for(
|
||||
mocked_bot.add_result_for(
|
||||
GetFile, ok=True, result=File(file_id="file id", file_unique_id="file id")
|
||||
)
|
||||
|
||||
assert await bot.download(File(file_id="file id", file_unique_id="file id"))
|
||||
assert await bot.download("file id")
|
||||
assert await mocked_bot.download(File(file_id="file id", file_unique_id="file id"))
|
||||
assert await mocked_bot.download("file id")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
await bot.download(
|
||||
await mocked_bot.download(
|
||||
[PhotoSize(file_id="file id", file_unique_id="file id", width=123, height=123)]
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,61 +24,53 @@ class BareInputFile(InputFile):
|
|||
class TestAiohttpSession:
|
||||
async def test_create_session(self):
|
||||
session = AiohttpSession()
|
||||
|
||||
assert session._session is None
|
||||
aiohttp_session = await session.create_session()
|
||||
assert session._session is not None
|
||||
assert isinstance(aiohttp_session, aiohttp.ClientSession)
|
||||
await session.close()
|
||||
|
||||
async def test_create_proxy_session(self):
|
||||
session = AiohttpSession(
|
||||
proxy=("socks5://proxy.url/", aiohttp.BasicAuth("login", "password", "encoding"))
|
||||
)
|
||||
auth = aiohttp.BasicAuth("login", "password", "encoding")
|
||||
async with AiohttpSession(proxy=("socks5://proxy.url/", auth)) as session:
|
||||
assert session._connector_type == aiohttp_socks.ProxyConnector
|
||||
|
||||
assert session._connector_type == aiohttp_socks.ProxyConnector
|
||||
assert isinstance(session._connector_init, dict)
|
||||
assert session._connector_init["proxy_type"] is aiohttp_socks.ProxyType.SOCKS5
|
||||
|
||||
assert isinstance(session._connector_init, dict)
|
||||
assert session._connector_init["proxy_type"] is aiohttp_socks.ProxyType.SOCKS5
|
||||
|
||||
aiohttp_session = await session.create_session()
|
||||
assert isinstance(aiohttp_session.connector, aiohttp_socks.ProxyConnector)
|
||||
aiohttp_session = await session.create_session()
|
||||
assert isinstance(aiohttp_session.connector, aiohttp_socks.ProxyConnector)
|
||||
|
||||
async def test_create_proxy_session_proxy_url(self):
|
||||
session = AiohttpSession(proxy="socks4://proxy.url/")
|
||||
async with AiohttpSession(proxy="socks4://proxy.url/") as session:
|
||||
assert isinstance(session.proxy, str)
|
||||
|
||||
assert isinstance(session.proxy, str)
|
||||
assert isinstance(session._connector_init, dict)
|
||||
assert session._connector_init["proxy_type"] is aiohttp_socks.ProxyType.SOCKS4
|
||||
|
||||
assert isinstance(session._connector_init, dict)
|
||||
assert session._connector_init["proxy_type"] is aiohttp_socks.ProxyType.SOCKS4
|
||||
|
||||
aiohttp_session = await session.create_session()
|
||||
assert isinstance(aiohttp_session.connector, aiohttp_socks.ProxyConnector)
|
||||
aiohttp_session = await session.create_session()
|
||||
assert isinstance(aiohttp_session.connector, aiohttp_socks.ProxyConnector)
|
||||
|
||||
async def test_create_proxy_session_chained_proxies(self):
|
||||
session = AiohttpSession(
|
||||
proxy=[
|
||||
"socks4://proxy.url/",
|
||||
"socks5://proxy.url/",
|
||||
"http://user:password@127.0.0.1:3128",
|
||||
]
|
||||
)
|
||||
proxy_chain = [
|
||||
"socks4://proxy.url/",
|
||||
"socks5://proxy.url/",
|
||||
"http://user:password@127.0.0.1:3128",
|
||||
]
|
||||
async with AiohttpSession(proxy=proxy_chain) as session:
|
||||
assert isinstance(session.proxy, list)
|
||||
|
||||
assert isinstance(session.proxy, list)
|
||||
assert isinstance(session._connector_init, dict)
|
||||
|
||||
assert isinstance(session._connector_init, dict)
|
||||
assert isinstance(session._connector_init["proxy_infos"], list)
|
||||
assert isinstance(session._connector_init["proxy_infos"][0], aiohttp_socks.ProxyInfo)
|
||||
proxy_infos = session._connector_init["proxy_infos"]
|
||||
assert isinstance(proxy_infos, list)
|
||||
assert isinstance(proxy_infos[0], aiohttp_socks.ProxyInfo)
|
||||
assert proxy_infos[0].proxy_type is aiohttp_socks.ProxyType.SOCKS4
|
||||
assert proxy_infos[1].proxy_type is aiohttp_socks.ProxyType.SOCKS5
|
||||
assert proxy_infos[2].proxy_type is aiohttp_socks.ProxyType.HTTP
|
||||
|
||||
assert (
|
||||
session._connector_init["proxy_infos"][0].proxy_type is aiohttp_socks.ProxyType.SOCKS4
|
||||
)
|
||||
assert (
|
||||
session._connector_init["proxy_infos"][1].proxy_type is aiohttp_socks.ProxyType.SOCKS5
|
||||
)
|
||||
assert session._connector_init["proxy_infos"][2].proxy_type is aiohttp_socks.ProxyType.HTTP
|
||||
|
||||
aiohttp_session = await session.create_session()
|
||||
assert isinstance(aiohttp_session.connector, aiohttp_socks.ChainProxyConnector)
|
||||
aiohttp_session = await session.create_session()
|
||||
assert isinstance(aiohttp_session.connector, aiohttp_socks.ChainProxyConnector)
|
||||
|
||||
async def test_reset_connector(self):
|
||||
session = AiohttpSession()
|
||||
|
|
@ -93,6 +85,7 @@ class TestAiohttpSession:
|
|||
assert session._should_reset_connector
|
||||
await session.create_session()
|
||||
assert session._should_reset_connector is False
|
||||
|
||||
await session.close()
|
||||
|
||||
async def test_close_session(self):
|
||||
|
|
@ -170,54 +163,53 @@ class TestAiohttpSession:
|
|||
),
|
||||
)
|
||||
|
||||
session = AiohttpSession()
|
||||
async with AiohttpSession() as session:
|
||||
|
||||
class TestMethod(TelegramMethod[int]):
|
||||
__returning__ = int
|
||||
__api_method__ = "method"
|
||||
class TestMethod(TelegramMethod[int]):
|
||||
__returning__ = int
|
||||
__api_method__ = "method"
|
||||
|
||||
call = TestMethod()
|
||||
call = TestMethod()
|
||||
|
||||
result = await session.make_request(bot, call)
|
||||
assert isinstance(result, int)
|
||||
assert result == 42
|
||||
result = await session.make_request(bot, call)
|
||||
assert isinstance(result, int)
|
||||
assert result == 42
|
||||
|
||||
@pytest.mark.parametrize("error", [ClientError("mocked"), asyncio.TimeoutError()])
|
||||
async def test_make_request_network_error(self, error):
|
||||
bot = Bot("42:TEST")
|
||||
|
||||
async def side_effect(*args, **kwargs):
|
||||
raise error
|
||||
|
||||
with patch(
|
||||
"aiohttp.client.ClientSession._request",
|
||||
new_callable=AsyncMock,
|
||||
side_effect=side_effect,
|
||||
):
|
||||
with pytest.raises(TelegramNetworkError):
|
||||
await bot.get_me()
|
||||
async with Bot("42:TEST").context() as bot:
|
||||
with patch(
|
||||
"aiohttp.client.ClientSession._request",
|
||||
new_callable=AsyncMock,
|
||||
side_effect=side_effect,
|
||||
):
|
||||
with pytest.raises(TelegramNetworkError):
|
||||
await bot.get_me()
|
||||
|
||||
async def test_stream_content(self, aresponses: ResponsesMockServer):
|
||||
aresponses.add(
|
||||
aresponses.ANY, aresponses.ANY, "get", aresponses.Response(status=200, body=b"\f" * 10)
|
||||
)
|
||||
|
||||
session = AiohttpSession()
|
||||
stream = session.stream_content(
|
||||
"https://www.python.org/static/img/python-logo.png",
|
||||
timeout=5,
|
||||
chunk_size=1,
|
||||
raise_for_status=True,
|
||||
)
|
||||
assert isinstance(stream, AsyncGenerator)
|
||||
async with AiohttpSession() as session:
|
||||
stream = session.stream_content(
|
||||
"https://www.python.org/static/img/python-logo.png",
|
||||
timeout=5,
|
||||
chunk_size=1,
|
||||
raise_for_status=True,
|
||||
)
|
||||
assert isinstance(stream, AsyncGenerator)
|
||||
|
||||
size = 0
|
||||
async for chunk in stream:
|
||||
assert isinstance(chunk, bytes)
|
||||
chunk_size = len(chunk)
|
||||
assert chunk_size == 1
|
||||
size += chunk_size
|
||||
assert size == 10
|
||||
size = 0
|
||||
async for chunk in stream:
|
||||
assert isinstance(chunk, bytes)
|
||||
chunk_size = len(chunk)
|
||||
assert chunk_size == 1
|
||||
size += chunk_size
|
||||
assert size == 10
|
||||
|
||||
async def test_stream_content_404(self, aresponses: ResponsesMockServer):
|
||||
aresponses.add(
|
||||
|
|
@ -229,29 +221,30 @@ class TestAiohttpSession:
|
|||
body=b"File not found",
|
||||
),
|
||||
)
|
||||
session = AiohttpSession()
|
||||
stream = session.stream_content(
|
||||
"https://www.python.org/static/img/python-logo.png",
|
||||
timeout=5,
|
||||
chunk_size=1,
|
||||
raise_for_status=True,
|
||||
)
|
||||
async with AiohttpSession() as session:
|
||||
stream = session.stream_content(
|
||||
"https://www.python.org/static/img/python-logo.png",
|
||||
timeout=5,
|
||||
chunk_size=1,
|
||||
raise_for_status=True,
|
||||
)
|
||||
|
||||
with pytest.raises(ClientError):
|
||||
async for _ in stream:
|
||||
...
|
||||
with pytest.raises(ClientError):
|
||||
async for _ in stream:
|
||||
...
|
||||
|
||||
async def test_context_manager(self):
|
||||
session = AiohttpSession()
|
||||
assert isinstance(session, AsyncContextManager)
|
||||
async with AiohttpSession() as session:
|
||||
assert isinstance(session, AsyncContextManager)
|
||||
|
||||
with patch(
|
||||
"aiogram.client.session.aiohttp.AiohttpSession.create_session",
|
||||
new_callable=AsyncMock,
|
||||
) as mocked_create_session, patch(
|
||||
"aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=AsyncMock
|
||||
) as mocked_close:
|
||||
async with session as ctx:
|
||||
assert session == ctx
|
||||
mocked_close.assert_awaited_once()
|
||||
mocked_create_session.assert_awaited_once()
|
||||
with patch(
|
||||
"aiogram.client.session.aiohttp.AiohttpSession.create_session",
|
||||
new_callable=AsyncMock,
|
||||
) as mocked_create_session, patch(
|
||||
"aiogram.client.session.aiohttp.AiohttpSession.close",
|
||||
new_callable=AsyncMock,
|
||||
) as mocked_close:
|
||||
async with session as ctx:
|
||||
assert session == ctx
|
||||
mocked_close.assert_awaited_once()
|
||||
mocked_create_session.assert_awaited_once()
|
||||
|
|
|
|||
|
|
@ -68,15 +68,18 @@ class TestInputFile:
|
|||
|
||||
async def test_url_input_file(self, aresponses: ResponsesMockServer):
|
||||
aresponses.add(
|
||||
aresponses.ANY, aresponses.ANY, "get", aresponses.Response(status=200, body=b"\f" * 10)
|
||||
aresponses.ANY,
|
||||
aresponses.ANY,
|
||||
"get",
|
||||
aresponses.Response(status=200, body=b"\f" * 10),
|
||||
)
|
||||
bot = Bot(token="42:TEST")
|
||||
file = URLInputFile("https://test.org/", chunk_size=1)
|
||||
async with Bot(token="42:TEST").context() as bot:
|
||||
file = URLInputFile("https://test.org/", chunk_size=1)
|
||||
|
||||
size = 0
|
||||
async for chunk in file.read(bot):
|
||||
assert chunk == b"\f"
|
||||
chunk_size = len(chunk)
|
||||
assert chunk_size == 1
|
||||
size += chunk_size
|
||||
assert size == 10
|
||||
size = 0
|
||||
async for chunk in file.read(bot):
|
||||
assert chunk == b"\f"
|
||||
chunk_size = len(chunk)
|
||||
assert chunk_size == 1
|
||||
size += chunk_size
|
||||
assert size == 10
|
||||
|
|
|
|||
|
|
@ -187,8 +187,9 @@ class TestDispatcher:
|
|||
async def test_process_update_empty(self, bot: MockedBot):
|
||||
dispatcher = Dispatcher()
|
||||
|
||||
result = await dispatcher._process_update(bot=bot, update=Update(update_id=42))
|
||||
assert not result
|
||||
with pytest.warns(RuntimeWarning, match="Detected unknown update type") as record:
|
||||
result = await dispatcher._process_update(bot=bot, update=Update(update_id=42))
|
||||
assert not result
|
||||
|
||||
async def test_process_update_handled(self, bot: MockedBot):
|
||||
dispatcher = Dispatcher()
|
||||
|
|
@ -197,7 +198,8 @@ class TestDispatcher:
|
|||
async def update_handler(update: Update):
|
||||
pass
|
||||
|
||||
assert await dispatcher._process_update(bot=bot, update=Update(update_id=42))
|
||||
with pytest.warns(RuntimeWarning, match="Detected unknown update type"):
|
||||
assert await dispatcher._process_update(bot=bot, update=Update(update_id=42))
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"event_type,update,has_chat,has_user",
|
||||
|
|
@ -479,9 +481,11 @@ class TestDispatcher:
|
|||
|
||||
async def test_listen_unknown_update(self):
|
||||
dp = Dispatcher()
|
||||
|
||||
with pytest.raises(SkipHandler):
|
||||
pattern = "Detected unknown update type"
|
||||
with pytest.raises(SkipHandler), pytest.warns(RuntimeWarning, match=pattern) as record:
|
||||
await dp._listen_update(Update(update_id=42))
|
||||
if not record:
|
||||
pytest.fail("Expected 'Detected unknown update type' warning.")
|
||||
|
||||
async def test_listen_unhandled_update(self):
|
||||
dp = Dispatcher()
|
||||
|
|
@ -608,7 +612,9 @@ class TestDispatcher:
|
|||
async def update_handler(update: Update):
|
||||
raise Exception("Kaboom!")
|
||||
|
||||
assert await dispatcher._process_update(bot=bot, update=Update(update_id=42))
|
||||
with pytest.warns(RuntimeWarning, match="Detected unknown update type"):
|
||||
assert await dispatcher._process_update(bot=bot, update=Update(update_id=42))
|
||||
|
||||
log_records = [rec.message for rec in caplog.records]
|
||||
assert len(log_records) == 1
|
||||
assert "Cause exception while process update" in log_records[0]
|
||||
|
|
@ -834,12 +840,17 @@ class TestDispatcher:
|
|||
dispatcher = Dispatcher()
|
||||
dispatcher.message.register(invalid_message_handler)
|
||||
|
||||
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.1)
|
||||
assert response is None
|
||||
await asyncio.sleep(0.5)
|
||||
pattern = r"Detected slow response into webhook"
|
||||
with pytest.warns(RuntimeWarning, match=pattern) as record:
|
||||
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.1)
|
||||
assert response is None
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
log_records = [rec.message for rec in caplog.records]
|
||||
assert "Cause exception while process update" in log_records[0]
|
||||
log_records = [rec.message for rec in caplog.records]
|
||||
assert "Cause exception while process update" in log_records[0]
|
||||
|
||||
if not record:
|
||||
pytest.fail("Expected 'Detected slow response into webhook' warning.")
|
||||
|
||||
def test_specify_updates_calculation(self):
|
||||
def simple_msg_handler() -> None:
|
||||
|
|
|
|||
|
|
@ -72,4 +72,5 @@ class TestDispatchException:
|
|||
async def handler0(event):
|
||||
return "Handled"
|
||||
|
||||
assert await dp.feed_update(bot, Update(update_id=0)) == "Handled"
|
||||
with pytest.warns(RuntimeWarning, match="Detected unknown update type"):
|
||||
assert await dp.feed_update(bot, Update(update_id=0)) == "Handled"
|
||||
|
|
|
|||
|
|
@ -18,10 +18,11 @@ def create_storage_key(bot: MockedBot):
|
|||
],
|
||||
)
|
||||
class TestIsolations:
|
||||
@pytest.mark.filterwarnings("ignore::ResourceWarning")
|
||||
async def test_lock(
|
||||
self,
|
||||
isolation: BaseEventIsolation,
|
||||
storage_key: StorageKey,
|
||||
):
|
||||
async with isolation.lock(key=storage_key):
|
||||
assert True, "You are kidding me?"
|
||||
assert True, "Are you kidding me?"
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ from tests.mocked_bot import MockedBot
|
|||
|
||||
|
||||
class TestChatActionSender:
|
||||
async def test_wait(self, bot: Bot, loop: asyncio.BaseEventLoop):
|
||||
async def test_wait(self, bot: Bot, event_loop: asyncio.BaseEventLoop):
|
||||
sender = ChatActionSender.typing(bot=bot, chat_id=42)
|
||||
loop.call_soon(sender._close_event.set)
|
||||
event_loop.call_soon(sender._close_event.set)
|
||||
start = time.monotonic()
|
||||
await sender._wait(1)
|
||||
assert time.monotonic() - start < 1
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ class TestConstI18nMiddleware:
|
|||
|
||||
|
||||
class TestFSMI18nMiddleware:
|
||||
async def test_middleware(self, i18n: I18n, bot: MockedBot, extra):
|
||||
async def test_middleware(self, i18n: I18n, bot: MockedBot):
|
||||
middleware = FSMI18nMiddleware(i18n=i18n)
|
||||
storage = MemoryStorage()
|
||||
state = FSMContext(storage=storage, key=StorageKey(user_id=42, chat_id=42, bot_id=bot.id))
|
||||
|
|
@ -185,12 +185,14 @@ class TestFSMI18nMiddleware:
|
|||
}
|
||||
result = await middleware(next_call, Update(update_id=42), data)
|
||||
assert result == "test"
|
||||
|
||||
await middleware.set_locale(state, "uk")
|
||||
assert i18n.current_locale == "uk"
|
||||
|
||||
result = await middleware(next_call, Update(update_id=42), data)
|
||||
assert result == "тест"
|
||||
|
||||
async def test_without_state(self, i18n: I18n, bot: MockedBot, extra):
|
||||
async def test_without_state(self, i18n: I18n, bot: MockedBot):
|
||||
middleware = FSMI18nMiddleware(i18n=i18n)
|
||||
data = {
|
||||
"event_from_user": User(id=42, is_bot=False, language_code="it", first_name="Test"),
|
||||
|
|
@ -198,7 +200,3 @@ class TestFSMI18nMiddleware:
|
|||
result = await middleware(next_call, Update(update_id=42), data)
|
||||
assert i18n.current_locale == "en"
|
||||
assert result == "test"
|
||||
|
||||
assert i18n.current_locale == "en"
|
||||
result = await middleware(next_call, Update(update_id=42), data)
|
||||
assert i18n.current_locale == "en"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue