Files
Solo_bot/handlers/keys/operations/creation.py
T

256 lines
9.9 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 asyncio
from datetime import datetime
from sqlalchemy import update
from sqlalchemy.ext.asyncio import AsyncSession
from config import PUBLIC_LINK, REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD, SUPERNODE
from database import get_servers, get_tariff_by_id, store_key
from database.models import User
from handlers.utils import check_server_key_limit
from logger import logger
from panels._3xui import (
ClientConfig,
add_client,
get_xui_instance,
)
from panels.remnawave import RemnawaveAPI
async def create_key_on_cluster(
cluster_id: str,
tg_id: int,
client_id: str,
email: str,
expiry_timestamp: int,
plan: int = None,
session: AsyncSession = None,
remnawave_link: str = None,
hwid_limit: int = None,
traffic_limit_bytes: int = None,
is_trial: bool = False,
):
try:
servers = await get_servers(session)
cluster = servers.get(cluster_id)
server_id_to_store = cluster_id
if not cluster:
found_servers = []
for _key, server_list in servers.items():
for server_info in server_list:
if server_info.get("server_name", "").lower() == cluster_id.lower():
found_servers.append(server_info)
if found_servers:
cluster = found_servers
server_id_to_store = found_servers[0].get("server_name")
else:
raise ValueError(f"Кластер или сервер с ID/именем {cluster_id} не найден.")
enabled_servers = [s for s in cluster if s.get("enabled", True)]
if not enabled_servers:
logger.warning(f"[Key Creation] Нет доступных серверов в кластере {cluster_id}")
return
if plan is not None and traffic_limit_bytes is None:
tariff = await get_tariff_by_id(session, plan)
if not tariff:
raise ValueError(f"Тариф с id={plan} не найден.")
traffic_limit_bytes = int(tariff["traffic_limit"]) if tariff["traffic_limit"] else None
if hwid_limit is None and tariff.get("device_limit") is not None:
hwid_limit = int(tariff["device_limit"])
remnawave_servers = [
s
for s in enabled_servers
if s.get("panel_type", "3x-ui").lower() == "remnawave" and await check_server_key_limit(s, session)
]
xui_servers = [
s
for s in enabled_servers
if s.get("panel_type", "3x-ui").lower() == "3x-ui" and await check_server_key_limit(s, session)
]
if not remnawave_servers and not xui_servers:
logger.warning(f"[Key Creation] Нет серверов с доступным лимитом в кластере {cluster_id}")
return
semaphore = asyncio.Semaphore(2)
remnawave_created = False
remnawave_key = None
remnawave_client_id = None
if remnawave_servers:
remna = RemnawaveAPI(remnawave_servers[0]["api_url"])
logged_in = await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD)
if not logged_in:
logger.error("Не удалось войти в Remnawave API")
else:
expire_at = datetime.utcfromtimestamp(expiry_timestamp / 1000).isoformat() + "Z"
inbound_ids = [s.get("inbound_id") for s in remnawave_servers if s.get("inbound_id")]
if not inbound_ids:
logger.warning("Нет inbound_id у серверов Remnawave")
else:
short_uuid = None
if remnawave_link and "/" in remnawave_link:
short_uuid = remnawave_link.rstrip("/").split("/")[-1]
user_data = {
"username": email,
"trafficLimitStrategy": "NO_RESET",
"expireAt": expire_at,
"telegramId": tg_id,
"activeInternalSquads": inbound_ids,
}
if traffic_limit_bytes and traffic_limit_bytes > 0:
user_data["trafficLimitBytes"] = traffic_limit_bytes * 1024 * 1024 * 1024
if short_uuid:
user_data["shortUuid"] = short_uuid
if hwid_limit is not None:
user_data["hwidDeviceLimit"] = hwid_limit
logger.info(f"[Key Creation] Данные для создания клиента в Remnawave: {user_data}")
result = await remna.create_user(user_data)
if not result:
logger.error("Ошибка при создании пользователя в Remnawave")
else:
remnawave_created = True
remnawave_key = result.get("subscriptionUrl")
remnawave_client_id = result.get("uuid")
logger.info(f"[Key Creation] Пользователь создан в Remnawave: {result}")
public_link = f"{PUBLIC_LINK}{email}/{tg_id}" if xui_servers else None
final_client_id = remnawave_client_id or client_id
logger.info(f"[Debug] 3x-ui servers для кластера {cluster_id}: {[s['server_name'] for s in xui_servers]}")
if xui_servers:
if SUPERNODE:
for server_info in xui_servers:
await create_client_on_server(
server_info,
tg_id,
final_client_id,
email,
expiry_timestamp,
semaphore,
plan=plan,
session=session,
is_trial=is_trial,
)
else:
await asyncio.gather(
*[
create_client_on_server(
server,
tg_id,
final_client_id,
email,
expiry_timestamp,
semaphore,
plan=plan,
session=session,
is_trial=is_trial,
)
for server in xui_servers
],
return_exceptions=True,
)
if (remnawave_created and remnawave_client_id) or xui_servers:
await store_key(
session=session,
tg_id=tg_id,
client_id=final_client_id,
email=email,
expiry_time=expiry_timestamp,
key=public_link,
server_id=server_id_to_store,
remnawave_link=remnawave_key,
tariff_id=plan,
)
await session.execute(update(User).where(User.tg_id == tg_id, User.trial.in_([0, -1])).values(trial=1))
await session.commit()
except Exception as e:
logger.error(f"Ошибка при создании ключа: {e}")
raise e
async def create_client_on_server(
server_info: dict,
tg_id: int,
client_id: str,
email: str,
expiry_timestamp: int,
semaphore: asyncio.Semaphore,
plan: int = None,
session=None,
is_trial: bool = False,
):
"""
Создает клиента на указанном 3x-ui сервере с лимитом по тарифу или триалу.
"""
logger.info(
f"[Client] Вход в create_client_on_server: сервер={server_info.get('server_name')}, план={plan}, is_trial={is_trial}"
)
async with semaphore:
xui = await get_xui_instance(server_info["api_url"])
inbound_id = server_info.get("inbound_id")
server_name = server_info.get("server_name", "unknown")
if not inbound_id:
logger.warning(f"[Client] INBOUND_ID отсутствует для сервера {server_name}. Пропуск.")
return
if SUPERNODE:
unique_email = f"{email}_{server_name.lower()}"
sub_id = email
else:
unique_email = email
sub_id = unique_email
total_gb_value = 0
device_limit_value = 0
if plan is not None:
tariff = await get_tariff_by_id(session, plan)
logger.info(f"[Tariff Debug] Получен тариф: {tariff}")
if not tariff:
raise ValueError(f"Тариф с id={plan} не найден.")
total_gb_value = int(tariff["traffic_limit"]) if tariff["traffic_limit"] else 0
device_limit_value = int(tariff["device_limit"]) if tariff.get("device_limit") is not None else 0
try:
logger.info(
f"[Client] Вызов add_client: email={email}, client_id={client_id}, GB={total_gb_value}, Devices={device_limit_value}"
)
traffic_limit_bytes = total_gb_value * 1024 * 1024 * 1024
await add_client(
xui,
ClientConfig(
client_id=client_id,
email=unique_email,
tg_id=tg_id,
limit_ip=device_limit_value,
total_gb=traffic_limit_bytes,
expiry_time=expiry_timestamp,
enable=True,
flow="xtls-rprx-vision",
inbound_id=int(inbound_id),
sub_id=sub_id,
),
)
logger.info(f"[Client] Клиент успешно добавлен на сервер {server_name}")
except Exception as e:
logger.error(f"[Client Error] Не удалось создать клиента на {server_name}: {e}")
if SUPERNODE:
await asyncio.sleep(0.7)