generated from geekiot/python-template
Merge pull request 'feature/handlers - add base utils for handlers' (#4) from feature/handlers into develop
Reviewed-on: #4
This commit is contained in:
commit
c9501b3a87
11 changed files with 191 additions and 4 deletions
|
|
@ -1,12 +1,27 @@
|
|||
__all__ = ["start_bot"]
|
||||
|
||||
|
||||
from aiogram import Bot, Dispatcher
|
||||
from redis.asyncio.client import Redis
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from .handlers import connect_handlers, registry
|
||||
from .middlewares import connect_middlewares
|
||||
|
||||
|
||||
async def start_bot(
|
||||
bot_token: str,
|
||||
redis_client: Redis,
|
||||
database_session: AsyncSession,
|
||||
) -> None: ...
|
||||
) -> None:
|
||||
bot = Bot(bot_token)
|
||||
|
||||
dispatcher = Dispatcher()
|
||||
|
||||
connect_handlers(dispatcher)
|
||||
connect_middlewares(dispatcher)
|
||||
|
||||
async def on_startup(bot: Bot):
|
||||
await bot.delete_webhook(drop_pending_updates=True)
|
||||
await registry.set_menu(bot)
|
||||
|
||||
await dispatcher.start_polling(bot, on_startup=on_startup)
|
||||
|
|
|
|||
|
|
@ -1 +1,17 @@
|
|||
__all__ = []
|
||||
__all__ = ["connect_handlers", "registry"]
|
||||
|
||||
|
||||
import importlib
|
||||
|
||||
from aiogram import Router
|
||||
|
||||
from .utils.registry import RouterRegistry
|
||||
|
||||
registry: RouterRegistry = RouterRegistry()
|
||||
|
||||
|
||||
def connect_handlers(dispatcher: Router) -> None:
|
||||
importlib.import_module("bot.handlers.callbacks")
|
||||
importlib.import_module("bot.handlers.commands")
|
||||
|
||||
dispatcher.include_router(registry.router)
|
||||
|
|
|
|||
36
src/bot/handlers/utils/filters/__init__.py
Normal file
36
src/bot/handlers/utils/filters/__init__.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
__all__ = ["ChatTypeFilter"]
|
||||
|
||||
|
||||
from typing import Union
|
||||
|
||||
from aiogram.filters import BaseFilter
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
|
||||
from bot.handlers.utils.types import ChatType
|
||||
|
||||
|
||||
class ChatTypeFilter(BaseFilter):
|
||||
def __init__(
|
||||
self,
|
||||
chat_types: Union[ChatType, list[ChatType]],
|
||||
) -> None:
|
||||
if isinstance(chat_types, ChatType):
|
||||
self.chat_type = [chat_types.value]
|
||||
else:
|
||||
self.chat_type = [chat.value for chat in chat_types]
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
event: Union[Message, CallbackQuery],
|
||||
) -> bool:
|
||||
current_chat_type: str
|
||||
|
||||
if isinstance(event, Message):
|
||||
current_chat_type = event.chat.type
|
||||
else:
|
||||
if event.message is None:
|
||||
return False
|
||||
|
||||
current_chat_type = event.message.chat.type
|
||||
|
||||
return current_chat_type in self.chat_type
|
||||
82
src/bot/handlers/utils/registry/__init__.py
Normal file
82
src/bot/handlers/utils/registry/__init__.py
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
__all__ = ["RouterRegistry"]
|
||||
|
||||
|
||||
from typing import Any, Callable, Optional, Union
|
||||
|
||||
from aiogram import Bot, Router
|
||||
from aiogram.filters import BaseFilter, Command
|
||||
from aiogram.types.bot_command import BotCommand
|
||||
from aiogram.types.bot_command_scope_all_private_chats import (
|
||||
BotCommandScopeAllPrivateChats,
|
||||
)
|
||||
|
||||
from bot.handlers.utils.filters import ChatTypeFilter
|
||||
from bot.handlers.utils.types import (
|
||||
ChatType,
|
||||
HandlerMeta,
|
||||
HandlerType,
|
||||
)
|
||||
|
||||
|
||||
class RouterRegistry:
|
||||
def __init__(self) -> None:
|
||||
self._router = Router()
|
||||
self._handlers: list[HandlerMeta] = list()
|
||||
|
||||
@property
|
||||
def router(self) -> Router:
|
||||
return self._router
|
||||
|
||||
def register(
|
||||
self,
|
||||
*,
|
||||
description: str,
|
||||
chat_types: Union[ChatType, list[ChatType]],
|
||||
filters: Union[BaseFilter, list[BaseFilter]] = [],
|
||||
command: Optional[str] = None,
|
||||
) -> Callable[[Any], HandlerType]:
|
||||
if isinstance(chat_types, ChatType):
|
||||
chat_types = [chat_types]
|
||||
|
||||
if isinstance(filters, BaseFilter):
|
||||
filters = [filters]
|
||||
|
||||
def decorator(func: HandlerType) -> HandlerType:
|
||||
meta = HandlerMeta(
|
||||
func=func,
|
||||
description=description,
|
||||
chat_types=chat_types,
|
||||
command=command,
|
||||
)
|
||||
self._handlers.append(meta)
|
||||
|
||||
if command is None:
|
||||
self._router.callback_query.register(
|
||||
func,
|
||||
*filters,
|
||||
ChatTypeFilter(chat_types=chat_types),
|
||||
)
|
||||
else:
|
||||
self._router.message.register(
|
||||
func,
|
||||
*filters,
|
||||
ChatTypeFilter(chat_types=chat_types),
|
||||
Command(commands=[command]),
|
||||
)
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
async def set_menu(self, bot: Bot) -> None:
|
||||
await bot.set_my_commands(
|
||||
[
|
||||
BotCommand(
|
||||
command=meta.command,
|
||||
description=meta.description,
|
||||
)
|
||||
for meta in self._handlers
|
||||
if meta.command is not None
|
||||
and ChatType.PRIVATE in meta.chat_types
|
||||
],
|
||||
scope=BotCommandScopeAllPrivateChats(),
|
||||
)
|
||||
5
src/bot/handlers/utils/types/__init__.py
Normal file
5
src/bot/handlers/utils/types/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
__all__ = ["ChatType", "HandlerType", "HandlerMeta"]
|
||||
|
||||
|
||||
from .chat import ChatType
|
||||
from .handler import HandlerMeta, HandlerType
|
||||
11
src/bot/handlers/utils/types/chat.py
Normal file
11
src/bot/handlers/utils/types/chat.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class ChatType(Enum):
|
||||
PRIVATE = "private"
|
||||
GROUP = "group"
|
||||
SUPERGROUP = "supergroup"
|
||||
CHANNEL = "channel"
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value
|
||||
16
src/bot/handlers/utils/types/handler.py
Normal file
16
src/bot/handlers/utils/types/handler.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Any, Awaitable, Callable, Optional, TypeVar
|
||||
|
||||
from bot.handlers.utils.types import ChatType
|
||||
|
||||
HandlerType = TypeVar(
|
||||
"HandlerType", bound=Callable[[Any], Awaitable[Any]]
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class HandlerMeta:
|
||||
func: Callable[[Any], Awaitable[Any]]
|
||||
description: str
|
||||
chat_types: list[ChatType]
|
||||
command: Optional[str] = None
|
||||
|
|
@ -1 +1,7 @@
|
|||
__all__ = []
|
||||
__all__ = ["connect_middlewares"]
|
||||
|
||||
|
||||
from aiogram import Router
|
||||
|
||||
|
||||
def connect_middlewares(dispatcher: Router): ...
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue