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