v1 milestone

This commit is contained in:
YOLANDO
2026-03-25 11:20:05 +07:00
commit 4ec369c18e
7307 changed files with 873141 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
from abc import ABC, abstractmethod
from typing import Any, Awaitable, Callable, Dict, TypeVar
from aiogram.types import TelegramObject
T = TypeVar("T")
class BaseMiddleware(ABC):
"""
Generic middleware class
"""
@abstractmethod
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
) -> Any: # pragma: no cover
"""
Execute middleware
:param handler: Wrapped handler in middlewares chain
:param event: Incoming event (Subclass of :class:`aiogram.types.base.TelegramObject`)
:param data: Contextual data. Will be mapped to handler arguments
:return: :class:`Any`
"""
pass

View File

@@ -0,0 +1,36 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, cast
from ...types import TelegramObject, Update
from ...types.error_event import ErrorEvent
from ..event.bases import UNHANDLED, CancelHandler, SkipHandler
from .base import BaseMiddleware
if TYPE_CHECKING:
from ..router import Router
class ErrorsMiddleware(BaseMiddleware):
def __init__(self, router: Router):
self.router = router
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
) -> Any:
try:
return await handler(event, data)
except (SkipHandler, CancelHandler): # pragma: no cover
raise
except Exception as e:
response = await self.router.propagate_event(
update_type="error",
event=ErrorEvent(update=cast(Update, event), exception=e),
**data,
)
if response is not UNHANDLED:
return response
raise

View File

@@ -0,0 +1,65 @@
import functools
from typing import Any, Callable, Dict, List, Optional, Sequence, Union, overload
from aiogram.dispatcher.event.bases import (
MiddlewareEventType,
MiddlewareType,
NextMiddlewareType,
)
from aiogram.dispatcher.event.handler import CallbackType
from aiogram.types import TelegramObject
class MiddlewareManager(Sequence[MiddlewareType[TelegramObject]]):
def __init__(self) -> None:
self._middlewares: List[MiddlewareType[TelegramObject]] = []
def register(
self,
middleware: MiddlewareType[TelegramObject],
) -> MiddlewareType[TelegramObject]:
self._middlewares.append(middleware)
return middleware
def unregister(self, middleware: MiddlewareType[TelegramObject]) -> None:
self._middlewares.remove(middleware)
def __call__(
self,
middleware: Optional[MiddlewareType[TelegramObject]] = None,
) -> Union[
Callable[[MiddlewareType[TelegramObject]], MiddlewareType[TelegramObject]],
MiddlewareType[TelegramObject],
]:
if middleware is None:
return self.register
return self.register(middleware)
@overload
def __getitem__(self, item: int) -> MiddlewareType[TelegramObject]:
pass
@overload
def __getitem__(self, item: slice) -> Sequence[MiddlewareType[TelegramObject]]:
pass
def __getitem__(
self, item: Union[int, slice]
) -> Union[MiddlewareType[TelegramObject], Sequence[MiddlewareType[TelegramObject]]]:
return self._middlewares[item]
def __len__(self) -> int:
return len(self._middlewares)
@staticmethod
def wrap_middlewares(
middlewares: Sequence[MiddlewareType[MiddlewareEventType]], handler: CallbackType
) -> NextMiddlewareType[MiddlewareEventType]:
@functools.wraps(handler)
def handler_wrapper(event: TelegramObject, kwargs: Dict[str, Any]) -> Any:
return handler(event, **kwargs)
middleware = handler_wrapper
for m in reversed(middlewares):
middleware = functools.partial(m, middleware)
return middleware

View File

@@ -0,0 +1,89 @@
from typing import Any, Awaitable, Callable, Dict, Optional, Tuple
from aiogram.dispatcher.middlewares.base import BaseMiddleware
from aiogram.types import Chat, InaccessibleMessage, TelegramObject, Update, User
EVENT_FROM_USER_KEY = "event_from_user"
EVENT_CHAT_KEY = "event_chat"
EVENT_THREAD_ID_KEY = "event_thread_id"
class UserContextMiddleware(BaseMiddleware):
async def __call__(
self,
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
event: TelegramObject,
data: Dict[str, Any],
) -> Any:
if not isinstance(event, Update):
raise RuntimeError("UserContextMiddleware got an unexpected event type!")
chat, user, thread_id = self.resolve_event_context(event=event)
if user is not None:
data[EVENT_FROM_USER_KEY] = user
if chat is not None:
data[EVENT_CHAT_KEY] = chat
if thread_id is not None:
data[EVENT_THREAD_ID_KEY] = thread_id
return await handler(event, data)
@classmethod
def resolve_event_context(
cls, event: Update
) -> Tuple[Optional[Chat], Optional[User], Optional[int]]:
"""
Resolve chat and user instance from Update object
"""
if event.message:
return (
event.message.chat,
event.message.from_user,
event.message.message_thread_id if event.message.is_topic_message else None,
)
if event.edited_message:
return (
event.edited_message.chat,
event.edited_message.from_user,
event.edited_message.message_thread_id
if event.edited_message.is_topic_message
else None,
)
if event.channel_post:
return event.channel_post.chat, None, None
if event.edited_channel_post:
return event.edited_channel_post.chat, None, None
if event.inline_query:
return None, event.inline_query.from_user, None
if event.chosen_inline_result:
return None, event.chosen_inline_result.from_user, None
if event.callback_query:
if event.callback_query.message:
return (
event.callback_query.message.chat,
event.callback_query.from_user,
event.callback_query.message.message_thread_id
if not isinstance(event.callback_query.message, InaccessibleMessage)
and event.callback_query.message.is_topic_message
else None,
)
return None, event.callback_query.from_user, None
if event.shipping_query:
return None, event.shipping_query.from_user, None
if event.pre_checkout_query:
return None, event.pre_checkout_query.from_user, None
if event.poll_answer:
return None, event.poll_answer.user, None
if event.my_chat_member:
return event.my_chat_member.chat, event.my_chat_member.from_user, None
if event.chat_member:
return event.chat_member.chat, event.chat_member.from_user, None
if event.chat_join_request:
return event.chat_join_request.chat, event.chat_join_request.from_user, None
if event.message_reaction:
return event.message_reaction.chat, event.message_reaction.user, None
if event.message_reaction_count:
return event.message_reaction_count.chat, None, None
if event.chat_boost:
return event.chat_boost.chat, None, None
if event.removed_chat_boost:
return event.removed_chat_boost.chat, None, None
return None, None, None