from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, Message from sqlalchemy import func, select, update, delete from sqlalchemy.ext.asyncio import AsyncSession import hashlib from database.models import Key, Admin from filters.admin import IsAdminFilter from logger import logger from middlewares import maintenance from ..panel.keyboard import build_admin_back_kb from .keyboard import AdminPanelCallback, build_management_kb, build_admins_kb, build_single_admin_menu, build_role_selection_kb, build_admin_back_kb_to_admins, build_token_result_kb from asyncio import sleep router = Router() class AdminManagementStates(StatesGroup): waiting_for_new_domain = State() class AdminState(StatesGroup): waiting_for_tg_id = State() @router.callback_query( AdminPanelCallback.filter(F.action == "management"), IsAdminFilter() ) async def handle_management(callback_query: CallbackQuery, session: AsyncSession): tg_id = callback_query.from_user.id result = await session.execute(select(Admin).where(Admin.tg_id == tg_id)) admin = result.scalar_one_or_none() if not admin: await callback_query.message.edit_text("❌ Вы не зарегистрированы как администратор.") return await callback_query.message.edit_text( text="🤖 Управление ботом", reply_markup=build_management_kb(admin.role), ) @router.callback_query( AdminPanelCallback.filter(F.action == "change_domain"), IsAdminFilter() ) async def request_new_domain(callback_query: CallbackQuery, state: FSMContext): """Запрашивает у администратора новый домен.""" await state.set_state(AdminManagementStates.waiting_for_new_domain) await callback_query.message.edit_text( text="🌐 Введите новый домен (без https://):\nПример: solobotdomen.ru", ) @router.message(AdminManagementStates.waiting_for_new_domain) async def process_new_domain( message: Message, state: FSMContext, session: AsyncSession ): """Обновляет домен в таблице keys.""" new_domain = message.text.strip() logger.info( f"[DomainChange] Новый домен, введённый администратором: '{new_domain}'" ) if not new_domain or " " in new_domain or not new_domain.replace(".", "").isalnum(): logger.warning("[DomainChange] Некорректный домен") await message.answer( "🚫 Некорректный домен! Введите домен без http:// и без пробелов.", reply_markup=build_admin_back_kb("admin"), ) return new_domain_url = f"https://{new_domain}" logger.info(f"[DomainChange] Новый домен с протоколом: '{new_domain_url}'") try: stmt = ( update(Key) .where(~Key.key.startswith(new_domain_url)) .values(key=func.regexp_replace(Key.key, r"^https://[^/]+", new_domain_url)) ) await session.execute(stmt) await session.commit() logger.info("[DomainChange] Запрос на обновление домена выполнен успешно.") except Exception as e: logger.error(f"[DomainChange] Ошибка при выполнении запроса: {e}") await message.answer( f"❌ Ошибка при обновлении домена: {e}", reply_markup=build_admin_back_kb("admin"), ) return try: sample = await session.execute(select(Key.key).limit(1)) example = sample.scalar() logger.info(f"[DomainChange] Пример обновленной записи: {example}") except Exception as e: logger.error(f"[DomainChange] Ошибка при выборке обновленной записи: {e}") await message.answer( f"✅ Домен успешно изменен на {new_domain}!", reply_markup=build_admin_back_kb("admin"), ) await state.clear() @router.callback_query(AdminPanelCallback.filter(F.action == "toggle_maintenance")) async def toggle_maintenance_mode(callback: CallbackQuery, session: AsyncSession): tg_id = callback.from_user.id result = await session.execute(select(Admin).where(Admin.tg_id == tg_id)) admin = result.scalar_one_or_none() if not admin: await callback.answer("❌ Админ не найден.", show_alert=True) return maintenance.maintenance_mode = not maintenance.maintenance_mode new_status = "включён" if maintenance.maintenance_mode else "выключен" await callback.answer(f"🛠️ Режим обслуживания {new_status}.", show_alert=True) await callback.message.edit_reply_markup( reply_markup=build_management_kb(admin.role) ) @router.callback_query(AdminPanelCallback.filter(F.action == "admins")) async def show_admins(callback: CallbackQuery, session: AsyncSession): result = await session.execute(select(Admin.tg_id, Admin.role)) admins = result.all() await callback.message.edit_text( "👑 Список админов", reply_markup=build_admins_kb(admins) ) @router.callback_query(AdminPanelCallback.filter(F.action == "add_admin")) async def prompt_new_admin(callback: CallbackQuery, state: FSMContext): await callback.message.edit_text( "Введите tg_id нового админа:", reply_markup=build_admin_back_kb_to_admins() ) await state.set_state(AdminState.waiting_for_tg_id) @router.message(AdminState.waiting_for_tg_id) async def save_new_admin(message: Message, session: AsyncSession, state: FSMContext): try: tg_id = int(message.text.strip()) except ValueError: await message.answer("❌ Неверный формат. Введите числовой tg_id.") return result = await session.execute(select(Admin).where(Admin.tg_id == tg_id)) if result.scalar_one_or_none(): await message.answer("⚠️ Такой админ уже существует.") else: session.add(Admin( tg_id=tg_id, role="moderator", description="Добавлен вручную" )) await session.commit() await message.answer( f"✅ Админ {tg_id} добавлен.", reply_markup=build_admin_back_kb_to_admins() ) await state.clear() @router.callback_query(AdminPanelCallback.filter(F.action.startswith("admin_menu|"))) async def open_admin_menu(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession): tg_id = int(callback_data.action.split("|")[1]) result = await session.execute(select(Admin.role).where(Admin.tg_id == tg_id)) admin = result.scalar_one_or_none() role = admin or "moderator" await callback.message.edit_text( f"👤 Управление админом {tg_id}", reply_markup=build_single_admin_menu(tg_id, role) ) @router.callback_query(AdminPanelCallback.filter(F.action.startswith("generate_token|"))) async def generate_token(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession): tg_id = int(callback_data.action.split("|")[1]) result = await session.execute(select(Admin).where(Admin.tg_id == tg_id)) admin = result.scalar_one_or_none() if not admin: await callback.message.edit_text("❌ Админ не найден.") return token = Admin.generate_token() token_hash = hashlib.sha256(token.encode()).hexdigest() admin.token = token_hash await session.commit() msg = await callback.message.edit_text( f"🎟 Новый токен для {tg_id}:\n\n" f"{token}\n\n" f"⚠️ Это сообщение исчезнет через 5 минут.", reply_markup=build_token_result_kb(token) ) await sleep(300) try: await msg.delete() except Exception: pass @router.callback_query(AdminPanelCallback.filter(F.action.startswith("edit_role|"))) async def edit_admin_role(callback: CallbackQuery, callback_data: AdminPanelCallback): tg_id = int(callback_data.action.split("|")[1]) await callback.message.edit_text( f"✏ Выберите новую роль для {tg_id}:", reply_markup=build_role_selection_kb(tg_id) ) @router.callback_query(AdminPanelCallback.filter(F.action.startswith("set_role|"))) async def set_admin_role(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession): try: _, tg_id_str, role = callback_data.action.split("|") tg_id = int(tg_id_str) if role not in ("superadmin", "moderator"): raise ValueError except Exception: await callback.message.edit_text("❌ Неверный формат.") return result = await session.execute(select(Admin).where(Admin.tg_id == tg_id)) admin = result.scalar_one_or_none() if not admin: await callback.message.edit_text("❌ Админ не найден.") return admin.role = role await session.commit() await callback.message.edit_text( f"✅ Роль админа {tg_id} изменена на {role}.", reply_markup=build_single_admin_menu(tg_id) ) @router.callback_query(AdminPanelCallback.filter(F.action.startswith("delete_admin|"))) async def delete_admin(callback: CallbackQuery, callback_data: AdminPanelCallback, session: AsyncSession): tg_id = int(callback_data.action.split("|")[1]) await session.execute(delete(Admin).where(Admin.tg_id == tg_id)) await session.commit() await callback.message.edit_text( f"🗑 Админ {tg_id} удалён.", reply_markup=build_admin_back_kb_to_admins() )