aiogram/docs/source/migration_1_to_2.rst

217 lines
6.3 KiB
ReStructuredText
Raw Normal View History

2018-09-07 21:24:13 +03:00
==========================
Migration FAQ (1.4 -> 2.0)
==========================
This update make breaking changes in aiogram API and drop backward capability with previous versions of framework.
From this point aiogram supports only Python 3.7 and newer.
Changelog
=========
- Used contextvars instead of `aiogram.utils.context`;
- Implemented filters factory;
- Implemented new filters mechanism;
- Allowed to customize command prefix in CommandsFilter;
- Implemented mechanism of passing results from filters (as dicts) as kwargs in handlers (like fixtures in pytest);
- Implemented states group feature;
- Implemented FSM storage's proxy;
2018-09-07 21:24:13 +03:00
- Changed files uploading mechanism;
- Implemented I18n Middleware;
- Errors handlers now should accept only two arguments (current update and exception);
- Used `aiohttp_socks` instead of `aiosocksy` for Socks4/5 proxy;
2018-09-07 22:14:04 +03:00
- `types.ContentType` was divided to `types.ContentType` and `types.ContentTypes`;
- Allowed to use rapidjson instead of ujson/json;
2018-09-07 21:24:13 +03:00
- (**in process**) Implemented utils for Telegram Passport;
- (**in process**) Webhook security improvements;
- (**in process**) Updated examples.
Instructions
============
Contextvars
-----------
Context utility (`aiogram.utils.context`) now is removed due to new features of Python 3.7 and all subclasses of :obj:`aiogram.types.base.TelegramObject`, :obj:`aiogram.Bot` and :obj:`aiogram.Dispatcher` has `.get_current()` and `.set_current()` methods for getting/setting contextual instances of objects.
Example:
.. code-block:: python
async def my_handler(message: types.Message):
bot = Bot.get_current()
user = types.User.get_current()
...
Filters
-------
Custom filters
~~~~~~~~~~~~~~
Now `func` keyword argument can't be used for passing filters to the list of filters instead of that you can pass the filters as arguments:
.. code-block:: python
@dp.message_handler(lambda message: message.text == 'foo')
2018-09-07 22:14:04 +03:00
@dp.message_handler(types.ChatType.is_private, my_filter)
2018-09-07 21:24:13 +03:00
async def ...
Filters factory
~~~~~~~~~~~~~~~
Also you can bind your own filters for using as keyword arguments:
.. code-block:: python
from aiogram.dispatcher.filters import BoundFilter
class MyFilter(BoundFilter):
key = 'is_admin'
async def check(self, message: types.Message):
member = await bot.get_chat_member(message.chat.id, message.from_user.id)
return member.is_admin()
dp.filters_factory.bind(MyFilter)
@dp.message_handler(is_admin=True)
async def ...
Customize commands prefix
~~~~~~~~~~~~~~~~~~~~~~~~~
Commands prefix can be changed by following one of two available methods:
.. code-block:: python
@dp.message_handler(commands=['admin'], commands_prefix='!/')
@dp.message_handler(Command('admin', prefixes='!/'))
async def ...
Passing data from filters as keyword arguments to the handlers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can pass any data from any filter to the handler by returning :obj:`dict`
If any key from the received dictionary not in the handler specification the key will be skipped and and will be unavailable from the handler
.. code-block:: python
async def my_filter(message: types.Message):
# do something here
return {'foo': 'foo', 'bar': 42}
@dp.message_handler(my_filter)
async def my_message_handler(message: types.Message, bar: int):
await message.reply(f'bar = {bar}')
Other
~~~~~
Filters can also be used as logical expressions:
.. code-block:: python
Text(equals='foo') | Text(endswith='Bar') | ~Text(contains='spam')
States group
------------
You can use States objects and States groups instead of string names of the states.
String values is still also be available.
Writing states group:
.. code-block:: python
from aiogram.dispatcher.filters.state import State, StatesGroup
class UserForm(StatesGroup):
name = State() # Will be represented in storage as 'Form:name'
age = State() # Will be represented in storage as 'Form:age'
gender = State() # Will be represented in storage as 'Form:gender'
After that you can use states as `UserForm.name` and etc.
FSM storage's proxy
-------------------
Now `Dispatcher.current_context()` can't be used as context-manager.
Implemented `FSMContext.proxy()` method which returns asynchronous `FSMContextProxy` context manager and can be used for more simply getting data from the storage.
`FSMContextProxy` load all user-related data on initialization and dump it to the storage when proxy is closing if any part of the data was changed.
Usage:
.. code-block:: python
@dp.message_handler(commands=['click'])
async def cmd_start(message: types.Message, state: FSMContext):
2018-09-22 03:04:09 +03:00
async with state.proxy() as proxy: # proxy = FSMContextProxy(state); await proxy.load()
proxy.setdefault('counter', 0)
proxy['counter'] += 1
return await message.reply(f"Counter: {proxy['counter']}")
2018-09-07 21:24:13 +03:00
File uploading mechanism
------------------------
Fixed uploading files. Removed `BaseBot.send_file` method. This allowed to send the `thumb` field.
I18n Middleware
---------------
You can internalize your bot by following next steps:
First usage
~~~~~~~~~~~
1. Extract texts
.. code-block:: bash
pybabel extract i18n_example.py -o locales/mybot.pot
2. Create `*.po` files. For e.g. create `en`, `ru`, `uk` locales.
3. Translate texts
4. Compile translations
.. code-block:: bash
pybabel compile -d locales -D mybot
Updating translations
~~~~~~~~~~~~~~~~~~~~~
When you change the code of your bot you need to update `po` & `mo` files:
1. Regenerate pot file:
.. code-block:: bash
pybabel extract i18n_example.py -o locales/mybot.pot
2. Update po files
.. code-block:: bash
pybabel update -d locales -D mybot -i locales/mybot.pot
3. Update your translations
4. Compile `mo` files
.. code-block:: bash
pybabel compile -d locales -D mybot
Error handlers
--------------
Previously errors handlers had to have three arguments `dispatcher`, `update` and `exception` now `dispatcher` argument is removed and will no longer be passed to the error handlers.
2018-09-07 22:14:04 +03:00
Content types
-------------
Content types helper was divided to `types.ContentType` and `types.ContentTypes`.
In filters you can use `types.ContentTypes` but for comparing content types you must use `types.ContentType` class.