diff --git a/api/main.py b/api/main.py index 83a052ed..9c722deb 100644 --- a/api/main.py +++ b/api/main.py @@ -2,8 +2,8 @@ from fastapi import FastAPI from api.routes import coupons, gifts, keys, misc, referrals, servers, settings, tariffs, users app = FastAPI( - title="SoloBot API (preAlpha)", - version="0.3.0", + title="SoloBot API (Alpha)", + version="0.4.0", docs_url="/api/docs", redoc_url="/api/redoc", openapi_url="/api/openapi.json", diff --git a/api/routes/keys.py b/api/routes/keys.py index 54614714..2d7daaab 100644 --- a/api/routes/keys.py +++ b/api/routes/keys.py @@ -93,15 +93,31 @@ async def edit_key_by_email( setattr(db_key, field, value) try: - new_expiry_time = db_key.expiry_time + tariff = None + if db_key.tariff_id is not None: + tariff_result = await session.execute(select(Tariff).where(Tariff.id == db_key.tariff_id)) + tariff = tariff_result.scalar_one_or_none() + + total_gb = db_key.current_traffic_limit + if total_gb is None: + total_gb = db_key.selected_traffic_limit + if total_gb is None and tariff is not None: + total_gb = tariff.traffic_limit + + hwid_device_limit = db_key.current_device_limit + if hwid_device_limit is None: + hwid_device_limit = db_key.selected_device_limit + if hwid_device_limit is None and tariff is not None: + hwid_device_limit = tariff.device_limit + await renew_key_in_cluster( cluster_id=db_key.server_id, email=db_key.email, client_id=db_key.client_id, - new_expiry_time=new_expiry_time, - total_gb=getattr(db_key, "traffic_limit", None), + new_expiry_time=db_key.expiry_time, + total_gb=total_gb, session=session, - hwid_device_limit=getattr(db_key, "device_limit", None), + hwid_device_limit=hwid_device_limit, reset_traffic=True, ) await session.commit() diff --git a/api/schemas/keys.py b/api/schemas/keys.py index 5bfd38b4..caae97bb 100644 --- a/api/schemas/keys.py +++ b/api/schemas/keys.py @@ -16,6 +16,13 @@ class KeyBase(BaseModel): notified: bool | None = False notified_24h: bool | None = False + selected_device_limit: int | None = None + selected_traffic_limit: int | None = None + selected_price_rub: int | None = None + + current_device_limit: int | None = None + current_traffic_limit: int | None = None + class KeyResponse(KeyBase): class Config: @@ -41,6 +48,13 @@ class KeyDetailsResponse(BaseModel): location_name: str | None tariff_id: int | None + selected_device_limit: int | None = None + selected_traffic_limit: int | None = None + selected_price_rub: int | None = None + + current_device_limit: int | None = None + current_traffic_limit: int | None = None + class Config: from_attributes = True @@ -57,6 +71,13 @@ class KeyUpdate(BaseModel): notified: bool | None = None notified_24h: bool | None = None + selected_device_limit: int | None = None + selected_traffic_limit: int | None = None + selected_price_rub: int | None = None + + current_device_limit: int | None = None + current_traffic_limit: int | None = None + class Config: from_attributes = True diff --git a/api/schemas/tariffs.py b/api/schemas/tariffs.py index c91f2640..93b07dc2 100644 --- a/api/schemas/tariffs.py +++ b/api/schemas/tariffs.py @@ -1,4 +1,5 @@ from datetime import datetime +from typing import Any from pydantic import BaseModel @@ -13,6 +14,19 @@ class TariffBase(BaseModel): is_active: bool = True subgroup_title: str | None = None sort_order: int | None = None + vless: bool = False + external_squad: str | None = None + + configurable: bool = False + + device_options: list[int] | None = None + traffic_options_gb: list[int] | None = None + + device_step_rub: int | None = None + device_overrides: dict[str, int] | None = None + + traffic_step_rub: int | None = None + traffic_overrides: dict[str, int] | None = None class TariffResponse(TariffBase): @@ -34,6 +48,19 @@ class TariffUpdate(BaseModel): is_active: bool | None = None subgroup_title: str | None = None sort_order: int | None = None + vless: bool | None = None + external_squad: str | None = None + + configurable: bool | None = None + + device_options: list[int] | None = None + traffic_options_gb: list[int] | None = None + + device_step_rub: int | None = None + device_overrides: dict[str, int] | None = None + + traffic_step_rub: int | None = None + traffic_overrides: dict[str, int] | None = None class Config: from_attributes = True diff --git a/handlers/admin/management/import_remnawave.py b/handlers/admin/management/import_remnawave.py index fbfa5f1f..3462144f 100644 --- a/handlers/admin/management/import_remnawave.py +++ b/handlers/admin/management/import_remnawave.py @@ -1,4 +1,5 @@ import json +import re import time from datetime import datetime @@ -24,20 +25,35 @@ def extract_tg_id_from_username(value: str | None) -> int | None: return None value = value.strip() - if "_" not in value: + match = re.search(r"_(\d+)(?:\D|$)", value) + if not match: return None - tail = value.rsplit("_", 1)[-1] - if not tail.isdigit(): - return None - - tg_id = int(tail) + tg_id = int(match.group(1)) if tg_id <= 0: return None return tg_id +def extract_tg_id_from_user_payload(user: dict) -> int | None: + tg_id = user.get("telegramId") + + if isinstance(tg_id, int): + if tg_id > 0: + return tg_id + return None + + if isinstance(tg_id, str): + tg_id = tg_id.strip() + if tg_id.isdigit(): + tg_id_int = int(tg_id) + return tg_id_int if tg_id_int > 0 else None + + tg_id = extract_tg_id_from_username(user.get("username")) or extract_tg_id_from_username(user.get("email")) + return tg_id + + @router.callback_query(AdminPanelCallback.filter(F.action == "export_remnawave")) async def show_remnawave_clients(callback: CallbackQuery, session: AsyncSession): result = await session.execute(select(Server).where(Server.panel_type == "remnawave", Server.enabled.is_(True))) @@ -93,9 +109,7 @@ async def import_remnawave_users(session: AsyncSession, users: list[dict]) -> in added = 0 for user in users: - tg_id = user.get("telegramId") - if not tg_id: - tg_id = extract_tg_id_from_username(user.get("username")) or extract_tg_id_from_username(user.get("email")) + tg_id = extract_tg_id_from_user_payload(user) if not tg_id: continue @@ -132,9 +146,7 @@ async def import_remnawave_keys(session: AsyncSession, users: list[dict], server added = 0 for user in users: - tg_id = user.get("telegramId") - if not tg_id: - tg_id = extract_tg_id_from_username(user.get("username")) or extract_tg_id_from_username(user.get("email")) + tg_id = extract_tg_id_from_user_payload(user) client_id = user.get("uuid") email = user.get("email") or user.get("username")