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 c82c3d6d..e0037853 100644 --- a/bot.py +++ b/bot.py @@ -4,20 +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_admin_confirmation = State() - waiting_for_statistics = State() - waiting_for_expiry_date = State() + key_to_create = State() # Состояние для хранения ключа, который будет создан bot = Bot(token=API_TOKEN) storage = MemoryStorage() @@ -27,9 +25,6 @@ router = Router() # Создаем сессию при старте бота session = login_with_credentials(ADMIN_USERNAME, ADMIN_PASSWORD) -# ID администратора -ADMIN_ID = 476217106 # Замените на ваш Telegram ID - @dp.message(Command("start")) async def start_command(message: Message): welcome_text = "Добро пожаловать! Вы можете создать ключ для подключения VPN, просмотреть статистику использования или узнать дату окончания ключа." @@ -41,10 +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 state.update_data(tg_id=callback_query.from_user.id) # Сохраняем tg_id для дальнейшего использования await callback_query.answer() @dp.message() @@ -59,30 +71,57 @@ async def handle_text(message: types.Message, state: FSMContext): await state.clear() return - # Сохраняем введенное имя профиля - profile_name = message.text - await state.update_data(profile_name=profile_name) + # Сохраняем имя ключа и tg_id для подтверждения + await state.set_state(Form.waiting_for_admin_confirmation) + await state.update_data(key_name=message.text, tg_id=tg_id) # Отправляем запрос на подтверждение админу - admin_text = f"Пользователь {tg_id} запросил создание ключа для профиля '{profile_name}'. Подтвердите создание ключа (Да/Нет)." - await bot.send_message(ADMIN_ID, admin_text, reply_markup=InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text="Да", callback_data=f'confirm_create_{tg_id}_{profile_name}')], - [InlineKeyboardButton(text="Нет", callback_data=f'reject_create_{tg_id}')] - ])) - - await message.reply("Запрос на создание ключа отправлен администратору для подтверждения.") - await state.set_state(Form.waiting_for_admin_confirmation) + 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() - elif current_state == Form.waiting_for_admin_confirmation.state: - await message.reply("Ожидание подтверждения от администратора.") +@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 -@dp.callback_query(F.data.startswith('confirm_create_')) + if await has_active_key(tg_id): + await message.reply("У вас уже есть активный ключ. Вы не можете создать новый.") + await state.clear() + 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): - _, tg_id, profile_name = callback_query.data.split('_', 2) - tg_id = int(tg_id) + 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() @@ -90,84 +129,69 @@ async def process_admin_confirmation(callback_query: types.CallbackQuery, state: enable = True flow = "xtls-rprx-vision" - add_client(session, client_id, profile_name, tg_id, limit_ip, total_gb, expiry_time, enable, flow) + add_client(session, client_id, email, tg_id, limit_ip, total_gb, expiry_time, enable, flow) - connection_link = link(session, profile_name) + # Генерация ссылки для подключения + connection_link = link(session, email) - await add_connection(tg_id, client_id, profile_name, expiry_time) + # Сохранение данных о подключении в базе данных + await add_connection(tg_id, client_id, email, expiry_time) - user_message = f"Ключ создан:\n
{connection_link}"
- await bot.send_message(tg_id, user_message, parse_mode="HTML")
+ # Отправка ключа клиенту
+ 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.message.reply("Ключ успешно создан и отправлен пользователю.")
+ await callback_query.answer("Ключ создан и отправлен клиенту.")
except Exception as e:
- await callback_query.message.reply(f"Ошибка при создании ключа: {e}")
-
- await state.clear()
-
-@dp.callback_query(F.data.startswith('reject_create_'))
-async def process_admin_rejection(callback_query: types.CallbackQuery, state: FSMContext):
- _, tg_id = callback_query.data.split('_', 1)
- tg_id = int(tg_id)
-
- await bot.send_message(tg_id, "Создание ключа было отклонено администратором.")
- await callback_query.message.reply("Запрос на создание ключа был отклонен.")
+ 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, state: FSMContext):
+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:
- 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 = "У вас нет активных ключей."
-
+ 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:
- statistics = f"Ошибка при получении статистики: {e}"
-
- await callback_query.message.reply(f"Ваша статистика:\n{statistics}")
- await callback_query.answer()
+ await callback_query.message.reply(f"Ошибка при получении статистики: {e}")
-@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 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