Третий коммит, добавление share, share_kb, а также ADMIN_ID
This commit is contained in:
@@ -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
|
@@ -0,0 +1,97 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, TypedDict
|
||||
|
||||
from typing_extensions import NotRequired
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from aiogram import Bot, Dispatcher, Router
|
||||
from aiogram.dispatcher.event.handler import HandlerObject
|
||||
from aiogram.dispatcher.middlewares.user_context import EventContext
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.fsm.storage.base import BaseStorage
|
||||
from aiogram.types import Chat, Update, User
|
||||
from aiogram.utils.i18n import I18n, I18nMiddleware
|
||||
|
||||
|
||||
class DispatcherData(TypedDict, total=False):
|
||||
"""
|
||||
Dispatcher and bot related data.
|
||||
"""
|
||||
|
||||
dispatcher: Dispatcher
|
||||
"""Instance of the Dispatcher from which the handler was called."""
|
||||
bot: Bot
|
||||
"""Bot that received the update."""
|
||||
bots: NotRequired[list[Bot]]
|
||||
"""List of all bots in the Dispatcher. Used only in polling mode."""
|
||||
event_update: Update
|
||||
"""Update object that triggered the handler."""
|
||||
event_router: Router
|
||||
"""Router that was used to find the handler."""
|
||||
handler: NotRequired[HandlerObject]
|
||||
"""Handler object that was called.
|
||||
Available only in the handler itself and inner middlewares."""
|
||||
|
||||
|
||||
class UserContextData(TypedDict, total=False):
|
||||
"""
|
||||
Event context related data about user and chat.
|
||||
"""
|
||||
|
||||
event_context: EventContext
|
||||
"""Event context object that contains user and chat data."""
|
||||
event_from_user: NotRequired[User]
|
||||
"""User object that triggered the handler."""
|
||||
event_chat: NotRequired[Chat]
|
||||
"""Chat object that triggered the handler.
|
||||
.. deprecated:: 3.5.0
|
||||
Use :attr:`event_context.chat` instead."""
|
||||
event_thread_id: NotRequired[int]
|
||||
"""Thread ID of the chat that triggered the handler.
|
||||
.. deprecated:: 3.5.0
|
||||
Use :attr:`event_context.chat` instead."""
|
||||
event_business_connection_id: NotRequired[str]
|
||||
"""Business connection ID of the chat that triggered the handler.
|
||||
.. deprecated:: 3.5.0
|
||||
Use :attr:`event_context.business_connection_id` instead."""
|
||||
|
||||
|
||||
class FSMData(TypedDict, total=False):
|
||||
"""
|
||||
FSM related data.
|
||||
"""
|
||||
|
||||
fsm_storage: BaseStorage
|
||||
"""Storage used for FSM."""
|
||||
state: NotRequired[FSMContext]
|
||||
"""Current state of the FSM."""
|
||||
raw_state: NotRequired[str | None]
|
||||
"""Raw state of the FSM."""
|
||||
|
||||
|
||||
class I18nData(TypedDict, total=False):
|
||||
"""
|
||||
I18n related data.
|
||||
|
||||
Is not included by default, you need to add it to your own Data class if you need it.
|
||||
"""
|
||||
|
||||
i18n: I18n
|
||||
"""I18n object."""
|
||||
i18n_middleware: I18nMiddleware
|
||||
"""I18n middleware."""
|
||||
|
||||
|
||||
class MiddlewareData(
|
||||
DispatcherData,
|
||||
UserContextData,
|
||||
FSMData,
|
||||
# I18nData, # Disabled by default, add it if you need it to your own Data class.
|
||||
total=False,
|
||||
):
|
||||
"""
|
||||
Data passed to the handler by the middlewares.
|
||||
|
||||
You can add your own data by extending this class.
|
||||
"""
|
@@ -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
|
@@ -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) # type: ignore[assignment]
|
||||
return middleware
|
@@ -0,0 +1,182 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Awaitable, Callable, Dict, Optional
|
||||
|
||||
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
||||
from aiogram.types import (
|
||||
Chat,
|
||||
ChatBoostSourcePremium,
|
||||
InaccessibleMessage,
|
||||
TelegramObject,
|
||||
Update,
|
||||
User,
|
||||
)
|
||||
|
||||
EVENT_CONTEXT_KEY = "event_context"
|
||||
|
||||
EVENT_FROM_USER_KEY = "event_from_user"
|
||||
EVENT_CHAT_KEY = "event_chat"
|
||||
EVENT_THREAD_ID_KEY = "event_thread_id"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EventContext:
|
||||
chat: Optional[Chat] = None
|
||||
user: Optional[User] = None
|
||||
thread_id: Optional[int] = None
|
||||
business_connection_id: Optional[str] = None
|
||||
|
||||
@property
|
||||
def user_id(self) -> Optional[int]:
|
||||
return self.user.id if self.user else None
|
||||
|
||||
@property
|
||||
def chat_id(self) -> Optional[int]:
|
||||
return self.chat.id if self.chat else None
|
||||
|
||||
|
||||
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!")
|
||||
event_context = data[EVENT_CONTEXT_KEY] = self.resolve_event_context(event=event)
|
||||
|
||||
# Backward compatibility
|
||||
if event_context.user is not None:
|
||||
data[EVENT_FROM_USER_KEY] = event_context.user
|
||||
if event_context.chat is not None:
|
||||
data[EVENT_CHAT_KEY] = event_context.chat
|
||||
if event_context.thread_id is not None:
|
||||
data[EVENT_THREAD_ID_KEY] = event_context.thread_id
|
||||
|
||||
return await handler(event, data)
|
||||
|
||||
@classmethod
|
||||
def resolve_event_context(cls, event: Update) -> EventContext:
|
||||
"""
|
||||
Resolve chat and user instance from Update object
|
||||
"""
|
||||
if event.message:
|
||||
return EventContext(
|
||||
chat=event.message.chat,
|
||||
user=event.message.from_user,
|
||||
thread_id=(
|
||||
event.message.message_thread_id if event.message.is_topic_message else None
|
||||
),
|
||||
)
|
||||
if event.edited_message:
|
||||
return EventContext(
|
||||
chat=event.edited_message.chat,
|
||||
user=event.edited_message.from_user,
|
||||
thread_id=(
|
||||
event.edited_message.message_thread_id
|
||||
if event.edited_message.is_topic_message
|
||||
else None
|
||||
),
|
||||
)
|
||||
if event.channel_post:
|
||||
return EventContext(chat=event.channel_post.chat)
|
||||
if event.edited_channel_post:
|
||||
return EventContext(chat=event.edited_channel_post.chat)
|
||||
if event.inline_query:
|
||||
return EventContext(user=event.inline_query.from_user)
|
||||
if event.chosen_inline_result:
|
||||
return EventContext(user=event.chosen_inline_result.from_user)
|
||||
if event.callback_query:
|
||||
callback_query_message = event.callback_query.message
|
||||
if callback_query_message:
|
||||
return EventContext(
|
||||
chat=callback_query_message.chat,
|
||||
user=event.callback_query.from_user,
|
||||
thread_id=(
|
||||
callback_query_message.message_thread_id
|
||||
if not isinstance(callback_query_message, InaccessibleMessage)
|
||||
and callback_query_message.is_topic_message
|
||||
else None
|
||||
),
|
||||
business_connection_id=(
|
||||
callback_query_message.business_connection_id
|
||||
if not isinstance(callback_query_message, InaccessibleMessage)
|
||||
else None
|
||||
),
|
||||
)
|
||||
return EventContext(user=event.callback_query.from_user)
|
||||
if event.shipping_query:
|
||||
return EventContext(user=event.shipping_query.from_user)
|
||||
if event.pre_checkout_query:
|
||||
return EventContext(user=event.pre_checkout_query.from_user)
|
||||
if event.poll_answer:
|
||||
return EventContext(
|
||||
chat=event.poll_answer.voter_chat,
|
||||
user=event.poll_answer.user,
|
||||
)
|
||||
if event.my_chat_member:
|
||||
return EventContext(
|
||||
chat=event.my_chat_member.chat, user=event.my_chat_member.from_user
|
||||
)
|
||||
if event.chat_member:
|
||||
return EventContext(chat=event.chat_member.chat, user=event.chat_member.from_user)
|
||||
if event.chat_join_request:
|
||||
return EventContext(
|
||||
chat=event.chat_join_request.chat, user=event.chat_join_request.from_user
|
||||
)
|
||||
if event.message_reaction:
|
||||
return EventContext(
|
||||
chat=event.message_reaction.chat,
|
||||
user=event.message_reaction.user,
|
||||
)
|
||||
if event.message_reaction_count:
|
||||
return EventContext(chat=event.message_reaction_count.chat)
|
||||
if event.chat_boost:
|
||||
# We only check the premium source, because only it has a sender user,
|
||||
# other sources have a user, but it is not the sender, but the recipient
|
||||
if isinstance(event.chat_boost.boost.source, ChatBoostSourcePremium):
|
||||
return EventContext(
|
||||
chat=event.chat_boost.chat,
|
||||
user=event.chat_boost.boost.source.user,
|
||||
)
|
||||
|
||||
return EventContext(chat=event.chat_boost.chat)
|
||||
if event.removed_chat_boost:
|
||||
return EventContext(chat=event.removed_chat_boost.chat)
|
||||
if event.deleted_business_messages:
|
||||
return EventContext(
|
||||
chat=event.deleted_business_messages.chat,
|
||||
business_connection_id=event.deleted_business_messages.business_connection_id,
|
||||
)
|
||||
if event.business_connection:
|
||||
return EventContext(
|
||||
user=event.business_connection.user,
|
||||
business_connection_id=event.business_connection.id,
|
||||
)
|
||||
if event.business_message:
|
||||
return EventContext(
|
||||
chat=event.business_message.chat,
|
||||
user=event.business_message.from_user,
|
||||
thread_id=(
|
||||
event.business_message.message_thread_id
|
||||
if event.business_message.is_topic_message
|
||||
else None
|
||||
),
|
||||
business_connection_id=event.business_message.business_connection_id,
|
||||
)
|
||||
if event.edited_business_message:
|
||||
return EventContext(
|
||||
chat=event.edited_business_message.chat,
|
||||
user=event.edited_business_message.from_user,
|
||||
thread_id=(
|
||||
event.edited_business_message.message_thread_id
|
||||
if event.edited_business_message.is_topic_message
|
||||
else None
|
||||
),
|
||||
business_connection_id=event.edited_business_message.business_connection_id,
|
||||
)
|
||||
if event.purchased_paid_media:
|
||||
return EventContext(
|
||||
user=event.purchased_paid_media.from_user,
|
||||
)
|
||||
return EventContext()
|
Reference in New Issue
Block a user