import json from typing import Optional, Dict, Any from pyxui import XUI from pyxui.errors import BadLogin from bot.config import ( XUI_FULL_ADDRESS, XUI_PANEL_NAME, XUI_USERNAME, XUI_PASSWORD ) from bot.utils.logging import logger class XUIService: """Сервис для работы с XUI панелью.""" def __init__(self): self.xui = XUI( full_address=XUI_FULL_ADDRESS, panel=XUI_PANEL_NAME, https=True ) def login(self) -> None: """Авторизация в XUI панели.""" if self.xui.session_string: logger.debug("Уже залогинен в XUI.") return try: self.xui.login(XUI_USERNAME, XUI_PASSWORD) logger.info("Успешный вход в XUI.") except BadLogin: logger.error("Неверный логин или пароль XUI.") raise except Exception as e: logger.error(f"Ошибка при входе в XUI: {e}") raise def get_client(self, inbound_id: int, email: str) -> Optional[Dict[str, Any]]: """Получение информации о клиенте (работает и для SS, и для VLESS).""" try: self.login() inbounds = self.xui.get_inbounds() if "obj" not in inbounds: logger.error("Некорректный ответ от XUI при запросе inbounds") return None for inbound in inbounds["obj"]: if inbound["id"] != inbound_id: continue settings = json.loads(inbound["settings"]) for client in settings.get("clients", []): if client.get("email") == email: # У SS-клиентов id нет — подставляем email if "id" not in client: client["id"] = client["email"] return client logger.warning(f"Клиент {email} не найден в inbound {inbound_id}") return None except Exception as e: logger.error(f"Ошибка получения клиента {email}: {e}") return None def get_client_stats(self, inbound_id: int, email: str) -> Optional[Dict[str, Any]]: """Получение статистики клиента по inbound и email.""" try: self.login() inbounds = self.xui.get_inbounds() if "obj" not in inbounds: logger.error("Некорректный ответ от XUI при запросе inbounds") return None for inbound in inbounds["obj"]: if inbound["id"] != inbound_id: continue for client_stat in inbound.get("clientStats", []): if client_stat.get("email") == email: return client_stat logger.warning(f"Статистика по клиенту {email} не найдена в inbound {inbound_id}") return None except Exception as e: logger.error(f"Ошибка получения статистики клиента {email}: {e}") return None def add_vless_client( self, inbound_id: int, email: str, uuid: str, telegram_id: str, enable: bool = True, flow: str = "xtls-rprx-vision", limit_ip: int = 0, total_gb: int = 0, expire_time: int = 0 ) -> bool: """Добавление VLESS клиента.""" try: self.login() self.xui.add_client( inbound_id=inbound_id, email=email, uuid=uuid, enable=enable, flow=flow, limit_ip=limit_ip, total_gb=total_gb, expire_time=expire_time, telegram_id=telegram_id, subscription_id=telegram_id ) logger.info(f"VLESS клиент создан: {email}") return True except Exception as e: logger.error(f"Ошибка создания VLESS клиента {email}: {e}") return False def get_inbound(self, inbound_id: int) -> Optional[Dict[str, Any]]: """Получение информации об inbound.""" try: self.login() return self.xui.get_inbound(inbound_id) except Exception as e: logger.error(f"Ошибка получения inbound {inbound_id}: {e}") return None def get_cookies(self) -> Dict[str, str]: """Получение cookies для API запросов.""" cookies = {} if hasattr(self.xui, 'session') and self.xui.session and hasattr(self.xui.session, 'cookies'): for cookie in self.xui.session.cookies: if cookie.name == 'x-ui': cookies['x-ui'] = cookie.value break if self.xui.session_string: cookies['x-ui'] = self.xui.session_string return cookies def reset_session(self) -> None: """Сброс сессии для повторной авторизации.""" self.xui.session_string = None