diff --git a/handlers/admin/users/users_keys.py b/handlers/admin/users/users_keys.py
index 040f59a2..515d2cb9 100644
--- a/handlers/admin/users/users_keys.py
+++ b/handlers/admin/users/users_keys.py
@@ -105,8 +105,9 @@ async def handle_key_edit(
is_configurable = False
if key_obj.tariff_id:
result = await session.execute(
- select(Tariff.name, Tariff.subgroup_title, Tariff.device_limit, Tariff.traffic_limit, Tariff.configurable)
- .where(Tariff.id == key_obj.tariff_id)
+ select(
+ Tariff.name, Tariff.subgroup_title, Tariff.device_limit, Tariff.traffic_limit, Tariff.configurable
+ ).where(Tariff.id == key_obj.tariff_id)
)
row = result.first()
if row:
@@ -122,13 +123,21 @@ async def handle_key_edit(
sel_dev, cur_dev = key_obj.selected_device_limit, key_obj.current_device_limit
if sel_dev is not None or cur_dev is not None:
base_dev = sel_dev if sel_dev is not None else (base_devices if base_devices is not None else cur_dev)
- extra = f" + {cur_dev - base_dev} (докуплено)" if (base_dev is not None and cur_dev is not None and cur_dev > base_dev) else ""
+ extra = (
+ f" + {cur_dev - base_dev} (докуплено)"
+ if (base_dev is not None and cur_dev is not None and cur_dev > base_dev)
+ else ""
+ )
devices_line = f"📱 Устройства: {base_dev}{extra}\n"
sel_traf, cur_traf = key_obj.selected_traffic_limit, key_obj.current_traffic_limit
if sel_traf is not None or cur_traf is not None:
base_traf = sel_traf if sel_traf is not None else (base_traffic if base_traffic is not None else cur_traf)
- extra = f" + {cur_traf - base_traf} ГБ (докуплено)" if (base_traf is not None and cur_traf is not None and cur_traf > base_traf) else ""
+ extra = (
+ f" + {cur_traf - base_traf} ГБ (докуплено)"
+ if (base_traf is not None and cur_traf is not None and cur_traf > base_traf)
+ else ""
+ )
traffic_line = f"📊 Трафик: {base_traf} ГБ{extra}\n"
text = (
diff --git a/handlers/keys/key_create.py b/handlers/keys/key_create.py
index 63fed615..c3f2dc9b 100644
--- a/handlers/keys/key_create.py
+++ b/handlers/keys/key_create.py
@@ -42,11 +42,11 @@ from hooks.processors import (
process_purchase_tariff_group_override,
process_tariff_menu,
)
-from .utils import add_tariff_button_generic
from logger import logger
from .key_mode.key_cluster_mode import key_cluster_mode
from .key_mode.key_country_mode import key_country_mode
+from .utils import add_tariff_button_generic
router = Router()
diff --git a/handlers/keys/key_mode/key_country_mode.py b/handlers/keys/key_mode/key_country_mode.py
index c4ec1a52..2372c2a0 100644
--- a/handlers/keys/key_mode/key_country_mode.py
+++ b/handlers/keys/key_mode/key_country_mode.py
@@ -95,9 +95,7 @@ async def key_country_mode(
if state and plan:
await state.update_data(tariff_id=plan)
- if state and any(
- value is not None for value in (selected_device_limit, selected_traffic_gb, selected_price_rub)
- ):
+ if state and any(value is not None for value in (selected_device_limit, selected_traffic_gb, selected_price_rub)):
data = await state.get_data()
if selected_device_limit is not None:
data["config_selected_device_limit"] = selected_device_limit
diff --git a/handlers/keys/key_renew.py b/handlers/keys/key_renew.py
index 90390c9e..a480c771 100644
--- a/handlers/keys/key_renew.py
+++ b/handlers/keys/key_renew.py
@@ -53,9 +53,10 @@ from hooks.processors import (
process_renewal_complete,
process_renewal_forbidden_groups,
)
-from .utils import add_tariff_button_generic
from logger import logger
+from .utils import add_tariff_button_generic
+
router = Router()
moscow_tz = pytz.timezone("Europe/Moscow")
diff --git a/handlers/keys/operations/renewal.py b/handlers/keys/operations/renewal.py
index 675332a3..205f3a5f 100644
--- a/handlers/keys/operations/renewal.py
+++ b/handlers/keys/operations/renewal.py
@@ -10,10 +10,10 @@ from database import (
filter_cluster_by_subgroup,
get_key_details,
get_servers,
+ get_tariff_by_id,
resolve_device_limit_from_group,
update_key_expiry,
update_key_link,
- get_tariff_by_id,
)
from hooks.processors import process_get_cryptolink_after_renewal
from logger import (
@@ -57,7 +57,6 @@ async def renew_on_remnawave(
target_server_name: str | None = None,
external_squad_uuid: str | None = None,
) -> bool:
- """Продлевает подписку на Remnawave-узлах кластера."""
remnawave_nodes = [
s for s in cluster if str(s.get("panel_type", "3x-ui")).lower() == "remnawave" and s.get("inbound_id")
]
@@ -67,23 +66,24 @@ async def renew_on_remnawave(
remnawave_nodes = [s for s in remnawave_nodes if s.get("server_name") == target_server_name] or remnawave_nodes[
:1
]
+
remna = RemnawaveAPI(remnawave_nodes[0]["api_url"])
if not await remna.login(REMNAWAVE_LOGIN, REMNAWAVE_PASSWORD):
logger.error(f"{PANEL_REMNA} Не удалось войти в Remnawave API")
return False
+
expire_iso = datetime.utcfromtimestamp(new_expiry_time // 1000).isoformat() + "Z"
traffic_limit_bytes = total_gb * 1024 * 1024 * 1024 if total_gb else 0
active_inbounds = [s["inbound_id"] for s in remnawave_nodes]
- update_kwargs = dict(
- uuid=client_id,
- expire_at=expire_iso,
- active_user_inbounds=active_inbounds,
- traffic_limit_bytes=traffic_limit_bytes,
- hwid_device_limit=hwid_device_limit,
- )
- if external_squad_uuid:
- update_kwargs["external_squad_uuid"] = external_squad_uuid
+ update_kwargs = {
+ "uuid": client_id,
+ "expire_at": expire_iso,
+ "active_user_inbounds": active_inbounds,
+ "traffic_limit_bytes": traffic_limit_bytes,
+ "hwid_device_limit": hwid_device_limit,
+ "external_squad_uuid": external_squad_uuid,
+ }
updated = await remna.update_user(**update_kwargs)
if updated:
@@ -222,7 +222,11 @@ async def renew_key_in_cluster(
if plan is not None:
tariff = await get_tariff_by_id(session, plan)
if tariff:
- external_squad_uuid = tariff.get("external_squad")
+ raw_external_squad = tariff.get("external_squad")
+ if raw_external_squad:
+ external_squad_uuid = raw_external_squad
+ else:
+ external_squad_uuid = ""
is_configurable = tariff.get("configurable", False)
if not is_configurable:
tariff_device_limit = tariff.get("device_limit")
@@ -247,6 +251,7 @@ async def renew_key_in_cluster(
reset_traffic=reset_traffic,
old_subgroup=old_subgroup,
target_subgroup=target_subgroup,
+ external_squad_uuid=external_squad_uuid,
)
await update_key_expiry(session, new_client_id or client_id, new_expiry_time)
diff --git a/handlers/keys/operations/subgroup_migration.py b/handlers/keys/operations/subgroup_migration.py
index ac6b37ed..ed8ca01c 100644
--- a/handlers/keys/operations/subgroup_migration.py
+++ b/handlers/keys/operations/subgroup_migration.py
@@ -29,6 +29,7 @@ async def ensure_on_remnawave(
hwid_device_limit: int,
reset_traffic: bool,
attempt_update_first: bool,
+ external_squad_uuid: str | None = None,
) -> tuple[str | None, str | None]:
if not servers:
return None, None
@@ -74,6 +75,7 @@ async def ensure_on_remnawave(
active_user_inbounds=inbounds,
traffic_limit_bytes=traffic_bytes,
hwid_device_limit=hwid_device_limit,
+ external_squad_uuid=external_squad_uuid,
)
if not updated:
return None, None
@@ -113,6 +115,8 @@ async def ensure_on_remnawave(
if traffic_bytes > 0:
payload["trafficLimitBytes"] = traffic_bytes
payload["hwidDeviceLimit"] = hwid_device_limit
+ if external_squad_uuid is not None:
+ payload["externalSquadUuid"] = external_squad_uuid or None
created = await api.create_user(payload)
if not created:
@@ -237,6 +241,7 @@ async def migrate_between_subgroups(
reset_traffic: bool,
old_subgroup: str,
target_subgroup: str,
+ external_squad_uuid: str | None = None,
) -> tuple[str, str | None]:
target = await filter_cluster_by_subgroup(session, cluster_all, target_subgroup, cluster_id)
xui_tgt, remna_tgt = split_by_panel(target)
@@ -293,6 +298,7 @@ async def migrate_between_subgroups(
hwid_device_limit=hwid_device_limit,
reset_traffic=reset_traffic,
attempt_update_first=was_on_remna_before,
+ external_squad_uuid=external_squad_uuid,
)
if remna_old_non:
await delete_on_remnawave(remna_old_non, client_id)
@@ -315,6 +321,7 @@ async def migrate_between_subgroups(
hwid_device_limit=hwid_device_limit,
reset_traffic=reset_traffic,
attempt_update_first=was_on_remna_before,
+ external_squad_uuid=external_squad_uuid,
)
if remna_old_non:
diff --git a/hooks/constants.py b/hooks/constants.py
index 699e01a3..08d99803 100644
--- a/hooks/constants.py
+++ b/hooks/constants.py
@@ -1 +1 @@
-DEFAULT_HOOK_TIMEOUT = 4.0
\ No newline at end of file
+DEFAULT_HOOK_TIMEOUT = 4.0
diff --git a/hooks/hooks.py b/hooks/hooks.py
index 40a7cbb9..9aff042d 100644
--- a/hooks/hooks.py
+++ b/hooks/hooks.py
@@ -49,6 +49,7 @@ async def run_hooks(name: str, require_enabled: bool = True, **kwargs) -> list[A
if require_enabled and owner:
try:
from utils.modules_manager import manager
+
if not manager.is_enabled(owner):
continue
except Exception:
@@ -57,14 +58,16 @@ async def run_hooks(name: str, require_enabled: bool = True, **kwargs) -> list[A
if inspect.iscoroutinefunction(func):
coro = func(**kwargs)
else:
+
async def _run_sync():
return func(**kwargs)
+
coro = _run_sync()
result = await asyncio.wait_for(coro, timeout=DEFAULT_HOOK_TIMEOUT)
if result:
results.append(result)
- except asyncio.TimeoutError:
+ except TimeoutError:
logger.error(
f"[HOOK:{name}] Таймаут в {getattr(func, '__name__', func)} при timeout={DEFAULT_HOOK_TIMEOUT}",
exc_info=True,
@@ -74,4 +77,4 @@ async def run_hooks(name: str, require_enabled: bool = True, **kwargs) -> list[A
f"[HOOK:{name}] Ошибка в {getattr(func, '__name__', func)}: {e}",
exc_info=True,
)
- return results
\ No newline at end of file
+ return results
diff --git a/panels/remnawave.cpython-312-x86_64-linux-gnu.so b/panels/remnawave.cpython-312-x86_64-linux-gnu.so
index e4341e00..f54c8d0f 100644
Binary files a/panels/remnawave.cpython-312-x86_64-linux-gnu.so and b/panels/remnawave.cpython-312-x86_64-linux-gnu.so differ