125 lines
4.0 KiB
Python
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 |