diff --git a/auth.py b/auth.py index 6d3cbc00..48ea4624 100644 --- a/auth.py +++ b/auth.py @@ -7,7 +7,7 @@ session = None def login_with_credentials(username, password): global session session = requests.Session() - auth_url = "https://vpn.pocomacho.ru:34268/solonet/login/" + auth_url = "https://solonet.pocomacho.ru:62553/solonet/login/" data = { "username": username, "password": password @@ -48,6 +48,6 @@ def link(session, client_id: str): flow = stream_settings.get('flow', 'xtls-rprx-vision') # Создание ссылки для подключения VLESS - val = f"vless://{client_id}@vpn.pocomacho.ru:443?type={tcp}&security={reality}&fp=chrome&pbk=ZIMoEnEd8-qMJkReVU5JxbiEj8CCSrvpm_ckvJ-46TE&sni=yahoo.com&sid=0fb4b595&spx=%2F&flow={flow}#VPN_F-{client_id}" + val = f"vless://{client_id}@solonet.pocomacho.ru:443?type={tcp}&security={reality}&fp=chrome&pbk=Ik3cfHdM5RXYCuzTxhaFfo58Fzwjvw11AtKsz5-izxA&fp=chrome&sni=discord.com&sid=6fc9&spx=%2F&flow={flow}#VPN_F-{client_id}" return val diff --git a/bot.py b/bot.py index 579a3d32..e0037853 100644 --- a/bot.py +++ b/bot.py @@ -4,19 +4,18 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message from aiogram.fsm.storage.memory import MemoryStorage from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from auth import login_with_credentials, link +from auth import login_with_credentials, link, get_clients from client import add_client from datetime import datetime, timedelta -from config import API_TOKEN, ADMIN_PASSWORD, ADMIN_USERNAME -from database import add_connection, has_active_key, get_active_key_email, DATABASE_PATH +from config import API_TOKEN, ADMIN_PASSWORD, ADMIN_USERNAME, ADMIN_CHAT_ID +from database import add_connection, has_active_key, get_active_key_email, get_key_expiry_time import uuid -import re import aiosqlite class Form(StatesGroup): waiting_for_key_name = State() - waiting_for_statistics = State() - waiting_for_expiry_date = State() + waiting_for_admin_confirmation = State() + key_to_create = State() # Состояние для хранения ключа, который будет создан bot = Bot(token=API_TOKEN) storage = MemoryStorage() @@ -37,62 +36,27 @@ async def start_command(message: Message): await message.reply(welcome_text, reply_markup=keyboard) +@router.callback_query(F.data == "view_expiry") +async def process_callback_view_expiry_date(callback_query: types.CallbackQuery): + tg_id = callback_query.from_user.id + + # Получаем дату окончания ключа из базы данных + expiry_time = await get_key_expiry_time(tg_id) + + if expiry_time: + expiry_date = expiry_time.strftime('%Y-%m-%d %H:%M:%S') + response_message = f"Дата окончания вашего ключа: {expiry_date}" + else: + response_message = "У вас нет активного ключа." + + await callback_query.message.reply(response_message) + await callback_query.answer() + @dp.callback_query(F.data == 'create_key') async def process_callback_create_key(callback_query: types.CallbackQuery, state: FSMContext): await callback_query.message.reply("Введите имя вашего профиля VPN:") await state.set_state(Form.waiting_for_key_name) - await callback_query.answer() - -@dp.callback_query(F.data == 'view_stats') -async def process_callback_view_stats(callback_query: types.CallbackQuery, state: FSMContext): - tg_id = callback_query.from_user.id - - try: - email = await get_active_key_email(tg_id) - if email: - connection_link = link(session, email) - - # Извлечение данных о загрузке и выгрузке из ссылки - up_match = re.search(r'up=(\d+)', connection_link) - down_match = re.search(r'down=(\d+)', connection_link) - - up = up_match.group(1) if up_match else "Неизвестно" - down = down_match.group(1) if down_match else "Неизвестно" - - statistics = f"Статистика вашего ключа:\nЗагрузка: {up} MB\nВыгрузка: {down} MB" - else: - statistics = "У вас нет активных ключей." - - except Exception as e: - statistics = f"Ошибка при получении статистики: {e}" - - await callback_query.message.reply(f"Ваша статистика:\n{statistics}") - await callback_query.answer() - -@dp.callback_query(F.data == 'view_expiry') -async def process_callback_view_expiry(callback_query: types.CallbackQuery): - tg_id = callback_query.from_user.id - - try: - email = await get_active_key_email(tg_id) - if email: - async with aiosqlite.connect(DATABASE_PATH) as db: - async with db.execute("SELECT expiry_time FROM connections WHERE tg_id = ? AND expiry_time > ?", - (tg_id, int(datetime.utcnow().timestamp() * 1000))) as cursor: - record = await cursor.fetchone() - if record: - expiry_time = record[0] - expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime("%Y-%m-%d %H:%M:%S") - message_text = f"Дата окончания вашего ключа: {expiry_date}" - else: - message_text = "У вас нет активных ключей." - else: - message_text = "У вас нет активных ключей." - - except Exception as e: - message_text = f"Ошибка при получении даты окончания ключа: {e}" - - await callback_query.message.reply(message_text) + await state.update_data(tg_id=callback_query.from_user.id) # Сохраняем tg_id для дальнейшего использования await callback_query.answer() @dp.message() @@ -102,38 +66,132 @@ async def handle_text(message: types.Message, state: FSMContext): if current_state == Form.waiting_for_key_name.state: tg_id = message.from_user.id - # Проверка, есть ли уже активный ключ у клиента if await has_active_key(tg_id): await message.reply("У вас уже есть активный ключ. Вы не можете создать новый.") await state.clear() return - try: - client_id = str(uuid.uuid4()) - email = message.text - limit_ip = 1 - total_gb = 0 - current_time = datetime.utcnow() - expiry_time = int((current_time + timedelta(days=30)).timestamp() * 1000) - enable = True - flow = "xtls-rprx-vision" - - # Создание клиента - add_client(session, client_id, email, tg_id, limit_ip, total_gb, expiry_time, enable, flow) - - # Получение ссылки на подключение - connection_link = link(session, email) - - # Сохранение данных о ключе в базе данных - await add_connection(tg_id, client_id, email, expiry_time) - - # Отправка ключа пользователю - await message.reply(f"Ключ создан:\n
{connection_link}
", parse_mode="HTML") + # Сохраняем имя ключа и tg_id для подтверждения + await state.set_state(Form.waiting_for_admin_confirmation) + await state.update_data(key_name=message.text, tg_id=tg_id) + + # Отправляем запрос на подтверждение админу + admin_message = f"Запрос на создание ключа от пользователя {message.from_user.username}. Имя ключа: {message.text}. Подтвердите создание ключа." + button_yes = InlineKeyboardButton(text='Подтвердить', callback_data='confirm_key') + button_no = InlineKeyboardButton(text='Отклонить', callback_data='reject_key') + keyboard = InlineKeyboardMarkup(inline_keyboard=[[button_yes, button_no]]) + await bot.send_message(ADMIN_CHAT_ID, admin_message, reply_markup=keyboard) + await message.reply("Ваш запрос на создание ключа отправлен администратору. Ожидайте подтверждения.") + await state.clear() +@dp.message() +async def handle_text(message: types.Message, state: FSMContext): + current_state = await state.get_state() + + if current_state == Form.waiting_for_key_name.state: + tg_id = message.from_user.id + + if await has_active_key(tg_id): + await message.reply("У вас уже есть активный ключ. Вы не можете создать новый.") await state.clear() - except Exception as e: - await message.reply(f"Ошибка: {e}") + return + + await state.set_state(Form.waiting_for_admin_confirmation) + await state.update_data(key_name=message.text, tg_id=tg_id) # Сохраняем имя ключа и tg_id для подтверждения + + # Отправляем запрос на подтверждение админу + admin_message = f"Запрос на создание ключа от пользователя {message.from_user.username}. Имя ключа: {message.text}. Подтвердите создание ключа." + button_yes = InlineKeyboardButton(text='Подтвердить', callback_data='confirm_key') + button_no = InlineKeyboardButton(text='Отклонить', callback_data='reject_key') + keyboard = InlineKeyboardMarkup(inline_keyboard=[[button_yes, button_no]]) + await bot.send_message(476217106, admin_message, reply_markup=keyboard) + await message.reply("Ваш запрос на создание ключа отправлен администратору. Ожидайте подтверждения.") + await state.clear() +@dp.callback_query(F.data == 'confirm_key') +async def process_admin_confirmation(callback_query: types.CallbackQuery, state: FSMContext): + user_data = await state.get_data() + key_name = user_data.get('key_name') + tg_id = user_data.get('tg_id') + + if not key_name or not tg_id: + await callback_query.answer("Ошибка: не удалось получить данные для создания ключа.") + return + + try: + # Генерация уникального client_id и создание ключа + client_id = str(uuid.uuid4()) + email = key_name + limit_ip = 1 + total_gb = 0 + current_time = datetime.utcnow() + expiry_time = int((current_time + timedelta(days=30)).timestamp() * 1000) + enable = True + flow = "xtls-rprx-vision" + + add_client(session, client_id, email, tg_id, limit_ip, total_gb, expiry_time, enable, flow) + + # Генерация ссылки для подключения + connection_link = link(session, email) + + # Сохранение данных о подключении в базе данных + await add_connection(tg_id, client_id, email, expiry_time) + + # Отправка ключа клиенту + await bot.send_message(tg_id, f"Ключ создан:\n
{connection_link}
", parse_mode="HTML") + await bot.send_message(callback_query.from_user.id, "Ключ успешно создан и отправлен клиенту.") + + await callback_query.answer("Ключ создан и отправлен клиенту.") + except Exception as e: + await bot.send_message(callback_query.from_user.id, f"Ошибка: {e}") + + await state.clear() + +@dp.callback_query(F.data == 'view_stats') +async def process_callback_view_stats(callback_query: types.CallbackQuery): + tg_id = callback_query.from_user.id + + # Проверяем, есть ли активный ключ для этого пользователя + email = await get_active_key_email(tg_id) + + if not email: + await callback_query.message.reply("У вас нет активного ключа для просмотра статистики.") + return + + # Получаем статистику через API сервера + try: + clients = get_clients(session) # Предположим, что get_clients возвращает список клиентов с их данными + client_data = next((client for client in clients if client['email'] == email), None) + + if not client_data: + await callback_query.message.reply("Не удалось получить статистику для этого пользователя.") + return + + # Подготавливаем статистику для отображения + used_gb = client_data.get('usedGB', 0) # Использованные данные + limit_gb = client_data.get('totalGB', 0) # Лимит данных + expiry_time = client_data.get('expiryTime', 0) # Время окончания подписки + + expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S') + + stats_message = (f"Статистика использования:\n" + f"Использовано: {used_gb} ГБ\n" + f"Лимит: {limit_gb} ГБ\n" + f"Дата окончания ключа: {expiry_date}") + + await callback_query.message.reply(stats_message) + + except Exception as e: + await callback_query.message.reply(f"Ошибка при получении статистики: {e}") + + await callback_query.answer() + + +@dp.callback_query(F.data == 'reject_key') +async def process_admin_rejection(callback_query: types.CallbackQuery, state: FSMContext): + await bot.send_message(callback_query.from_user.id, "Создание ключа отклонено.") + await callback_query.answer() + await state.clear() async def main(): dp.include_router(router) # Подключение роутера diff --git a/database.py b/database.py index e2fd22a3..3b4ed63a 100644 --- a/database.py +++ b/database.py @@ -38,3 +38,12 @@ async def get_active_key_email(tg_id: int) -> str: record = await cursor.fetchone() return record[0] if record else None +async def get_key_expiry_time(tg_id: int) -> datetime: + async with aiosqlite.connect(DATABASE_PATH) as db: + async with db.execute("SELECT expiry_time FROM connections WHERE tg_id = ? AND expiry_time > ?", + (tg_id, int(datetime.utcnow().timestamp() * 1000))) as cursor: + record = await cursor.fetchone() + if record: + expiry_time = record[0] + return datetime.utcfromtimestamp(expiry_time / 1000) + return None \ No newline at end of file