UdoChudo 755280d7de
All checks were successful
Build and Push Docker Image / build (push) Successful in 2m15s
Сгенерил базовую модульную структуру спасибо Claude AI
2025-06-25 00:28:00 +05:00

178 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<code>{subscription_link}</code>"
)
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