Ін’єкція залежностей

Ін’єкція залежностей — це техніка програмування, яка робить класи незалежними від їхніх залежностей. Вона досягається шляхом відділення використання об’єкта від його створення. Це допомагає дотримуватися принципів інверсії залежностей та єдиної відповідальності за SOLID.

Як це працює в aiogram

Для кожного оновлення aiogram.dispatcher.dispatcher.Dispatcher передає дані контексту обробки. Фільтри та проміжне програмне забезпечення також можуть вносити зміни до контексту.

Щоб отримати доступ до контекстних даних, ви повинні вказати відповідний параметр ключового слова в обробнику або фільтрі. Наприклад, щоб отримати aiogram.fsm.context.FSMContext, ми робимо це так:

@router.message(ProfileCompletion.add_photo, F.photo)
async def add_photo(
    message: types.Message, bot: Bot, state: FSMContext
) -> Any:
    ... # do something with photo

Ін’єкція власних залежностей

Aiogram надає кілька способів для доповнення/модифікації контекстних даних.

Перший і найпростіший спосіб — це просто вказати іменовані аргументи під час ініціалізації aiogram.dispatcher.dispatcher.Dispatcher, запуску методів опитування або ініціалізації aiogram.webhook.aiohttp_server.SimpleRequestHandler, якщо ви використовуєте вебхуки.

async def main() -> None:
    dp = Dispatcher(..., foo=42)
    return await dp.start_polling(
        bot, bar="Bazz"
    )

Аналогія для вебхука:

async def main() -> None:
    dp = Dispatcher(..., foo=42)
    handler = SimpleRequestHandler(dispatcher=dp, bot=bot, bar="Bazz")
    ... # starting webhook

Дані робочого процесу aiogram.dispatcher.dispatcher.Dispatcher також можуть доповнюватися шляхом встановлення значень, як у словнику:

dp = Dispatcher(...)
dp["eggs"] = Spam()

Проміжне програмне забезпечення досить часто оновлює контекст. Ви можете прочитати про це більше на цій сторінці:

Останній спосіб — повернути словник із фільтра:

from typing import Any, Dict, Optional, Union

from aiogram import Router
from aiogram.filters import Filter
from aiogram.types import Message, User

router = Router(name=__name__)


class HelloFilter(Filter):
    def __init__(self, name: Optional[str] = None) -> None:
        self.name = name

    async def __call__(
        self,
        message: Message,
        event_from_user: User,
        # Filters also can accept keyword parameters like in handlers
    ) -> Union[bool, Dict[str, Any]]:
        if message.text.casefold() == "hello":
            # Returning a dictionary that will update the context data
            return {"name": event_from_user.mention_html(name=self.name)}
        return False


@router.message(HelloFilter())
async def my_handler(
    message: Message, name: str  # Now we can accept "name" as named parameter
) -> Any:
    return message.answer("Hello, {name}!".format(name=name))

…або використовуючи MagicFilter з методом .as_(...).

Використання підказок типів

Примітка

Використання підказок типів для даних проміжного програмного забезпечення необов’язкове й не потрібне для правильної роботи диспетчера. Проте рекомендується використовувати їх для покращення читабельності коду.

Ви можете використовувати підказки типів для визначення типу контекстних даних у проміжному ПЗ, фільтрах та обробниках.

Словник типів даних за замовчуванням для проміжного ПЗ можна знайти в aiogram.dispatcher.middlewares.data.MiddlewareData.

Якщо ви розширили контекстні дані, ви можете використовувати aiogram.dispatcher.middlewares.data.MiddlewareData як базовий клас та вказати підказки типів для нових полів.

Попередження

Якщо ви використовуєте інструменти перевірки типів, такі як mypy, ви можете отримати попередження про те, що ці підказки типів порушують принцип підстановки Лісков через те, що суворіший тип не є підкласом dict[str, Any]. Це відома проблема, і це не є помилкою. Ви можете проігнорувати це попередження або використати коментар # type: ignore.

Приклад використання підказок типів:

from aiogram.dispatcher.middlewares.data import MiddlewareData


class MyMiddlewareData(MiddlewareData, total=False):
    my_custom_value: int


class MyMessageMiddleware(BaseMiddleware):
    async def __call__(
        self,
        handler: Callable[[Message, MyMiddlewareData], Awaitable[Any]],
        event: Message,
        data: MyMiddlewareData,
    ) -> Any:
        bot = data["bot"]  # <-- IDE will show you that data has `bot` key and its type is `Bot`

        data["my_custom_value"] = bot.id * 42  # <-- IDE will show you that you can set `my_custom_value` key with int value and warn you if you try to set it with other type
        return await handler(event, data)

Доступні помічники для типів контекстних даних

class aiogram.dispatcher.middlewares.data.MiddlewareData[source]

Дані, передані обробнику проміжним ПЗ.

Ви можете додати власні дані, розширивши цей клас.

dispatcher: Dispatcher
bot: Bot
bots: NotRequired[list[Bot]]
event_update: Update
event_router: Router
handler: NotRequired[HandlerObject]
event_context: EventContext
event_from_user: NotRequired[User]
event_chat: NotRequired[Chat]
event_thread_id: NotRequired[int]
event_business_connection_id: NotRequired[str]
fsm_storage: BaseStorage
state: NotRequired[FSMContext]
raw_state: NotRequired[str | None]
class aiogram.dispatcher.middlewares.data.I18nData[source]

Дані, пов’язані з I18n.

За замовчуванням не включено, вам потрібно додати це до власного класу Даних, якщо це необхідно.

i18n: I18n

Об’єкт I18n.

i18n_middleware: I18nMiddleware

Проміжне ПЗ для I18n.