-
-
Notifications
You must be signed in to change notification settings - Fork 806
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7821025
commit 7dd80d2
Showing
7 changed files
with
429 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
=========== | ||
Middlewares | ||
=========== | ||
|
||
**aiogram** provides powerful mechanism for customizing event handlers via middlewares. | ||
|
||
Middlewares in bot framework seems like Middlewares mechanism in web-frameworks | ||
like `aiohttp <https://docs.aiohttp.org/en/stable/web_advanced.html#aiohttp-web-middlewares>`_, | ||
`fastapi <https://fastapi.tiangolo.com/tutorial/middleware/>`_, | ||
`Django <https://docs.djangoproject.com/en/3.0/topics/http/middleware/>`_ or etc.) | ||
with small difference - here is implemented two layers of middlewares (before and after filters). | ||
|
||
.. note:: | ||
|
||
Middleware is function that triggered on every event received from | ||
Telegram Bot API in many points on processing pipeline. | ||
|
||
Base theory | ||
=========== | ||
|
||
As many books and other literature in internet says: | ||
|
||
Middleware is reusable software that leverages patterns and frameworks to bridge | ||
the gap between the functional requirements of applications and the underlying operating systems, | ||
network protocol stacks, and databases. | ||
|
||
Middleware can modify, extend or reject processing event in many places of pipeline. | ||
|
||
Basics | ||
====== | ||
|
||
Middleware instance can be applied for every type of Telegram Event (Update, Message, etc.) in two places | ||
|
||
1. Outer scope - before processing filters (:code:`<router>.<event>.outer_middleware(...)`) | ||
2. Inner scope - after processing filters but before handler (:code:`<router>.<event>.middleware(...)`) | ||
|
||
.. image:: ../_static/basics_middleware.png | ||
:alt: Middleware basics | ||
|
||
.. attention:: | ||
|
||
Middleware should be subclass of :code:`BaseMiddleware` (:code:`from aiogram import BaseMiddleware`) or any async callable | ||
|
||
Arguments specification | ||
======================= | ||
|
||
.. autoclass:: aiogram.dispatcher.middlewares.base.BaseMiddleware | ||
:members: | ||
:show-inheritance: | ||
:member-order: bysource | ||
:special-members: __init__, __call__ | ||
:undoc-members: True | ||
|
||
|
||
Examples | ||
======== | ||
|
||
.. danger:: | ||
|
||
Middleware should always call :code:`await handler(event, data)` to propagate event for next middleware/handler | ||
|
||
|
||
Class-based | ||
----------- | ||
.. code-block:: python | ||
from aiogram import BaseMiddleware | ||
from aiogram.api.types import Message | ||
class CounterMiddleware(BaseMiddleware[Message]): | ||
def __init__(self) -> None: | ||
self.counter = 0 | ||
async def __call__( | ||
self, | ||
handler: Callable[[Message, Dict[str, Any]], Awaitable[Any]], | ||
event: Message, | ||
data: Dict[str, Any] | ||
) -> Any: | ||
self.counter += 1 | ||
data['counter'] = self.counter | ||
return await handler(event, data) | ||
and then | ||
|
||
.. code-block:: python3 | ||
router = Router() | ||
router.message.middleware(CounterMiddleware()) | ||
Function-based | ||
-------------- | ||
|
||
.. code-block:: python3 | ||
@dispatcher.update.outer_middleware() | ||
async def database_transaction_middleware( | ||
handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]], | ||
event: Update, | ||
data: Dict[str, Any] | ||
) -> Any: | ||
async with database.transaction(): | ||
return await handler(event, data) | ||
Facts | ||
===== | ||
|
||
1. Middlewares from outer scope will be called on every incoming event | ||
2. Middlewares from inner scope will be called only when filters pass | ||
3. Inner middlewares is always calls for :class:`aiogram.types.update.Update` event type in due to all incoming updates going to specific event type handler through built in update handler |
49 changes: 49 additions & 0 deletions
49
tests/test_api/test_methods/test_create_chat_invite_link.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import pytest | ||
|
||
from aiogram.methods import CreateChatInviteLink, Request | ||
from aiogram.types import ChatInviteLink, User | ||
from tests.mocked_bot import MockedBot | ||
|
||
|
||
class TestCreateChatInviteLink: | ||
@pytest.mark.asyncio | ||
async def test_method(self, bot: MockedBot): | ||
prepare_result = bot.add_result_for( | ||
CreateChatInviteLink, | ||
ok=True, | ||
result=ChatInviteLink( | ||
invite_link="https://t.me/username", | ||
creator=User(id=42, is_bot=False, first_name="User"), | ||
is_primary=False, | ||
is_revoked=False, | ||
), | ||
) | ||
|
||
response: ChatInviteLink = await CreateChatInviteLink( | ||
chat_id=-42, | ||
) | ||
request: Request = bot.get_request() | ||
assert request.method == "createChatInviteLink" | ||
# assert request.data == {"chat_id": -42} | ||
assert response == prepare_result.result | ||
|
||
@pytest.mark.asyncio | ||
async def test_bot_method(self, bot: MockedBot): | ||
prepare_result = bot.add_result_for( | ||
CreateChatInviteLink, | ||
ok=True, | ||
result=ChatInviteLink( | ||
invite_link="https://t.me/username", | ||
creator=User(id=42, is_bot=False, first_name="User"), | ||
is_primary=False, | ||
is_revoked=False, | ||
), | ||
) | ||
|
||
response: ChatInviteLink = await bot.create_chat_invite_link( | ||
chat_id=-42, | ||
) | ||
request: Request = bot.get_request() | ||
assert request.method == "createChatInviteLink" | ||
# assert request.data == {"chat_id": -42} | ||
assert response == prepare_result.result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import pytest | ||
|
||
from aiogram.methods import EditChatInviteLink, Request | ||
from aiogram.types import ChatInviteLink, User | ||
from tests.mocked_bot import MockedBot | ||
|
||
|
||
class TestEditChatInviteLink: | ||
@pytest.mark.asyncio | ||
async def test_method(self, bot: MockedBot): | ||
prepare_result = bot.add_result_for( | ||
EditChatInviteLink, | ||
ok=True, | ||
result=ChatInviteLink( | ||
invite_link="https://t.me/username2", | ||
creator=User(id=42, is_bot=False, first_name="User"), | ||
is_primary=False, | ||
is_revoked=False, | ||
), | ||
) | ||
|
||
response: ChatInviteLink = await EditChatInviteLink( | ||
chat_id=-42, invite_link="https://t.me/username", member_limit=1 | ||
) | ||
request: Request = bot.get_request() | ||
assert request.method == "editChatInviteLink" | ||
# assert request.data == {} | ||
assert response == prepare_result.result | ||
|
||
@pytest.mark.asyncio | ||
async def test_bot_method(self, bot: MockedBot): | ||
prepare_result = bot.add_result_for( | ||
EditChatInviteLink, | ||
ok=True, | ||
result=ChatInviteLink( | ||
invite_link="https://t.me/username2", | ||
creator=User(id=42, is_bot=False, first_name="User"), | ||
is_primary=False, | ||
is_revoked=False, | ||
), | ||
) | ||
|
||
response: ChatInviteLink = await bot.edit_chat_invite_link( | ||
chat_id=-42, invite_link="https://t.me/username", member_limit=1 | ||
) | ||
request: Request = bot.get_request() | ||
assert request.method == "editChatInviteLink" | ||
# assert request.data == {} | ||
assert response == prepare_result.result |
51 changes: 51 additions & 0 deletions
51
tests/test_api/test_methods/test_revoke_chat_invite_link.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import pytest | ||
|
||
from aiogram.methods import Request, RevokeChatInviteLink | ||
from aiogram.types import ChatInviteLink, User | ||
from tests.mocked_bot import MockedBot | ||
|
||
|
||
class TestRevokeChatInviteLink: | ||
@pytest.mark.asyncio | ||
async def test_method(self, bot: MockedBot): | ||
prepare_result = bot.add_result_for( | ||
RevokeChatInviteLink, | ||
ok=True, | ||
result=ChatInviteLink( | ||
invite_link="https://t.me/username", | ||
creator=User(id=42, is_bot=False, first_name="User"), | ||
is_primary=False, | ||
is_revoked=True, | ||
), | ||
) | ||
|
||
response: ChatInviteLink = await RevokeChatInviteLink( | ||
chat_id=-42, | ||
invite_link="https://t.me/username", | ||
) | ||
request: Request = bot.get_request() | ||
assert request.method == "revokeChatInviteLink" | ||
# assert request.data == {} | ||
assert response == prepare_result.result | ||
|
||
@pytest.mark.asyncio | ||
async def test_bot_method(self, bot: MockedBot): | ||
prepare_result = bot.add_result_for( | ||
RevokeChatInviteLink, | ||
ok=True, | ||
result=ChatInviteLink( | ||
invite_link="https://t.me/username", | ||
creator=User(id=42, is_bot=False, first_name="User"), | ||
is_primary=False, | ||
is_revoked=True, | ||
), | ||
) | ||
|
||
response: ChatInviteLink = await bot.revoke_chat_invite_link( | ||
chat_id=-42, | ||
invite_link="https://t.me/username", | ||
) | ||
request: Request = bot.get_request() | ||
assert request.method == "revokeChatInviteLink" | ||
# assert request.data == {} | ||
assert response == prepare_result.result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import pytest | ||
|
||
from aiogram.types import MessageEntity | ||
from tests.deprecated import check_deprecated | ||
|
||
|
||
class TestMessageEntity: | ||
def test_extract(self): | ||
entity = MessageEntity(type="hashtag", length=4, offset=5) | ||
assert entity.extract("#foo #bar #baz") == "#bar" | ||
|
||
def test_get_text(self): | ||
entity = MessageEntity(type="hashtag", length=4, offset=5) | ||
with check_deprecated("3.2", exception=AttributeError): | ||
assert entity.get_text("#foo #bar #baz") == "#bar" |
Empty file.
Oops, something went wrong.