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