Files
quickbot/bot/handlers/user_handlers/__init__.py
Alexander Kalinovsky 3898a333fa refactoring
2025-01-09 13:11:10 +01:00

125 lines
4.0 KiB
Python

from dataclasses import dataclass, field
from typing import Any, Callable, TYPE_CHECKING
from aiogram import Router, F
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
from aiogram.fsm.context import FSMContext
from aiogram.utils.i18n import I18n
from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlmodel.ext.asyncio.session import AsyncSession
from ..context import ContextData, CallbackCommand
from ....model.user import UserBase
from ....model.settings import Settings
if TYPE_CHECKING:
from ....main import QBotApp
router = Router()
@dataclass(kw_only = True)
class CommandCallbackContext[UT: UserBase]:
keyboard_builder: InlineKeyboardBuilder = field(default_factory = InlineKeyboardBuilder)
message_text: str | None = None
register_navigation: bool = True
message: Message | CallbackQuery
callback_data: ContextData
db_session: AsyncSession
user: UT
app: "QBotApp"
state_data: dict[str, Any]
state: FSMContext
i18n: I18n
kwargs: dict[str, Any] = field(default_factory = dict)
@dataclass(kw_only = True)
class Command:
name: str
handler: Callable[[CommandCallbackContext], None]
caption: str | dict[str, str] | None = None
register_navigation: bool = True
clear_navigation: bool = False
clear_state: bool = True
@router.message(F.text.startswith("/"))
async def command_text(message: Message, **kwargs):
str_command = message.text.lstrip("/")
callback_data = ContextData(command = CallbackCommand.USER_COMMAND,
user_command = str_command)
await command_handler(message = message, callback_data = callback_data, **kwargs)
@router.callback_query(ContextData.filter(F.command == CallbackCommand.USER_COMMAND))
async def command_callback(message: CallbackQuery, **kwargs):
await command_handler(message = message, **kwargs)
async def command_handler(message: Message | CallbackQuery, **kwargs):
callback_data: ContextData = kwargs.pop("callback_data")
str_command = callback_data.user_command
app: "QBotApp" = kwargs.pop("app")
command = app.bot_commands.get(str_command)
if not command:
return
state: FSMContext = kwargs.pop("state")
state_data = await state.get_data()
if command.register_navigation:
clear_state(state_data = state_data)
if command.clear_navigation:
state_data.pop("navigation_stack", None)
state_data.pop("navigation_context", None)
if command.register_navigation:
stack = save_navigation_context(callback_data = callback_data, state_data = state_data)
callback_context = CommandCallbackContext[app.user_class](
message = message,
callback_data = callback_data,
db_session = kwargs.pop("db_session"),
user = kwargs.pop("user"),
app = app,
state_data = state_data,
state = state,
i18n = kwargs.pop("i18n"),
kwargs = kwargs)
await command.handler(callback_context)
await state.set_data(state_data)
if command.register_navigation:
stack, navigation_context = get_navigation_context(state_data = state_data)
back_callback_data = pop_navigation_context(stack = stack)
if back_callback_data:
callback_context.keyboard_builder.row(
InlineKeyboardButton(
text = (await Settings.get(Settings.APP_STRINGS_BACK_BTN)),
callback_data = back_callback_data.pack()))
send_message = get_send_message(message)
if isinstance(message, CallbackCommand):
message = message.message
if callback_context.message_text:
await send_message(text = callback_context.message_text,
reply_markup = callback_context.keyboard_builder.as_markup())
else:
await message.edit_reply_markup(reply_markup = callback_context.keyboard_builder.as_markup())
from ..common import get_send_message
from ..navigation import save_navigation_context, get_navigation_context, clear_state, pop_navigation_context