mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Added base code and make code improvements
This commit is contained in:
parent
5bd1162f57
commit
bd2a348aa0
16 changed files with 522 additions and 424 deletions
2
Makefile
2
Makefile
|
|
@ -47,7 +47,7 @@ help:
|
||||||
|
|
||||||
.PHONY: install
|
.PHONY: install
|
||||||
install:
|
install:
|
||||||
poetry install
|
poetry install -E fast -E redis -E proxy -E i18n -E docs
|
||||||
$(py) pre-commit install
|
$(py) pre-commit install
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
|
||||||
|
|
@ -231,6 +231,14 @@ class Bot(ContextInstanceMixin["Bot"]):
|
||||||
async for chunk in stream:
|
async for chunk in stream:
|
||||||
await f.write(chunk)
|
await f.write(chunk)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def __aiofiles_reader(
|
||||||
|
cls, file: str, chunk_size: int = 65536
|
||||||
|
) -> AsyncGenerator[bytes, None]:
|
||||||
|
async with aiofiles.open(file, "rb") as f:
|
||||||
|
while chunk := await f.read(chunk_size):
|
||||||
|
yield chunk
|
||||||
|
|
||||||
async def download_file(
|
async def download_file(
|
||||||
self,
|
self,
|
||||||
file_path: str,
|
file_path: str,
|
||||||
|
|
@ -254,15 +262,26 @@ class Bot(ContextInstanceMixin["Bot"]):
|
||||||
if destination is None:
|
if destination is None:
|
||||||
destination = io.BytesIO()
|
destination = io.BytesIO()
|
||||||
|
|
||||||
url = self.session.api.file_url(self.__token, file_path)
|
close_stream = False
|
||||||
stream = self.session.stream_content(url=url, timeout=timeout, chunk_size=chunk_size)
|
if self.session.api.is_local:
|
||||||
|
stream = self.__aiofiles_reader(
|
||||||
if isinstance(destination, (str, pathlib.Path)):
|
self.session.api.wrap_local_file(file_path), chunk_size=chunk_size
|
||||||
return await self.__download_file(destination=destination, stream=stream)
|
|
||||||
else:
|
|
||||||
return await self.__download_file_binary_io(
|
|
||||||
destination=destination, seek=seek, stream=stream
|
|
||||||
)
|
)
|
||||||
|
close_stream = True
|
||||||
|
else:
|
||||||
|
url = self.session.api.file_url(self.__token, file_path)
|
||||||
|
stream = self.session.stream_content(url=url, timeout=timeout, chunk_size=chunk_size)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if isinstance(destination, (str, pathlib.Path)):
|
||||||
|
return await self.__download_file(destination=destination, stream=stream)
|
||||||
|
else:
|
||||||
|
return await self.__download_file_binary_io(
|
||||||
|
destination=destination, seek=seek, stream=stream
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
if close_stream:
|
||||||
|
await stream.aclose()
|
||||||
|
|
||||||
async def download(
|
async def download(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Any, Protocol
|
||||||
|
|
||||||
|
|
||||||
|
class WrapLocalFileCallbackCallbackProtocol(Protocol):
|
||||||
|
def __call__(self, value: str) -> str:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
|
@ -8,8 +14,13 @@ class TelegramAPIServer:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
base: str
|
base: str
|
||||||
|
"""Base URL"""
|
||||||
file: str
|
file: str
|
||||||
|
"""Files URL"""
|
||||||
is_local: bool = False
|
is_local: bool = False
|
||||||
|
"""Mark this server is in `local mode <https://core.telegram.org/bots/api#using-a-local-bot-api-server>`_."""
|
||||||
|
wrap_local_file: WrapLocalFileCallbackCallbackProtocol = lambda v: v
|
||||||
|
"""Callback to wrap files path in local mode"""
|
||||||
|
|
||||||
def api_url(self, token: str, method: str) -> str:
|
def api_url(self, token: str, method: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
@ -32,19 +43,18 @@ class TelegramAPIServer:
|
||||||
return self.file.format(token=token, path=path)
|
return self.file.format(token=token, path=path)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_base(cls, base: str, is_local: bool = False) -> "TelegramAPIServer":
|
def from_base(cls, base: str, **kwargs: Any) -> "TelegramAPIServer":
|
||||||
"""
|
"""
|
||||||
Use this method to auto-generate TelegramAPIServer instance from base URL
|
Use this method to auto-generate TelegramAPIServer instance from base URL
|
||||||
|
|
||||||
:param base: Base URL
|
:param base: Base URL
|
||||||
:param is_local: Mark this server is in `local mode <https://core.telegram.org/bots/api#using-a-local-bot-api-server>`_.
|
|
||||||
:return: instance of :class:`TelegramAPIServer`
|
:return: instance of :class:`TelegramAPIServer`
|
||||||
"""
|
"""
|
||||||
base = base.rstrip("/")
|
base = base.rstrip("/")
|
||||||
return cls(
|
return cls(
|
||||||
base=f"{base}/bot{{token}}/{{method}}",
|
base=f"{base}/bot{{token}}/{{method}}",
|
||||||
file=f"{base}/file/bot{{token}}/{{path}}",
|
file=f"{base}/file/bot{{token}}/{{path}}",
|
||||||
is_local=is_local,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,19 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Any, Awaitable, Callable, Dict, NoReturn, Optional, Union
|
from typing import Any, Awaitable, Callable, Dict, NoReturn, Optional, TypeVar, Union
|
||||||
from unittest.mock import sentinel
|
from unittest.mock import sentinel
|
||||||
|
|
||||||
from ...types import TelegramObject
|
from ...types import TelegramObject
|
||||||
from ..middlewares.base import BaseMiddleware
|
from ..middlewares.base import BaseMiddleware
|
||||||
|
|
||||||
NextMiddlewareType = Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]]
|
MiddlewareEventType = TypeVar("MiddlewareEventType", bound=TelegramObject)
|
||||||
|
NextMiddlewareType = Callable[[MiddlewareEventType, Dict[str, Any]], Awaitable[Any]]
|
||||||
MiddlewareType = Union[
|
MiddlewareType = Union[
|
||||||
BaseMiddleware, Callable[[NextMiddlewareType, TelegramObject, Dict[str, Any]], Awaitable[Any]]
|
BaseMiddleware,
|
||||||
|
Callable[
|
||||||
|
[NextMiddlewareType[MiddlewareEventType], MiddlewareEventType, Dict[str, Any]],
|
||||||
|
Awaitable[Any],
|
||||||
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
UNHANDLED = sentinel.UNHANDLED
|
UNHANDLED = sentinel.UNHANDLED
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,14 @@ from pydantic import ValidationError
|
||||||
|
|
||||||
from ...types import TelegramObject
|
from ...types import TelegramObject
|
||||||
from ..filters.base import BaseFilter
|
from ..filters.base import BaseFilter
|
||||||
from .bases import REJECTED, UNHANDLED, MiddlewareType, NextMiddlewareType, SkipHandler
|
from .bases import (
|
||||||
|
REJECTED,
|
||||||
|
UNHANDLED,
|
||||||
|
MiddlewareEventType,
|
||||||
|
MiddlewareType,
|
||||||
|
NextMiddlewareType,
|
||||||
|
SkipHandler,
|
||||||
|
)
|
||||||
from .handler import CallbackType, FilterObject, FilterType, HandlerObject, HandlerType
|
from .handler import CallbackType, FilterObject, FilterType, HandlerObject, HandlerType
|
||||||
|
|
||||||
if TYPE_CHECKING: # pragma: no cover
|
if TYPE_CHECKING: # pragma: no cover
|
||||||
|
|
@ -29,8 +36,8 @@ class TelegramEventObserver:
|
||||||
|
|
||||||
self.handlers: List[HandlerObject] = []
|
self.handlers: List[HandlerObject] = []
|
||||||
self.filters: List[Type[BaseFilter]] = []
|
self.filters: List[Type[BaseFilter]] = []
|
||||||
self.outer_middlewares: List[MiddlewareType] = []
|
self.outer_middlewares: List[MiddlewareType[TelegramObject]] = []
|
||||||
self.middlewares: List[MiddlewareType] = []
|
self.middlewares: List[MiddlewareType[TelegramObject]] = []
|
||||||
|
|
||||||
# Re-used filters check method from already implemented handler object
|
# Re-used filters check method from already implemented handler object
|
||||||
# with dummy callback which never will be used
|
# with dummy callback which never will be used
|
||||||
|
|
@ -78,7 +85,7 @@ class TelegramEventObserver:
|
||||||
yield filter_
|
yield filter_
|
||||||
registry.append(filter_)
|
registry.append(filter_)
|
||||||
|
|
||||||
def _resolve_middlewares(self, *, outer: bool = False) -> List[MiddlewareType]:
|
def _resolve_middlewares(self, *, outer: bool = False) -> List[MiddlewareType[TelegramObject]]:
|
||||||
"""
|
"""
|
||||||
Get all middlewares in a tree
|
Get all middlewares in a tree
|
||||||
:param *:
|
:param *:
|
||||||
|
|
@ -137,8 +144,8 @@ class TelegramEventObserver:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _wrap_middleware(
|
def _wrap_middleware(
|
||||||
cls, middlewares: List[MiddlewareType], handler: HandlerType
|
cls, middlewares: List[MiddlewareType[MiddlewareEventType]], handler: HandlerType
|
||||||
) -> NextMiddlewareType:
|
) -> NextMiddlewareType[MiddlewareEventType]:
|
||||||
@functools.wraps(handler)
|
@functools.wraps(handler)
|
||||||
def mapper(event: TelegramObject, kwargs: Dict[str, Any]) -> Any:
|
def mapper(event: TelegramObject, kwargs: Dict[str, Any]) -> Any:
|
||||||
return handler(event, **kwargs)
|
return handler(event, **kwargs)
|
||||||
|
|
@ -194,8 +201,11 @@ class TelegramEventObserver:
|
||||||
|
|
||||||
def middleware(
|
def middleware(
|
||||||
self,
|
self,
|
||||||
middleware: Optional[MiddlewareType] = None,
|
middleware: Optional[MiddlewareType[TelegramObject]] = None,
|
||||||
) -> Union[Callable[[MiddlewareType], MiddlewareType], MiddlewareType]:
|
) -> Union[
|
||||||
|
Callable[[MiddlewareType[TelegramObject]], MiddlewareType[TelegramObject]],
|
||||||
|
MiddlewareType[TelegramObject],
|
||||||
|
]:
|
||||||
"""
|
"""
|
||||||
Decorator for registering inner middlewares
|
Decorator for registering inner middlewares
|
||||||
|
|
||||||
|
|
@ -215,7 +225,7 @@ class TelegramEventObserver:
|
||||||
<event>.middleware(my_middleware) # via method
|
<event>.middleware(my_middleware) # via method
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def wrapper(m: MiddlewareType) -> MiddlewareType:
|
def wrapper(m: MiddlewareType[TelegramObject]) -> MiddlewareType[TelegramObject]:
|
||||||
self.middlewares.append(m)
|
self.middlewares.append(m)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
@ -225,8 +235,11 @@ class TelegramEventObserver:
|
||||||
|
|
||||||
def outer_middleware(
|
def outer_middleware(
|
||||||
self,
|
self,
|
||||||
middleware: Optional[MiddlewareType] = None,
|
middleware: Optional[MiddlewareType[TelegramObject]] = None,
|
||||||
) -> Union[Callable[[MiddlewareType], MiddlewareType], MiddlewareType]:
|
) -> Union[
|
||||||
|
Callable[[MiddlewareType[TelegramObject]], MiddlewareType[TelegramObject]],
|
||||||
|
MiddlewareType[TelegramObject],
|
||||||
|
]:
|
||||||
"""
|
"""
|
||||||
Decorator for registering outer middlewares
|
Decorator for registering outer middlewares
|
||||||
|
|
||||||
|
|
@ -246,7 +259,7 @@ class TelegramEventObserver:
|
||||||
<event>.outer_middleware(my_middleware) # via method
|
<event>.outer_middleware(my_middleware) # via method
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def wrapper(m: MiddlewareType) -> MiddlewareType:
|
def wrapper(m: MiddlewareType[TelegramObject]) -> MiddlewareType[TelegramObject]:
|
||||||
self.outer_middlewares.append(m)
|
self.outer_middlewares.append(m)
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from .base import BaseFilter
|
||||||
from .command import Command, CommandObject
|
from .command import Command, CommandObject
|
||||||
from .content_types import ContentTypesFilter
|
from .content_types import ContentTypesFilter
|
||||||
from .exception import ExceptionMessageFilter, ExceptionTypeFilter
|
from .exception import ExceptionMessageFilter, ExceptionTypeFilter
|
||||||
|
from .state import StateFilter
|
||||||
from .text import Text
|
from .text import Text
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
|
|
@ -15,21 +16,46 @@ __all__ = (
|
||||||
"ContentTypesFilter",
|
"ContentTypesFilter",
|
||||||
"ExceptionMessageFilter",
|
"ExceptionMessageFilter",
|
||||||
"ExceptionTypeFilter",
|
"ExceptionTypeFilter",
|
||||||
|
"StateFilter",
|
||||||
)
|
)
|
||||||
|
|
||||||
BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
|
BUILTIN_FILTERS: Dict[str, Tuple[Type[BaseFilter], ...]] = {
|
||||||
"message": (Text, Command, ContentTypesFilter),
|
"message": (
|
||||||
"edited_message": (Text, Command, ContentTypesFilter),
|
Text,
|
||||||
"channel_post": (Text, ContentTypesFilter),
|
Command,
|
||||||
"edited_channel_post": (Text, ContentTypesFilter),
|
ContentTypesFilter,
|
||||||
"inline_query": (Text,),
|
StateFilter,
|
||||||
"chosen_inline_result": (),
|
),
|
||||||
"callback_query": (Text,),
|
"edited_message": (
|
||||||
"shipping_query": (),
|
Text,
|
||||||
"pre_checkout_query": (),
|
Command,
|
||||||
"poll": (),
|
ContentTypesFilter,
|
||||||
"poll_answer": (),
|
StateFilter,
|
||||||
"my_chat_member": (),
|
),
|
||||||
"chat_member": (),
|
"channel_post": (
|
||||||
|
Text,
|
||||||
|
ContentTypesFilter,
|
||||||
|
StateFilter,
|
||||||
|
),
|
||||||
|
"edited_channel_post": (
|
||||||
|
Text,
|
||||||
|
ContentTypesFilter,
|
||||||
|
StateFilter,
|
||||||
|
),
|
||||||
|
"inline_query": (
|
||||||
|
Text,
|
||||||
|
StateFilter,
|
||||||
|
),
|
||||||
|
"chosen_inline_result": (StateFilter,),
|
||||||
|
"callback_query": (
|
||||||
|
Text,
|
||||||
|
StateFilter,
|
||||||
|
),
|
||||||
|
"shipping_query": (StateFilter,),
|
||||||
|
"pre_checkout_query": (StateFilter,),
|
||||||
|
"poll": (StateFilter,),
|
||||||
|
"poll_answer": (StateFilter,),
|
||||||
|
"my_chat_member": (StateFilter,),
|
||||||
|
"chat_member": (StateFilter,),
|
||||||
"error": (ExceptionMessageFilter, ExceptionTypeFilter),
|
"error": (ExceptionMessageFilter, ExceptionTypeFilter),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
from typing import Any, Dict, Optional, Sequence, Union
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence, Union
|
||||||
|
|
||||||
from pydantic import root_validator
|
from pydantic import root_validator
|
||||||
|
|
||||||
from aiogram.dispatcher.filters import BaseFilter
|
from aiogram.dispatcher.filters import BaseFilter
|
||||||
from aiogram.types import CallbackQuery, InlineQuery, Message, Poll
|
from aiogram.types import CallbackQuery, InlineQuery, Message, Poll
|
||||||
|
|
||||||
TextType = str
|
if TYPE_CHECKING:
|
||||||
|
from aiogram.utils.i18n.lazy_proxy import LazyProxy
|
||||||
|
|
||||||
|
TextType = Union[str, "LazyProxy"]
|
||||||
|
|
||||||
|
|
||||||
class Text(BaseFilter):
|
class Text(BaseFilter):
|
||||||
|
|
@ -35,6 +38,9 @@ class Text(BaseFilter):
|
||||||
text_ignore_case: bool = False
|
text_ignore_case: bool = False
|
||||||
"""Ignore case when checks"""
|
"""Ignore case when checks"""
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
@root_validator
|
@root_validator
|
||||||
def _validate_constraints(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
def _validate_constraints(cls, values: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
# Validate that only one text filter type is presented
|
# Validate that only one text filter type is presented
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,10 @@ from aiogram.dispatcher.fsm.context import FSMContext
|
||||||
from aiogram.dispatcher.fsm.storage.base import BaseStorage
|
from aiogram.dispatcher.fsm.storage.base import BaseStorage
|
||||||
from aiogram.dispatcher.fsm.strategy import FSMStrategy, apply_strategy
|
from aiogram.dispatcher.fsm.strategy import FSMStrategy, apply_strategy
|
||||||
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
||||||
from aiogram.types import Update
|
from aiogram.types import TelegramObject
|
||||||
|
|
||||||
|
|
||||||
class FSMContextMiddleware(BaseMiddleware[Update]):
|
class FSMContextMiddleware(BaseMiddleware):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
storage: BaseStorage,
|
storage: BaseStorage,
|
||||||
|
|
@ -21,8 +21,8 @@ class FSMContextMiddleware(BaseMiddleware[Update]):
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]],
|
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
|
||||||
event: Update,
|
event: TelegramObject,
|
||||||
data: Dict[str, Any],
|
data: Dict[str, Any],
|
||||||
) -> Any:
|
) -> Any:
|
||||||
bot: Bot = cast(Bot, data["bot"])
|
bot: Bot = cast(Bot, data["bot"])
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,8 @@ class StatesGroup(metaclass=StatesGroupMeta):
|
||||||
return cls
|
return cls
|
||||||
return cls.__parent__.get_root()
|
return cls.__parent__.get_root()
|
||||||
|
|
||||||
def __call__(cls, event: TelegramObject, raw_state: Optional[str] = None) -> bool:
|
def __call__(self, event: TelegramObject, raw_state: Optional[str] = None) -> bool:
|
||||||
return raw_state in type(cls).__all_states_names__
|
return raw_state in type(self).__all_states_names__
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"StatesGroup {type(self).__full_group_name__}"
|
return f"StatesGroup {type(self).__full_group_name__}"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Any, Awaitable, Callable, Dict, Generic, TypeVar
|
from typing import Any, Awaitable, Callable, Dict, TypeVar
|
||||||
|
|
||||||
|
from aiogram.types import TelegramObject
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
class BaseMiddleware(ABC, Generic[T]):
|
class BaseMiddleware(ABC):
|
||||||
"""
|
"""
|
||||||
Generic middleware class
|
Generic middleware class
|
||||||
"""
|
"""
|
||||||
|
|
@ -12,8 +14,8 @@ class BaseMiddleware(ABC, Generic[T]):
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
handler: Callable[[T, Dict[str, Any]], Awaitable[Any]],
|
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
|
||||||
event: T,
|
event: TelegramObject,
|
||||||
data: Dict[str, Any],
|
data: Dict[str, Any],
|
||||||
) -> Any: # pragma: no cover
|
) -> Any: # pragma: no cover
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict
|
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict
|
||||||
|
|
||||||
from ...types import Update
|
from ...types import TelegramObject
|
||||||
from ..event.bases import UNHANDLED, CancelHandler, SkipHandler
|
from ..event.bases import UNHANDLED, CancelHandler, SkipHandler
|
||||||
from .base import BaseMiddleware
|
from .base import BaseMiddleware
|
||||||
|
|
||||||
|
|
@ -10,14 +10,14 @@ if TYPE_CHECKING: # pragma: no cover
|
||||||
from ..router import Router
|
from ..router import Router
|
||||||
|
|
||||||
|
|
||||||
class ErrorsMiddleware(BaseMiddleware[Update]):
|
class ErrorsMiddleware(BaseMiddleware):
|
||||||
def __init__(self, router: Router):
|
def __init__(self, router: Router):
|
||||||
self.router = router
|
self.router = router
|
||||||
|
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
handler: Callable[[Any, Dict[str, Any]], Awaitable[Any]],
|
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
|
||||||
event: Any,
|
event: TelegramObject,
|
||||||
data: Dict[str, Any],
|
data: Dict[str, Any],
|
||||||
) -> Any:
|
) -> Any:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,18 @@ from contextlib import contextmanager
|
||||||
from typing import Any, Awaitable, Callable, Dict, Iterator, Optional, Tuple
|
from typing import Any, Awaitable, Callable, Dict, Iterator, Optional, Tuple
|
||||||
|
|
||||||
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
from aiogram.dispatcher.middlewares.base import BaseMiddleware
|
||||||
from aiogram.types import Chat, Update, User
|
from aiogram.types import Chat, TelegramObject, Update, User
|
||||||
|
|
||||||
|
|
||||||
class UserContextMiddleware(BaseMiddleware[Update]):
|
class UserContextMiddleware(BaseMiddleware):
|
||||||
async def __call__(
|
async def __call__(
|
||||||
self,
|
self,
|
||||||
handler: Callable[[Update, Dict[str, Any]], Awaitable[Any]],
|
handler: Callable[[TelegramObject, Dict[str, Any]], Awaitable[Any]],
|
||||||
event: Update,
|
event: TelegramObject,
|
||||||
data: Dict[str, Any],
|
data: Dict[str, Any],
|
||||||
) -> Any:
|
) -> Any:
|
||||||
|
if not isinstance(event, Update):
|
||||||
|
raise RuntimeError("UserContextMiddleware got an unexpected event type!")
|
||||||
chat, user = self.resolve_event_context(event=event)
|
chat, user = self.resolve_event_context(event=event)
|
||||||
with self.context(chat=chat, user=user):
|
with self.context(chat=chat, user=user):
|
||||||
if user is not None:
|
if user is not None:
|
||||||
|
|
|
||||||
|
|
@ -216,8 +216,7 @@ class OrderedHelperMeta(type):
|
||||||
|
|
||||||
setattr(cls, PROPS_KEYS_ATTR_NAME, props_keys)
|
setattr(cls, PROPS_KEYS_ATTR_NAME, props_keys)
|
||||||
|
|
||||||
# ref: https://gitter.im/python/typing?at=5da98cc5fa637359fc9cbfe1
|
return cls
|
||||||
return cast(OrderedHelperMeta, cls)
|
|
||||||
|
|
||||||
|
|
||||||
class OrderedHelper(Helper, metaclass=OrderedHelperMeta):
|
class OrderedHelper(Helper, metaclass=OrderedHelperMeta):
|
||||||
|
|
|
||||||
3
mypy.ini
3
mypy.ini
|
|
@ -32,3 +32,6 @@ ignore_missing_imports = True
|
||||||
|
|
||||||
[mypy-aioredis]
|
[mypy-aioredis]
|
||||||
ignore_missing_imports = True
|
ignore_missing_imports = True
|
||||||
|
|
||||||
|
[mypy-babel.*]
|
||||||
|
ignore_missing_imports = True
|
||||||
|
|
|
||||||
684
poetry.lock
generated
684
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -32,32 +32,39 @@ classifiers = [
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.8"
|
python = "^3.8"
|
||||||
magic-filter = "^1.0.0"
|
magic-filter = "^1.0.2"
|
||||||
aiohttp = "^3.7.4"
|
aiohttp = "^3.7.4"
|
||||||
pydantic = "^1.8.1"
|
pydantic = "^1.8.2"
|
||||||
Babel = "^2.9.1"
|
aiofiles = "^0.7.0"
|
||||||
aiofiles = "^0.6.0"
|
|
||||||
async_lru = "^1.0.2"
|
async_lru = "^1.0.2"
|
||||||
|
# Fast
|
||||||
|
uvloop = { version = "^0.16.0", markers = "sys_platform == 'darwin' or sys_platform == 'linux'", optional = true }
|
||||||
|
# i18n
|
||||||
|
Babel = { version = "^2.9.1", optional = true }
|
||||||
|
# Proxy
|
||||||
aiohttp-socks = { version = "^0.5.5", optional = true }
|
aiohttp-socks = { version = "^0.5.5", optional = true }
|
||||||
aioredis = { version = "^2.0.0", allow-prereleases = true, optional = true }
|
# Redis
|
||||||
sphinx = { version = "^3.1.0", optional = true }
|
aioredis = { version = "^2.0.0", optional = true }
|
||||||
|
# Docs
|
||||||
|
Sphinx = { version = "^4.2.0", optional = true }
|
||||||
sphinx-intl = { version = "^2.0.1", optional = true }
|
sphinx-intl = { version = "^2.0.1", optional = true }
|
||||||
sphinx-autobuild = { version = "^2020.9.1", optional = true }
|
sphinx-autobuild = { version = "^2021.3.14", optional = true }
|
||||||
sphinx-copybutton = { version = "^0.3.1", optional = true }
|
sphinx-copybutton = { version = "^0.4.0", optional = true }
|
||||||
furo = { version = "^2021.6.18-beta.36", optional = true }
|
furo = { version = "^2021.9.8", optional = true }
|
||||||
sphinx-prompt = { version = "^1.3.0", optional = true }
|
sphinx-prompt = { version = "^1.5.0", optional = true }
|
||||||
Sphinx-Substitution-Extensions = { version = "^2020.9.30", optional = true }
|
Sphinx-Substitution-Extensions = { version = "^2020.9.30", optional = true }
|
||||||
|
towncrier = { version = "^21.3.0", optional = true }
|
||||||
|
pygments = { version = "^2.4", optional = true }
|
||||||
|
pymdown-extensions = { version = "^8.0", optional = true }
|
||||||
|
markdown-include = { version = "^0.6", optional = true }
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
aiohttp-socks = "^0.5"
|
|
||||||
aioredis = { version = "^2.0.0a1", allow-prereleases = true }
|
|
||||||
ipython = "^7.22.0"
|
ipython = "^7.22.0"
|
||||||
uvloop = { version = "^0.15.2", markers = "sys_platform == 'darwin' or sys_platform == 'linux'" }
|
|
||||||
black = "^21.4b2"
|
black = "^21.4b2"
|
||||||
isort = "^5.8.0"
|
isort = "^5.8.0"
|
||||||
flake8 = "^3.9.1"
|
flake8 = "^3.9.1"
|
||||||
flake8-html = "^0.4.1"
|
flake8-html = "^0.4.1"
|
||||||
mypy = "^0.812"
|
mypy = "^0.910"
|
||||||
pytest = "^6.2.3"
|
pytest = "^6.2.3"
|
||||||
pytest-html = "^3.1.1"
|
pytest-html = "^3.1.1"
|
||||||
pytest-asyncio = "^0.15.1"
|
pytest-asyncio = "^0.15.1"
|
||||||
|
|
@ -68,27 +75,17 @@ pytest-cov = "^2.11.1"
|
||||||
aresponses = "^2.1.4"
|
aresponses = "^2.1.4"
|
||||||
asynctest = "^0.13.0"
|
asynctest = "^0.13.0"
|
||||||
toml = "^0.10.2"
|
toml = "^0.10.2"
|
||||||
pygments = "^2.4"
|
|
||||||
pymdown-extensions = "^8.0"
|
|
||||||
markdown-include = "^0.6"
|
|
||||||
pre-commit = "^2.15.0"
|
pre-commit = "^2.15.0"
|
||||||
packaging = "^20.3"
|
packaging = "^20.3"
|
||||||
typing-extensions = "^3.7.4"
|
typing-extensions = "^3.7.4"
|
||||||
sphinx = "^3.1.0"
|
|
||||||
sphinx-intl = "^2.0.1"
|
|
||||||
sphinx-autobuild = "^2020.9.1"
|
|
||||||
sphinx-copybutton = "^0.3.1"
|
|
||||||
furo = "^2021.6.18-beta.36"
|
|
||||||
sphinx-prompt = "^1.3.0"
|
|
||||||
Sphinx-Substitution-Extensions = "^2020.9.30"
|
|
||||||
towncrier = "^21.3.0"
|
|
||||||
diagrams = "^0.20.0"
|
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.extras]
|
[tool.poetry.extras]
|
||||||
fast = ["uvloop"]
|
fast = ["uvloop"]
|
||||||
redis = ["aioredis"]
|
redis = ["aioredis"]
|
||||||
proxy = ["aiohttp-socks"]
|
proxy = ["aiohttp-socks"]
|
||||||
|
i18n = ["Babel"]
|
||||||
docs = [
|
docs = [
|
||||||
"sphinx",
|
"sphinx",
|
||||||
"sphinx-intl",
|
"sphinx-intl",
|
||||||
|
|
@ -98,6 +95,10 @@ docs = [
|
||||||
"black",
|
"black",
|
||||||
"sphinx-prompt",
|
"sphinx-prompt",
|
||||||
"Sphinx-Substitution-Extensions",
|
"Sphinx-Substitution-Extensions",
|
||||||
|
"towncrier",
|
||||||
|
"pygments",
|
||||||
|
"pymdown-extensions",
|
||||||
|
"markdown-include",
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.black]
|
[tool.black]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue