from typing import get_args, get_origin from aiogram import Router, F from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import StatesGroup, State from aiogram.types import Message, CallbackQuery, InlineKeyboardButton from aiogram.utils.i18n import I18n from aiogram.utils.keyboard import InlineKeyboardBuilder from logging import getLogger from sqlmodel.ext.asyncio.session import AsyncSession from ....main import QBotApp from ....model.bot_entity import BotEntity from ....model.bot_enum import BotEnum from ....model.owned_bot_entity import OwnedBotEntity from ....model.settings import Settings from ....model.user import UserBase from ....model.descriptors import EntityFieldDescriptor, EntityDescriptor from ....model import EntityPermission from ....utils import serialize, deserialize, get_user_permissions from ..context import ContextData, CallbackCommand, CommandContext from ..common import get_send_message, get_local_text, get_entity_descriptor, get_callable_str, get_value_repr logger = getLogger(__name__) router = Router() @router.callback_query(ContextData.filter(F.command == CallbackCommand.ENTITY_ITEM)) async def entity_item_callback(query: CallbackQuery, callback_data: ContextData, **kwargs): await clear_state(state = kwargs["state"]) stack = await save_navigation_context(callback_data = callback_data, state = kwargs["state"]) await entity_item(query = query, callback_data = callback_data, navigation_stack = stack, **kwargs) async def entity_item(query: CallbackQuery, callback_data: ContextData, db_session: AsyncSession, user: UserBase, app: QBotApp, navigation_stack: list[ContextData], **kwargs): entity_descriptor = get_entity_descriptor(app, callback_data) user_permissions = get_user_permissions(user, entity_descriptor) entity_type: BotEntity = entity_descriptor.type_ keyboard_builder = InlineKeyboardBuilder() entity_item = await entity_type.get(session = db_session, id = callback_data.entity_id) if not entity_item: return await query.answer(text = (await Settings.get(Settings.APP_STRINGS_NOT_FOUND))) is_owned = issubclass(entity_type, OwnedBotEntity) if (EntityPermission.READ not in user_permissions and EntityPermission.READ_ALL not in user_permissions): return await query.answer(text = (await Settings.get(Settings.APP_STRINGS_FORBIDDEN))) if (is_owned and EntityPermission.READ_ALL not in user_permissions and entity_item.user_id != user.id): return await query.answer(text = (await Settings.get(Settings.APP_STRINGS_FORBIDDEN))) can_edit = (EntityPermission.UPDATE_ALL in user_permissions or (EntityPermission.UPDATE in user_permissions and not is_owned) or (EntityPermission.UPDATE in user_permissions and is_owned and entity_item.user_id == user.id)) can_delete = (EntityPermission.DELETE_ALL in user_permissions or (EntityPermission.DELETE in user_permissions and not is_owned) or (EntityPermission.DELETE in user_permissions and is_owned and entity_item.user_id == user.id)) edit_delete_row = [] if can_edit: edit_delete_row.append( InlineKeyboardButton( text = (await Settings.get(Settings.APP_STRINGS_EDIT_BTN)), callback_data = ContextData( command = CallbackCommand.FIELD_EDITOR, context = CommandContext.ENTITY_EDIT, entity_name = entity_descriptor.name, entity_id = str(entity_item.id), field_name = entity_descriptor.field_sequence[0], save_state = True).pack())) if can_delete: edit_delete_row.append( InlineKeyboardButton( text = (await Settings.get(Settings.APP_STRINGS_DELETE_BTN)), callback_data = ContextData( command = CallbackCommand.ENTITY_DELETE, entity_name = entity_descriptor.name, entity_id = str(entity_item.id)).pack())) if edit_delete_row: keyboard_builder.row(*edit_delete_row) entity_caption = get_callable_str(entity_descriptor.caption_msg, entity_descriptor, entity_item) entity_item_name = get_local_text(entity_item.name, user.lang) if entity_descriptor.fields_descriptors["name"].localizable else entity_item.name item_text = f"{entity_caption or entity_descriptor.name}: {entity_item_name}" for field_descriptor in entity_descriptor.fields_descriptors.values(): if field_descriptor.name in ["name", "id"] or not field_descriptor.is_visible: continue field_caption = get_callable_str(field_descriptor.caption_str, field_descriptor, entity_item) value = get_value_repr(value = getattr(entity_item, field_descriptor.name), field_descriptor = field_descriptor, locale = user.lang) item_text += f"\n{field_caption or field_descriptor.name}:{f" {value}" if value else ""}" context = pop_navigation_context(navigation_stack) if context: keyboard_builder.row( InlineKeyboardButton( text = (await Settings.get(Settings.APP_STRINGS_BACK_BTN)), callback_data = context.pack())) send_message = get_send_message(query) await send_message(text = item_text, reply_markup = keyboard_builder.as_markup()) @router.callback_query(ContextData.filter(F.command == CallbackCommand.ENTITY_DELETE)) async def entity_delete_callback(query: CallbackQuery, **kwargs): callback_data: ContextData = kwargs["callback_data"] user: UserBase = kwargs["user"] db_session: AsyncSession = kwargs["db_session"] app: QBotApp = kwargs["app"] entity_descriptor = get_entity_descriptor(app = app, callback_data = callback_data) user_permissions = get_user_permissions(user, entity_descriptor) entity = await entity_descriptor.type_.get(session = db_session, id = int(callback_data.entity_id)) if not (EntityPermission.DELETE_ALL in user_permissions or (EntityPermission.DELETE in user_permissions and not issubclass(entity_descriptor.type_, OwnedBotEntity)) or (EntityPermission.DELETE in user_permissions and issubclass(entity_descriptor.type_, OwnedBotEntity) and entity.user_id == user.id)): return await query.answer(text = (await Settings.get(Settings.APP_STRINGS_FORBIDDEN))) if not callback_data.data: field_descriptor = entity_descriptor.fields_descriptors["name"] entity = await entity_descriptor.type_.get(session = db_session, id = int(callback_data.entity_id)) return await query.message.edit_text( text = (await Settings.get(Settings.APP_STRINGS_CONFIRM_DELETE_P_NAME)).format( name = get_value_repr( value = getattr(entity, field_descriptor.name), field_descriptor = field_descriptor, locale = user.lang)), reply_markup = InlineKeyboardBuilder().row( InlineKeyboardButton( text = (await Settings.get(Settings.APP_STRINGS_YES_BTN)), callback_data = ContextData( command = CallbackCommand.ENTITY_DELETE, entity_name = callback_data.entity_name, entity_id = callback_data.entity_id, data = "yes").pack()), InlineKeyboardButton( text = (await Settings.get(Settings.APP_STRINGS_NO_BTN)), callback_data = ContextData( command = CallbackCommand.ENTITY_ITEM, entity_name = callback_data.entity_name, entity_id = callback_data.entity_id, data = "no").pack())).as_markup()) if callback_data.data == "yes": await entity_descriptor.type_.remove( session = db_session, id = int(callback_data.entity_id), commit = True) await route_callback(message = query, **kwargs) if callback_data.data == "no": await route_callback(message = query, back = False, **kwargs) from ..navigation import pop_navigation_context, save_navigation_context, clear_state, route_callback