from aiogram import Router, types, F from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery from aiogram.fsm.context import FSMContext from aiogram.fsm.state import StatesGroup, State from config import DATABASE_URL, SERVERS import asyncpg from datetime import datetime from bot import bot from database import update_key_expiry, get_client_id_by_email, get_tg_id_by_client_id from config import DATABASE_URL, ADMIN_PASSWORD, ADMIN_USERNAME from datetime import datetime import asyncpg from auth import login_with_credentials from client import extend_client_key_admin from handlers.admin.admin_panel import back_to_admin_menu from client import delete_client router = Router() class UserEditorState(StatesGroup): waiting_for_tg_id = State() displaying_user_info = State() waiting_for_new_balance = State() waiting_for_key_name = State() waiting_for_expiry_time = State() @router.callback_query(lambda c: c.data == "search_by_tg_id") async def prompt_tg_id(callback_query: CallbackQuery, state: FSMContext): await callback_query.message.edit_text("Введите tg_id клиента:") await state.set_state(UserEditorState.waiting_for_tg_id) @router.message(UserEditorState.waiting_for_tg_id, F.text.isdigit()) async def handle_tg_id_input(message: types.Message, state: FSMContext): tg_id = int(message.text) conn = await asyncpg.connect(DATABASE_URL) try: balance = await conn.fetchval("SELECT balance FROM connections WHERE tg_id = $1", tg_id) key_records = await conn.fetch("SELECT email FROM keys WHERE tg_id = $1", tg_id) referral_count = await conn.fetchval("SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1", tg_id) if balance is None: await message.reply("Пользователь с указанным tg_id не найден.") await state.clear() return key_buttons = [ [InlineKeyboardButton(text=email, callback_data=f"edit_key_{email}")] for email, in key_records ] keyboard = InlineKeyboardMarkup(inline_keyboard=[ *key_buttons, [InlineKeyboardButton(text="📝 Изменить баланс", callback_data=f"change_balance_{tg_id}")], [InlineKeyboardButton(text="Назад", callback_data="back_to_user_editor")] ]) user_info = ( f"Информация о пользователе:\n" f"Баланс: {balance}\n" f"Количество рефералов: {referral_count}\n" f"Ключи (для редактирования нажмите на ключ):" ) await message.reply(user_info, reply_markup=keyboard, parse_mode="HTML") await state.set_state(UserEditorState.displaying_user_info) finally: await conn.close() @router.callback_query(lambda c: c.data.startswith('change_balance_')) async def process_balance_change(callback_query: CallbackQuery, state: FSMContext): tg_id = int(callback_query.data.split('_')[2]) await state.update_data(tg_id=tg_id) await callback_query.message.edit_text("Введите новую сумму баланса:") await callback_query.answer() await state.set_state(UserEditorState.waiting_for_new_balance) @router.message(UserEditorState.waiting_for_new_balance) async def handle_new_balance_input(message: types.Message, state: FSMContext): if not message.text.isdigit() or int(message.text) < 0: await message.reply("Пожалуйста, введите корректную сумму для изменения баланса.") return new_balance = int(message.text) user_data = await state.get_data() tg_id = user_data.get('tg_id') conn = await asyncpg.connect(DATABASE_URL) try: await conn.execute("UPDATE connections SET balance = $1 WHERE tg_id = $2", new_balance, tg_id) response_message = f"Баланс успешно изменен на {new_balance}." back_button = InlineKeyboardButton(text="Назад в меню админа", callback_data="back_to_user_editor") keyboard = InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await message.reply(response_message, reply_markup=keyboard, parse_mode="HTML") finally: await conn.close() await state.clear() @router.callback_query(lambda c: c.data.startswith('edit_key_')) async def process_key_edit(callback_query: CallbackQuery): email = callback_query.data.split('_', 2)[2] try: conn = await asyncpg.connect(DATABASE_URL) try: record = await conn.fetchrow(''' SELECT k.key, k.expiry_time, k.server_id FROM keys k WHERE k.email = $1 ''', email) if record: key = record['key'] expiry_time = record['expiry_time'] server_id = record['server_id'] server_name = SERVERS.get(server_id, {}).get('name', 'Неизвестный сервер') expiry_date = datetime.utcfromtimestamp(expiry_time / 1000) current_date = datetime.utcnow() time_left = expiry_date - current_date if time_left.total_seconds() <= 0: days_left_message = "Ключ истек." elif time_left.days > 0: days_left_message = f"Осталось дней: {time_left.days}" else: hours_left = time_left.seconds // 3600 days_left_message = f"Осталось часов: {hours_left}" formatted_expiry_date = expiry_date.strftime('%d %B %Y года') response_message = ( f"Ключ:
{key}
\n" f"Дата истечения: {formatted_expiry_date}\n" f"{days_left_message}\n" f"Сервер: {server_name}" ) change_expiry_button = types.InlineKeyboardButton(text='⏳ Изменить время истечения', callback_data=f'change_expiry|{email}') delete_button = types.InlineKeyboardButton(text='❌ Удалить ключ', callback_data=f'delete_key_admin|{email}') keyboard = types.InlineKeyboardMarkup( inline_keyboard=[ [change_expiry_button, delete_button], [InlineKeyboardButton(text="Назад", callback_data="back_to_user_editor")] # Кнопка "Назад" ] ) await callback_query.message.edit_text(response_message, reply_markup=keyboard, parse_mode="HTML") else: await callback_query.message.edit_text("Информация о ключе не найдена.", parse_mode="HTML") finally: await conn.close() except Exception as e: await handle_error(callback_query.from_user.id, callback_query, f"Ошибка при получении информации о ключе: {e}") await callback_query.answer() @router.callback_query(lambda c: c.data == "search_by_key_name") async def prompt_key_name(callback_query: CallbackQuery, state: FSMContext): await callback_query.message.edit_text("Введите имя ключа:") await state.set_state(UserEditorState.waiting_for_key_name) @router.message(UserEditorState.waiting_for_key_name) async def handle_key_name_input(message: types.Message, state: FSMContext): key_name = message.text conn = await asyncpg.connect(DATABASE_URL) try: user_records = await conn.fetch(''' SELECT c.tg_id, c.balance, k.email, k.key, k.expiry_time, k.server_id FROM connections c JOIN keys k ON c.tg_id = k.tg_id WHERE k.email = $1 ''', key_name) if not user_records: await message.reply("Пользователь с указанным именем ключа не найден.") await state.clear() return response_messages = [] key_buttons = [] for record in user_records: tg_id = record['tg_id'] balance = record['balance'] email = record['email'] key = record['key'] expiry_time = record['expiry_time'] server_id = record['server_id'] server_name = SERVERS.get(server_id, {}).get('name', 'Неизвестный сервер') expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%d %B %Y') response_messages.append( f"Ключ:
{key}
\n" f"Дата истечения: {expiry_date}\n" f"Баланс пользователя: {balance}\n" f"Сервер: {server_name}" ) change_expiry_button = InlineKeyboardButton(text='⏳ Изменить время истечения', callback_data=f'change_expiry|{email}') delete_button = InlineKeyboardButton(text='❌ Удалить ключ', callback_data=f'delete_key_admin|{email}') key_buttons.append([change_expiry_button, delete_button]) key_buttons.append([InlineKeyboardButton(text="Назад", callback_data="back_to_user_editor")]) keyboard = InlineKeyboardMarkup(inline_keyboard=key_buttons) await message.reply("\n".join(response_messages), reply_markup=keyboard, parse_mode="HTML") finally: await conn.close() await state.clear() @router.callback_query(lambda c: c.data.startswith('change_expiry|')) async def prompt_expiry_change(callback_query: CallbackQuery, state: FSMContext): email = callback_query.data.split('|')[1] await callback_query.message.edit_text( f"Введите новое время истечения для ключа {email} в формате YYYY-MM-DD HH:MM:SS:", parse_mode="HTML" ) await state.update_data(email=email) await state.set_state(UserEditorState.waiting_for_expiry_time) @router.message(UserEditorState.waiting_for_expiry_time) async def handle_expiry_time_input(message: types.Message, state: FSMContext): user_data = await state.get_data() email = user_data.get('email') if not email: await message.reply("Email не найден в состоянии.") await state.clear() return try: expiry_time_str = message.text expiry_time = int(datetime.strptime(expiry_time_str, '%Y-%m-%d %H:%M:%S').timestamp() * 1000) client_id = await get_client_id_by_email(email) if client_id is None: await message.reply(f"Клиент с email {email} не найден.") await state.clear() return await update_key_expiry(client_id, expiry_time) conn = await asyncpg.connect(DATABASE_URL) try: record = await conn.fetchrow('SELECT server_id FROM keys WHERE client_id = $1', client_id) if not record: await message.reply("Клиент не найден в базе данных.") await state.clear() return server_id = record['server_id'] tg_id = await get_tg_id_by_client_id(client_id) session = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) print(f"Попытка обновить панель для server_id: {server_id}, tg_id: {tg_id}, client_id: {client_id}, email: {email}, expiryTime: {expiry_time}") success = await extend_client_key_admin(session, server_id, tg_id, client_id, email, expiry_time) print(f"Статус обновления панели: {'Успешно' if success else 'Не удалось'}") if success: response_message = ( f"Время истечения ключа для клиента {client_id} ({email}) успешно обновлено и синхронизировано с панелью." ) else: response_message = ( f"Время истечения ключа для клиента {client_id} ({email}) обновлено, но не удалось синхронизировать с панелью." ) back_button = InlineKeyboardButton(text="Назад", callback_data="back_to_user_editor") keyboard = InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await message.reply(response_message, reply_markup=keyboard, parse_mode="HTML") finally: await conn.close() except ValueError: await message.reply("Пожалуйста, используйте формат: YYYY-MM-DD HH:MM:SS.") except Exception as e: await message.reply(f"Произошла ошибка: {e}") await state.clear() @router.callback_query(lambda c: c.data.startswith('delete_key_admin|')) async def process_callback_delete_key(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id email = callback_query.data.split('|')[1] conn = await asyncpg.connect(DATABASE_URL) try: client_id = await conn.fetchval('SELECT client_id FROM keys WHERE email = $1', email) if client_id is None: await bot.edit_message_text("Ключ не найден.", chat_id=tg_id, message_id=callback_query.message.message_id) return confirmation_keyboard = types.InlineKeyboardMarkup(inline_keyboard=[ [types.InlineKeyboardButton(text='✅ Да, удалить', callback_data=f'confirm_delete_admin|{client_id}')], [types.InlineKeyboardButton(text='❌ Нет, отменить', callback_data='view_keys')] ]) await bot.edit_message_text("Вы уверены, что хотите удалить ключ?", chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=confirmation_keyboard, parse_mode="HTML") finally: await conn.close() await callback_query.answer() @router.callback_query(lambda c: c.data.startswith('confirm_delete_admin|')) async def process_callback_confirm_delete(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id client_id = callback_query.data.split('|')[1] try: conn = await asyncpg.connect(DATABASE_URL) try: record = await conn.fetchrow('SELECT email, server_id FROM keys WHERE client_id = $1', client_id) if record: email = record['email'] server_id = record['server_id'] session = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) success = await delete_client(session, server_id, client_id) if success: await conn.execute('DELETE FROM keys WHERE client_id = $1', client_id) response_message = "Ключ был успешно удален." else: response_message = "Ошибка при удалении клиента через API." else: response_message = "Ключ не найден или уже удален." back_button = types.InlineKeyboardButton(text='Назад', callback_data='view_keys') keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=keyboard) finally: await conn.close() except Exception as e: await bot.edit_message_text(f"Ошибка при удалении ключа: {e}", chat_id=tg_id, message_id=callback_query.message.message_id) await callback_query.answer() @router.callback_query(lambda c: c.data == "back_to_user_editor") async def back_to_user_editor(callback_query: CallbackQuery): await back_to_admin_menu(callback_query) async def handle_error(tg_id, callback_query, message): await bot.edit_message_text(message, chat_id=tg_id, message_id=callback_query.message.message_id, parse_mode="HTML")