from aiogram import F from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, Message from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from database.models import Key from handlers.keys.operations import update_subscription from logger import logger from . import router from .keyboard import AdminPanelCallback, build_back_to_db_menu, build_post_import_kb class Import3xuiStates(StatesGroup): waiting_for_file = State() @router.callback_query(AdminPanelCallback.filter(F.action == "request_3xui_file")) async def prompt_for_3xui_file(callback: CallbackQuery, state: FSMContext): await callback.message.edit_text( "📂 Пришлите файл базы данных x-ui.db для восстановления подписок и клиентов.\n\n" "Формат: SQLite-файл с таблицей inbounds.\n\n" "⚠️ Важно! Убедитесь, что у всех подписок в панели прописан telegram_id.\n" "После восстановления обязательно выполните синхронизацию с текущими серверами!", reply_markup=build_back_to_db_menu(), ) await state.set_state(Import3xuiStates.waiting_for_file) @router.message(Import3xuiStates.waiting_for_file, F.document) async def handle_3xui_db_upload(message: Message, state: FSMContext, session: AsyncSession): file = message.document if not file.file_name.endswith(".db"): await message.reply("❌ Пожалуйста, пришли файл с расширением .db") return file_path = f"/tmp/{file.file_name}" await message.bot.download(file, destination=file_path) processing_message = await message.reply("📥 Файл получен. Начинаю восстановление...") try: from database.importer import import_keys_from_3xui_db imported, skipped = await import_keys_from_3xui_db(file_path, session) await processing_message.edit_text( f"✅ Восстановление завершено:\n" f"🔐 Импортировано подписок: {imported}\n" f"⏭ Пропущено (уже есть): {skipped}", reply_markup=build_post_import_kb(), ) except Exception as e: logger.error(f"[Import 3x-ui] Ошибка: {e}") await processing_message.edit_text( "❌ Произошла ошибка при импорте. Убедись, что это валидный файл x-ui.db", reply_markup=build_back_to_db_menu(), ) await state.clear() @router.callback_query(AdminPanelCallback.filter(F.action == "resync_after_import")) async def handle_resync_after_import(callback: CallbackQuery, session: AsyncSession): await callback.answer("🔁 Начинаю перевыпуск подписок...") result = await session.execute(select(Key.tg_id, Key.email)) keys = result.all() success = 0 failed = 0 for tg_id, email in keys: try: await update_subscription(tg_id=tg_id, email=email, session=session) success += 1 except Exception as e: logger.error(f"[Resync] Ошибка при перевыпуске {email}: {e}") failed += 1 await callback.message.edit_text( f"🔁 Перевыпуск завершён:\n✅ Успешно: {success}\n❌ Ошибки: {failed}", reply_markup=build_back_to_db_menu(), )