import json import uuid from typing import Tuple, Optional, Dict, Any import aiohttp from bot.config import ( XUI_FULL_ADDRESS, INBOUND_VLESS_ID, INBOUND_SS_ID, SUB_BASE_URL ) from bot.services.xui import XUIService from bot.utils.logging import logger class ClientService: """Сервис для работы с клиентами VPN.""" def __init__(self, xui_service: XUIService): self.xui_service = xui_service def generate_client_credentials(self, telegram_id: str) -> Tuple[str, str, str, str]: """Генерация учетных данных для клиента.""" vless_email = f"{telegram_id}_vl_ssl" ss_email = f"{telegram_id}_ss" vless_uuid = str(uuid.uuid4()) ss_password = str(uuid.uuid4()) return vless_email, ss_email, vless_uuid, ss_password def get_subscription_link(self, telegram_id: str) -> str: """Получение ссылки на подписку.""" return f"{SUB_BASE_URL}{telegram_id}?name={telegram_id}" async def create_shadowsocks_client(self, telegram_id: str, password: str) -> bool: """Создание Shadowsocks клиента через API.""" try: self.xui_service.login() except Exception as e: logger.error(f"Ошибка авторизации в XUI: {e}") return False ss_email = f"{telegram_id}_ss" settings_str = json.dumps({ "clients": [{ "method": "chacha20-ietf-poly1305", "password": password, "email": ss_email, "totalGB": 0, "expiryTime": 0, "enable": True, "tgId": telegram_id, "subId": telegram_id, "reset": 0 }] }, separators=(',', ':')) payload = { "id": INBOUND_SS_ID, "settings": settings_str } api_url = f"{XUI_FULL_ADDRESS}/xui/API/inbounds/addClient" cookies = self.xui_service.get_cookies() async with aiohttp.ClientSession() as session: async def do_request(): async with session.post( api_url, json=payload, cookies=cookies, headers={ "Content-Type": "application/json", "User-Agent": "Mozilla/5.0" }, ssl=True ) as resp: text = await resp.text() logger.debug(f"Response status: {resp.status}") logger.debug(f"Response text: {text}") return resp.status, text status, text = await do_request() # Если сессия истекла, пробуем повторно залогиниться if status == 401 or (status == 200 and "login" in text.lower()): logger.info("Сессия XUI истекла, повторный вход...") try: self.xui_service.reset_session() self.xui_service.login() cookies = self.xui_service.get_cookies() except Exception as e: logger.error(f"Ошибка повторной авторизации в XUI: {e}") return False status, text = await do_request() if status == 200: if "successfully" in text.lower() or "success" in text.lower(): logger.info(f"Shadowsocks клиент успешно создан: {ss_email}") return True elif not text.strip(): logger.warning(f"Пустой ответ при статусе 200. Проверяем наличие клиента: {ss_email}") return await self.verify_client_created(telegram_id, ss_email) else: logger.error(f"Неожиданный ответ при создании SS клиента: {text}") return False else: logger.error(f"Ошибка создания Shadowsocks клиента: {status} {text}") return False async def verify_client_created(self, telegram_id: str, ss_email: str) -> bool: """Проверка, что клиент существует в inbound SS.""" try: inbound_info = self.xui_service.get_inbound(INBOUND_SS_ID) if not inbound_info: logger.error(f"Inbound {INBOUND_SS_ID} не найден.") return False settings = json.loads(inbound_info.get('settings', '{}')) clients = settings.get('clients', []) for client in clients: if client.get('email') == ss_email: logger.info(f"Клиент {ss_email} найден.") return True logger.warning(f"Клиент {ss_email} не найден.") return False except Exception as e: logger.error(f"Ошибка при проверке клиента: {e}") return False async def create_client_profile(self, telegram_id: str) -> Tuple[bool, str]: """Создание полного профиля клиента (VLESS + Shadowsocks).""" try: vless_email, ss_email, vless_uuid, ss_password = self.generate_client_credentials(telegram_id) logger.info(f"Создаём клиентов для telegram_id={telegram_id}") # Создаём VLESS клиента vless_success = self.xui_service.add_vless_client( inbound_id=INBOUND_VLESS_ID, email=vless_email, uuid=vless_uuid, telegram_id=telegram_id ) if not vless_success: return False, "Ошибка при создании VLESS клиента" # Создаём Shadowsocks клиента ss_success = await self.create_shadowsocks_client(telegram_id, ss_password) if not ss_success: return False, "Ошибка при создании Shadowsocks клиента" subscription_link = self.get_subscription_link(telegram_id) success_message = ( f"✅ Профиль для {telegram_id} успешно создан!\n" f"🔗 Подписочная ссылка:\n{subscription_link}" ) return True, success_message except Exception as e: logger.error(f"Ошибка при создании профиля: {e}") return False, "Произошла ошибка при создании профиля" def get_client_info(self, telegram_id: str) -> Tuple[Optional[Dict[str, Any]], Optional[Dict[str, Any]]]: """Получение информации о клиентах.""" vless_email = f"{telegram_id}_vl_ssl" ss_email = f"{telegram_id}_ss" vless_client = self.xui_service.get_client(INBOUND_VLESS_ID, vless_email) ss_client = self.xui_service.get_client(INBOUND_SS_ID, ss_email) return vless_client, ss_client