generated from geekiot/python-template
feature/handlers - add base utils for handlers #4
11 changed files with 191 additions and 4 deletions
|
|
@ -1,12 +1,27 @@
|
||||||
__all__ = ["start_bot"]
|
__all__ = ["start_bot"]
|
||||||
|
|
||||||
|
from aiogram import Bot, Dispatcher
|
||||||
from redis.asyncio.client import Redis
|
from redis.asyncio.client import Redis
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from .handlers import connect_handlers, registry
|
||||||
|
from .middlewares import connect_middlewares
|
||||||
|
|
||||||
|
|
||||||
async def start_bot(
|
async def start_bot(
|
||||||
bot_token: str,
|
bot_token: str,
|
||||||
redis_client: Redis,
|
redis_client: Redis,
|
||||||
database_session: AsyncSession,
|
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