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")